【问题标题】:Perl: Does the time taken to dereference an array reference depend on the size of the array?Perl:取消引用数组引用所花费的时间是否取决于数组的大小?
【发布时间】:2012-04-11 21:16:05
【问题描述】:

好奇是否有人知道(或可以轻松测试)引用然后取消引用数组所需的时间。

my @foo = (0..1500000);     # (~1.5M nodes).
join('',@{\@foo});          # any noticeable time difference vs join('',@foo) ?

这显然没有正当理由,但我遇到了不合理的代码:)

【问题讨论】:

  • use Benchmark;开始,比较你的不同方法。

标签: perl reference dereference


【解决方案1】:

我执行的类似测试的基准给出了每个 deref 大约 10 纳秒的结果。您发布的代码中只有 deref,所以我们说的是 0.000,000,010 秒的差异。


呸,差别太小了,我什至无法确定哪个更快!

Benchmark: running array, array_ref for at least 3 CPU seconds...
     array:  3 wallclock secs ( 3.04 usr +  0.02 sys =  3.06 CPU) @ 11.12/s (n=34)
 array_ref:  3 wallclock secs ( 3.13 usr +  0.00 sys =  3.13 CPU) @ 11.48/s (n=36)

Benchmark: running array, array_ref for at least 3 CPU seconds...
     array:  3 wallclock secs ( 3.06 usr +  0.03 sys =  3.09 CPU) @ 11.33/s (n=35)
 array_ref:  3 wallclock secs ( 3.12 usr +  0.05 sys =  3.17 CPU) @ 11.37/s (n=36)

Benchmark: running array, array_ref for at least 3 CPU seconds...
     array:  3 wallclock secs ( 3.06 usr +  0.00 sys =  3.06 CPU) @ 11.45/s (n=35)
 array_ref:  3 wallclock secs ( 3.18 usr +  0.00 sys =  3.18 CPU) @ 11.31/s (n=36)

Benchmark: running array, array_ref for at least 3 CPU seconds...
     array:  3 wallclock secs ( 3.09 usr +  0.00 sys =  3.09 CPU) @ 11.66/s (n=36)
 array_ref:  3 wallclock secs ( 3.17 usr +  0.00 sys =  3.17 CPU) @ 11.37/s (n=36)

array 在 50% 的时间里更快,array ref 在 50% 的时间里更快。

use strict;
use warnings;

use Benchmark qw( timethese );

my %tests = (
   array_ref => 'my $x = join("", @$foo);',
   array     => 'my $x = join("", @foo);',
);

$_ = 'use strict; use warnings; our $foo; our @foo; ' . $_
   for values(%tests);

our @foo = 1..1_500_000;
our $foo = \@foo;

timethese(-3, \%tests);

这是一个比您发布的测试更好的测试。您发布的唯一一个花费不到 1% 的时间做您想要测试的事情。

但同样,差异是如此之小,以至于无法衡量。有时数组 ref 出现得更快,有时数组出现得更快。

Actual speed is actually 1000x larger than indicated.
Benchmark: running array, array_ref for at least 3 CPU seconds...
     array:  3 wallclock secs ( 3.09 usr +  0.00 sys =  3.09 CPU) @ 1015.54/s (n=3136)
 array_ref:  3 wallclock secs ( 3.24 usr +  0.00 sys =  3.24 CPU) @ 1040.99/s (n=3378)

Actual speed is actually 1000x larger than indicated.
Benchmark: running array, array_ref for at least 3 CPU seconds...
     array:  3 wallclock secs ( 3.25 usr +  0.00 sys =  3.25 CPU) @ 1011.09/s (n=3281)
 array_ref:  3 wallclock secs ( 3.07 usr +  0.00 sys =  3.07 CPU) @ 1022.13/s (n=3141)

Actual speed is actually 1000x larger than indicated.
Benchmark: running array, array_ref for at least 3 CPU seconds...
     array:  3 wallclock secs ( 3.29 usr +  0.00 sys =  3.29 CPU) @ 1020.96/s (n=3361)
 array_ref:  3 wallclock secs ( 3.20 usr +  0.00 sys =  3.20 CPU) @ 1016.26/s (n=3250)

