BenchmarkDotNet
2023-01-30 C# BenchmarkDotNet benchmark StopwatchSometimes you need to measure elapsed time of a process. Probably simplest approach is something like this
Stopwatch timer = new();
timer.Start();
DoSomething();
Console.WriteLine(timer.Elapsed);
timer.Stop();
This gives some idea about the performance, but if you need something more complex, it is useful to write a benchmark. Here is small example written using BenchmarkDotNet to measure ways to put strings together. The class below initializes N
string of length 20
, then compare three methods to put them together separated by space.
public class StringBenchmark
{
private const int N = 1000;
private const int StringLength = 20;
private readonly string[] data;
public StringBenchmark()
{
data = new string[N];
for(int i = 0; i < N; i++)
data[i] = new string('*', StringLength);
}
[Benchmark]
public string Join()
{
return string.Join(" ", data);
}
[Benchmark]
public string Additions()
{
string result = "";
foreach(string one in data)
result += one + " ";
return result;
}
[Benchmark]
public string StringBuilder()
{
StringBuilder sb = new();
foreach(string one in data)
{
sb.Append(one);
sb.Append(" ");
}
return sb.ToString();
}
}
When you run it with
var summary = BenchmarkRunner.Run<StringBenchmark>();
It provides lengthy output with many details, but quite handy summary like this:
// * Summary *
BenchmarkDotNet=v0.13.4, OS=Windows 10 (10.0.19042.2486/20H2/October2020Update)
Intel Core i5-8400H CPU 2.50GHz (Coffee Lake), 1 CPU, 8 logical and 4 physical cores
.NET SDK=7.0.102
[Host] : .NET 6.0.13 (6.0.1322.58009), X64 RyuJIT AVX2 [AttachedDebugger]
DefaultJob : .NET 6.0.13 (6.0.1322.58009), X64 RyuJIT AVX2
| Method | Mean | Error | StdDev |
|-------------- |-------------:|-----------:|-----------:|
| Join | 9.016 us | 0.1775 us | 0.2866 us |
| Additions | 1,687.048 us | 32.5457 us | 34.8236 us |
| StringBuilder | 15.119 us | 0.3010 us | 0.5193 us |
// * Warnings *
Environment
Summary -> Benchmark was executed with attached debugger
// * Hints *
Outliers
StringBenchmark.Join: Default -> 2 outliers were removed (9.99 us, 10.37 us)
StringBenchmark.Additions: Default -> 3 outliers were removed (1.83 ms..2.13 ms)
StringBenchmark.StringBuilder: Default -> 1 outlier was removed (17.18 us)
// * Legends *
Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
1 us : 1 Microsecond (0.000001 sec)
// ***** BenchmarkRunner: End *****
Run time: 00:01:20 (80.14 sec), executed benchmarks: 3
Global total time: 00:01:28 (88.77 sec), executed benchmarks: 3
It also builds number of files in BenchmarkDotNet.Artifacts
directory, including a markdown to include on your github
BenchmarkDotNet=v0.13.4, OS=Windows 10 (10.0.19042.2486/20H2/October2020Update)
Intel Core i5-8400H CPU 2.50GHz (Coffee Lake), 1 CPU, 8 logical and 4 physical cores
.NET SDK=7.0.102
[Host] : .NET 6.0.13 (6.0.1322.58009), X64 RyuJIT AVX2 [AttachedDebugger]
DefaultJob : .NET 6.0.13 (6.0.1322.58009), X64 RyuJIT AVX2
Method | Mean | Error | StdDev |
---|---|---|---|
Join | 9.016 μs | 0.1775 μs | 0.2866 μs |
Additions | 1,687.048 μs | 32.5457 μs | 34.8236 μs |
StringBuilder | 15.119 μs | 0.3010 μs | 0.5193 μs |
From the output is clearly visible the performance of String.Join
method and StringBuilder
, where plain additions of strings that leads to many allocations and reallocations is very slow. The library also makes quite easy to parameterize the task, finding how those methods would perform with shorter or longer strings, more or less data, etc.