Perlの最適化 - aelemfast
先の記事のOpcodeトレース(APVM版)に以下のようなOpcodeがあった。
.aelemfast[@ARGV[0]] SCALAR
これはPerlの最適化器*1が最適化の結果生成したOpcodeで,配列の添え字が定数かつ255以下のときにOpcodeのaelemから変換されるものだ。
もともとのaelemは,配列と添え字をスタックに積み,その二つの値を参照して配列要素を返すOpcodeである。これがaelemfastになると,一つのOpcodeに配列と添え字を共に保存するため,実行されるOpcodeの数は減り,スタックも使わなくなる。
細かい話はさておき,実際の効果を見てみる。
perl 5.10.0 linux-thread-multi, -DDEBUGGING:
Rate aelem aelemfast scalar aelem 3829/s -- -37% -41% aelemfast 6108/s 60% -- -5% scalar 6461/s 69% 6% --
Rate aelem aelemfast scalar aelem 5219/s -- -20% -32% aelemfast 6516/s 25% -- -15% scalar 7657/s 47% 18% --
aelemは$a[300],aelemfastは$a[100], scalarは比較のためのただのスカラー変数の参照である。
バージョンやビルドオプションによっても異なるが,この最適化が効くとスカラー変数の参照には劣るものの,通常の配列要素参照よりも25%から60%ほど高速になる。
これだけみると非常に素晴らしい。しかし,実際にはこの最適化を役立てるのは難しい。この最適化は配列を直接参照しないと効かず,リファレンスによる参照には適応されないからである。
これがリファレンス参照のときにも効くようになれば,オブジェクトを配列のリファレンスで実装することの優位性が高まると思われるのだが。
#!perl -w use strict; use Benchmark qw(:all); our @x; $x[100]++; # aelemfast $x[300]++; # aelem my $s; $s++; # scalar printf "Benchmark (perl %vd, $^O)\n", $^V; cmpthese -1 => { aelemfast => sub{ for my $i (1 .. 1000){ $x[100] = $i; } }, scalar => sub{ for my $i (1 .. 1000){ $s = $i; } }, aelem => sub{ for my $i (1 .. 1000){ $x[300] = $i; } }, }; __END__