Arrays 在MATLAB中,何时使用bsxfun是最佳选择?
我注意到,很多关于堆栈溢出的MATLAB问题的好答案经常使用函数Arrays 在MATLAB中,何时使用bsxfun是最佳选择?,arrays,matlab,bsxfun,Arrays,Matlab,Bsxfun,我注意到,很多关于堆栈溢出的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
。为什么?
动机:在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)将函数handle fun指定的逐元素二进制操作应用于数组A和B,并启用单例扩展。”。短语“启用单例扩展”是什么意思?非常有趣的问题!我最近在回答这个问题时,恰好遇到了这种情况。考虑下面的代码,通过向量<代码> < <代码> > < /P>计算大小为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
允许您这样做:在对两个向量的所有元素对应用任意运算符的基础上创建矩阵,而不是对通过复制向量获得的两个矩阵进行显式运算。这就是单例扩展。您也可以将其视为外部产品,来自:
将两个向量相乘得到一个矩阵。只是外积只执行乘法,bsxfun
可以应用任意运算符。作为旁注,非常有趣的是看到bsxfun
与BLAS外部产品一样快。BLAS通常被认为提供了性能
多亏了Dan的评论,这里有一个很好的讨论。我使用bsxfun的原因有三个(,)
bsxfun
比repmat
快(见下文)bsxfun
需要更少的键入bsxfun
,就像使用accumarray
,使我对MATLAB的理解感觉良好bsxfun
将沿着其“单一维度”(即数组大小为1的维度)复制输入数组,以便它们与另一个数组的相应维度的大小匹配。这就是所谓的“我”。另一方面,如果调用挤压
,将删除单例维度
对于非常小的问题,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
在我的例子中,我使用了
bsxfun
,因为它避免了我考虑列或行的问题
为了编写您的示例:
A = A - (ones(size(A, 1), 1) * mean(A));
我必须解决几个问题:
尺寸(A,1)
或尺寸(A,2)
one(尺寸(A,1),1)
或one(1,尺寸(A,1))
one(尺寸(A,1),1)*平均值(A)
或平均值(A)*one(尺寸(A,1),1)
平均值(A)
或平均值(A,2)
bsxfun
时,我只需要解决最后一个问题:
a) 平均值(a)
或平均值(a,2)
您可能会认为它很懒或是什么,但是当我使用bsxfun
时,我的bug更少了,编程速度也更快了
此外,它较短,从而提高了键入速度和可读性事情并不总是与三种常用方法一致:
repmat
、逐项扩展索引和bsxfun
。当进一步增加向量大小时,它会变得更有趣。见图表:
bsxfun
实际上在某一点上会比其他两个稍微慢一点,但让我吃惊的是,如果向量大小增加更多(>13E6个输出元素),bsxfun会突然变快3倍左右。他们的速度似乎一步一步地加快,而且顺序并不总是一致的。我的猜测是,它也可能取决于处理器/内存大小,但通常我认为只要有可能,我会坚持使用bsxfun
。从R2016b开始,MATLAB支持多种运算符,因此在大多数情况下不再需要使用bsxfun
A = A - (ones(size(A, 1), 1) * mean(A));