performance - 比较BSXFUN和REPMAT

标签 performance matlab benchmarking vectorization bsxfun

关于 bsxfun repmat 之间的性能比较,之前很少有人问过。

  • 其中之一是: Matlab - bsxfun no longer faster than repmat? 。这篇文章试图研究repmatbsxfun之间的性能比较,特定于沿着输入数组本身的列对输入数组的均值进行减法运算,因此,将仅探索@minusbsxfun部分与repmat等效项。
  • 另一个是: In Matlab, when is it optimal to use bsxfun? 。那个试图通过均值沿列执行相同的减法运算,并且也没有扩展到其他内置运算。

  • 在这篇文章中,我正在尝试研究 bsxfun repmat 之间的性能数字,以涵盖所有 bsxfun 内置函数,以便为这两种当前良好的矢量化解决方案提供更广阔的视野。

    具体来说,我对这篇文章的疑问是:
  • bsxfun 进行的各种内置操作如何针对 repmat 等价物执行? bsxfun 支持诸如@plus@minus@times等的浮点运算,还支持诸如@ge@and等的关系和逻辑运算。因此,是否有特定的内置函数可以使我比使用他们的 bsxfun 等效项?
  • Loren在自己的 repmat 中已将 blog post repmat 进行了基准测试,并分别针对bsxfun@() A - repmat(mean(A),size(A,1),1)进行了计时。如果我需要涵盖所有内置程序的基准测试,是否可以使用其他一些适用于浮点,关系和逻辑运算的比较模型?
  • 最佳答案

    介绍

    关于bsxfun是否比repmat更好,反之亦然的争论一直持续不断。在本文中,我们将尝试比较MATLAB附带的各种内置程序如何在运行时性能上与repmat等效项进行抗衡,并希望从中得出一些有意义的结论。

    了解BSXFUN内置

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

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

  • @plus(总和)
  • @减(减)
  • @times(乘法)
  • @rdivide(右分隔)
  • @ldivide(左分隔)
  • @pow(电源)
  • @rem(其余)
  • @mod(模数)
  • @ atan2(四象限反切线)
  • @ atan2d(度数的四象限反正切)
  • @hypot(平方和的平方根)。

  • 第二组包括按元素的关系操作,它们是:
  • @eq(等于)
  • @ne(不等于)
  • @lt(小于)
  • @le(小于或等于)
  • @gt(大于)
  • @ge(大于或等于)。

  • 第三组也是最后一组包含逻辑操作,如下所示:
  • @and(逻辑和)
  • @或(逻辑或)
  • @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所获得的加速比被绘制为三组,如下图所示。

    A.浮点运算:

    可以从加速图中得出很少的观察结果:
  • 对于bsxfunatan2,使用atan2d的两个明显的提速案例非常明显。
  • 该列表中的下一个是与30% - 50%等效代码相比,使用repmat进行提升的左右除法运算。
  • 在该列表中排在最下的是其余的7操作,它们的加速似乎非常接近于统一,因此需要仔细检查。加速图可以缩小到仅那些7操作,如下所示-


  • 根据上面的图,可以看到,除非使用@hypot@mod一次性完成案例,否则bsxfun的性能仍然比repmat好10%。

    B.关系运算:

    这是7支持的接下来的6个内置关系操作的第二组基准测试。

    查看上面的加速图,忽略了在bsxfunbsxfun之间具有可比的运行时的起始情况,可以很容易地看到repmat在这些关系操作中胜出。在触摸 bsxfun 的情况下,对于这些情况,10x总是首选。

    C.逻辑操作:

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

    在一开始就忽略了bsxfun的一次性可比运行时情况,@xor在这组逻辑操作中似乎也占了上风。

    结论
  • 在使用关系和逻辑运算时,很容易忘记bsxfun,而使用repmat。在其余情况下,如果可以容忍bsxfun的性能较差的一种情况仍然可以继续使用bsxfun
  • 在将关系和逻辑运算与5 - 7%一起使用时,看到了那种巨大的性能提升,人们可以考虑使用bsxfunbsxfun进行数据处理,这样可以提高性能。我喜欢将这些解决方案案例称为使用 ragged patterns的屏蔽功能的案例。这基本上意味着我们创建逻辑数组,即使用bsxfun的掩码,可用于在单元格数组和数字数组之间交换数据。在数字数组中具有可用数据的优点之一是可以使用矢量化方法来处理它们。同样,由于bsxfun是矢量化的一个很好的工具,您可能会发现自己再次使用它来解决相同的问题,因此有更多的理由来了解bsxfun。为了读者的利益,这里很少有我能够探索此类方法的解决方案案例的链接:
    12
    34
    5

  • future 的工作

    目前的工作集中在使用bsxfun沿一维复制数据。现在,repmat可以沿多个维度复制,因此repmat的扩展等同于复制。因此,使用这两个功能对复制和扩展到多个维度执行类似的测试将很有趣。

    关于performance - 比较BSXFUN和REPMAT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29719674/

    相关文章:

    loops - 在 Julia 的 For 循环中单独计时不同的行?

    MATLAB - 如何(优雅地)对矩阵进行插值?

    matlab - 标记散点数据点

    python - 使用 time.clock() 与 time.time() 对 Python 程序进行计时

    sql - 多字符串匹配性能

    matlab - 在 Matlab 子图中链接不同的数据源

    sql - B 树、数据库、顺序插入与随机插入以及速度。随机就是胜利

    c# - coderush 搜索特定问题

    c++ - 如果从未调用函数,编译器会在创建程序时忽略它吗?

    MySQL:使用 LONGTEXT 而不是 MEDIUMTEXT 是否缺乏性能?