Actual speed is actually 1000x larger than indicated.
Benchmark: running array, array_ref for at least 3 CPU seconds...
     array:  3 wallclock secs ( 3.07 usr +  0.00 sys =  3.07 CPU) @ 1053.03/s (n=3237)
 array_ref:  4 wallclock secs ( 3.23 usr +  0.00 sys =  3.23 CPU) @ 1006.50/s (n=3250)

同样,array 在 50% 的时间里更快,array ref 在 50% 的时间里更快。

use strict;
use warnings;

use Benchmark qw( timethese );

my %tests = (
   array_ref => 'my $x = join("", @$foo);',
   array     => 'my $x = join("", @foo);',
);

$_ = 'use strict; use warnings; our $foo; our @foo; for (1..1000) { '.$_.' }'
   for values(%tests);

our @foo = 1..15;
our $foo = \@foo;

print("Actual speed is actually 1000x larger than indicated.\n");
timethese(-3, \%tests);

【讨论】:

  • @vol7ron,添加了基准。然后添加了更好的基准。速度差很小,甚至无法测量。
  • 从这段代码中看不出速度上的差异,因为你已经把它淹没在不相关的循环测试、标量分配和大多数join 调用中。取消引用数组引用所花费的时间可能不取决于数组的大小,因此它只需要一个元素,并且被比较的代码应该只是@foo vs @$foo。我分别做到了 240ns 和 440ns。
  • @Borodin:这是我真正想弄清楚的第二点。 Does the time taken to dereference an array reference depend on the size of the array? 很好的表达方式,谢谢。
  • @vol7ron,取消引用一个标量的时间不取决于任何数组的大小。从我回答的第一句话就应该很清楚了。
  • @Borodin,你大错特错。测试表明@foo 有时比@$foo 快,有时@$foo@foo 快。你觉得什么是溺水?如果时间差有时大于零,有时小于零,则无法测量。没有任何东西被淹没;结果非常具体。
【解决方案2】:

一个简单的基准测试表明,将 1 到 1,500,000 的整数连接成单个字符串的不同方法之间没有明显的区别(除了一种错误的方法——下面没有显示)。

我确实想知道为什么需要创建这样一个字符串,但我想知道很多。

#!/usr/bin/env perl

use strict; use warnings;
use Benchmark qw( cmpthese );

my @nodes = (1 .. 1_500_000);

cmpthese -5, {
    derefref_join => sub {
        my $str = join('', @{ \@nodes });
    },
    plain_join => sub {
        my $str = join('', @nodes);
    },
    interpolate => sub {
        local $" = '';
        my $str = "@nodes";
    },
};

输出:

 速率插值 deref_join plain_join
插值 4.76/s -- -3% -3%
derefref_join 4.89/s 3% -- -1%
plain_join 4.92/s 4% 1% --
C:\temp> perl -v

这是为 MSWin32-x86-multi-thread 构建的 perl 5,版本 14,subversion 2 (v5.14.2)
ActiveState http://www.ActiveState.com 提供的二进制版本 1402 [295342]
建于 2011 年 10 月 7 日 15:49:44

Intel Core2 Duo T2300E@1.66Ghz,2GB 内存。

【讨论】:

  • 这是一个如何不专注于次要细节,而专注于所使用的算法的示例。不是一个很好的例子,因为结果仍然在 5% 之内。 (任何低于 5% 的都是不可靠的。不同的运行可能会有这么大的差异。)
  • @ikegami 添加了注释以澄清基准脚本没有显示任何区别——只是为了让它非常清楚;-)
  • 我想了这么多,没有时间测试它。这就是我一直在寻找的 :)
【解决方案3】:

显然,由于处理器速度的不同,每个主机都会有所不同。

衡量这一点的一种方法是使用time 函数来记录时间。然后创建并执行一个包含数十万个解引用操作的循环(因为测量单个解引用会非常快,您将无法测量它。)然后,再次记录时间。减去次数并除以循环的次数。从中减去在没有取消引用的情况下通过循环所需的时间。一点数学,你就有了。

【讨论】:

  • 您还应该忽略异常结果。 Benchmark 模块为您完成所有这些工作。
猜你喜欢
  • 1970-01-01
  • 2012-04-19
  • 2019-04-19
  • 1970-01-01
  • 1970-01-01
  • 2021-07-04
  • 2012-06-22
  • 2017-12-19
  • 1970-01-01
相关资源
最近更新 更多