mcpField
M
microbenchmarking

microbenchmarking

github.com/dotnetテスト・品質5.8k3.6k 回インストール1 日前に更新
リポジトリ

microbenchmarking について

microbenchmarkingは、BenchmarkDotNetでマイクロベンチマークを作成・実行するためのスキルです。実装、ランタイム、設定の性能比較を行う.NET開発者向けで、比較目的に沿ったケース設計と最新の動作を示す資料の確認を重視します。

主な機能

  1. 01ベンチマークの比較軸の計画
  2. 02パラメーター化と入力データの選択
  3. 03BenchmarkDotNetの入口とオプション設定
  4. 04ケース数と実行時間の管理
  5. 05結果の検証とレポート確認

活用シーン

  1. 01実装方式の性能比較
  2. 02特定の性能問題の調査
  3. 03コード変更による性能影響の評価
  4. 04開発中の確認用に一時ベンチマークを作成

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dotnet/skills --skill microbenchmarking

インストール後、Claude Code で/microbenchmarkingを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
bdn-internals-and-tuning.md
comparison-strategies.md
diagnosers-and-exporters.md
project-setup-and-running.md
writing-benchmarks.md

詳細

ベンチマーク作成ガイドライン

BenchmarkDotNet(BDN)は、マイクロベンチマークの作成と実行に使用する .NET ライブラリです。このスキルでは、「BDN」は BenchmarkDotNet を指します。

注: BenchmarkDotNet のベンチマークを作成する LLM の評価から、BDN の動作に関する古い前提が原因でよく見られる失敗パターンが明らかになっています。特に、ランタイムの比較、ジョブの構成、最近のバージョンで変更された実行時の既定値が該当します。このスキルの参照ファイルには、検証済みの最新情報が記載されています。コードを書く前に、タスクに関連する参照ファイルを必ず読んでください。学習データには古い、または誤った BDN のパターンが含まれている可能性があります。

主な概念

  • ジョブ — ベンチマークの実行方法を記述します。ランタイム、反復回数、起動回数、実行戦略、環境設定などが含まれます。複数のジョブを構成して、同じベンチマークを異なる条件で実行できます。
  • ベンチマークケース — メソッド × パラメーターの組み合わせ × ジョブのことです。BDN が測定する最小単位です。
  • オペレーション — 測定対象となる論理的な作業単位です。BDN の出力列(Mean、Error など)は、すべてオペレーションあたりの時間を示します。
  • 呼び出し — ベンチマークメソッドを一度呼び出すことです。既定では、1 回の呼び出し = 1 オペレーションです。OperationsPerInvoke=N を指定すると、各呼び出しは N オペレーションとしてカウントされます。
  • イテレーション — 呼び出しを時間計測するバッチです。BDN はイテレーション内のすべての呼び出しの合計時間を測定し、それをオペレーションの合計数で割って、オペレーションあたりの時間を算出します。

ベンチマークは比較のための手段です

ベンチマークの数値を単独で見た場合、その価値は限られています。測定値のおおよその桁数は確認できますが、正確な値はマシン、オペレーティングシステム、ランタイムの構成によって変わります。ベンチマークは何かと比較したときに、最も有用な情報をもたらします。ベンチマークを作成する前に、現在のタスクにおける比較軸を特定してください。

  • アプローチ(A 対 B):同じ実行内で、代替となる実装を並べて比較します。
  • ランタイム:同じコードを異なる .NET バージョン(例:net8.0 対 net9.0)で比較します。
  • パッケージのバージョン:NuGet 依存関係の異なるバージョンを比較します。
  • ビルド(変更前/変更後):保存した古いコードの DLL と現在のソースを比較します。
  • ランタイム構成(GC モード、JIT 設定):ランタイム設定がパフォーマンスに与える影響を把握するため、1 回の実行内で複数のジョブを使って比較します。
  • 規模(N=100 対 N=1000):入力サイズの増加に伴うパフォーマンスの変化を把握します。
  • ハードウェア/OS:異なるマシンやオペレーティングシステム間で比較します。各環境で個別に実行する必要があります。
  • 過去の測定値:以前の時点で記録された測定値と比較します。

BDN では、最初の 6 つの比較軸を 1 回の実行内で並べて比較できます。ただし、それぞれに必要な CLI フラグや構成は想定と異なる場合があります。比較を構成する前に、各戦略の正しい手順について references/comparison-strategies.md を読んでください。

ユースケースとベンチマークのライフサイクル

