はじめに
今回はBenchmarkDotNet
というライブラリを利用して処理速度・メモリ使用量を計測する方法を紹介したいと思います。
概要
BenchimarkDotNet
を利用することでメソッドのメモリ確保量を含むパフォーマンス計測を行うことができます。
↓ 実際に計測した結果例
BenchmarkDotNet helps you to transform methods into benchmarks, track their performance, and share reproducible measurement experiments. It's no harder than writing unit tests! Under the hood, it performs a lot of magic that guarantees reliable and precise results thanks to the perfolizer statistical engine. BenchmarkDotNet protects you from popular benchmarking mistakes and warns you if something is wrong with your benchmark design or obtained measurements. The results are presented in a user-friendly form that highlights all the important facts about your experiment. BenchmarkDotNet is already adopted by 19100+ GitHub projects including .NET Runtime, .NET Compiler, .NET Performance, and many others.
// DeepL翻訳
BenchmarkDotNetは、メソッドをベンチマークに変換し、そのパフォーマンスを追跡し、再現可能な測定実験を共有するのに役立ちます。これはユニットテストを書くよりも難しいことではありません!perfolizer統計エンジンにより、信頼性の高い正確な結果を保証します。BenchmarkDotNetはよくあるベンチマークの間違いからあなたを守り、ベンチマークの設計や測定結果に何か問題があれば警告します。結果はユーザーフレンドリーな形式で表示され、実験に関するすべての重要な事実をハイライトします。BenchmarkDotNetは、.NET Runtime、.NET Compiler、.NET Performanceなど、19100以上のGitHubプロジェクトに採用されています。
GitHub - dotnet/BenchmarkDotNet: Powerful .NET library for benchmarking
インストール
NuGetからBenchmarkDotNet
をインストールします。
NuGet Gallery | BenchmarkDotNet 0.13.12
使い方
1. classを用意
まずはpublic
であり、static
でないclass
を用意します。
public class Sample { }
このときメモリ確保量も計測したい場合は[MemoryDiagnoser]
をつけます。
[MemoryDiagnoser] public class Sample { }
2. Benchmark属性をつけたメソッド定義
計測したい処理を含めたメソッドを[Benchmark]
をつけて定義します。このときpublic
であり、static
ではなく、引数がないメソッドでなければいけません。
[MemoryDiagnoser] public class Sample { [Benchmark] public int GenerateRandomInt() { return new Random().Next(); } }
3. BenchmarkRunner.Runを行う
作成したクラスを指定してベンチマークを走らせるためにBenchmarkRunner.Run
を実行する必要があります。
public class Program { public static void Main(string[] args) { BenchmarkRunner.Run<Sample>(); } } [MemoryDiagnoser] public class Sample { [Benchmark] public int GenerateRandomInt() { return new Random().Next(); } }
4. リリースビルドに設定する
また計測をするにあたって、リリースビルドに設定しないとエラーになってしまいます。こちらはsln
ファイルに対して設定します。
Rider
であれば〇〇.sln
を右クリックしプロパティを選択、リリースビルドに設定することができます。
5. 実行する
あとは実行すれば計測が開始されるはずです。
// * Summary * BenchmarkDotNet v0.13.12, macOS Sonoma 14.0 Apple M2 Pro, 1 CPU, 12 logical and 12 physical cores .NET SDK 8.0.100 [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD DefaultJob : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD | Method | Mean | Error | StdDev | Gen0 | Allocated | |------------------ |---------:|--------:|--------:|-------:|----------:| | GenerateRandomInt | 214.3 ns | 2.70 ns | 2.39 ns | 0.0086 | 72 B | // * Hints * Outliers Sample.GenerateRandomInt: Default -> 1 outlier was removed (239.54 ns) // * Legends * Mean : Arithmetic mean of all measurements Error : Half of 99.9% confidence interval StdDev : Standard deviation of all measurements Gen0 : GC Generation 0 collects per 1000 operations Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B) 1 ns : 1 Nanosecond (0.000000001 sec) // * Diagnostic Output - MemoryDiagnoser * // ***** BenchmarkRunner: End ***** Run time: 00:00:22 (22.23 sec), executed benchmarks: 1 Global total time: 00:00:25 (26 sec), executed benchmarks: 1 // * Artifacts cleanup * Artifacts cleanup is finished
サンプル
公式ドキュメントを参考にしながらMD5
とSha256
のパフォーマンス比較をするサンプルを用意しました。
public class Program { public static void Main(string[] args) { BenchmarkRunner.Run<Md5VsSha256>(); } } public class Md5VsSha256 { private SHA256 sha256 = SHA256.Create(); private MD5 md5 = MD5.Create(); private byte[] data; [Params(1000, 10000)] public int N; [GlobalSetup] public void Setup() { data = new byte[N]; new Random(42).NextBytes(data); } [Benchmark] public byte[] Sha256() => sha256.ComputeHash(data); [Benchmark] public byte[] Md5() => md5.ComputeHash(data); }
// * Summary * BenchmarkDotNet v0.13.12, macOS Sonoma 14.0 Apple M2 Pro, 1 CPU, 12 logical and 12 physical cores .NET SDK 8.0.100 [Host] : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD DefaultJob : .NET 8.0.0 (8.0.23.53103), Arm64 RyuJIT AdvSIMD | Method | N | Mean | Error | StdDev | |------- |------ |------------:|---------:|---------:| | Sha256 | 1000 | 454.5 ns | 3.42 ns | 3.04 ns | | Md5 | 1000 | 1,966.4 ns | 16.88 ns | 14.97 ns | | Sha256 | 10000 | 3,891.4 ns | 21.09 ns | 19.73 ns | | Md5 | 10000 | 17,504.0 ns | 43.60 ns | 38.65 ns | // * Hints * Outliers Md5VsSha256.Sha256: Default -> 1 outlier was removed (471.51 ns) Md5VsSha256.Md5: Default -> 1 outlier was removed (2.08 us) Md5VsSha256.Md5: Default -> 1 outlier was removed (18.52 us) // * Legends * N : Value of the 'N' parameter Mean : Arithmetic mean of all measurements Error : Half of 99.9% confidence interval StdDev : Standard deviation of all measurements 1 ns : 1 Nanosecond (0.000000001 sec) // ***** BenchmarkRunner: End ***** Run time: 00:01:07 (67.63 sec), executed benchmarks: 4 Global total time: 00:01:11 (71.89 sec), executed benchmarks: 4 // * Artifacts cleanup * Artifacts cleanup is finished
フィールドやプロパティに対して[Params(1, 2, 3)]
のようにマークをすると、指定された値を全て列挙してそれぞれのケースに対してベンチマークを実行します。
また[GlobalSetup]
を利用すると、計測する前に処理を走らせることができます。
Sometimes we want to write some logic which should be executed before or after a benchmark, but we don't want to measure it. For this purpose, BenchmarkDotNet provides a set of attributes: [GlobalSetup], [GlobalCleanup], [IterationSetup], [IterationCleanup].
// DeepL翻訳
ベンチマークの前や後に実行するロジックを書きたいが、測定はしたくない場合があります。そのためにBenchmarkDotNetは属性セットを提供しています: [GlobalSetup]、[GlobalCleanup]、[IterationSetup]、[IterationCleanup]。
詳細
より詳細な使い方を調べたい方は公式が一番です。
github.com