转发注意:为了本文的讨论,让我们暂时忽略这样一个事实:通过使用https://metacpan.org/module/Class::Accessor,甚至简单地使用https://metacpan.org/module/Moose (在考虑代码可读性和可维护性时,可能会有更好的结果),就可以达到相同的目的。
关于面向对象的Perl,书Programming Perl讨论了用闭包生成访问器方法的能力。例如,这是一段有效的代码:
#!perl
use v5.12;
use warnings;
# at run-time
package Person1;
my @attributes = qw/name age address/;
for my $att ( @attributes )
{
my $accessor = __PACKAGE__ . "::$att";
no strict 'refs'; # allow symbolic refs to typeglob
*$accessor = sub {
my $self = shift;
$self->{$att} = shift if @_;
return $self->{$att};
};
}
sub new { bless {}, shift }
package main;
use Data::Dumper;
my $dude = Person1->new;
$dude->name('Lebowski');
say Dumper($dude);在上面的例子中,如果我没有弄错,类是在运行时组成的,它的访问器是在类被实例化的同时创建的。这意味着对象的创建将受到速度上的惩罚。
现在考虑以下备选方案:
#!perl
use v5.12;
use warnings;
package Person2;
BEGIN
{
for my $att (qw/name age address/)
{
my $accessor = __PACKAGE__ . "::$att";
no strict 'refs'; # allow symbolic refs to typeglob
*$accessor = sub {
my $self = shift;
$self->{$att} = shift if @_;
return $self->{$att};
};
}
}
sub new { bless {}, shift }
package main;
use Data::Dumper;
my $dude = Person2->new;
$dude->name('Lebowski');
say Dumper($dude);在这个版本中,组合是在一个BEGIN块内完成的(即编译时),我相信通过在程序的生命周期中尽快处理这个任务,我可以在运行时节省对象实例化期间的时间。
一个简单的Benchmark,
# benchmark it!
package main;
use Benchmark qw/cmpthese/;
cmpthese(-2, {
accessors_new => sub { Person1->new },
accessors_begin => sub { Person2->new },
});这些结果似乎支持了我的理论:
Rate accessors_begin accessors_new
accessors_begin 853234/s -- -9%
accessors_new 937924/s 10% --假设到目前为止我的推理是正确的,
BEGIN块作为执行此类类操作的有效方法是一个好主意吗?发布于 2012-03-07 12:51:10
当我运行您的基准测试时,我会得到相当大的震动,这可以解释您的差异。对于任何10%或更少的差异,运行几次,以确保。
Rate accessors_begin accessors_new
accessors_begin 1865476/s -- -4%
accessors_new 1943339/s 4% --
Rate accessors_begin accessors_new
accessors_begin 1978799/s -- -1%
accessors_new 2001062/s 1% --
Rate accessors_new accessors_begin
accessors_new 1943339/s -- -2%
accessors_begin 1988089/s 2% --
Rate accessors_begin accessors_new
accessors_begin 1796509/s -- -8%
accessors_new 1949296/s 9% --
Rate accessors_begin accessors_new
accessors_begin 1916122/s -- -3%
accessors_new 1969595/s 3% --但实际上,你所要做的就是sub new { bless {}, shift }。用同样的东西来衡量自己也会强调颤振。生成访问器的工作已经在加载代码时完成,并且从未进入它,无论是否开始阻塞。
Perl没有一个编译时和运行时.相反,used、required或evaled的每一件事都要经过它自己的编译和运行时步骤。use Some::Class使Some/Class.pm同时通过编译和运行时执行BEGIN,编译子程序,然后执行任何其他代码。无论代码在模块内部还是外部,模块内的BEGIN块对该模块外的代码都没有多大影响。
发布于 2012-03-07 13:19:49
在上面的例子中,如果我没有弄错,类是在运行时组成的,它的访问器是在类被实例化的同时创建的。这意味着对象的创建将受到速度上的惩罚。
您正确地说,访问器是在运行时创建的,但是在代码中,只在执行开始时创建了访问器一次--当然不是在实例化的时候。您可以看到构造函数的作用:
sub new { bless {}, shift }这是相当简短和相应的快速。将BEGIN块应用到访问器构建循环中,只会将工作从运行时开始移动到编译时结束,而您没有取得任何成果。你在你的基准中得到的变化是微不足道的,我想主要是由于噪音。
我已经在我自己的系统上复制了您的基准测试,将运行时间提高到10秒,并在四次测试中得到了以下结果。添加一个BEGIN块似乎可以稍微提高性能,但这是一个极小的改进,我无法对其进行永无休止的解释。
Rate accessors_new accessors_begin
accessors_new 1463771/s -- -1%
accessors_begin 1476583/s 1% --
Rate accessors_new accessors_begin
accessors_new 1469833/s -- -0%
accessors_begin 1472234/s 0% --
Rate accessors_new accessors_begin
accessors_new 1454942/s -- -1%
accessors_begin 1469680/s 1% --
Rate accessors_new accessors_begin
accessors_new 1462613/s -- -1%
accessors_begin 1473985/s 1% --发布于 2012-03-07 12:42:38
如果将包分离到它们自己的文件并使用use,那么区别就消失了:模块的代码正在编译时运行。
https://stackoverflow.com/questions/9601545
复制相似问题