複数バージョンのモジュールで同じコードを実行してBenchmark.pmで計測したい、というケースにこう書けばいいかな、という例。
普通にBenchmark.pmを使ってしまうと同一モジュールを違うバージョンで複数読み込むことができないため、
- バージョンごとに子プロセスを fork
- 子プロセスで use lib して @INC を追加してから use
- 計測した結果をファイルに保存して親プロセスで集約、表示
という流れで書きます。
use strict; use Benchmark qw/ :all /; use File::Temp qw/ tempfile /; use Storable qw/ nstore retrieve /; # 計測したいコード my $code = sub { my $c = Cache::Redis->new; for ( 1 .. 10 ) { $c->set( $_ => $_ ); $c->get($_); } }; my $result = {}; for my $name (qw/ extlib-0.01 extlib-0.02 /) { my ($fh, $fn) = tempfile(); my $pid = fork; if ($pid) { close $fh; wait; # 子プロセスの終了を待つ } else { eval qq{use lib '$name/lib/perl5'}; # 読み込み先追加 eval qq{use Cache::Redis}; # use する my $r = timethis(1000, $code); # 計測して nstore $r, $fn; # ファイルに保存 exit; # 子プロセスは終了 } $result->{$name} = retrieve $fn; # 親で読み込んで集約 }; cmpthese $result; # 結果表示
あとは cpanm で、git のタグごとに別の場所に install して計測できます。
$ cpanm -n -l extlib-0.01 git://github.com/Songmu/p5-Cache-Redis.git@0.01 $ cpanm -n -l extlib-0.02 git://github.com/Songmu/p5-Cache-Redis.git@0.02 $ perl bench.pl timethis 1000: 3 wallclock secs ( 1.49 usr + 0.53 sys = 2.02 CPU) @ 495.05/s (n=1000) timethis 1000: 3 wallclock secs ( 1.61 usr + 0.56 sys = 2.17 CPU) @ 460.83/s (n=1000) Rate extlib-0.02 extlib-0.01 extlib-0.02 461/s -- -7% extlib-0.01 495/s 7% --
このような働きをモジュールにして切り出したら嬉しいですかね…?
[2013-12-12 追記]
モジュール化してほしいとリクエストされたので念のため検索してみたら、Benchmark::Forking というモジュールを発見。これを使うと、上記のコードは以下のように書けます。すっきり。
use strict; use Benchmark::Forking qw/ :all /; my $code = sub { my $c = Cache::Redis->new; for ( 1 .. 10 ) { $c->set( $_ => $_ ); $c->get($_); } }; cmpthese(1000, { 'extlib-0.02' => sub { unless ($INC{"Cache/Redis.pm"}) { eval qq{use lib 'extlib-0.02/lib/perl5'}; eval qq{use Cache::Redis}; } $code->(); }, 'extlib-0.01' => sub { unless ($INC{"Cache/Redis.pm"}) { eval qq{use lib 'extlib-0.01/lib/perl5'}; eval qq{use Cache::Redis}; } $code->(); }, });