criterionを使った性能測定の実行

 それでは,性能測定用のプログラムを実行してみましょう。他のプログラムはなるべく終了させておいてください。省電力機能を無効にし,スクリーンセーバーのようなプロセスもベンチマーク中に起動しないよう設定してください。

 システムのアイドル時まで実行が保留されたプロセスは,先に実行しておきましょう。Windowsでは,保留中のプロセスを実行するためのProcessIdleTasksというAPIが用意されています。このAPIをRundll32.exeなどを使って呼び出します(参考リンク1参考リンク2)。

Rundll32.exe advapi32.dll,ProcessIdleTasks

 UNIX環境では,保留中のプロセスを実行するためのAPIは用意されていません。そこで,sleepを使って意図的にアイドル状態を作り,保留されたプロセスの実行を促します(参考リンク)。

sleep 100

 Windowsでの性能測定のための準備については,Microsoftが提供する「Windows のパフォーマンス テスト ガイド」という文書に詳しく書かれています。より正確に試験を行いたい方は参考にするとよいでしょう。

 なお,これらの準備は,GHCを使って性能測定用のプログラムを作成する前ではなく,プログラムを作成した後に行ってください。GHCの実行中や実行後に溜まったタスクの後始末を行う必要があるからです。

 準備ができたらプログラムを実行します。

warming up
estimating clock resolution...
mean is 12.10953 us (80001 iterations)
found 159936 outliers among 79999 samples (199.9%)
  79937 (99.9%) low severe
  79999 (100.0%) high severe
estimating cost of a clock call...
mean is 256.3477 ns (64 iterations)
~ 続く ~

 criterionでは,性能測定の前に,プログラムが安定して動作するよう暖機運転(warming up)を行います。次に「計測の分解能(clock resolution)」や「計測コスト(cost of a clock call)」といった,性能測定の処理自体から発生する測定誤差を除去するのに必要な環境情報を取得します(参考リンク1参考リンク2)。これらの環境情報を基に性能を測定します(参考リンク1参考リンク2)。

~ 続き ~
benchmarking sort/ascending
collecting 100 samples, 1 iterations each, in estimated 1.562500 s
bootstrapping with 100000 resamples
Warning: Couldn't open "/dev/urandom"
Warning: using system clock for seed instead (quality will be lower)
mean: 7.499744 ms, lb 6.093494 ms, ub 9.062244 ms, ci 0.950
std dev: 7.845574 ms, lb 7.750703 ms, ub 7.851858 ms, ci 0.950
variance introduced by outliers: 1.000%
variance is unaffected by outliers

benchmarking sort/descending
collecting 100 samples, 1 iterations each, in estimated 1.562500 s
bootstrapping with 100000 resamples
mean: 6.718494 ms, lb 5.155994 ms, ub 8.124744 ms, ci 0.950
std dev: 7.774529 ms, lb 7.438988 ms, ub 7.851858 ms, ci 0.950
variance introduced by outliers: 1.000%
variance is unaffected by outliers

benchmarking sort/randoms
collecting 100 samples, 1 iterations each, in estimated 4.687500 s
bootstrapping with 100000 resamples
mean: 12.65599 ms, lb 11.24974 ms, ub 13.59349 ms, ci 0.950
std dev: 6.160582 ms, lb 5.103104 ms, ub 6.971813 ms, ci 0.950
found 181 outliers among 100 samples (181.0%)
  100 (100.0%) low severe
  81 (81.0%) high severe
variance introduced by outliers: 1.000%
variance is unaffected by outliers

 「benchmarking ...」の部分に表示された「sort/ascending」は,「<bgroup関数に渡したグループ名>/<bench関数に渡した測定対象名>」を表しています。criterionは,Benchmark型を作成する時に渡した文字列から測定対象名を生成します。

 測定対象名に続く2行は,測定するデータの標本に関する情報と,性能を推定するために使用する統計手法を表示しています。

collecting 100 samples, 1 iterations each, in estimated 1.562500 s
bootstrapping with 100000 resamples

 「collecting ... samples」には抽出する標本数,「iterations each」には一つの標本あたりの処理の試行回数,「in estimated ...」には標本を抽出するのにかかるだいたいの実行時間が表示されています。次の行は,ブートストラップ法(bootstrapping)という統計手法を使い,標本の再抽出(reasampling)を100000回行うことを示しています。ブートストラップ法については,Wikipediaのブートストラップ法の項目やブートストラップ法について解説した統計学のページなどを見てください(参考リンク1参考リンク2)。

 なお,Windows環境では,最初の測定対象に対してブートストラップ法を使って性能測定を行う前に,以下の警告メッセージが表示されます。

