Introduction
Performance optimisation is a crucial aspect of software development, especially when working on high-performance applications or libraries. Benchmarking your code allows you to identify bottlenecks, optimise critical sections, and ensure that your software meets its performance goals.
In the world of .NET, BenchmarkDotNet is a powerful tool that simplifies the process of benchmarking your C# code. In this blog post, we’ll explore how BenchmarkDotNet works, including its runner, attributes, and how to interpret benchmark results.
Getting Started with BenchmarkDotNet
BenchmarkDotNet is a .NET library and a command-line tool that helps you benchmark C# code with ease. It provides a set of attributes and a runner that automates the benchmarking process. Let’s start by setting up a simple benchmarking project.
Installing BenchmarkDotNet:
You can install BenchmarkDotNet via NuGet package manager:
dotnet add package BenchmarkDotNet
Creating a Benchmark Class:
In your project, create a benchmark class and decorate it with attributes to define the benchmarks:
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
public class MyBenchmark
{
[Benchmark]
public void MyMethod()
{
// Code to benchmark
}
}
Running Benchmarks:
To run your benchmarks, use the BenchmarkRunner:
class Program
{
static void Main(string[] args)
{
BenchmarkRunner.Run<MyBenchmark>();
}
}
BenchmarkDotNet Attributes
BenchmarkDotNet provides various attributes that allow you to customise and configure your benchmarks. Let’s look at some commonly used attributes:
[Benchmark]
:
The [Benchmark]
attribute marks a method as a benchmark that BenchmarkDotNet will execute. You can have multiple benchmark methods within the same benchmark class.
[Benchmark]
public void MyMethod()
{
// Code to benchmark
}
[Params]
:
The [Params]
attribute allows you to specify different sets of input values for your benchmark method. This is useful when you want to test how your code performs with various inputs.
[Benchmark]
[Params(10, 100, 1000)]
public void MyMethod(int input)
{
// Code to benchmark with different input values
}
[Setup]
and [Cleanup]
:
The [Setup]
and [Cleanup]
attributes mark methods that are executed before and after each benchmark method, respectively. They can be used to set up and clean up resources.
[Setup]
public void Setup()
{
// Code to set up resources before benchmark
}
[Cleanup]
public void Cleanup()
{
// Code to clean up resources after benchmark
}
Running Benchmarks and Interpreting Results
Once you’ve defined your benchmarks and decorated them with attributes, you can run them using the BenchmarkRunner as shown earlier. BenchmarkDotNet will execute the benchmarks multiple times, collect data, and generate detailed reports.
Benchmark Output:
The output from the BenchmarkRunner provides crucial information about the benchmark:
- Mean: The average time taken by the benchmarked code.
- Error: The margin of error for the mean.
- StdDev: The standard deviation of the measurements.
- Median: The median time taken.
- Min/Max: The minimum and maximum times recorded.
BenchmarkDotNet=v0.14.1, OS=Windows 10
Intel Core i7-6700HQ CPU 2.60GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
.NET SDK=6.0.100-rc.2.21505.57
[Host] : .NET 6.0.0 (6.0.21.45113), X64 RyuJIT
DefaultJob : .NET 6.0.0 (6.0.21.45113), X64 RyuJIT
| Method | Mean | Error | StdDev | Median | Min | Max | Ratio | RatioSD |
|--------- |----------:|----------:|----------:|----------:|-------:|-------:|------:|--------:|
| MyMethod | 100.00 ns | 20.45 ns | 42.64 ns | 85.00 ns | 85.00 ns | 175.00 ns | 1.00 | 0.00 |
Analysing Results:
- Mean: The average time taken by the benchmarked code.
- Error: The margin of error for the mean.
- StdDev: The standard deviation of the measurements.
- Median: The median time taken.
- Min/Max: The minimum and maximum times recorded.
In the example output above, the MyMethod
benchmark took an average of 100.00 nanoseconds to execute, with an error margin of 20.45 nanoseconds.
Conclusion
BenchmarkDotNet is a valuable tool for profiling and optimising your C# code. By defining benchmark classes, decorating them with attributes, and running benchmarks using the BenchmarkRunner, you can gain insights into your code’s performance characteristics. This enables you to make informed decisions about optimisations, ensuring your software meets its performance goals.
When working on performance-critical applications, consider making BenchmarkDotNet a part of your development workflow. Accurate and repeatable benchmarks help you identify bottlenecks, compare different implementations, and ultimately deliver faster and more efficient software.
The official website for BenchmarkDotNet is here: – https://github.com/dotnet/BenchmarkDotNet