BenchmarkDotNetを使用したパフォーマンスの測定

画像 こんにちは 1週間前、3回目に、ライブラリを使用して.NETベンチマークBenchmarkDotNetを作成/起動しました。 ライブラリは非常に便利であることが判明しましたが、実際にはハブで点灯していません。これを修正します。



ベンチマークとは、メソッドの実行時間を測定することを意味します。 最初に、手でベンチマークを作成するプロセスを想像してください。 テストメソッドを作成し、リリースビルドを選択し、「測定」メソッドを作成し、その中のガベージを収集し、StopWatchを最初と最後に配置し、ウォームアップを開始し、テストメソッドを開始します。 テストメソッドが1つのStopWatchティックよりも速い場合、テストメソッドを何度も実行し(100万回)、合計時間を100万で割って結果を取得します(合計時間から100万回の操作あたりのアイドルサイクル時間を引くことを忘れないでください) 。







ご覧のとおり、すでに多くの詳細があり、それらと一緒に生きることができる場合、さまざまなアーキテクチャ(x86 \ x64)およびさまざまなコンパイラのパフォーマンスを測定すると、すべてが本当に悪くなります(ライブラリの作成者の1人であるAndrey DreamWalkerは、 ベンチマークの作成とマイクロ最適化の詳細について説明します)あきんしん) ご想像のとおり、これらの詳細はBenchmarkDotNetが処理します。



設置



Nugetパッケージ、依存関係なし; 記事の発行時点で、バージョンv0.9.1。



最も簡単な例



まず、ライブラリを「シラミ用」にチェックしました。

public class TheEasiestBenchmark { [Benchmark(Description = "Summ100")] public int Test100() { return Enumerable.Range(1, 100).Sum(); } [Benchmark(Description = "Summ200")] public int Test200() { return Enumerable.Range(1, 200).Sum(); } } [TestClass] public class UnitTest1 { [TestMethod] public void TestMethod1() { BenchmarkRunner.Run<TheEasiestBenchmark>(); } }
      
      







ご覧のとおり、簡単なスタートとして、[Benchmark(Description = "TestName")]属性をテスト済みのメソッドに添付し、コンソールまたは単体テストでコードを実行するだけです。 メソッドの要件は小さく、パブリックである必要があり(そうでない場合は測定値がありません)、引数を受け入れない(そうでない場合は例外が発生します)。 ベンチマークが完了すると、コンソールに詳細なテストレポートが表示され、最後に要約表が表示されます。



方法 中央値 Stddev
Summ100 1.0282 us 0.1071 us
Summ200 1.9573 us 0.0648 us




デフォルトでは、メソッド名、中央値、標準偏差が表示されます。 [メソッド]列の[ベンチマーク]属性でDescriptionプロパティを設定しない場合、メソッド名が表示されます。 ところで、テーブル行はDescriptionプロパティの値(メソッド名)に従ってソートされます。 また、メソッドでキャッチされなかった例外が測定を停止することにも注意してください(特にこのメソッドの)。



引数を持つメソッドのパフォーマンスを測定するには、追加の「測定」メソッドを作成できます。

 private double SomeBusinessLogic(int arg){ ... } [Benchmark(Description = "Summ100")] public void MeasurmentMethod() { SomeBusinessLogic(42); }
      
      







ベンチマーク設定



ベンチマークは、構成属性を使用して構成されます。 環境\プラットフォーム\ジッター設定、起動数、出力設定、ロガー、アナライザーなどの可能性があります。設定の例はgithubのライブラリページにあります。



最も簡単な構成オプション:Benchmarkメソッドを含むクラスのConfig属性を切り、コンストラクターで設定を含む行を渡します。 したがって、ファイナルテーブルで最大開始時間を確認する場合は、次のコードを使用します。



 [Config("columns=Max")] public class TheEasiestBenchmark { [Benchmark(Description = "Summ100")] public int Test100() { return Enumerable.Range(1, 100).Sum(); } }
      
      







方法 中央値 Stddev マックス
Summ100 1.0069私たち 0.0124 us 1.0441 us




