1

perl subs 的参数在@_ 中传递。为了使我的程序更易于阅读,我一直使用这种模式来获取命名参数:

sub foo1 {
    my ($bar, $baz) = @_;
    do_something($bar,$baz);
}

但它会导致$_[0]并被复制。如果我要直接访问而不是在上述模式中访问,我可以使用按引用调用的常见警告对调用者参数进行按值调用/别名访问,但它要快得多(参见下面的演示)。$_[1]$_[0]$bar

我怀疑这种my ($param1, $param2 ...) = @_;模式出于性能原因是不好的。所以我发现我必须在快速和可读的程序之间做出选择,这是一个不可能的选择。

我最终编写了以性能为重点的潜艇,$_[<n>]而其他一切都与上述模式一致。麻烦的是,我通常事先不知道瓶颈在哪里;-)

有没有办法获得也很快的命名参数?或者这件事似乎是经典?$_[0]还是$bar

附录:速度演示

use Time::HiRes qw(time);

# Lets just do *something* with the parameters - here we just add up all
# their lengths
my $totalLength = 0;

sub foo1 {
    # Access $_[0] directly - effectively call-by-reference
    $totalLength += length($_[0]);
}

sub foo2 {
    # Access a copy of $_[0] - effectively call-by-value  - involves
    # copying
    my ($bar) = @_;
    $totalLength += length($bar);
}

my $a = 'b' x 10_000;
my $t0 = time;
foreach (0..1_000_000) {
    foo1($a);
}
printf "foo1 %2.6f\n", time - $t0;

$t0 = time;
foreach (0..1_000_000) {
    foo2($a);
}
printf "foo2 %2.6f\n", time - $t0;

打印出来

foo1 0.329470
foo2 1.280364

foo1 几乎比 foo2 快 4 倍,因为它避免了复制$_[0]

4

2 回答 2

6

通过 hashref 传递它们:

my %args = (bar=>1, baz=>2);
mySub(\%args);
sub mySub {
    my $args = shift;
    doSomething($args); # pass entire parameter list for cheap!
    doSomething2($args{bar}); # Use specific parameter
}

坦率地说,我对性能优势(哈希访问不是免费的)有点怀疑,但如果你真的需要,你可以对其进行基准测试。但这不是最佳性能选项(见下文),甚至可能不需要(见最后部分),我认为没有太多需要尝试。


另一种选择(有点糟糕,但性能更好)是使用$_[1]etc...,但通过大量评论来对抗不可读性。

           # pass `baz`
doSomething($_[1]);

另一个更高性能但设计不佳的选项是绕过所有参数传递,并使用全局变量来传递参数。

our $bar = 1;
mySub();
sub mySub {
    #do something with $bar. Pray some other code didn't clobber it
}

最后考虑:

如果您的代码调整得非常好并且对性能非常敏感,以至于复制几个标量会产生显着差异,那么您可能希望从 Perl 中退出这些函数的纯 C 语言

但是,正如 Knuth 所说,请不要过早优化

首先,分析您的整个应用程序并确保标量参数复制确实是您最大的瓶颈所在。我不认为这是合理的,但通常瓶颈在其他地方(IO、DB、慢速数据结构等)。

换句话说,$operation_X可以快 4 倍的速度实现这一事实,如果$operation_X总共占用 0.01% 的运行时间,则毫无意义。鉴于可读性降低,将其加速 4 倍根本不值得麻烦。

于 2013-07-09T23:38:36.027 回答
1

好吧,如果你按顺序将 $bar 和 $baz 传递给 sub do_something(),另一个不好的选择是使用以下可怕的——但记录在案的——语法:

sub foo1 { goto &do_something}

...这会立即将上下文传递给 do_something() 。记录参数没有帮助,但它可能是最快的传递到另一个例程的机制。:-)

哎呀,我自己会否决这个答案....

于 2013-07-10T01:59:09.913 回答