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でデータを作成し、
以後それをそのまま永久に使います。
全てが直接アクセスするため、引数で渡すとか、複製するとか、そうゆう概念自体がありません。
と言う事で、速度面やコスト面はクロージャの勝利なのでした。
ただしこれが原因で遅いのかどうかは確認してないので、ご了承をば