代码之家  ›  专栏  ›  技术社区  ›  Greg Kennedy

局部变量对数组访问的性能

  •  2
  • Greg Kennedy  · 技术社区  · 7 年前

    我在做Perl性能的基准测试,遇到了一个我认为有些奇怪的情况。假设有一个函数多次使用数组中的值。在这种情况下,您经常会看到一些代码:

    sub foo {
      my $value = $array[17];
    
      do_something_with($value);
      do_something_else_with($value);
    }
    

    sub foo {
      do_something_with($array[17]);
      do_something_else_with($array[17]);
    }
    

    为了可读性,第一个更清晰。我假设在第一种情况下,性能至少是相同的(或更好),毕竟数组查找需要乘法和加法。


    想象一下当这个测试程序显示相反的结果时我的惊讶。在我的机器上,重新进行数组查找实际上是 更快

    use Benchmark qw(:all);
    
    use constant { ITERATIONS => 4, TIME => -5 };
    
    # sample array
    my @array = (1 .. 100);
    
    cmpthese(TIME, {
      # local variable version
      'local_variable' => sub {
        my $index = int(rand(scalar @array));
        my $val = $array[$index];
        my $ret = '';
    
        for (my $i = 0; $i < ITERATIONS; $i ++) {
           $ret .= $val;
         }
    
        return $ret;
      },
    
      # multiple array access version
      'multi_access' => sub {
        my $index = int(rand(scalar @array));
        my $ret = '';
    
        for (my $i = 0; $i < ITERATIONS; $i ++) {
          $ret .= $array[$index];
        }
    
        return $ret;
      }
    });
    

    结果:

                       Rate local_variable   multi_access
    local_variable 245647/s             --            -5%
    multi_access   257907/s             5%             --
    

    这没什么大的区别,但它提出了我的问题: 阅读其他的S.O.文章,我看到其他语言/编译器确实有预期的结果,有时甚至将它们转换成相同的代码。Perl在做什么?

    2 回复  |  直到 7 年前
        1
  •  1
  •   zdim    7 年前

    让我们首先转一下语句:缓存查找预计会更快,因为它避免了重复查找,即使它确实需要一些开销,而且一旦完成7次以上的查找,它就会开始更快。我想这不算太令人震惊。

    至于为什么不到七次迭代的速度会慢。。。我猜标量创建的成本仍然大于那些少数查找。它肯定不止一次查找,是吗?那两个怎么样?我认为“少数”可能是一个很好的衡量标准。

        2
  •  1
  •   Greg Kennedy    7 年前

    我今天做了更多的调查,我确定的是 标量 任何种类的

    这似乎只是重申了最初的问题,但我觉得我找到了更清晰的答案。例如,如果我修改本地变量子例程来执行另一个赋值,如下所示:

    my $index = int(rand(scalar @array));
    my $val = 0; # <- this is new
    $val = $array[$index];
    my $ret = '';
    

    我还测试了范围是否导致 $var 若要阻止性能,请将其切换到全局范围而不是本地范围。这个差异可以忽略不计(见上面对@zdim的评论) 离开


    最后,我的困惑是基于 错误的假设 那个 . 我习惯于在C中工作,在C中,将值复制到局部变量是一个非常快速的操作(1-2 asm指令)。

    确切地 为什么,没关系)。标量赋值是一个相对“慢”的操作。。。相比之下,Perl内部为获取数组对象的第n个元素所做的任何工作实际上都相当快。我在第一篇文章中提到的“乘法和加法”仍然比标量赋值的代码要少得多。

    这就是为什么需要如此多的查找来匹配缓存结果的性能:简单地分配给“cache”变量大约慢7倍(对于我的设置)。