在Matlab中,什么时候使用bsxfun最合适?

我的问题:我注意到很多关于SO的Matlab问题的很好的答案经常使用函数bsxfun 。 为什么?

动机:bsxfun的Matlab文档中,提供了以下示例:

 A = magic(5); A = bsxfun(@minus, A, mean(A)) 

当然,我们可以使用以下方法进行相同的操作

 A = A - (ones(size(A, 1), 1) * mean(A)); 

而实际上一个简单的速度testing表明第二种方法快了大约20%。 那为什么要用第一种方法? 我猜测在某些情况下,使用bsxfun比“手动”的方法要快得多。 我真的很想看到这样的情况的例子,并解释为什么它更快。

此外,对于这个问题的最后一个元素,同样来自bsxfun的Matlab文档:“C = bsxfun(fun,A,B)将函数handle fun指定的逐个元素的二进制操作应用于数组A和B,单身扩展启用“。 “单启扩展”是什么意思?

有三个原因我使用bsxfun ( 文档 , 博客链接 )

  1. bsxfunrepmat快(见下文)
  2. bsxfun需要更less的input
  3. 使用bsxfun ,就像使用accumarray ,使我对Matlab的理解感觉很好。

bsxfun将复制input数组沿着它们的“单一维度”,也就是说数组的大小是1的维度,以便它们匹配另一个数组的相应维度的大小。 这就是所谓的“单身扩张”。 顺便说一下,如果你叫squeeze ,那么单体维度就是那个下降的维度。

对于非常小的问题, repmat方法可能会更快 – 但是在这个arrays尺寸下,两个操作都非常快,以至于在整体性能方面可能没有任何区别。 bsxfun的速度有两个重要的原因:(1)计算发生在编译代码中,这意味着数组的实际复制不会发生,(2) bsxfun是multithreadingMatlab函数之一。

我已经在我体面快速的笔记本电脑上用R2012b运行了repmatbsxfun之间的速度比较。

在这里输入图像描述

对我来说, bsxfunrepmat快3倍。 如果arrays变大,差异就会变得更加明显

在这里输入图像描述

repmat运行时间跳转发生在1Mb的数组大小,这可能与我的处理器caching的大小有关 – bsxfun并没有像跳转那样坏,因为它只需要分配输出数组。

下面你可以find我用于计时的代码:

 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)); 

我必须解决几个问题:

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 ,我有更less的错误 ,我编程更快

此外,它更短,这提高了打字速度可读性

非常有趣的问题! 我最近在回答这个问题时偶然发现了这种情况。 考虑以下通过向量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几乎快了一倍! 这是有用的和快速的,因为它避免了matrixidx0idx1 的显存分配 ,将它们保存到内存中,然后再次读取它们以添加它们。 由于内存带宽是宝贵的资产,通常是今天体系结构的瓶颈,所以要明智地使用内存带宽,并降低代码的内存要求以提高性能。

bsxfun允许你做到这一点:创build一个matrix的基础上,应用一个任意的运算符到两个向量的所有元素对上,而不是明确地操作两个通过复制向量获得的matrix。 这是单身扩张 。 您也可以将其视为BLAS的外部产品

 v1=[0:2]'; v2 = 1:numel(a)-2; tic; vout = v1*v2; toc Elapsed time is 0.309763 seconds. 

你乘两个向量来获得一个matrix。 只是外部产品只能执行乘法,而bsxfun可以应用任意的操作符。 作为一个侧面说明,看到bsxfun和BLAS外部产品一样快,这是非常有趣的。 BLAS通常被认为是提供性能。

编辑感谢丹的评论,这是一个伟大的文章由罗兰讨论到这一点。

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

以前,这个function可以通过bsxfun函数获得。 现在build议您将bsxfun大部分用法直接调用到支持隐式扩展的函数和运算符。 与使用bsxfun相比, 隐式扩展提供了更快的速度更好的内存使用 ,并提高了代码的可读性

在Loren的博客上有关于 Implicit Expansion及其performance的详细讨论 。 引用 MathWorks的Steve Eddins:

在R2016b中, 隐式扩展在大多数情况下比bsxfun快或者更快。 隐式扩展的最佳性能收益是小matrix和数组大小。 对于大的matrix大小,隐式扩展往往与bsxfun速度大致相同。

事情并不总是与3种常用的方法一致: repmatbsxfun的索引和bsxfun 。 当你进一步增加vector大小时,它会变得更有趣。 看情节:

对照

bsxfun实际上比其他两个稍微慢一些,但是让我吃惊的是,如果你增加vector的大小(> 13E6输出元素),bsxfun会突然再次快速增加大约3倍。 他们的速度似乎在跳跃,顺序并不总是一致的。 我的猜测是它可能是处理器/内存大小,但通常我认为我会尽可能坚持bsxfun