別のオプションは、ManualConfigから派生クラスを作成し、そのタイプをConfig属性コンストラクターに渡すことです。



 [Config(typeof(HabrExampleConfig))] public class TheEasiestBenchmark { private class HabrExampleConfig : ManualConfig { public HabrExampleConfig() { Add(StatisticColumn.Max); //    } } [Benchmark(Description = "Summ100")] public int Test100() { return Enumerable.Range(1, 100).Sum(); } }
      
      







方法 中央値 Stddev マックス
Summ100 1.0114 us 0.0041 us 1.0201 us




一方では、より多くのコードがありますが、他方では、クラスを作成するときにオートコンプリートが機能します。設定が簡単で、ミスを犯しにくいです。



設定について少し


多くの設定があり、タイプごとに分けられています。



設定の最初のタイプはジョブです。 ドキュメントから次のように、環境を構成する必要があります:予想されるプラットフォーム(x64 \ x86)、ジッター、ランタイム。 さらに、テスト実行時間に満足できない場合(ライブラリは基準に従って最適な精度/開始時間を選択しようとします)、ウォームアップおよびターゲット実行の回数を設定するか、単に目的の実行時間を指定できます。 さらに、環境設定に注意する必要があります:クラスが.NET 4.6向けのプロジェクトにあり、構成が.NET 4.5に構成されている場合、起動プロセス中にエラーが発生します(一般に論理的です)。



次のタイプの設定:すでにおなじみの列。 表示される情報を構成できます。 使用可能な列の完全なリストは、 ドキュメントの [列]-> [デフォルト]セクションにあります 。 主に使用される列は、PropertyColumn。*(たとえば、PropertyColumn.Runtime)、StatisticColumn。*(たとえば、StatisticColumn.Median)のようなものです。



別の設定項目:エクスポーター。 生成する追加の結果ファイルを示します。 可能なファイル:html、txt、csv、Rプロット、SOのマークダウンマークアップ、github。 したがって、Rグラフとcsvドキュメントを作成するには、Add(RPlotExporter.Default、CsvExporter.Default)をMyConfigコンストラクターに追加します。



これらすべての設定を持つクラスは次のようになります。

 internal class HabrExampleConfig : ManualConfig { public HabrExampleConfig () { Add(new Job {IterationTime = 1,WarmupCount = 1,TargetCount = 1}); Add(StatisticColumn.Max); Add(RPlotExporter.Default, CsvExporter.Default); } } [Config(typeof(HabrExampleConfig ))] public class TheEasiestBenchmark{...}
      
      







また、別の構成方法の結果は、独自の構成属性を作成するように見えます。

  [MyConfigSource] public class TheEasiestBenchmark { private class MyConfigSourceAttribute : Attribute, IConfigSource { public IConfig Config { get; private set; } public MyConfigSourceAttribute() { Config = ManualConfig.CreateEmpty() .With(StatisticColumn.Max) .With(new Job {Platform = Platform.X64}) .With(RPlotExporter.Default); } } [Benchmark(Description = "Summ100")] public int Test100() { return Enumerable.Range(1, 100).Sum(); } }
      
      







3つの構成方法はすべて、基本構成に何かを追加するだけであることに注意してください。 そのため、3つの基本列Method \ Median \ StdDevが常にコンソールに表示されます。



出力(および結果のファイルの生成)を制限する場合は、UnionRuleプロパティを使用できます。

  [Config(typeof(HabrExampleConfig))] public class TheEasiestBenchmark { private class HabrExampleConfig : ManualConfig { public HabrExampleConfig() { Add(PropertyColumn.Method, StatisticColumn.Max); //       Add(ConsoleLogger.Default); //     UnionRule = ConfigUnionRule.AlwaysUseLocal; //     } } [Benchmark(Description = "Summ100")] public int Test100() { return Enumerable.Range(1, 100).Sum(); } }
      
      







方法 マックス
Summ100 1.0308 us




この方法は、CIプロセスでベンチマークの起動を構成する場合に役立ちます。これは、結果を含む追加の生成ファイルが不要になる可能性が高いためです。



追加機能



パラメータ化されたテスト


アルゴリズムの複雑さを実験的に検証する場合、またはさまざまな引数を使用してメソッドの速度を把握したい場合は、Params属性を使用できます。

したがって、さまざまな行の文字「a」の包含をカウントする速度を測定できます。



  [Params("habrahabr", "geektimes", "toster", "megamozg")] public string arg; [Benchmark(Description = "Test")] public int CountLetterAIncludings() { int res = 0; for (int i = 0; i < arg.Length; i++) { if (arg[i] == 'a'){res++;} } return res; }
      
      







方法 中央値 Stddev arg
テスト 112.4087 ns 1.1556 ns オタク
テスト 113.0916 ns 1.4137 ns ハブラハブル
テスト 104.3207 ns 4.2854 ns メガモズグ
テスト 80.3665 ns 0.4564 ns トースター




相対開始時間


テストメソッドの絶対時間だけでなく、相対時間も知りたいとします。 これを行うには、時間が「通常」と見なされるメソッドを選択し、BaseLineをtrueに設定してベンチマーク属性を変更します。



  [Benchmark(Description = "Summ100")] public int Test100() { return Enumerable.Range(1, 100).Sum(); } [Benchmark(Description = "Summ200", Baseline = true)] public int Test200() { return Enumerable.Range(1, 200).Sum(); }
      
      







方法 中央値 Stddev スケーリング済み
Summ100 1.0113 us 0.0055 us 0.52
Summ200 1.9516 us 0.0120 us 1.00




結果処理



何らかの形で統計をゆがめる必要がある場合、またはエクスポーターを作成する場合は、Summaryクラスがサービスにあります。 単体テストでテストを実行する

 Summary result = BenchmarkRunner.Run<TheEasiestBenchmark>();
      
      





そして、無料でSMSなしで各ベンチマークに関するすべての情報を使用します。

result.Benchmarks [index]にはJob'eとパラメーターに関する情報が含まれ、result.Reports [index]にはテスト実行の時間とそのタイプ(ウォームアップ/バトル)に関するデータが格納されます。



さらに、上で書いたように、このライブラリはテスト結果をhtml、csv、txt形式で保存することを可能にし、マークダウンマークアップで保存し、R png画像で生成することもサポートします。 したがって、この記事のすべてのテスト結果は、生成されたhtmlファイルからコピーされます。



サンプル写真
画像

画像





上記をまとめると、BenchmarkDotNetはベンチマークをコンパイルするルーチンを引き継ぎ、最小限の労力で適切なフォーマット結果を提供します。 そのため、メソッドの速度をすばやく測定したい場合、短いリードタイムでメソッドの正確な結果を取得したい場合、または管理のための美しいスケジュールを取得したい場合は、すでに何をすべきかを知っています。 :)



All Articles