Benchmark Your .NET Code With Benchmark.NET

Benchmark Your .NET Code With Benchmark.NET

ยท

4 min read

What is Benchmark.NET?

Benchmark.NET 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. Benchmark.NET 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.

Benchmark Example

Defining Benchmarks

This Is A Sample Benchmark Using Benchmark.NET Comparing The Performance Of MD5 And SHA256 For Hashing:

using System;
using System.Security.Cryptography;
using BenchmarkDotNet.Attributes;

public class Md5VsSha256
{
        private const int N = 10000;
        private readonly byte[] data;

        private readonly SHA256 sha256 = SHA256.Create();
        private readonly MD5 md5 = MD5.Create();

        public Md5VsSha256()
        {
            data = new byte[N];
            new Random(42).NextBytes(data);
        }

        [Benchmark]
        public byte[] Sha256() => sha256.ComputeHash(data);

        [Benchmark]
        public byte[] Md5() => md5.ComputeHash(data);
}

As You Can See Benchmark Is Not More Than Just A Method Inside Of A Class Which Is A Super Amazing And Super Clean Design In My Opinion. Now To Run These Benchmarks Use The BenchmarkRunner Class Like This:

Running Benchmarks

using BenchmarkDotNet.Running;

BenchmarkRunner.Run<Md5VsSha256>();

Now When You Are Gonna Run This Project You Will See Some Stuff Happening. This Library Under The Hood Gonna Run Your Benchmark A Lot Of Time To Get The Best Average Results. After Some Time You Will See The Benchmark Results Printed To The Console Which Looks Like This.

| Method |      Mean |     Error |    StdDev |
|------- |----------:|----------:|----------:|
| Sha256 | 100.90 us | 0.5070 us | 0.4494 us |
|    Md5 |  37.66 us | 0.1290 us | 0.1207 us |

The Benchmark Results Is In Markdown Table Format So You Can Render It Like A Table In A Markdown View Like This.

MethodMeanErrorStdDev
Sha256100.90 us0.5070 us0.4494 us
Md537.66 us0.1290 us0.1207 us

This Is Just One Benchmark To Calculate The Speed Of The Hashing Function. But Now We Have Another Benchmark Class Which Needs To Calculate The Speed But Also The Memory Allocation.

Benchmark Example With Memory Diagnoser

Defining Benchmarks

public class StringBenchmark
{
    [Benchmark]
    public string StringFormat()
    {
        return String.Format("{Year}/{Month}/{Day}", 2010, 02, 03);
    }

    [Benchmark]
    public string StringConcat()
    {
        return String.Concat(2010, "/", 02, "/", 03);
    }

    [Benchmark]
    public string StringConcat_ToString()
    {
        return String.Concat(2010.ToString(), "/", 02.ToString(), "/", 03.ToString());
    }

    [Benchmark]
    public string StringBuilder()
    {
        var builder = new StringBuilder();
        builder.Append(2010);
        builder.Append("/");
        builder.Append(02);
        builder.Append("/");
        builder.Append(03);
        return builder.ToString();
    }

    [Benchmark]
    public string StringInterpolation()
    {
        return $"{2010}/{02}/{03}";
    }
}

Now This Is A Normal Benchmark Comparing The Different Methods For Building A String With Integers In It. In This Benchmark I Don't Care About The Speed A Lot But I Care About The Memory Allocation Because Strings Are Expensive For The Memory To Diagnose The Memory We Can Add The MemoryDiagnoser Attribute To Our Class.

[MemoryDiagnoser]
public class StringBenchmark

Now Run The Benchmarks With The Same BenchmarkRunner.Run Method Like This:

Running The Benchmarks With Memory Diagonser

using BenchmarkDotNet.Running;

BenchmarkRunner.Run<StringBenchmark>();

Now The Results Have Information About The Memory Allocation And The Garbage Collection Time Which We Need In This Scenario.

|                Method |      Mean |     Error |    StdDev |    Median |  Gen 0 | Allocated |
|---------------------- |----------:|----------:|----------:|----------:|-------:|----------:|   
|          StringFormat | 183.87 ns |  5.614 ns | 15.834 ns | 180.06 ns | 0.1070 |     112 B |
|          StringConcat | 194.34 ns |  5.565 ns | 15.421 ns | 190.29 ns | 0.2599 |     272 B |
| StringConcat_ToString | 130.83 ns |  2.715 ns |  3.806 ns | 130.38 ns | 0.1299 |     136 B |
|         StringBuilder |  94.70 ns |  5.123 ns | 14.780 ns |  87.33 ns | 0.1377 |     144 B |
|   StringInterpolation | 157.69 ns | 13.882 ns | 40.931 ns | 142.50 ns | 0.0381 |      40 B |
MethodMeanErrorStdDevMedianGen 0Allocated
StringFormat183.87 ns5.614 ns15.834 ns180.06 ns0.1070112 B
StringConcat194.34 ns5.565 ns15.421 ns190.29 ns0.2599272 B
StringConcat_ToString130.83 ns2.715 ns3.806 ns130.38 ns0.1299136 B
StringBuilder94.70 ns5.123 ns14.780 ns87.33 ns0.1377144 B
StringInterpolation157.69 ns13.882 ns40.931 ns142.50 ns0.038140 B

Now We Can Know That StringConcat Is The Slowest And The Least Memory Efficient Because Of Boxing In C# But The StringInterpolation Is The Best In Memory Allocation Because It Does Not Do Any Boxing Or Unboxing. But As You Can See It Is Very Easy To Do Benchmark Your .NET Code With Benchmark.NET.

Next Steps

Learn More About Benchmark.NET On benchmarkdotnet.org

Did you find this article valuable?

Support ProgrammingFire ๐Ÿš€ Blog by becoming a sponsor. Any amount is appreciated!

ย