Performance issues inside applications comes in many forms and in the best of times they can be easily identified. But they can also get you real stuck and impossible to get your head around. It can be hard to know what changes to the application are needed in order for it to execute faster, in those scenarios benchmarking can be a great tool to consider. We can rely on a baseline, benchmark what we have in place today, and then work from that to validate our efforts to make it faster.
Setting up benchmarks in dotnet
To setup a benchmark we start by creating a new console application and then install the nuget package BenchmarkDotNet
.
After that we need to specify what benchmarks we need to run. We can do this with the BenchmarkRunner
.
public class Program
{
public static void Main(string[] args)
{
BenchmarkRunner.Run<CustomerBenchmarks>();
}
}
The CustomerBenchmark
class contains the code to benchmark. For simplicity, let say that we want to compare the performance of the linq .First()
vs .Singel()
. This is how that could look like:
public class CustomerBenchmarks
{
[Params(100, 200, 300, 400, 500)]
public int Amount { get; set; }
private Customer[] _customers = Array.Empty<Customer>();
[GlobalSetup]
public void GlobalSetup()
{
_customers = Seed(Amount);
}
[Benchmark]
public void FindFirstCustomerById()
{
_customers.First(x => x.Id == Amount/2);
}
[Benchmark]
public void FindSingleCustomerById()
{
_customers.Single(x => x.Id == Amount/2);
}
private static Customer[] Seed(int amount)
{
return Enumerable.Range(0, amount).Select(i => new Customer
{
Id = i,
Name = $"name-{i}"
}).ToArray();
}
}
BenchmarkDotNet
have a wide variety of attributes, I used the most fundamental ones:
[Params]
- PopulatesAmount
with different scenarios.[GlobalSetup]
- Will run for every scenario.[Benchmark]
- The method to benchmark
How to run the benchmark
In order for the benchmark to run reliable we need to build the solution with the release flag.
dotnet build --configuration release
After that we can navigate to the release folder and execute:
sudo dotnet Customer.Performance.dll
Note that Im using sudo, this is so that BenchmarkDotnet
can increase the CPU priority for the process.
And the results:
And that pretty much it. Maybe not so surprising, First()
is faster then Single()
. This is because Single()
takes the responsibility of checking for duplicates (which is a great feature). One really nice thing about dotnet core is that we can locate the exact line at github. https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/Single.cs#L74
Happy Coding!