在Matlab中,何时使用bsxfun是最佳的?

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(文档,博客链接)

  1. bsxfun快于repmat(见下文)
  2. bsxfun 需要更少的打字
  3. 使用bsxfun,如使用accumarray,让我感觉良好,我的Matlab的理解.

bsxfun将沿着它们的"单一维度"复制输入数组,即数组大小为1的维度,以便它们匹配另一个数组的相应维度的大小.这就是所谓的"单身人士逃亡".另外,如果你打电话,单身尺寸就会被丢弃squeeze.

对于非常小的问题,这种repmat方法可能更快 - 但是在该阵列大小的情况下,两种操作都非常快,以至于在整体性能方面可能没有任何差别.有两个重要原因bsxfun是更快:(1)计算发生在编译代码中,这意味着数组的实际复制永远不会发生,并且(2)bsxfun是多线程Matlab函数之一.

我已经在我的体面的笔记本电脑上repmatbsxfunR2012b 之间进行了速度比较.

在此输入图像描述

对我来说,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正在讨论的一篇很棒的文章.

  • 本文可能是相关的:http://blogs.mathworks.com/loren/2008/08/04/comparing-repmat-and-bsxfun-performance/ (7认同)

nir*_*msu 12

从R2016b开始,Matlab支持各种运算符的隐式扩展,因此在大多数情况下不再需要使用bsxfun:

以前,此功能可通过该bsxfun功能获得.现在建议您替换大多数使用bsxfun直接调用支持隐式扩展的函数和运算符.与使用相比bsxfun,隐式扩展提供更快的速度, 更好的内存使用更高的代码可读性.

有关隐式扩展及其在Loren博客上的表现的详细讨论.要引用来自MathWorks的史蒂夫Eddins:

在R2016b中,隐式扩展的工作速度比bsxfun大多数情况下快或快.隐式扩展的最佳性能增益是小矩阵和数组大小.对于大矩阵大小,隐式扩展往往与大致相同bsxfun.


小智 8

事情并不总是与3种常见方法一致:repmat通过索引进行扩展,以及bsxfun.当你进一步增加矢量大小时,它会变得更加有趣.见情节:

对照

bsxfun实际上变得比其他两个在某些时候稍微慢一些,但让我吃惊是,如果你约3倍再次增长较快的矢量大小更(> 13E6输出元素),bsxfun突然变.他们的速度似乎逐步跳跃,顺序并不总是一致的.我的猜测也可能是处理器/内存大小也有所不同,但一般来说我认为我会bsxfun尽可能地坚持下去.