開発者がベンチマークを書く理由は、大きく分けて4つあり、それぞれベンチマークの設計方法や配置場所が変わります。

  1. カバレッジスイート:実際の使用パターンを幅広くカバーするベンチマークを作成し、ほとんどのユーザーに影響するリグレッションを検出します。これらのベンチマークは恒久的なもので、プロジェクトのベンチマークスイートに追加し、その規約(ディレクトリ構成、基底クラス、命名規則)に従って、リポジトリにチェックインします。

  2. 問題の調査:特定のパフォーマンス問題が報告されています。その問題を再現して診断するためのベンチマークを作成します。これらのベンチマークはタスク単位のもので、調査(再現 → 切り分け → 修正の検証)の間は維持しますが、恒久的なスイートには含めません。

  3. 変更の検証:開発者がPRや変更を作成し、マージ前にパフォーマンス特性を把握したい場合です。これらのベンチマークはタスク単位のもので、レビューの間は維持しますが、リポジトリにはチェックインしません。

  4. 開発時のフィードバック:開発者がタスクに取り組んでおり、ベンチマークを使ってアプローチを評価し、早い段階で情報を得たい場合です。これらのベンチマークはタスク単位の使い捨てのもので、開発セッション中は維持しますが、方針が決まったら削除します。

ユースケース1では、既存のベンチマークプロジェクトに、その規約に従って追加します。ユースケース2~4では、タスク中は維持しつつ、恒久的なコードベースの一部ではないことが明確なスタンドアロンプロジェクトを、作業ディレクトリに作成します。

カバレッジスイートのベンチマークは、実際の呼び出し元の視点から設計します。どのようなコードパターンでこのAPIが使われ、どのような入力が渡され、どのようなパフォーマンス特性が重要なのかを考えます。恒久的なベンチマークはそれぞれ、実際の使用における関連性によって保守コストを正当化できる必要があります。一時的なベンチマークでは、ケース数を意図的に絞ってください。テストケースが1つ増えるごとに、実行時間が増加します(コストへの配慮を参照)。

コストへの配慮

ベンチマークケース(1つのメソッド × 1つのパラメーターの組み合わせ × 1つのジョブ)の所要時間は、デフォルト設定で15~25秒です。[Params]は直積を作成します。たとえば、値がそれぞれ3個と4個の[Params]があり、メソッドが5つある場合、ケース数は60件となり、所要時間は約20分です。ジョブが複数ある場合、所要時間はさらに増加します。実行前にケースの総数を見積もり、状況に合ったジョブプリセットを選んでください。

プリセット1ケースあたりの時間使用する場面
--job Dry<1s正しさの検証 — 測定は行わず、コンパイルと実行を確認します
--job Short5–8s開発中や調査中の簡易測定
(default)15–25sカバレッジスイートの最終測定
--job Medium33–52s結果が重要な場合の信頼性向上
--job Long3–12 min高い統計的信頼性

ベンチマークの実行に予想以上の時間がかかる場合、結果が不安定に見える場合、または反復回数や実行設定の調整が必要な場合は、BDNの実行パイプラインと構成オプションの詳細についてreferences/bdn-internals-and-tuning.mdを参照してください。

エントリーポイントと構成

BDN プログラムでは、BenchmarkSwitcher(人向けの対話型ベンチマーク選択を提供し、CLI 引数を解析します)または BenchmarkRunner(指定したベンチマークを直接実行します)のいずれかを使用します。どちらも --filter や --runtimes などの CLI フラグをサポートしますが、args を渡した場合に限ります。渡さないと、CLI フラグは無視されます。BenchmarkSwitcher を使用する場合は、対話型プロンプトで停止しないよう、必ず --filter を渡してください。

BDN の動作は、属性、構成オブジェクト、CLI フラグを使ってカスタマイズできます。

エントリーポイントの設定、構成オブジェクトのパターン、CLI フラグについては、references/project-setup-and-running.md をお読みください。実時間以外のデータ(メモリ割り当て、ハードウェアカウンター、プロファイリングトレースなど)を収集する場合は、references/diagnosers-and-exporters.md をお読みください。

ベンチマークの実行

BenchmarkDotNet のコンソール出力は非常に冗長です。各ケースについて、内部キャリブレーション、ウォームアップ、測定の詳細が数百行にわたって表示されます。反復処理の冗長な出力でコンテキストを消費しないよう、すべての出力をファイルにリダイレクトしてください。

dotnet run -c Release -- --filter "*MethodName" --noOverwrite > benchmark.log 2>&1

ベンチマークメソッドの実行には、それぞれ数分かかることがあります。すべてのベンチマークを一度に実行するのではなく、--filter を使って一度に一部(呼び出しごとに 1 つまたは 2 つのメソッドなど)を実行し、結果を確認してから次の範囲を実行してください。これにより、各呼び出しの時間を短く保ち、セッションやターミナルのタイムアウトを回避できるほか、段階的に結果を検証できます。フィルター構文、CLI フラグ、プロジェクトの設定については、references/project-setup-and-running.md をお読みください。

実行後は、結果ディレクトリにある Markdown レポート(*-report-github.md)を読み、概要テーブルを確認してください。エラーや予期しない結果の調査が必要な場合に限り、benchmark.log を読んでください。

