在 MATLAB 中,啥时候最适合使用 bsxfun?
Posted
技术标签:
【中文标题】在 MATLAB 中,啥时候最适合使用 bsxfun?【英文标题】:In MATLAB, when is it optimal to use bsxfun?在 MATLAB 中,什么时候最适合使用 bsxfun? 【发布时间】:2012-10-08 17:20:18 【问题描述】:我注意到很多关于 *** 的 MATLAB 问题的好答案经常使用函数 bsxfun
。为什么?
动机:在bsxfun
的 MATLAB 文档中,提供了以下示例:
A = magic(5);
A = bsxfun(@minus, A, mean(A))
当然我们也可以使用:
A = A - (ones(size(A, 1), 1) * mean(A));
事实上,一个简单的速度测试表明第二种方法快了大约 20%。那么为什么要使用第一种方法呢?我猜在某些情况下使用bsxfun
会比“手动”方法快得多。我真的很想看到这种情况的示例以及为什么它更快的解释。
此外,这个问题的最后一个元素,同样来自bsxfun
的 MATLAB 文档:“C = bsxfun(fun,A,B) 将函数句柄 fun 指定的逐元素二元运算应用于数组A 和 B,启用了单例扩展。”。 “启用单例扩展”是什么意思?
【问题讨论】:
请注意,您获得的速度读数取决于您执行的测试。如果你在重启 Matlab 后运行上面的代码并简单地将tic...toc
放在行周围,代码的速度将取决于是否必须将函数读入内存。
@Jonas 是的,我刚刚通过阅读您/angainor/Dan 提供的链接中的timeit
函数了解了这一点。
请注意,现在(自 R16b 起),bsxfun
已被隐式扩展取代,请参阅 Is bsxfun still optimal in MATLAB?
【参考方案1】:
从 R2016b 开始,MATLAB 支持多种运算符的Implicit Expansion,因此在大多数情况下不再需要使用bsxfun
:
以前,此功能可通过
bsxfun
函数使用。 现在建议您将bsxfun
的大多数用法替换为直接 调用支持隐式扩展的函数和运算符。 与使用bsxfun
相比,隐式扩展提供了更快的速度, 更好的内存使用率,以及提高代码的可读性。
在 Loren 的博客上有 Implicit Expansion 的 detailed discussion 及其性能。来自MathWorks的quote Steve Eddins:
在 R2016b 中,隐式扩展在大多数情况下与
bsxfun
一样快或更快。 隐式扩展的最佳性能增益是使用较小的矩阵和数组大小。对于大矩阵大小,隐式扩展的速度往往与bsxfun
大致相同。
【讨论】:
【参考方案2】:就我而言,我使用bsxfun
,因为它避免了我去思考列或行的问题。
为了写你的例子:
A = A - (ones(size(A, 1), 1) * mean(A));
我必须解决几个问题:
size(A,1)
或 size(A,2)
ones(sizes(A,1),1)
或 ones(1,sizes(A,1))
ones(size(A, 1), 1) * mean(A)
或 mean(A)*ones(size(A, 1), 1)
mean(A)
或 mean(A,2)
当我使用bsxfun
时,我只需要解决最后一个:
a) mean(A)
或 mean(A,2)
你可能会认为它很懒惰,但是当我使用bsxfun
时,我错误更少并且我编程速度更快。
而且,它更短,提高了打字速度和可读性。
【讨论】:
感谢奥利的回复。 +1,因为我认为除了 angainor 和 Jonas 的回应之外,这个答案还做出了贡献。我特别喜欢你在给定的代码行中列出需要解决的概念问题数量的方式。【参考方案3】:非常有趣的问题!我最近在回答this question 时偶然发现了这种情况。考虑以下代码,它通过向量a
计算大小为 3 的滑动窗口的索引:
a = rand(1e7, 1);
tic;
idx = bsxfun(@plus, [0:2]', 1:numel(a)-2);
toc
% Equivalent code from im2col function in MATLAB
tic;
idx0 = repmat([0:2]', 1, numel(a)-2);
idx1 = repmat(1:numel(a)-2, 3, 1);
idx2 = idx0+idx1;
toc;
isequal(idx, idx2)
Elapsed time is 0.297987 seconds.
Elapsed time is 0.501047 seconds.
ans =
1
在这种情况下bsxfun
几乎快两倍!它非常有用且快速,因为它避免显式分配内存矩阵idx0
和idx1
,将它们保存到内存中,然后再次读取它们以添加它们。由于内存带宽是一项宝贵的资产,并且通常是当今架构的瓶颈,因此您希望明智地使用它并减少代码的内存需求以提高性能。
bsxfun
允许您这样做:基于将任意运算符应用于两个向量的所有元素对来创建矩阵,而不是显式地对通过复制向量获得的两个矩阵进行运算。那就是单例扩展。您也可以将其视为BLAS 的外部产品:
v1=[0:2]';
v2 = 1:numel(a)-2;
tic;
vout = v1*v2;
toc
Elapsed time is 0.309763 seconds.
你将两个向量相乘得到一个矩阵。只是外积只执行乘法,bsxfun
可以应用任意运算符。附带说明一下,很有趣的是,bsxfun
与 BLAS 外部产品一样快。并且 BLAS 通常被认为可以提供 性能...
感谢 Dan 的评论,这里有一个很棒的 article by Loren 正在讨论这个问题。
【讨论】:
这篇文章可能是相关的:blogs.mathworks.com/loren/2008/08/04/… @Dan 感谢您的精彩参考。 感谢您的出色回应。 +1 是第一个以一个很好的例子明确说明bsxfun
的主要优势的人。【参考方案4】:
我使用bsxfun
的三个原因(documentation,blog link)
bsxfun
比 repmat
快(见下文)
bsxfun
需要更少的输入
使用bsxfun
,就像使用accumarray
一样,让我对自己对MATLAB的理解感觉良好。
bsxfun
将沿其“单一维度”复制输入数组,即数组大小为 1 的维度,以便它们与另一个数组的相应维度的大小相匹配。这就是所谓的“singleton expansion”。顺便说一句,如果您调用 squeeze
,单例维度将被删除。
对于非常小的问题,repmat
方法可能更快 - 但在该数组大小下,这两个操作都非常快,以至于它可能不会对整体性能产生任何影响。 bsxfun
更快有两个重要原因:(1)计算发生在编译代码中,这意味着数组的实际复制永远不会发生,以及(2)bsxfun
是多线程 MATLAB 函数之一。
我在我相当快的笔记本电脑上使用 MATLAB R2012b 对 repmat
和 bsxfun
进行了速度比较。
对我来说,bsxfun
比 repmat
快大约三倍。如果数组变大,差异会变得更加明显:
repmat
在运行时的跳转发生在 1 MB 的数组大小附近,这可能与我的处理器缓存的大小有关 - bsxfun
的跳转并没有那么糟糕,因为它只需要分配输出数组。
您可以在下面找到我用于计时的代码:
n = 300;
k=1; %# k=100 for the second graph
a = ones(10,1);
rr = zeros(n,1);
bb = zeros(n,1);
ntt = 100;
tt = zeros(ntt,1);
for i=1:n;
r = rand(1,i*k);
for it=1:ntt;
tic,
x = bsxfun(@plus,a,r);
tt(it) = toc;
end;
bb(i) = median(tt);
for it=1:ntt;
tic,
y = repmat(a,1,i*k) + repmat(r,10,1);
tt(it) = toc;
end;
rr(i) = median(tt);
end
【讨论】:
感谢您的出色回复 +1。我已将此标记为答案,因为它是最全面的讨论,并且(此时)也获得了最多的支持。【参考方案5】:事情并不总是与 3 种常用方法一致:repmat
、按索引扩展和bsxfun
。当您进一步增加矢量大小时,它会变得更加有趣。见情节:
bsxfun
实际上在某些时候变得比其他两个稍慢,但令我惊讶的是,如果你进一步增加向量大小(>13E6 个输出元素),bsxfun 突然又变得快了大约 3 倍。他们的速度似乎在逐步跳跃,并且顺序并不总是一致的。我的猜测是它也可能取决于处理器/内存大小,但通常我认为我会尽可能坚持使用bsxfun
。
【讨论】:
以上是关于在 MATLAB 中,啥时候最适合使用 bsxfun?的主要内容,如果未能解决你的问题,请参考以下文章
MATLAB中的\ite是啥?啥情况下要用括起来,啥时候不要?