比较BSXFUN和REPMAT

时间:2015-04-18 16:08:08

标签: performance matlab benchmarking vectorization bsxfun

之前就 bsxfun repmat 之间的性能比较提出了很少的问题。

  • 其中一个是:Matlab - bsxfun no longer faster than repmat?。这个尝试调查repmatbsxfun之间的性能比较,特别是在输入数组本身的列中执行减去输入数组的平均值,因此只会探索{@minusbsxfun 1}} repmat的一部分与其bsxfun等效。{/ li>
  • 另一个是:In Matlab, when is it optimal to use bsxfun?。那个人试图通过列中的平均值进行相同的减法操作,并且没有扩展到其他内置操作。

通过这篇文章,我正在尝试调查 repmat bsxfun 之间的效果数字,以涵盖所有 {{ 1}} 内置函数可以为它提供更广泛的视角,因为它们都提供了良好的矢量化解决方案。

具体来说,我对这篇文章的提问是:

  1. bsxfun 的各种内置操作如何针对 repmat 等效执行? bsxfun 支持@plus@minus@times等浮点运算,以及@ge等关系和逻辑运算, @and等等。那么,是否有特定的内置插件可以使用 bsxfun 比使用 repmat 给我带来明显的加速当量?

  2. Loren在她的blog post中针对 repmat bsxfun 进行了基准测试,时间@() A - repmat(mean(A),size(A,1),1)针对{分别为{1}}。如果我需要涵盖所有内置函数的基准测试,我是否可以使用其他可用于浮点,关系和逻辑运算的比较模型?

1 个答案:

答案 0 :(得分:43)

简介

关于bsxfun是否优于repmat或反之亦然的争论一直在进行。在这篇文章中,我们将尝试比较MATLAB附带的不同内置函数如何在运行时性能方面与repmat等价物进行对比,并希望从中得出一些有意义的结论。

了解BSXFUN内置插件

如果从MATLAB环境或Mathworks website中提取官方文档,可以看到bsxfun支持的完整内置函数列表。该列表具有浮点,关系和逻辑运算的功能。

MATLAB 2015A上,支持的逐元素浮点运算是:

  • @plus(求和)
  • @minus(减法)
  • @times(乘法)
  • @rdivide(右分)
  • @ldivide(left-divide)
  • @pow(power)
  • @rem(余数)
  • @mod(模数)
  • @ atan2(四象限反正切)
  • @ atan2d(四象限反正切度数)
  • @hypot(平方和的平方根)。

第二组由元素关系操作组成,它们是:

  • @eq(相等)
  • @ne(不等于)
  • @lt(小于)
  • @le(小于或等于)
  • @gt(大于)
  • @ge(大于或等于)。

第三组也是最后一组包括此处列出的逻辑操作:

  • @and(逻辑和)
  • @or(逻辑或)
  • @xor(logical xor)。

请注意,我们已从比较测试中排除了两个内置插件@max (maximum)@min (minimum),因为可以通过多种方式实现其repmat等效内容。

比较模型

要真实地比较repmatbsxfun之间的效果,我们需要确保时间只需要涵盖预期的操作。因此,像bsxfun(@minus,A,mean(A))这样的东西不会是理想的,因为它必须在mean(A)调用内计算bsxfun,但是时间可能无关紧要。相反,我们可以使用与B相同大小的其他输入mean(A)

因此,我们可以使用:A = rand(m,n)& B = rand(1,n),其中mn是我们可以改变的大小参数,并根据它们研究性能。这完全在我们下一节列出的基准测试中完成。

对这些输入进行操作的repmatbsxfun版本看起来像这样 -

REPMAT: A + repmat(B,size(A,1),1)
BSXFUN: bsxfun(@plus,A,B)

基准

最后,我们正处于这篇文章的关键时刻,看看这两个家伙是否正在努力解决这个问题。我们将基准测试分为三组,一组用于浮点运算,另一组用于关系,第三组用于逻辑运算。我们已经将前面讨论的比较模型扩展到所有这些操作。

Set1:浮点运算

这是第一套使用repmatbsxfun进行浮点运算的基准测试代码 -

datasizes = [ 100 100; 100 1000; 100 10000; 100 100000;
              1000 100; 1000 1000; 1000 10000;
              10000 100; 10000 1000; 10000 10000;
              100000 100; 100000 1000];

num_funcs = 11;
tsec_rep = NaN(size(datasizes,1),num_funcs);
tsec_bsx = NaN(size(datasizes,1),num_funcs);
for iter = 1:size(datasizes,1)
    m = datasizes(iter,1);
    n = datasizes(iter,2);
    A = rand(m,n);
    B = rand(1,n);

    fcns_rep= {@() A + repmat(B,size(A,1),1),@() A - repmat(B,size(A,1),1),...
        @() A .* repmat(B,size(A,1),1), @() A ./ repmat(B,size(A,1),1),...
        @() A.\repmat(B,size(A,1),1), @() A .^ repmat(B,size(A,1),1),...
        @() rem(A ,repmat(B,size(A,1),1)), @() mod(A,repmat(B,size(A,1),1)),...
        @() atan2(A,repmat(B,size(A,1),1)),@() atan2d(A,repmat(B,size(A,1),1)),...
        @() hypot( A , repmat(B,size(A,1),1) )};

    fcns_bsx = {@() bsxfun(@plus,A,B), @() bsxfun(@minus,A,B), ...
        @() bsxfun(@times,A,B),@() bsxfun(@rdivide,A,B),...
        @() bsxfun(@ldivide,A,B), @() bsxfun(@power,A,B), ...
        @() bsxfun(@rem,A,B), @() bsxfun(@mod,A,B), @() bsxfun(@atan2,A,B),...
        @() bsxfun(@atan2d,A,B), @() bsxfun(@hypot,A,B)};

    for k1 = 1:numel(fcns_bsx)
        tsec_rep(iter,k1) = timeit(fcns_rep{k1});
        tsec_bsx(iter,k1) = timeit(fcns_bsx{k1});
    end
end
speedups = tsec_rep./tsec_bsx;

Set2:关系操作

时间关系操作的基准测试代码会将早期基准测试代码中的fcns_repfcns_bsx替换为这些对应项 -

fcns_rep = {
    @() A == repmat(B,size(A,1),1), @() A ~= repmat(B,size(A,1),1),...
    @() A < repmat(B,size(A,1),1), @() A <= repmat(B,size(A,1),1), ...
    @() A > repmat(B,size(A,1),1), @() A >= repmat(B,size(A,1),1)};

fcns_bsx = {
    @() bsxfun(@eq,A,B), @() bsxfun(@ne,A,B), @() bsxfun(@lt,A,B),...
    @() bsxfun(@le,A,B), @() bsxfun(@gt,A,B), @() bsxfun(@ge,A,B)};

Set3:逻辑操作

最后一组基准测试代码将使用此处列出的逻辑操作 -

fcns_rep = {
    @() A & repmat(B,size(A,1),1), @() A | repmat(B,size(A,1),1), ...
    @() xor(A,repmat(B,size(A,1),1))};

fcns_bsx = {
    @() bsxfun(@and,A,B), @() bsxfun(@or,A,B), @() bsxfun(@xor,A,B)};

请注意,对于此特定集,所需的输入数据A和B是逻辑阵列。因此,我们必须在早期的基准测试代码中进行这些编辑以创建逻辑数组 -

A = rand(m,n)>0.5;
B = rand(1,n)>0.5;

运行时和观察

基准测试代码在此系统配置上运行:

MATLAB Version: 8.5.0.197613 (R2015a)
Operating System: Windows 7 Professional 64-bit
RAM: 16GB
CPU Model: Intel® Core i7-4790K @4.00GHz

在运行基准测试后,bsxfun超过repmat获得的加速比如下图所示为三组绘制。

:一种。浮点运算:

enter image description here

从加速图中可以得出很少的观察结果:

  • bsxfun atan2atan2d的两个优秀加速案例主要针对30% - 50%repmat
  • 该列表中的下一步是左右分频操作,与7等效代码相比,7执行提升。
  • 在该列表中进一步向下是剩余的@hypot操作,其加速似乎非常接近统一,因此需要进一步检查。加速图可以缩小到那些@mod操作,如下所示 -

enter image description here

根据上面的情节,我们可以看到,除了bsxfunrepmat7的一次性案例,bsxfun仍然比bsxfun大约好10% repmat次操作。

<强> B中。关系操作:

这是bsxfun支持的后续6个内置关系操作的第二组基准测试。

enter image description here

观察上面的加速图,忽略在10xbsxfun之间具有可比运行时间的起始情况,可以很容易地看到bsxfun赢得这些关系操作。加速 @xor 时,bsxfun总是更适合这些情况。

<强>℃。逻辑操作:

这是repmat支持的其余3个内置逻辑操作的第三组基准测试。

enter image description here

在开始时忽略bsxfun的一次性可比运行时案例,bsxfun似乎也对这组逻辑运算有优势。

结论

  1. 在处理关系和逻辑操作时,5 - 7%可能很容易被遗忘,而不是bsxfun。对于其他情况,如果可以容忍bsxfun性能较低的一个案例,则仍然可以使用ragged patterns。{/ li>
  2. bsxfun使用关系和逻辑操作时,看到了巨大的性能提升,可以考虑使用bsxfun来处理数据 bsxfun ,类似于单元阵列的性能优势。我喜欢将这些解决方案案例称为使用 bsxfun的屏蔽功能。这基本上意味着我们创建逻辑数组,即具有repmat的掩码,可用于在单元数组和数字数组之间交换数据。在数值数组中使用可行数据的一个优点是可以使用矢量化方法来处理它们。同样,由于repmat是一个很好的矢量化工具,你可能会发现自己再次使用它来解决同一个问题,因此有更多理由去了解bsxfun。我能够探索这些方法的几个解决方案案例链接在这里是为了读者的利益: 12345
  3. 未来的工作

    目前的工作重点是使用{{1}}在一个维度上复制数据。现在,{{1}}可以在多个维度上进行复制,因此{{1}}的扩展等同于复制。{{1}}因此,使用这两个函数对复制和扩展进行多个维度的类似测试会很有趣。