在下面的示例模块中,通过将匿名子例程添加到符号表来生成getter和setter.在以这种方式创建方法之后,生成的代码在功能上是否等同于(在行为,速度等方面)具有手动编写的getter和setter的模块,或者这种方法是否具有某种固有的责任? (我已经做了一些基本的速度基准测试,到目前为止还没有发现任何差异.)
package Module; use strict; use warnings; BEGIN { my @attr = qw(author title number); no strict 'refs'; for my $a (@attr){ *{__PACKAGE__ . "::get_$a"} = sub { $_[0]->{$a} }; *{__PACKAGE__ . "::set_$a"} = sub { $_[0]->{$a} = $_[1] }; } } sub new { my $class = shift; bless { @_ },$class; } 1;
解决方法
如果在两种情况下生成的代码都相同,则运行时性能应该没有差异.但是,这通常是不可能的,除非您使用字符串eval来创建子例程.例如,您提供的代码:
... = sub { $_[0]->{$a} };
将比您手动编写的代码慢得多:
sub foo { $_[0]->{'foo'} }
只是因为前者必须在将变量$a作为哈希的键之前获取变量$a的值,而后者使用常量作为其哈希键.另外,顺便说一下,换班通常比$_ [0]快.这是一些基准代码:
use Benchmark qw(cmpthese); package Foo; sub manual_shift { shift->{'foo'} } sub manual_index { $_[0]->{'foo'} } my $attr = 'foo'; *dynamic_shift = sub { shift->{$attr} }; *dynamic_index = sub { $_[0]->{$attr} }; package main; my $o = bless { foo => 123 },'Foo'; cmpthese(-2,{ manual_shift => sub { my $a = $o->manual_shift },manual_index => sub { my $a = $o->manual_index },dynamic_shift => sub { my $a = $o->dynamic_shift },dynamic_index => sub { my $a = $o->dynamic_index },});
和我的系统上的结果:
Rate dynamic_index manual_index dynamic_shift manual_shift dynamic_index 1799024/s -- -3% -4% -7% manual_index 1853616/s 3% -- -1% -4% dynamic_shift 1873183/s 4% 1% -- -3% manual_shift 1937019/s 8% 4% 3% --
它们非常接近以至于差异可能会在噪音中消失,但在许多试验中我认为你会发现“手动换档”变体是最快的.但是像所有这样的微基准测试一样,你必须在你的硬件和你的perl版本上测试你的确切场景以确保任何事情.
这里的字符串eval被投入到混合中.
eval "sub eval_index { \$_[0]->{'$attr'} }"; eval "sub eval_shift { shift->{'$attr'} }";
它应该与“手动”变体完全相同,加上或减去统计噪声.我的结果:
Rate dynamic_index manual_index dynamic_shift manual_shift eval_shift eval_index dynamic_index 1820444/s -- -1% -2% -3% -4% -5% manual_index 1835005/s 1% -- -1% -2% -3% -4% dynamic_shift 1858131/s 2% 1% -- -1% -2% -3% manual_shift 1876708/s 3% 2% 1% -- -1% -2% eval_shift 1894132/s 4% 3% 2% 1% -- -1% eval_index 1914060/s 5% 4% 3% 2% 1% --
同样,这些都非常接近,你必须付出巨大的努力,并进行许多试验来整理噪音信号.但是使用常量作为散列键和使用变量(必须首先检索其值)作为散列键之间的区别应该显示出来. (移位优化是一个单独的问题,更有可能在perl的过去或未来版本中以某种方式改变.)