我注意到很多关于堆栈和溢出的MATLAB问题的好答案经常使用函数bsxfun
。为什么?
动机:在bsxfun
的MATLAB文档中,提供了以下示例:
A = magic(5);
A = bsxfun(@minus, A, mean(A))
当然,我们可以使用以下命令执行相同的操作:
A = A - (ones(size(A, 1), 1) * mean(A));
事实上,一个简单的速度测试表明,第二种方法大约快20%。那么为什么要使用第一种方法呢?我猜在某些情况下,使用bsxfun
会比使用“手动”方法快得多。我真的很有兴趣看到这种情况的例子,并解释为什么它更快。
同样,这个问题的最后一个元素,同样来自MATLAB的bsxfun
文档:"C = bsxfun( fun,A,B)将由函数句柄fun指定的逐个元素的二进制操作应用于数组A和B,并启用单例扩展。“短语“启用单例扩展”是什么意思?
发布于 2012-10-18 21:19:42
我使用bsxfun
有三个原因(documentation、blog link)
bsxfun
比repmat
更快(参见below)bsxfun
需要更少的输入bsxfun
,就像使用accumarray
一样,这让我对自己对MATLAB的理解感觉很好。bsxfun
将沿着输入数组的“单一维数”(即数组大小为1的维数)复制输入数组,以便它们与其他数组的相应维数的大小相匹配。这就是所谓的"singleton expansion“。顺便说一句,如果调用squeeze
,单例维度将被删除。
对于非常小的问题,repmat
方法可能会更快-但在这种数组大小下,这两种操作都非常快,可能不会对整体性能产生任何影响。bsxfun
速度更快有两个重要原因:(1)计算发生在编译代码中,这意味着实际的数组复制永远不会发生,(2) bsxfun
是多线程MATLAB函数之一。
我已经在我相当快的笔记本电脑上用MATLAB R2012b进行了repmat
和bsxfun
之间的速度比较。
对我来说,bsxfun
大约比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
发布于 2012-10-19 03:54:27
在我的例子中,我使用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
时,我有更少的bugs和我更快的。
此外,它更短,这提高了和readability.的键入速度
发布于 2012-10-18 17:45:30
非常有趣的问题!我最近在回答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通常被认为可以为提供性能...
多亏了丹的评论,这里有一个很棒的article by Loren讨论了这一点。
https://stackoverflow.com/questions/12951453
复制相似问题