我的问题:我注意到很多关于SO的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)应用由函数句柄乐趣指定的逐个元素的二元运算到阵列A和B,启用单例扩展。“短语“启用单例扩展”是什么意思?
答案 0 :(得分:151)
我使用bsxfun
有三个原因(documentation,blog link)
bsxfun
比repmat
(见下文)bsxfun
需要更少的输入bsxfun
,就像使用accumarray
一样,让我对Matlab的理解感觉良好。 bsxfun
将沿着"单例维度"复制输入数组,即数组大小为1的维度,以便它们匹配相应维度的大小。其他阵列。这就是所谓的"单身人士逃亡"。另外,如果你拨打squeeze
,单身尺寸就会被删除。
对于非常小的问题,repmat
方法可能更快 - 但是在该阵列大小的情况下,两种操作都非常快,以至于它可能在整体性能方面没有任何差别。 bsxfun
更快有两个重要原因:(1)计算发生在编译代码中,这意味着数组的实际复制永远不会发生,并且(2)bsxfun
是多线程Matlab之一功能
我在速度相当快的笔记本电脑上使用R2012b对repmat
和bsxfun
进行了速度比较。
对我来说,bsxfun
比repmat
快3倍左右。如果阵列变大,差异会变得更明显
repmat
的运行时跳转发生在1Mb的数组大小周围,这可能与我的处理器缓存的大小有关 - 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 :(得分:40)
在我的情况下,我使用bsxfun
因为它避免让我考虑列或行问题。
为了写下你的例子:
A = A - (ones(size(A, 1), 1) * mean(A));
我必须解决几个问题:
1)size(A,1)
或size(A,2)
2)ones(sizes(A,1),1)
或ones(1,sizes(A,1))
3)ones(size(A, 1), 1) * mean(A)
或mean(A)*ones(size(A, 1), 1)
4)mean(A)
或mean(A,2)
当我使用bsxfun
时,我只需要解决最后一个问题:
a)mean(A)
或mean(A,2)
你可能认为它是懒惰的东西,但是当我使用bsxfun
时,我有更少的错误而我编程更快。
此外,它更短,提高了打字速度和可读性。
答案 2 :(得分:16)
非常有趣的问题!我最近在回答this问题时偶然发现了这种情况。考虑以下代码,通过向量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正在讨论这个问题。
答案 3 :(得分:12)
自R2016b起,Matlab支持Implicit Expansion用于各种运算符,因此在大多数情况下不再需要使用bsxfun
:
以前,此功能可通过
bsxfun
功能获得。 现在建议您直接替换bsxfun
的大部分用法 调用支持隐式扩展的函数和运算符。 与使用bsxfun
相比,隐式扩展提供更快的速度, 更好的内存使用,提高了代码的可读性。
Implicit Expansion detailed discussion及其在Loren博客上的表现。来自MathWorks的quote Steve Eddins:
在R2016b中,在大多数情况下,隐式扩展的工作速度比
bsxfun
快或快。 隐式扩展的最佳性能增益是小矩阵和数组大小。对于大矩阵大小,隐式扩展往往与bsxfun
大致相同。
答案 4 :(得分:8)
事情并不总是与3种常见方法一致:repmat
,按索引扩展和bsxfun
。当你进一步增加矢量大小时,它会变得更加有趣。见情节:
bsxfun
实际上在某些时候变得比其他两个稍慢,但令我惊讶的是,如果你更多地增加矢量大小(> 13E6输出元素),bsxfun会突然变得更快约3倍。他们的速度似乎逐步跳跃,顺序并不总是一致的。我的猜测也可能是处理器/内存大小也有所不同,但一般来说我认为我会尽可能坚持使用bsxfun
。