When developing software, especially one that is responsible for the flow of large amounts of data, we aim to make our code as efficient as possible. In such cases, any improvement is valuable. Therefore, to check the performance of our code, we use benchmarking. This involves turning methods into benchmarks, tracking those methods, and then providing insight into the performance data. In this blog post, I will show how to do this in C#, using an example of checking Span performance.
It is an open-source benchmarking library for .NET, using for optimizing the performance of our code. Thanks to that, you can collect numbers about the efficiency of your methods. Generally, benchmarking is a pretty tough thing. If you would like to do this without using the mentioned library, you would be exposed to various pitfalls. BenchmarkDotNet takes care of this whole complicated process for you. Among other things, it generates a separate project for each benchmark method, runs this project several times, etc. You can read more here.
What about using Stopwatch?
Let’s assume that you do not want to use the mentioned library and want to write the benchmarking code itself. For example, you can do this with the StopWatch type from System.Diagnostic namespace. But why should you think twice before doing so? Writing benchmarking is truly hard. .NET is a managed environment what means that in most cases your code will be treat compiled so the first execution of a method can be significantly slower than the second execution. Another thing is that for example if you write a library then it can be run on different frameworks such as .NET Core or Xamarin. Those frameworks have different CLR implementations what also causes a lot of problems. These are just sample issues that you would encounter. As programmers, we should make our lives easier, not reinvent the wheel!
First of all, create a console application project and download BenchmarkDotNet from NuGet Packages:
Span<T> is a value type that allows the representation of contiguous regions of arbitrary memory regardless of how it was created. What does it mean? We can imagine Span<T> as a window into some existing memory, in spite of where it has been allocated. It is also worth mentioning that Span has an associated type ReadOnlySpan, which provides a read-only view of data in memory. Read more about this here.
To demonstrate how performance can be studied using benchmarking, I will use an example about comparing Span with our much-loved string.
Ok, so let’s assume we have the address of a certain place and we want to extract the city with the postcode from it at the beginning, and finally, we want the city name only. We will now write two methods. One with the usual use of the string and the Substring method, and the other with the ReadOnlySpan type. It looks like this:
Of course, both methods will return the name of the city. But which one has better performance? We can check it as follows:
As you can see, it is limited to adding the MemoryDiagnoser attribute to the class from which the code will be analyzed to see how much memory is being allocated here and the Benchmark attribute to specific methods. Additionally, we have to change into Release mode. Executing the above code will give us information on how fast the methods are and how much memory they allocate.
This is how the obtained results look like:
As you may notice, using the Substring method takes almost twice as long as using the Span and the Slice method. The two times are obviously very short, but with commercial systems, any optimization is valuable. In addition, we can see that the first method allocates much more memory. This is because Span is part of the memory that is guaranteed to always be on the stack. It means it can’t be allocated physically on the managed heap. The allocated memory for the second method is simply the result of using the ToString method.
In today’s blog post I wanted to introduce you to an interesting type that is Span and also familiarize you with the topic of benchmarking. Thanks for your time and I encourage you to read the previous posts. Follow my social media to stay up to date!