細かいプログラムのベンチマークは、誤差の影響をものすごく受けます。
たとえば、intとdoubleの足し算はどちらが速いかを調べるのに、
int sum = i + j;
Console.WriteLine(sum);
なんてプログラムを書いた時には、計測されるのは殆ど
Console.WriteLine
の性能です。
ベンチマークを正しく取るのは、結構大変です。
一つでも変な事をすると、役に立たないゴミデータができるだけです。
さて、最近気を付けていることを以下にメモしておきます。
・Consoleに出さない
Consoleに書き出すのは、結構遅いので、実際の計測の際には、必ず外します。
動きを見たくて入れる場合は当然ありますが、#if DEBUG とか使って、Releaseでは入らないようにしたりします。
なお、Consoleがだめなら、ファイルに出そうと思う人もいるかもしれませんが、ファイルに出すのも遅いのでダメです。
出力する必要がある時は、メモリに貯めておいて、計測後に出力するようにしています。
その場合、出力を整形する作業とかが、計測対象になるべく入らないように気を付けます。
・Debugビルドで実行しない
BenchmarkDotNetを使う場合は、そもそもDebugでは実行できないから良いのだけれど、Stopwatchを使う場合は要注意。
DeubgとReleaseではかなり速度差が出ます。
・時間が違うデータをミックスしない
裏でUpdateが動いていたりして、遅くなったりする場合もあります。
他のプログラムがどの程度動いているかの影響も受けます。
なので、複数のコードの比較をする場合、続けて計測するようにしています。
計測する時間が空いただけ、不確実な状態変化が増えるからです。
シビアな場合は、A→B→A→B→A→B
みたいに複数回計測して、変化がないことを確かめたりもします。
・ウォームアップ
これも、主にStopwatchを使うケースです。
特に、初回の動作が遅い場合が多いです。
A,Bを計測したら、Bの方が速いと出た場合、B,Aで計測したら、Aの方が速いと出たりします。
事前に、ダミーループを回したり、複数回計測したりしています。
また、ネットワークの計測の際、他のプログラムが(ベンチマークと全く関係の無い)通信をしていると、速くなるという現象もありました。
ネットワークのウォームアップも必要ということですが、
なかなか、どうするべきかは悩ましいところです。
・計測回数の統一
小さなプログラムの計測をする場合、1000回回して計測したりします。
その場合、途中で遅すぎたりして、回数を変えたりすることもあります。
問題が起きるのは、A,Bの比較で、Aは1000回なのに、Bが100回だったりすると、もう、比較が意味ないことになります。
面倒でも、ループ回数とかは定数(const)にして、A,Bで回数が違いというったことが、絶対に起きないようにしておくのがお勧めです。
・計測する対象をよく考える
Listの計測をする場合、作る場合と使う場合のどちらを測りたいのかとか。
使うときの速度を測りたいのに、作る部分まで計測対象に含めたりすると、欲しいデータが手に入りません。
ちなみに、破棄のコストは、GCが動くタイミングになるので、
繰り返し実行していると、作る時のコストに、破棄コストが含まれるようになるそうです。
・テストをする
ベンチマークの対象プログラムにバグがあって、そもそも動作が間違っていたら、意味がありません。
ベンチマークを実行する前に、テストコードを書いて実行して、結果が正しいことを先に確認してから、ベンチマークを実行します。