Boost Your C# Code’s Performance with BenchmarkDotNet

Boost Your C# Code’s Performance with BenchmarkDotNet

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

Stephen

Hi, my name is Stephen Finchett. I have been a software engineer for over 30 years and worked on complex, business critical, multi-user systems for all of my career. For the last 15 years, I have been concentrating on web based solutions using the Microsoft Stack including ASP.Net, C#, TypeScript, SQL Server and running everything at scale within Kubernetes.