Col*_*ers 132 arrays matlab bsxfun
我的问题:我注意到很多关于SO的Matlab问题的好答案经常使用这个功能bsxfun
.为什么?
动机:在Matlab文档中bsxfun
,提供了以下示例:
A = magic(5);
A = bsxfun(@minus, A, mean(A))
Run Code Online (Sandbox Code Playgroud)
当然我们可以使用以下方法执行相同的操作:
A = A - (ones(size(A, 1), 1) * mean(A));
Run Code Online (Sandbox Code Playgroud)
事实上,简单的速度测试表明第二种方法的速度提高了约20%.那么为什么要使用第一种方法?我猜测在某些情况下使用bsxfun
将比"手动"方法快得多.我真的很想看到这种情况的一个例子,并解释为什么它更快.
此外,这个问题的最后一个元素,再次来自Matlab文档bsxfun
:"C = bsxfun(fun,A,B)将函数句柄fun指定的逐元素二元运算应用于数组A和B,使用单例扩展已启用." 短语"启用单例扩展"是什么意思?
Jon*_*nas 152
bsxfun
快于repmat
(见下文)bsxfun
需要更少的打字bsxfun
,如使用accumarray
,让我感觉良好,我的Matlab的理解.bsxfun
将沿着它们的"单一维度"复制输入数组,即数组大小为1的维度,以便它们匹配另一个数组的相应维度的大小.这就是所谓的"单身人士逃亡".另外,如果你打电话,单身尺寸就会被丢弃squeeze
.
对于非常小的问题,这种repmat
方法可能更快 - 但是在该阵列大小的情况下,两种操作都非常快,以至于在整体性能方面可能没有任何差别.有两个重要原因bsxfun
是更快:(1)计算发生在编译代码中,这意味着数组的实际复制永远不会发生,并且(2)bsxfun
是多线程Matlab函数之一.
我已经在我的体面的笔记本电脑上repmat
和bsxfun
R2012b 之间进行了速度比较.
对我来说,bsxfun
快约3倍repmat
.如果阵列变大,差异会变得更明显
运行时的跳转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
Run Code Online (Sandbox Code Playgroud)
Oli*_*Oli 40
在我的情况下,我使用,bsxfun
因为它避免我考虑列或行问题.
为了写你的例子:
A = A - (ones(size(A, 1), 1) * mean(A));
Run Code Online (Sandbox Code Playgroud)
我必须解决几个问题:
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
,我有更少的错误,我编程更快.
而且,它更短,提高了打字速度和可读性.
ang*_*nor 16
非常有趣的问题!我最近在回答这个问题时偶然发现了这种情况.考虑以下代码,通过向量计算大小为3的滑动窗口的索引a
:
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
Run Code Online (Sandbox Code Playgroud)
在这种情况下bsxfun
几乎快两倍!它非常有用且快速,因为它避免了为矩阵显式分配内存idx0
并将idx1
它们保存到内存中,然后再次读取它们以添加它们.由于内存带宽是宝贵的资产,并且通常是当今架构的瓶颈,因此您希望明智地使用它并降低代码的内存需求以提高性能.
bsxfun
允许你这样做:基于将任意运算符应用于两个向量的所有元素对而不是通过复制向量获得的两个矩阵上显式操作来创建矩阵.这是单身扩张.您也可以将其视为BLAS 的外部产品:
v1=[0:2]';
v2 = 1:numel(a)-2;
tic;
vout = v1*v2;
toc
Elapsed time is 0.309763 seconds.
Run Code Online (Sandbox Code Playgroud)
您将两个向量相乘以获得矩阵.只是外部产品只执行乘法,并且bsxfun
可以应用任意运算符.作为旁注,看到它bsxfun
与BLAS外部产品一样快,这是非常有趣的.而BLAS通常被认为提供的性能..
编辑感谢Dan的评论,这是Loren正在讨论的一篇很棒的文章.
小智 8
事情并不总是与3种常见方法一致:repmat
通过索引进行扩展,以及bsxfun
.当你进一步增加矢量大小时,它会变得更加有趣.见情节:
bsxfun
实际上变得比其他两个在某些时候稍微慢一些,但让我吃惊是,如果你约3倍再次增长较快的矢量大小更(> 13E6输出元素),bsxfun突然变.他们的速度似乎逐步跳跃,顺序并不总是一致的.我的猜测也可能是处理器/内存大小也有所不同,但一般来说我认为我会bsxfun
尽可能地坚持下去.