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

时间:2018-10-09 06:52:07

标签: performance perl benchmarking micro-optimization perl5

我正在对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]);
}

为便于阅读,第一个更清晰。我还认为在第一种情况下性能至少也要相等(或更高)-毕竟数组查找需要乘加运算。


想象一下,当这个测试程序显示相反的结果时,我感到惊讶。在我的机器上,重新执行数组查找实际上比存储结果快得多,直到将ITERATIONS增加到7;换句话说,对我来说,创建局部变量只有在至少使用7次的情况下才是值得的!

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%             --

这不是一个巨大的区别,但是它提出了我的问题:为什么创建一个局部变量并缓存数组查找要比再次查找慢呢?帖子中,我已经看到其他语言/编译器确实具有预期的结果,有时甚至可以将它们转换为相同的代码。 Perl在做什么?

2 个答案:

答案 0 :(得分:1)

让我们首先声明:缓存缓存将避免重复查找,尽管这样做虽然会花费一些时间,但预计会更快,并且一旦完成7次以上查找,它就会开始变得更快。我认为,现在还不那么令人震惊。

关于为什么它的迭代速度少于七个迭代速度的原因……我想,标量创建的成本仍然大于这几个查询。它肯定大于一次查找,是吗?那两个呢?我会说“几个”可能是一个好方法。

答案 1 :(得分:1)

我今天对此做了更多的探索,我确定的是,相对于间接费用,标量赋值 是一项昂贵的操作一深度数组查找。

这似乎只是在重申最初的问题,但我觉得我已经更加清楚了。例如,如果我修改了我的local_variable子例程来进行另一种分配,例如:

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

...除了单赋值版本外,该代码还会遭受5%的速度损失-即使它仅对变量进行虚拟赋值。

我还测试了示波器是否将$var的设置/拆卸造成阻碍性能,方法是将其切换到全局而不是本地范围。差异可以忽略不计(请参阅上面对@zdim的评论),将远离指向构造/破坏作为性能瓶颈。


最后,我的困惑是基于错误的假设,即标量分配应该很快。我曾经在C语言中工作过,在C语言中,将值复制到本地变量是非常快速的操作(1-2条ASM指令)。

事实证明,在Perl中并非如此(尽管我不确切地知道 为什么,这没关系)。标量分配是一个相对“缓慢”的操作...相较之下,Perl内部人员为获得Array对象的第n个元素所做的一切实际上都非常快。我在第一篇文章中提到的“乘加”仍然比标量分配的代码少得多。

这就是为什么要进行大量查找才能匹配结果缓存性能的原因:简单地分配给“ cache”变量要慢7倍(对于我的设置)。