BenchmarkDotNetの概要

github.com

  • .NETアプリケーション用の標準ベンチマークパッケージ
  • Stopwatchでログ吐くより信頼性高い
  • JITGC・CPU動作クロックなどによる結果のばらつきを極力排除(たぶん)

こんな感じ

Method Mean Error StdDev Scaled Allocated
Dictionary 145.376 ns 6.3041 ns 0.3562 ns 1.000 0 B
Cache 1.294 ns 0.0947 ns 0.0053 ns 0.009 0 B

Mean:平均、Error:誤差、StdDev:標準偏差、Scaled:基準値に対する倍率、Allocated:ヒープ上のメモリアロケーション

以下、公式のドキュメントから抜粋など。 Overview | BenchmarkDotNet

使い方

  1. Consoleアプリのプロジェクトを追加
  2. NuGetでBenchmarkDotNetをインストールする
  3. ベンチマーク取りたいメソッドを書く
  4. 実行するところとかはコピペしてくる
  5. Releaseビルドにしてデバッグなし実行(Ctrl+F5)
    • Visual Studioやその他アプリをできるだけ終了して、直接exe起動した方が良い
    • ノートPCでは電源設定でパフォーマンス最優先にする

有償版LINQPad上でもNuGetは使えるので動きます。ただしLINQPad自体のOptimize設定(=Releaseビルド設定)が必要なのでデバッグ実行されなくなることに注意

FAQ (Frequently asked questions) | BenchmarkDotNet

サンプル

Sample: IntroBasic | BenchmarkDotNet

私が書いてみたのはこちら。

GitHub - cactuaroid/BenchmarkDotNetSample

注意事項

値を使わない以下のようなコードを書いてしまうとJITの最適化で消されてしまう。

void Foo()
{
    Math.Exp(1);
}

ので、値を返すようにする。

double Foo()
{
    return Math.Exp(1);
}

Good Practices | BenchmarkDotNet

例えばこんな時に

  • カジュアルに実装方法の実行時間・メモリアロケーション比較に
  • パフォーマンス問題の修正効果の測定に*1
  • 実行時間が重要な箇所の監視に
    • 日次ビルドでベンチマークを取るようにして閾値を超えたらレポートとか
    • CIに組み込んで閾値を超えたらビルド警告とか

機能

  • パラメータ振ったり組み合わせたりできる
  • 基準ケースを指定して比較できる
  • ベンチマーク前後にSetup/Cleanupできる
  • 計測結果の統計値を取れる
    • 通常は実行時間の平均を調べる
    • マイクロベンチマークではない場合は分布が広くなりがちなので、代わりにパーセンタイルを調べることも可能
  • IL吐ける(コンパイラ比較用)

Parameterization | BenchmarkDotNet

内部の動き

  1. 設定に応じて独立したプロジェクトを生成
  2. 条件の組み合わせに応じてベンチマークプロセスを起動
  3. 下記の各処理が実行される
    • Pilot:実行回数を決定する
    • OverheadWarmup, OverheadWorkload:BenchmarkDotNet自身のオーバーヘッド評価
    • ActualWarmup:対象メソッドのウォームアップ
    • ActualWorkload:計測
    • Result:計測結果の計算=ActualWorkload - 平均オーバーヘッド
  4. 結果出力

How it works | BenchmarkDotNet