避暑地でハッカソンがしたい。
前置きー。
JavaScript で、配列の長さを評価する必要がないとき、
以下のよう配列長をキャッシュすると思います。
配列の長さが変わらないなら、、
for ( var i = 0 ; i < arr.length; i++ ) { //do something}
配列長の変数に入れておく。
for ( var i = 0, l = arr.length; i < l; i++ ) { //do something}
それで、Perlでもおんなじ風味で、
キャッシュしておいた方がいいと思うと、やっぱり予想通りでした。
use strict; use warnings; use Benchmark qw/cmpthese/; my @arr = 0..100; # Benchmark js style cmpthese(100000, { 'js' => sub { my $total = 0; for (my $i = 0; $i < scalar @arr ; $i++) { $total += $arr[$i]; } }, 'js cache' => sub { my $total = 0; for (my $i = 0, my $l = scalar @arr; $i < $l ; $i++) { $total += $arr[$i]; } }, });
ベンチ。
Rate js js cache js 51020/s -- -19% js cache 63291/s 24% --
この後の本題のために、この例のfor文を簡素化してみてベンチ。
use strict; use warnings; use Benchmark qw/cmpthese/; my @arr = 0..100; # Benchmark coding style cmpthese(100000, { 'array' => sub { my $total = 0; $total += $_ for @arr; }, 'js cache' => sub { my $total = 0; for (my $i = 0, my $l = scalar @arr; $i < $l ; $i++) { $total += $arr[$i]; } }, });
変数の代入が無い分速いのかな?と疑問は残りつつ、前置き終了。
Rate js cache array js cache 66667/s -- -39% array 108696/s 63% --
本題ー。
ここで予想したのが、
「配列長をキャッシュしておいた方がいいなら、 配列のデリファレンスしたものもキャッシュしておいた方がいいのかな?」
でした。
ですが、違いました以下のように配列のキャッシュを十分活かせない形だと、デリファレンスするコストの方が高くなるようです(2013-11-19修正)
具体的には、以下のベンチ。
use strict; use warnings; use Benchmark qw/cmpthese/; my @arr = 0..100; my $arr = \@arr; sub get_arr { return @arr; } sub get_arr_ref { return $arr; } # Benchmark dereference cmpthese(100000, { 'array' => sub { my $total = 0; $total += $_ for @arr; }, 'get_array' => sub { my $total = 0; $total += $_ for get_arr(); }, 'get_array cache?' => sub { my $total = 0; my @a = get_arr(); #キャッシュ? $total += $_ for @a; }, }); # Benchmark dereference cmpthese(100000, { 'array_ref' => sub { my $total = 0; $total += $_ for @$arr; }, 'get_array_ref' => sub { my $total = 0; $total += $_ for @{ get_arr_ref() }; }, 'get_array_ref cache?' => sub { my $total = 0; my @a = @{ get_arr_ref() }; #キャッシュ? $total += $_ for @a; }, });
ベンチ結果。
Rate get_array cache? get_array array get_array cache? 40161/s -- -43% -64% get_array 69930/s 74% -- -38% array 112360/s 180% 61% --
リファレンスとして扱った方
Rate get_array_ref cache? get_array_ref array_ref get_array_ref cache? 49261/s -- -55% -56% get_array_ref 108696/s 121% -- -3% array_ref 112360/s 128% 3% -- 2% --
デリファレンスしておいたものを変数代入(キャッシュ?)しておいた方が遅かったです。
どうしてこういう結果になったのか、腹落ちしてないので、たぶん、、調べる、、、、
- 代入のコストが意外と大きい?
- あと意外なのが、ger_array より、get_array_ref の方が速かったこと。
蛇足。
Sledge のコードを読んでいて、登録されているトリガーをぶん回しているところを読んでいて沸いた疑問でした。
miyagawaさんが、書くなら、それがいいんだろうと思ってベンチを取ってみて、やっぱりそうでした。
追記
sort についても、、、
ループのたびに評価されるって思ってた、、、完全な勘違い、、残念すぎる、、orz
use List::Util qw/shuffle/; my @num = shuffle 0..9; # Benchmark dereference cmpthese(1000000, { 'simple' => sub { my $s = ''; $s .= $_ for sort @num; }, 'cache?' => sub { my $s = ''; my @sorted_num = sort @num; $s .= $_ for @sorted_num; }, });
ベンチ
Rate cache? simple cache? 128041/s -- -58% simple 305810/s 139% --
追記2 2013-11-19
ベンチが十分、arrayのキャッシュを活かせる形じゃなかった。
コメントのnyaさんの指摘で気づきました。ありがとうございます><