switch文とif連鎖の比較
Perl 5.10.0でswitch文が導入されたことにより,ifの連鎖はもっとわかりやすい構文に書き換えられるようになった。
use feature 'switch'; given($foo){ when("bar"){ ... } when("baz"){ ... } default{ ... } }
switch文の導入で便利になった一方,このwhen()で使われるマッチングは「スマートマッチ」という特殊な演算子によって行われることのコストが懸念される。
そこで,switch文とif連鎖の速度を比較してみた。
また,かつて単純なif連鎖とよく比較されたのは,ハッシュテーブルを参照する方式である。if連鎖の複雑さがO(n)なのに対し,ハッシュテーブルの参照はO(1)であるため,連鎖が長くなるとハッシュテーブルの方が高速であるとされる。『Perlベストプラクティス』では,ハッシュテーブルのほうが保守性もよいため,if連鎖は極力避けるべきだと提唱している。そこで,今回のベンチマークではハッシュテーブルの参照も考慮に入れた。
結果は上から,(1)平均的なケース,(2)if連鎖が有利なケース,(3)if連鎖が不利なケースとなっている。
結果(perl 5.10.0 linus multi-threaded -DDEBUGGING):
(1) Rate switch if-chain table switch 14355/s -- -11% -12% if-chain 16144/s 12% -- -1% table 16290/s 13% 1% -- (2) Rate switch table if-chain switch 15858/s -- -4% -11% table 16439/s 4% -- -7% if-chain 17772/s 12% 8% -- (3) Rate switch if-chain table switch 13524/s -- -13% -17% if-chain 15605/s 15% -- -4% table 16291/s 20% 4% --
これをみると,if連鎖とテーブル参照は差がなく,switch文はその他二つより常に遅いようである。意外なことに,10個程度の連鎖では特にテーブル参照が高速ともいえないようだ。
#!perl -w use strict; use Benchmark qw(:all); sub do_something{ my $sum = 0; for my $i(1 .. 5){ $sum += $i; } } my @procs = qw(cat dog rat mouse moose squirrel zebra bison yak python); my %tab = map{ $_ => sub{ do_something() } } @procs; my $i = 0; for my $x([@procs, @procs], [@procs, ($procs[0]) x @procs], [@procs, ($procs[-1]) x @procs]){ my @ary = @{$x}; $i++; print "($i)\n"; cmpthese -1 => { 'table' => sub{ for my $proc(@ary){ $tab{$proc}->(); } }, 'if-chain' => sub{ for my $proc(@ary){ if($proc eq 'cat'){ do_something(); } elsif($proc eq 'dog'){ do_something(); } elsif($proc eq 'rat'){ do_something(); } elsif($proc eq 'mouse'){ do_something(); } elsif($proc eq 'moose'){ do_something(); } elsif($proc eq 'squirrel'){ do_something(); } elsif($proc eq 'zebra'){ do_something(); } elsif($proc eq 'bison'){ do_something(); } elsif($proc eq 'yak'){ do_something(); } elsif($proc eq 'python'){ do_something(); } else{ die $proc; } } }, switch => sub{ for (@ary){ use feature 'switch'; when('cat'){ do_something(); } when('dog'){ do_something(); } when('rat'){ do_something(); } when('mouse'){ do_something(); } when('moose'){ do_something(); } when('squirrel'){ do_something(); } when('zebra'){ do_something(); } when('bison'){ do_something(); } when('yak'){ do_something(); } when('python'){ do_something(); } default{ die $_; } } } }; }