Overview
What is Benchmarking? It is used for measuring timing complexity for various algorithms and part of the code. A BenchmarkDotNet library is needed for implementation of benchmarks method. BenchmarkDotNet is an open-source library in DotNet 6. Embedded performance counters provided by the framework DotNET allows developers to monitor performance metrics such as disk, processor usage, network activity, memory and so on. Within this blog, we will measure the loop execution speed.
Introduction
Benchmarking is a process that helps developers measure the performance of their code. It is a critical step in optimizing code for faster execution and better user experience. There are different approaches to benchmarking in C#, including profiling tools, built-in performance counters, and custom performance testing frameworks.
Procedure of Benchmarking
- Create a Project (Console App) and Place a solution and project in same directory
- Install Libraries
- Setup the Benchmark
- Run the Benchmark
- Collect the analyzed result
Requirement of Benchmarking
The First Step to install NuGet Packages into your project. You can search the BenchmarkDotNet in NuGet Packages and install it otherwise install via package manager console :PM> Install-Package BenchmarkDotNet
Memory Diagnoser in Benchmarking
Memory Diagnoser is one of the features of benchmarking that measures how many bytes allocated and frequency of garbage collection. For Ex: Parallel loops which are going to execute on multiple threads. Memory diagnosis is useful for developers who need to optimize the memory usage of their application.
Declare a Class with MemoryDiagnoser attribute for enable memory profiling in class file.
public class MyBenchmarkDemo { //Code }
Setup and Cleanup
Setup and Cleanup are two important aspects of benchmarking. Setup is executed before benchmark iteration and Cleanup is executed after benchmark iteration. These methods can be used to prepare the system for benchmarking and clean up any resources that were used during the benchmark. It has many attributes: [GlobalSetup], [GlobalCleanup], [IterationSetup], [IterationCleanup]. It is a mandatory method.
- Define a Setup method that initialize any resource needed for the benchmark :
- It is used for initializing the HttpClient instance and creating a Database connection string.
- Define a Setup method that initialize any resource needed for the benchmark :
private static readonly Random Rng = new(8085); [Params (100,10000,100000,1000000,1000000000)] public int Size { get; set; } private List<int> items = new(); //[GlobalSetup] public void Setup() { items = Enumerable.Range(1,Size).Select(x => Rng.Next()).ToList(); }
- Define a Cleanup method that release any resource regarding to benchmark :
- It is used for cleanup logic.
- Define a Cleanup method that release any resource regarding to benchmark :
//[GlobalCleanup] public void Cleanup() { // Release resources here }
Benchmark in Loop Execution
Here , Example of various loop executions.
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; using Demo_Benchmark; using System; using System.Runtime.InteropServices; using System.Text; [MemoryDiagnoser] public class MyBenchmarkDemo { private static readonly Random Rng = new(8085); [Params (100,10000,100000,1000000,1000000000)] public int Size { get; set; } private List<int> items = new(); [GlobalSetup] public void Setup() { items = Enumerable.Range(1,Size).Select(x => Rng.Next()).ToList(); } [Benchmark] public void For() { for(int i=0; i< items.Count; i++) { var item = items[i]; } } [Benchmark] public void While() { var i = 0; while(i < items.Count) { var item = items[i]; i++; } } [Benchmark] public void ForEach() { foreach(var item in items) { //code } } [Benchmark] public void Foreach_Linq() { items.ForEach(item => { //code }); } [Benchmark] public void Parallel_ForEach() { Parallel.ForEach(items, item => { //code }); } [Benchmark] public void Parallel_Linq() { items.AsParallel().ForAll(item => { //code }); } [Benchmark] public void For_Span() { var asSpanList = CollectionsMarshal.AsSpan(items); for(var i=0;i<asSpanList.Length;i++) { var item = asSpanList[i]; } } [Benchmark] public void Foreach_Span() { foreach (var item in CollectionsMarshal.AsSpan(items)) { //code } } }
Below Code is execute for Benchmark.
class Program { static void Main(string[] args) { var summary = BenchmarkRunner.Run<MyBenchmarkDemo>(); } }
How to Run Benchmarking
For Benchmark, Code does not execute in directly debug mode, we can run through visual studio’s command prompt in release mode.
dotnet run -p ProjectName -c Release // Command dotnet run -p Demo_Benchmark -c Release //Example
Result of Benchmark
It shows a Mean Execution time of benchmarks methods, Garbage collection and memory allocation.
- Method : It has been name of methods which has been specify by benchmark methods
- Mean : It shows an average time of execution.
- StdDev : It stands for standard derivation of execution time.
- Median : It stands for median time of execution
- Gen 0 : This column specifies the Gen 0 collections performed for every 1000 operations.
- Gen 1 : This column specifies the Gen 1 collections performed for every 1000 operations.
- Gen 2 : This column specifies the Gen 2 collections performed for every 1000 operations.
- Allocated : It shows a managed memory allocation for a single operation.
Here in our example Gen1 and Gen2 are not showing because there were no Gen1 and Gen2 collections in our example.
(Fig 1.Result of Loop execution speed)
Conclusion
BenchmarkDotNet is a very useful library for finding the timing complexity of segments of code. We can also check the performance of a single method and module. It improves your code’s performance and scalability when one wants to achieve efficiency in .net development stages through simple means. For more information about .Net and other mobile app as well as web app development you can keep following our blogs, thank you for reading.