Warning: Couldn't open "/dev/urandom"
Warning: using system clock for seed instead (quality will be lower)

 これは,criterionパッケージが依存するmwc-randomパッケージの関数から発生したものです。この警告メッセージは,「乱数の種(seed)を決めるのにUnixの/dev/urandomが使えなかっため,代わりにシステムクロック(system clock)を乱数の種として使った」という意味です(参考リンク)。乱数の種は性能測定には影響を及ぼさないので,この警告メッセージは無視してかまいません。

 測定が終了したら,測定した性能の統計結果が出力されます。

mean: 7.499744 ms, lb 6.093494 ms, ub 9.062244 ms, ci 0.950
std dev: 7.845574 ms, lb 7.750703 ms, ub 7.851858 ms, ci 0.950

 「mean:」は平均実行時間(mean time),「std dev:」は実行時間のばらつきを示す標準偏差(standard deviation),lbは平均実行時間や標準偏差の下限値(lower bound),ubはこれらの上限値(upper bound),ciは100%を1とした場合の「信頼区間(confidence interval)」を表します。nsはナノ秒,usはマイクロ秒の略です。時間の表示単位は,Criterion.Measurementモジュールのsecs関数で決定されています(参考リンク)。

 最後に,何らかの原因で発生した異常値(outliers)が統計結果にどの程度影響を及ぼしたかが表示されます。

variance introduced by outliers: 1.000%
variance is unaffected by outliers

 この例では,異常値によって発生した分散(variance)が1%以下なので,異常値の影響は特に見られないという結果になっています。この異常値の発生頻度が多い場合には,測定結果が信頼できない可能性があります。特に50%以上の頻度で異常値が発生した場合には,測定結果が異常値に著しく影響を受けているため,測定をやり直す必要があるでしょう(参考リンク)。

 criterionは,デフォルトでは簡単な文字情報だけを出力します。これに加え,criterionでは統計結果を可視化する機能も提供されています。

 criterionのオプションとして--plot-timing(または,これを省略した-t)を指定することで,利用した標本を時系列に並べたデータを出力できます。

$ ./bench -t png

 また,--plot-kde(または,これを省略した-k)を指定することで,カーネル密度推定(kernel density estimation)を使って求めた標本内での値の頻度を出力できます(カーネル密度推定については,Wikipediaのカーネル密度推定の項目を参照してください)。

$ ./bench -k png

 「<出力形式>:<横幅>x<縦幅>」と指定することで,出力するファイルの形式やウィンドウのサイズを変更できます。

$ ./bench -k png:450x175

 -tオプションや-kオプションは単体では使えません。これらのオプションを使う場合には,出力するデータの形式を必ず指定する必要があります(参考リンク)。形式を指定しない場合には,以下のようなエラー・メッセージが表示されます。

$ ./bench -t
Error: option `-t' requires an argument TYPE
Run "bench.exe --help" for usage information

 グラフ描画機能を無効にしてインストールした場合には,CSV形式でしかデータを出力できません。一方,グラフ機能を有効にしてインストールした場合には,出力形式にwinを指定してウィンドウに表示したり,PDFやPNG,SVGといったデータ形式で出力したりできます(参考リンク1参考リンク2)。

 Windowsでファイルを出力する場合には,注意すべき点があります。criterion 0.5.0.5は,本来は「<bgroup関数に渡したグループ名>-<bench関数に渡した測定対象名>-<出力フォーマット>」というファイル名でデータを出力します。ところが,Windowsではファイル名が「<bgroup関数に渡したグループ名>/<bench関数に渡した測定対象名>-<出力フォーマット>」になってしまうバグがあります(参考リンク1参考リンク)。このため,「<bgroup関数に渡したグループ名>」のディレクトリを事前に作成しておかなければ,ファイルの出力に失敗してしまいます。

$ ./bench -k csv
~ 略 ~
bench.exe: sort/ascending-densities.csv: openBinaryFile: does not exist (No such
 file or directory)

$ ./bench -k png
~ 略 ~
bench.exe: user error (error while writing to output stream)

 ここまでは,オプションを使ってciriterionを使った性能測定プログラムの挙動を変えてきましたが,オプションを使わずに挙動を変更することもできます。Criterion.MainモジュールのdefaultMainWith関数に,Criterion.ConfigモジュールのdefaultConfigのフィールドを書き換えた値を渡す方法です(参考リンク)。特定のオプションを毎回使用する場合には,オプションをいちいち指定するよりも,defaultConfigのフィールドを書き換えてデフォルトの挙動を変えたほうがよいでしょう。