Lina  Biyinzika

Lina Biyinzika


5 Performance Tips For .Net Developers

Hi everyone! Today I want to share with you some .Net 5 performance tips with benchmarking!

My system:

  • BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042.985 (20H2/October2020Update)
  • Intel Core i7-9750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores
  • .NET SDK=5.0.104

I will provide benchmarks results in percentages where 100% is fastest result.

1. StringBuilder for concatenation

As you probably know, strings are immutable. So whenever you concatenate strings, a new string object is allocated, populated with content, and eventually garbage collected. All of that is expensive and that’s why StringBuilder will always have better performance.

2. Initial size for dynamic collections

.NET provides a lot of collections like List, Dictionary, and HashSet. All those collections have dynamic size capacity. They automatically expand their size as you add more items.

When the collection reaches its size limit, it will allocate a new larger memory buffer (usually an array double in size). That means an additional allocation and deallocation.

3. ArrayPool for short-lived large arrays

Allocation of arrays and the inevitable de-allocation can be quite costly. Performing these allocations in high frequency will cause GC pressure and hurt performance. An elegant solution is the System.Buffers.ArrayPool class found in the Systems.Buffers NuGet.

The idea is pretty similar to to the ThreadPool. A shared buffer for arrays is allocated, which you can reuse without actually allocating and de-allocating memory. The basic usage is by calling ArrayPool.Shared.Rent(size). This returns a regular array, which you can use any way you please. When finished, call ArrayPool.Shared.Return(array) to return the buffer back to the shared pool.

4. Structs instead of Classes

Structs have several benefits when it comes to deallocation:

  • When structs are not part of a class, they are allocated on the stack and don’t require garbage collection at all.
  • Structs are stored on the heap when they are part of a class (or any reference-type). In that case, they are stored inline and are deallocated when the containing type is deallocated. Inline means the struct’s data is stored as-is. As opposed to a reference type, where a pointer is stored to another location on the heap with the actual data. This is especially meaningful in collections, where a collection of structs is much cheaper to de-allocate because it’s just one buffer of memory.
  • Structs take less memory than a reference type because they don’t have an ObjectHeader and a MethodTable.

5. StackAlloc for short-lived array allocations

The StackAlloc keyword in C## allows for very fast allocation and deallocation of unmanaged memory. That is, classes won’t work, but primitives, structs, and arrays are supported.

6. ConcurrentQueue instead of ConcurrentBag

Never use ConcurrentBag without benchmarking. This collection has been designed for very specific use-cases (when most of the time an item is dequeued by the thread that enqueued it) and suffers from important performance issues if used otherwise. If in need of a concurrent collection, prefer ConcurrentQueue.

#c-sharp #programming #csharp

5 Performance Tips For .Net Developers