はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#】BenchmarkDotNetを用いて処理速度・メモリ確保量を計測する

はじめに

今回はBenchmarkDotNetというライブラリを利用して処理速度・メモリ使用量を計測する方法を紹介したいと思います。

github.com

概要

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

NuGetパッケージ

使い方

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

サンプル

公式ドキュメントを参考にしながらMD5Sha256のパフォーマンス比較をするサンプルを用意しました。

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]。

BenchmarkDotNet/docs/articles/features/setup-and-cleanup.md at master · dotnet/BenchmarkDotNet · GitHub

詳細

より詳細な使い方を調べたい方は公式が一番です。
github.com