如何获得速度快的命名子参数?

时间:2013-07-09 23:32:30

标签: perl

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]

2 个答案:

答案 0 :(得分: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]等......但是通过广泛的评论打击不可读性。

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

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

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

最后的考虑因素:

如果您的代码经过良好调整且对性能敏感,以至于复制几个标量会产生显着差异,那么可能希望将Perl从Perl中删除为纯C来实现这些功能。 / p>

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

首先,profile your entire app 并确保标量参数复制确实是您最大的瓶颈所在。我不怀疑这是合理的,但通常,瓶颈在其他地方(IO,DB,慢速数据结构等)。

换句话说,$operation_X可以实现快4倍的事实,如果$operation_X占用运行时总数的0.01%,则无效。如果降低可读性,加速4倍就不值得了。

答案 1 :(得分:1)

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

sub foo1 { goto &do_something}

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

哎呀,我自己也会回答这个问题......