Recently I’ve been on vacation. As most developers, I think vacation is a great time to take a step back from what I have been doing and evaluate some things in life. One of the things I’ve always intended to do was to learn about high-performant applications and how I could apply this to the software I am writing. To accomplish this, I’ve been reading the excellent book “Writing High Performance .NET code” by Ben Watson. In this blog post I will mention the most important things I’ve learned, but I really recommend you buy this book if you want to improve the performance of your .NET code.
Let me start by the most important lesson I’ve learned: measure, measure, measure! To accomplish this, I’ve created a benchmarking solution for Catel so the team can benchmark features and compare the performance against previous versions of Catel. This is all open source and can be found here.
The underlying framework for the benchmarking being used here is BenchmarkDotNet.
Try to minimize code generation
Most of the .NET code (with the exception of .NET native) must be compiled the first time it’s hit by the CLR. The smaller the code, the faster this “compilation” will be. Therefore it’s wise to really consider:
- Do I really need this code at all?
- Do I really need that complex linq statement (resulting in complex underlying code)?
- Does this method need to be async await? Async await generates a state machine under the hood resulting in both complexer code and task objects requiring garbage collection. Only use async await if it makes sense, don’t do it by default.
Try to minimize garbage collection
Garbage collection is “very expensive“ in .NET. Therefore you should try to minimize the number of allocations (and thus members on a class / struct). If you need large objects, consider using an pool manager. For example, if you need to read a lot of http messages inside a memory stream, try to re-use memory streams from a pool to prevent garbage collection.
Try to prevent boxing
Boxing can be expensive in highly critical code. This affects both CPU (boxing / unboxing) as the garbage collection. Therefore it’s better to prevent boxing where possible, for example by using generics. Another way is to cache boxed values for simple types (e.g. booleans) and return a cached version of a boxed boolean.
Try to avoid exceptions
Exceptions in .NET are really expensive. This is because the framework gathers a lot of information about an exception to provide a great debugging experience. Therefore it’s important to only use exceptions when they are really necessary, thus in exceptional cases.
There are tons of ways to improve performance in your code. If you are interested in improving your coding skills on performance level, I suggest that you buy the book and improve your skills. Note that posting benchmarks on the internet might make you feel very naked, some things might not be as optimized as you’d hoped. But remember that at least you’re learning and improving, it takes courage to do so.
In the meantime I will start adding more benchmarks to Catel and hopefully add some great performance benefits for all the users of Catel to enjoy.