【问题标题】:Why iterating with $_ takes longer为什么用 $_ 迭代需要更长的时间
【发布时间】:2014-01-28 11:29:15
【问题描述】:

我想知道基于 Internet 上的许多书籍,如果 $_ 真的是迭代数组的更快方法(不实例化新变量),但不知何故我总是得到不同的结果。下面是性能代码测试:

#!/usr/bin/perl
use Time::HiRes qw(time);

use strict;
use warnings;

# $_ is a default argument for many operators, and also for some control structures.

my $test_array = [1..1000000];

my $number_of_tests = 100;
my $dollar_wins = 0;
my $dollar_wins_sum = 0;

for (my $i = 1; $i <= $number_of_tests; $i++) {
    my $odd_void_array = [];
    my $start_time_1 = time();
    foreach my $item (@{$test_array}) {
        if ($item % 2 == 1) {
            push (@{$odd_void_array}, $item);
        }
    }
    foreach my $item_odd (@{$odd_void_array}) {
    }
    my $end_time_1 = time();

    $odd_void_array = [];
    my $start_time_2 = time();
    foreach (@{$test_array}) {
        if ($_ % 2 == 1) {
            push (@{$odd_void_array}, $_);
        }
    }
    foreach (@{$odd_void_array}) {
    }
    my $end_time_2 = time();

    my $diff = ($end_time_1-$start_time_1) - ($end_time_2-$start_time_2);
    if ($diff > 0) {
        $dollar_wins ++;
        $dollar_wins_sum += $diff;
        print "Dollar won ($dollar_wins out of $i) with diff $diff \n";
    }
}

print "=================================\n";
print "When using dollar underscore, execution was faster in $dollar_wins cases (".(($dollar_wins/$number_of_tests)*100)."%), with average difference of ".($dollar_wins_sum/$dollar_wins)."\n";

所以,我有两次迭代(一次分配给我的 $item,其他没有)。我主要是在大约 20-30% 的情况下使用 $_ 进行迭代更快。

在没有新变量的情况下迭代不应该更快吗?

【问题讨论】:

  • sa $_ je onda brze ili sporije?
  • 它实际上并没有将值复制到变量中。对此进行测试: perl -e '@a = (1, 2, 3); $_++ foreach (@a);打印@a;'你会看到它更新了@a。它实际上将列表中的变量别名为您定义的名称。这可能会影响性能。
  • 让我翻译一下@mpapec 问的内容:) “那么,$_ 是快还是慢?”我说,根据我的发现,$_ 更慢......我只是想知道那是怎么回事? :)
  • 无论$_ 或其他一些变量的用法如何,foreach 变量始终是别名。
  • 它与I get mostly that iterating with $_ was faster in about 20-30% cases 不在同一行 :)

标签: performance perl foreach


【解决方案1】:

您并没有真正用不同的变量对迭代进行基准测试。

  • 您的时间包括数组创建和其他计算。
  • 您只知道哪个更快,而不是多少。
  • 您的迭代次数太少,无法说出任何可靠的信息。

让我们进行这个更好的测试,它实际上对您声称的基准进行了基准测试:

use strict;
use warnings;
use Benchmark ':hireswallclock', 'cmpthese';

my @numbers = 1..100_000;

cmpthese -3, {
  '$_' => sub {
    for (@numbers) {
      1;
    }
  },
  'my $x' => sub {
    for my $x (@numbers) {
      1;
    }
  },
  '$x' => sub {
    my $x;
    for $x (@numbers) {
      1;
    }
  },
}

结果:

       Rate    $_ my $x    $x
$_    107/s    --   -0%   -0%
my $x 107/s    0%    --   -0%
$x    108/s    0%    0%    --

所以它们在我的测试系统上同样快(为 i686-linux-thread-multi-64int 构建的 perl 5.18.2)。

我怀疑 使用 $_ 比词法稍慢,因为它是一个全局变量。但是,迭代的速度是相当的。事实上,修改基准…

use strict;
use warnings;
use Benchmark ':hireswallclock', 'cmpthese';

my @numbers = 1..100_000;

cmpthese -3, {
  '$_' => sub {
    for (@numbers) {
      $_ % 2 == 0;
    }
  },
  'my $x' => sub {
    for my $x (@numbers) {
      $x % 2 == 0;
    }
  },
  '$x' => sub {
    my $x;
    for $x (@numbers) {
      $x % 2 == 0;
    }
  },
}

……给了

        Rate    $_    $x my $x
$_    40.3/s    --   -1%   -6%
$x    40.6/s    1%    --   -5%
my $x 42.9/s    7%    6%    --

但影响仍然太小,无法得出任何可靠的结论。

【讨论】:

  • 词法确实应该比包变量的性能略好,但我找不到任何关于这种说法的参考。
  • @mapeec 用-MO=Concise 看一些简单的sn-ps。使用全局本质上是在存储中查找(这只是一个特殊的哈希映射名称到 glob)。在 pad 上查找词汇(本质上是一个数组)。除了可能的缓存效果之外,这本身也使词法运算更快。但在实践中,差异很小。词汇的其他方面更为重要(主要是它们的作用域优势)。
  • 存储是C struct,而不是特殊的哈希。这只是从 Perl 访问它的方式。
  • 词法本质上是一个二维数组(想想递归)。 (不确定“pad”是指整个事物,还是仅仅指第二个维度。)
  • 如果将@numbers 数组更改为1..2 而不是1..100_000,则差异会更加明显;词汇可以快 10%。
猜你喜欢
  • 2019-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-09
  • 2022-01-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多