Perl - クロージャとオブジェクトの速度

簡単に説明すると、

Perlでは、巨大構造でないオブジェクトは、全てクロージャで同等の処理デザイン実装できる。

じゃあどっちが速いの?

細かい説明はあとにして、まずベンチマークと結果から

#クロージャ
{
package closure;
sub new{
my $self = shift;
my $method = {
up => sub{ $self++ },
down => sub{ $self-- },
get => sub{ $self },
};
return $method;
}
}


#オブジェクト指向
{
package object;
sub new{
my $class = shift;
my $self = shift;
return bless \$self, $class;
}
sub up { my $self = shift; $$self++ }
sub down{ my $self = shift; $$self-- }
sub get { my $self = shift; $$self }
}

#------------------------------------------------------

package main;

my $c = closure::new(100);
my $o = object->new(100);

my %measure = (
cls => sub{
$c->{up}->();
$c->{down}->();
},
obj => sub{
$o->up();
$o->down();
}

);

cmpthese timethese (10000000, \%measure);

__END__

 

 

Benchmark: timing 10000000 iterations of cls, obj...
cls: 4 wallclock secs ( 4.52 usr + 0.00 sys = 4.52 CPU) @ 2210433.24/s (n=10000000)
obj: 7 wallclock secs ( 6.58 usr + 0.00 sys = 6.58 CPU) @ 1519064.26/s (n=10000000)
Rate obj cls
obj 1519064/s -- -31%
cls 2210433/s 46% --

 

とりあえず、インデントを勝手に消されて見にくくなったが、ご容赦頂きたい。

まず、余裕でクロージャのほうが速いです。

 

なぜこうなったか、の前に簡単にクロージャオブジェクト指向の違いを。

クロージャはブロック構造依存のため、処理を書く位置が強制される。(静的なブロック構造に沿って書かねばならない)。

そのため、クラスや継承構造が巨大になると、デザインとして破綻する。

オブジェクト指向名前空間依存であり、仕様上名前空間は好きな位置に好きなように書ける。

そのため、構造や階層に沿って書く必要はない。

と言う感じです。

 

なぜ速度差がついたのか、これはおそらく以下が原因です。

 

1:オブジェクト指向のメソッド呼び出しは、実はシンボリックリファレンス。

名前空間の中にメソッドが入ってるようにみせかけておいて、実はそんな構造は保持されていない。毎回検索してるだけ。

そして、$o->m(); と言う書式の$oの中に入っているのは実はクラス名のそのまま文字列。

クロージャの場合は、メソッドのリファレンス(アドレス)を受け取ることで成立しているので、すでにメソッドにアクセスする方法を知っている。

 

2:オブジェクト指向は、メソッドを呼び出すたびに毎回暗黙に$selfやクラス名を送信する。

これは何度も積み重ねると無駄に重くなる。

クロージャはそんなの必要ない。関数側のブロックで個別に保持されてるから、渡すとかそうゆう概念はない。

 

3:オブジェクト指向は、$selfが毎回 my で複製されている。

my は動的なメモリ宣言。遅い処理の代名詞。

これに関しては、別にmyしなくても強引に処理を進めて行く事が可能だけど、

どう考えても変な記述になるので普通は必ずmyします。

対してクロージャの場合は、インスタンス作成時に一度だけmyでデータを作成し、

以後それをそのまま永久に使います。

全てが直接アクセスするため、引数で渡すとか、複製するとか、そうゆう概念自体がありません。

 

と言う事で、速度面やコスト面はクロージャの勝利なのでした。

ただしこれが原因で遅いのかどうかは確認してないので、ご了承をば