新しいベンチマークの作成

ステップ 1:テストケースを計画する

コードを書く前に、次の点を判断します。

  • このベンチマークがどのユースケースを対象とするか(カバレッジ、調査、変更の検証、開発フィードバック)。
  • どの比較軸が当てはまるか(数値を何と比較するか)。
  • 呼び出し元が実際に API をどのように使っているかに基づき、どの実世界のシナリオをベンチマークするか。

各ベンチマークケースでは、そのコストに見合う理由が必要です。未カバーのシナリオは通常、すでにカバーされているシナリオに別のパラメーターの組み合わせを追加するよりも価値があります。ただし、特定のパラメーターの次元がパフォーマンス特性に実際に影響する場合は、深く掘り下げる価値があります。

テストケースの一覧を決めます。各テストケースについて、次の点を検討します。

  • 変化の表現方法:BenchmarkDotNet には、ベンチマークのパラメーター化に使える仕組みがいくつかあります。プロパティレベルのパラメーターには [Params] と [ParamsSource]、メソッドレベルの引数には [Arguments] と [ArgumentsSource]、bool または enum のすべての値を列挙するには [ParamsAllValues]、ジェネリックベンチマーククラスで型パラメーターを変化させるには [GenericTypeArguments] を使います。変化させる次元に最適な仕組みを選んでください。選択肢と正確性を確保するパターンの全容については、references/writing-benchmarks.md をお読みください。
  • 入力データの取得元 — 適切な取得元を検討します(組み合わせて使うこともできます)。
    • ハードコード値 — 正確な入力が重要な、小さく固定された値(例:特定の文字列、既知のエッジケースのサイズ)。定数畳み込みを避けるため、フィールドまたは [Params] に格納します。
    • アセットファイル — バイナリデータのように、ソースコードへの埋め込みには大きすぎる、または現実的でない静的データ。
    • [ParamsSource]/[ArgumentsSource]/[GlobalSetup] によるプログラム生成 — 特定の内容よりもデータの形状が重要な場合や、入力をサイズに応じてパラメーター化する必要がある場合。
  • ランダム性が適切かどうか:データを生成する場合は、再現性を確保するためにシード付きの乱数を使います。ランダムデータを生成する場合は、生成される分布が代表的なものとなるよう、十分なサンプル数を使います(例:ランダム値が 4 個では狭い範囲に偏る可能性がありますが、1000 個あれば分布全体をより適切に評価できます)。
ステップ 2:ベンチマークを実装する

カバレッジスイートのベンチマークは、既存のベンチマークプロジェクトに追加し、その規約に従います。一時的なベンチマーク(調査、変更の検証、開発フィードバック)では、独立したプロジェクトを作成します。プロジェクトのセットアップとエントリポイントの設定については、references/project-setup-and-running.md をお読みください。

BenchmarkDotNet パッケージの追加:必ず dotnet add package BenchmarkDotNet(バージョン指定なし)を使ってください。これにより、NuGet が互換性のある最新バージョンを解決できます。バージョン番号を指定した <PackageReference> を .csproj に手動で記述しないでください。トレーニングデータ内の BDN バージョンは古く、現在の .NET ランタイムをサポートしていない可能性があります。

ベンチマークコードを記述します。よくある計測ミスを避けるため、references/writing-benchmarks.md のパターンに従ってください。特に次の点に注意します。

  • ベンチマークメソッドから結果を返す — デッドコード除去を防ぎます。
  • 初期化処理を [GlobalSetup] に移す — ベンチマークメソッド内のセットアップ処理は計測対象になります。ベンチマークが状態を変更し、反復の間にリセットする必要がある場合に限り、[IterationSetup] を使います。
  • 手動でループを追加しない — 呼び出し回数は BDN が自動的に制御します。
  • 代替手段を比較する場合はベースラインを指定する — メソッドレベルの比較では [Benchmark(Baseline = true)] を使い、複数ジョブの比較ではジョブに .AsBaseline() を使います。これにより、結果に相対比が表示されます。
  • 入力はリテラルや const 値ではなく、フィールドまたは [Params] に格納する — JIT は定数式をコンパイル時に畳み込むことがあります。その場合、実際の計算ではなく事前計算された結果がベンチマーク対象になります。
ステップ 3:検証して実行する

長時間の実行を開始する前に検証します。

  1. まず --job Dry で実行し、測定に時間を費やす前にコンパイルエラーや実行時例外を検出します。
  2. デフォルト設定で代表的なケースを 1 つ実行し、出力が正しく見えることと、数値が想定範囲内であることを確認します。
  3. 検証に合格してから、スイート全体を実行します。

ベンチマーク設計を反復するときは、自信が持てるまで --job Short を使用し、その後、最終的な数値を得るためにデフォルトに切り替えます。

コメント