Benchmark - Perl コードの実行時間のベンチマークを行なう
use Benchmark qw(:all) ;
timethis ($count, "code");
# Use Perl code in strings...
timethese($count, {
'Name1' => '...code1...',
'Name2' => '...code2...',
});
# ... or use subroutine references.
timethese($count, {
'Name1' => sub { ...code1... },
'Name2' => sub { ...code2... },
});
# cmpthese can be used both ways as well
cmpthese($count, {
'Name1' => '...code1...',
'Name2' => '...code2...',
});
cmpthese($count, {
'Name1' => sub { ...code1... },
'Name2' => sub { ...code2... },
});
# ...or in two stages
$results = timethese($count,
{
'Name1' => sub { ...code1... },
'Name2' => sub { ...code2... },
},
'none'
);
cmpthese( $results ) ;
$t = timeit($count, '...other code...')
print "$count loops of other code took:",timestr($t),"\n";
$t = countit($time, '...other code...')
$count = $t->iters ;
print "$count loops of other code took:",timestr($t),"\n";
# enable hires wallclock timing if possible
use Benchmark ':hireswallclock';
Benchmark モジュールは、コードの実行時間を計測する手助けをするルーチン群を カプセル化するものです。
timethis - コードを何回か実行する
timethese - いくつかのコードを何回か実行する
cmpthese - timethese の結果を比較表として表示する
timeit - コードを実行し、時間を計測する
countit - コードの塊を与えられた時間内に何回実行できるかを調べる
現在時刻を返します。 例えば:
use Benchmark;
$t0 = new Benchmark;
# ... your code here ...
$t1 = new Benchmark;
$td = timediff($t1, $t0);
print "the code took:",timestr($td),"\n";
$Benchmark::debug
フラグを設定することによって、デバッグを有効にしたり、 無効にしたりします:
debug Benchmark 1;
$t = timeit(10, ' 5 ** $Global ');
debug Benchmark 0;
繰り返し回数を返します。
以下のルーチンは、Benchmark モジュールを使うときに、現在の名前空間へ エクスポートされます:
引数: COUNT は、ループの実行回数で、CODE は実行するコードです。 CODE は、コードリファレンスか eval される文字列のどちらかです; どちらの場合も呼び出し側のパッケージで実行されます。
返り値: Benchmark オブジェクトを返します。
CODE を COUNT 回繰り返した時間を計ります。 CODE は eval する文字列か、コードリファレンスです; どちらの場合でも CODE は呼び出し側のパッケージで実行されます。 結果は、TITLE に引き続いて時間、という形で STDOUT に出力されます。 TITLE が指定されない場合、デフォルトは "timethis COUNT" です。 STYLE は出力形式で、後述する timestr() に記述されています。
COUNT はゼロや負数も指定できます: これは、実行する CPU 秒の最低値 を 意味します。 ゼロはデフォルトの 3 秒を意味します。 例えば、最低 10 秒実行するには:
timethis(-10, $code)
あるいは、2 つのコードの断片を最低 3 秒実行するには:
timethese(0, { test1 => '...', test2 => '...'})
CPU 秒は UNIX 用語で、実(時計)時間や子プロセスで使われた時間ではなく、 プロセス自身のユーザー時間にシステム時間を加えたものです。 0.1 秒より小さい値は指定できません (例えば、もし COUNT として -0.01 を 指定すると、致命的ランタイム例外が発生します)。
CPU 秒は 最低 時間であることに注意してください: CPU スケジューリングと、その他の OS の要素がこの試みを 複雑にしているので、ほんのもう少し多くの時間が使われます。 しかし、ベンチマーク出力は $code
の 1 秒当たりの実行数を表示します; これは実際にかかった時間より興味深い数字のはずです。
Benchmark オブジェクトを返します。
CODEHASHREF は、キーとして名前、値として eval する文字列かコードリファレンスを 含むハッシュへのリファレンスです。 CODEHASHREF のそれぞれの (KEY, VALUE) ペアについて、このルーチンが 呼び出されます
timethis(COUNT, VALUE, KEY, STYLE)
このルーチンは KEY の文字列比較順で呼び出されます。
COUNT は 0 や負数になることがあります; timethis() を参照してください。
名前をキーとした、Benchmark オブジェクトのハッシュリファレンスを返します。
二つの Benchmark 時間の差を、timestr() に渡すのに適した Benchmark オブジェクトで返します。
TIMEDIFF オブジェクトの時間を要求された STYLE で整形した 文字列を返します。 TIMEDIFF は timediff() で返されるのと同様の Benchmark オブジェクトであることを想定しています。
STYLE は 'all', 'none', 'noc', 'nop', 'auto' のいずれかです。 'all' は利用可能な五つの時間(「壁時計」時間、ユーザ時間、 システム時間、子プロセスのユーザ時間、子プロセスのシステム時間)を それぞれ表示します。 'noc' は二つの子プロセスの時間以外の全てを表示します。 'nop' は壁時計時間と二つの子プロセスの時間だけを表示します。 'auto' (これがデフォルトです) は、子プロセスの時間が両方とも 0 の場合は 'noc' として振る舞い、それ以外では 'all' として振る舞います。 'none' は何も出力しません。
FORMAT は、時間を表示するために使われる printf(3)-形式のフォーマット指定子 (先頭の '%' 抜き)です。 デフォルトは '5.2f' です。
以下のルーチンは、明示的にインポートを要求することで、現在の名前空間へ エクスポートされます:
キャッシュされている、空ループを COUNT 回回した時間をクリアします。
全てのキャッシュされた時間をクリアします。
状況に応じて timethese() を呼び出し、それから比較表を出力します。 これは:
cmpthese( -1, { a => "++\$i", b => "\$i *= 2" } ) ;
以下のような表を出力します:
Rate b a
b 2831802/s -- -61%
a 7208959/s 155% --
この表は遅いものから早いものの順にソートされ、それぞれのテスト間の 速度の差を百分率で表示します。
cmpthese
には timethese() が返すデータ構造体を渡すことも出来て:
$results = timethese( -1, { a => "++\$i", b => "\$i *= 2" } ) ;
cmpthese( $results );
両方の結果セットを見たいときに使えます。 最初の引数が bless されていないハッシュリファレンスの場合、これは RESULTSHASHREF です; それ以外の場合はこれは COUNT です。
それぞれの行が上述の(ラベル込みの)セルの配列からなる、行の配列への リファレンスを返します。 こうすると:
my $rows = cmpthese( -1, { a => '++$i', b => '$i *= 2' }, "none" );
以下のようなデータ構造を返します:
[
[ '', 'Rate', 'b', 'a' ],
[ 'b', '2885232/s', '--', '-59%' ],
[ 'a', '7099126/s', '146%', '--' ],
]
注意: この結果値は、timethese()
の結果構造体を返していた 以前のバージョンとは異なります。 もしそれがほしいなら、上述した timethese
...cmpthese
の 2 文の 記述法を使ってください。
偶然ながら、二つの例における結果値の違いに注意してください; これは ベンチマークでは典型的です。 もしこれが実際のベンチマークなら、おそらくもっとたくさんの回数繰り返して 実行したいでしょう。
引数: TIME は CODE を実行するための最短時間で、CODE は実行するコードです。 CODE はコードリファレンスか、eval される文字列です; どちらの場合も 呼び出し元のパッケージで実行されます。
TIME は負数では ありません 。 countit() は、TIME のために実行する前に、CORE の速度を測るために 何度もループを実行します。 実際の実行時間は普通はシステムクロックの粒度のために TIME よりも 大きくなるので、単に繰り返し回数ではなく、繰り返し回数を関心のある 回数で割ったものを見るのが最良です。
返り値: Benchmark オブジェクトです。
空ループのための時間のキャッシュを無効にします。 これにより、Benchmark にコードの時間を計る毎に空ループの時間を 再計算することを強制します。
空ループのための時間のキャッシュを有効にします。 異なる COUNT が使われる毎に 1 回だけ、COUNT 回の空ループの所要時間が 計算されます。
二つの Benchmark 時間の和を、timestr() に渡すのに適した Benchmark オブジェクトで返します。
Time::HiRes モジュールがインストールされているなら、Benchmark のための 特別な :hireswallclock
タグを指定できます (もし Time::HiRes が 利用できないなら、このタグは暗黙に無視されます)。 このタグにより、壁時計時間は整数秒ではなく、マイクロ秒で計測されます。 しかし、速度計算は以前として壁時計時間ではなく CPU 時間によって 行われることに注意してください。
データは、time 関数や times 関数による値のリストとして:
($real, $user, $system, $children_user, $children_system, $iters)
(各々の繰り返しごとではなく) ループ全体を秒数で計測して蓄えられます。
計時は、time(3) と times(3) を使って行なわれます。
コードは、呼び出し元のパッケージで実行されます。
空ループ (繰り返し数は同じですが、空のループ) の時間が、実際のループの 時間から差し引かれます。
計算された空ループの実行時間は、繰り返しの数をキーとして、 キャッシュされます。 キャッシュ化は、以下のようなサブルーチンの呼び出しで制御できます:
clearcache($key);
clearallcache();
disablecache();
enablecache();
キャッシュ化はデフォルトではオフです; これは(普通はわずかですが) 正確性を減少させ、普通はたいして実行時間に影響を与えないからです。
例えば、
use Benchmark qw( cmpthese ) ;
$x = 3;
cmpthese( -5, {
a => sub{$x*$x},
b => sub{$x**2},
} );
とすると、以下のようなものが出力されます:
Benchmark: running a, b, each for at least 5 CPU seconds...
Rate b a
b 1559428/s -- -62%
a 4152037/s 166% --
一方
use Benchmark qw( timethese cmpthese ) ;
$x = 3;
$r = timethese( -5, {
a => sub{$x*$x},
b => sub{$x**2},
} );
cmpthese $r;
は以下のようなものが出力されます:
Benchmark: running a, b, each for at least 5 CPU seconds...
a: 10 wallclock secs ( 5.14 usr + 0.13 sys = 5.27 CPU) @ 3835055.60/s (n=20210743)
b: 5 wallclock secs ( 5.41 usr + 0.00 sys = 5.41 CPU) @ 1574944.92/s (n=8520452)
Rate b a
b 1574945/s -- -59%
a 3835056/s 144% --
Benchmark は、Exporter からは当然継承を行なっていますが、その他の クラスからは継承を行ないません。
eval された文字列とコードリファレンスを比べると、不正確な結果となります; コードリファレンスは等価な eval された文字列よりも少し実行が遅いです。
実際の時間の計測は、time(2) を使って行なわれるので、精度は 1 秒程度しかありません。
perl では、空ループの方が短いテストよりも時間がかかる場合があるので、 短いテストでは、結果が負数になる場合があります; 以下のようにしてみてください:
timethis(100,'1');
空ループのシステム時間は、実際のコードを含むループのシステム時間よりも 多少多くかかることがあるため、最終的に差がゼロより小さくなることが あるのです。
Devel::DProf - a Perl コードプロファイラ
Jarkko Hietaniemi <jhi@iki.fi>, Tim Bunce <Tim.Bunce@ig.co.uk>
September 8th, 1994; by Tim Bunce.
March 28th, 1997; by Hugo van der Sanden: added support for code references and the already documented 'debug' method; revamped documentation.
April 04-07th, 1997: by Jarkko Hietaniemi, added the run-for-some-time functionality.
September, 1999; by Barrie Slaymaker: math fixes and accuracy and efficiency tweaks. Added cmpthese(). A result is now returned from timethese(). Exposed countit() (was runfor()).
December, 2001; by Nicholas Clark: make timestr() recognise the style 'none' and return an empty string. If cmpthese is calling timethese, make it pass the style in. (so that 'none' will suppress output). Make sub new dump its debugging output to STDERR, to be consistent with everything else. All bugs found while writing a regression test.
September, 2002; by Jarkko Hietaniemi: add ':hireswallclock' special tag.
February, 2004; by Chia-liang Kao: make cmpthese and timestr use time statistics for children instead of parent when the style is 'nop'.
November, 2007; by Christophe Grosjean: make cmpthese and timestr compute time consistently with style argument, default is 'all' not 'noc' any more.