我一直在研究处理大型固定宽度数据文件的几个Perl脚本,从每个数据记录中提取小的子串.我曾经想过,将子串的提取委托给方法调用会很昂贵,因为将数据记录复制到@_数组的开销很大.所以我运行以下命令来比较(a)直接调用substr(),(b)方法调用将数据记录作为字符串传递,以及(c)方法调用通过引用传递数据记录.
use strict; use warnings; use Benchmark qw(timethese); my $RECORD = '0' x 50000; my $direct = sub { my $v = substr( $RECORD,$_,1) for 0..999 }; my $byVal = sub { my $v = ByVal ( $RECORD,$_) for 0..999 }; my $byRef = sub { my $v = ByRef (\$RECORD,$_) for 0..999 }; sub ByVal { return substr( $_[0],$_[1],1) } sub ByRef { return substr(${$_[0]},1) } timethese( 10000,{ direct => $direct,byVal => $byVal,byRef => $byRef,} ); my $byVal2loc = sub { my $v = ByVal2loc( $RECORD,$_) for 0..999 }; my $byRef2loc = sub { my $v = ByRef2loc(\$RECORD,$_) for 0..999 }; sub ByVal2loc { my $arg = shift; return substr( $arg,$_[0],1) } sub ByRef2loc { my $arg = shift; return substr( $$arg,1) } timethese( $ARGV[0],{ byVal2loc => $byVal2loc,byRef2loc => $byRef2loc,} ); # Produces this output: Benchmark: timing 10000 iterations of byRef,byVal,direct... byRef: 19 wallclock secs... byVal: 15 wallclock secs... direct: 4 wallclock secs... Benchmark: timing 10000 iterations of byRef2loc,byVal2loc... byRef2loc: 21 wallclock secs... byVal2loc: 119 wallclock secs...
正如所料,直接方法是最快的.但是,我惊讶地发现没有与我想象的“复制数据”相关的惩罚.即使我将记录的宽度增加到奇怪的比例(例如,十亿个字符),按值和参考基准也基本相同.
似乎在将参数传递给方法时,Perl不会复制数据.我想这有助于进一步反思@_的混叠能力.参数通过引用传递,而不是通过值传递.
但是,它是一种受限制的引用传递形式,因为@_中的引用不能直接分配给子例程中的局部变量.这种分配确实导致数据复制,如第二组基准所示.
我理解正确吗?
解决方法
是的,作业复制;只是传递参数不.但是,您可以使用
Lexical::Alias将词法替换为@_中的元素.这个修改过的基准测试显示,使用引用的速度是第三快,但无论$RECORD的长度如何,都是如此:
use strict; use warnings; use Benchmark qw(timethese); use Lexical::Alias; my $RECORD = '0' x 5000000; my $byVal2loc = sub { my $v = ByVal2loc( $RECORD,$_) for 0..999 }; my $byAlias2loc = sub { my $v = ByAlias2loc( $RECORD,$_ ) for 0..999 }; sub ByVal2loc { my $arg = shift; return substr( $arg,1) } sub ByAlias2loc { my $arg; alias($_[0],$arg); return substr( $arg,1 ) } timethese( $ARGV[0],byAlias2loc => $byAlias2loc,} ); # output: Benchmark: running byAlias2loc,byRef2loc,byVal2loc for at least 3 cpu seconds... byAlias2loc: 3 wallclock secs ( 3.16 usr + 0.00 sys = 3.16 cpu) @ 430.70/s (n=1361) byRef2loc: 4 wallclock secs ( 3.24 usr + 0.00 sys = 3.24 cpu) @ 1329.63/s (n=4308) byVal2loc: 5 wallclock secs ( 4.95 usr + 0.01 sys = 4.96 cpu) @ 0.40/s (n=2) (warning: too few iterations for a reliable count)
(直接使用alias_r而不是别名辅助函数稍微快一些.)