Micro-optimization is “the process of meticulous tuning of small sections of code in order to address a perceived deficiency in some aspect of its operation (excessive memory usage, poor performance, etc.)”
We’ve all seen these kind of debates: Is X faster than Y? Should I have to replace A by B in my code? It’s not specific to any language, but it’s a real question for all developers. Programmers are generally advised to avoid micro-optimization, unless they have a solid justification. Yes, it depends.
Most of the time, micro-optimizations are confirmed by benchmarks: a very specific code section is executed thousands/millions to illustrate the problem and to confirm initial hypothesis. A is X times slower than B. Of course, in real world applications, we rarely call one piece of code so many times, so this stuff may seem inappropriate. But the trouble is that the extra N-milliseconds is CPU time on the server. A web server could simply be idle waiting for request, processing other requests, … but instead it is busy executing the same inefficient methods over and over. Those N-ms can even become N-s during peaks load.
Improving performance of an application is an endless road. It’s a very time-consuming activity and don’t forget that’s it’s not the core of your business: Your boss wants you to deploy new features and not pointless optimizations. It’s very common to spend several hours (days?) to reach your performance goals.
By performance, I mean something that limits scalability of your application. It could be CPU, network IO, Memory … You may know that all applications (systems) have throughput limits. Your job, as a chief performance officer, is to keep fast response times in all circumstances (unexpected system difficulties, light load/heavy load) and to be far from these limits.
The performance of a code section is a mix between frequency & efficiency. I don’t care to write an inefficient code if it’s rarely used but I’m really concerned by most called operations. Have you already counted how many times
ToString() is called in your .NET project ? We can’t blame someone else for having written un-optimized code that we want to use in our –different- context. For example,
.toString() has maybe not been coded to be used as a key for Dictionaries.
Since 10 years of professional programming, I’ve seen serious performance problems. “I don’t understand why it takes 5 sec to generate my page each evening. Everything is cached and I’ve run many benchmarks: all my methods take less than 5ms.” Yes, but these methods are called 100 times while generating your page. Simply speaking, 5 ms is too slow for your context !
Even with a clean architecture, it’s sometimes difficult to determine the critical rendering path. That’s why I’m a big fan of profiling tools like Visual Studio Profiler: they give you a complete overview without changing your application. Collected data during a load test session, can be precious.
I often see micro-optimization as a challenge: let me try to significantly improve the performance –and scalability- of your application by changing the fewest lines as possible. If I can’t do this, I’m either not good enough or there are no interesting optimizations.
Examples in .NET
A few months ago, I already explained on the Betclic Techblog one bad experience we had with Structuremap and Web API. To summarize, we’ve significantly reduced CPU usage of a web application, just by splitting IoC bindings into multiple containers.
More recently, I was mandated to do a performance review on a WCF service consuming too much CPU & memory. Without any doubts –thanks to my favorite profiler- , one method is causing several alerts & warnings.
This method is in a base class. Because the project follows the Onion architecture, dependency injection is heavily used here to manage dependencies (at core, service & repository layers). As a consequence to be in a pure stateless world, this method can be invoked several times during each request processing. The average call rate is approx. 150/sec during peaks load, so this is a good candidate for a micro-optimization.
How to improve this method? It doesn’t look so bad at the first view ….
A first idea could be to optimize
string operations. We all know the obvious beginner mistakes of
string concatenation. Memory allocations are fast on modern PCs, but they’re far from free.
StringBuilder seems better in this case.
A second better idea is to remove
Enum.ToString(). The type of Legislation/Brand/Platform is
enum. The method
Enum.ToString() is called implicitly in this code section (during concatenation). An interesting article on CodeProject explains the troubles with
A few minutes later, I finally produced this extension method (Gist):
public static class EnumExtensions
public static class EnumCache<TEnum>
public static Dictionary<TEnum, string> Values = new Dictionary<TEnum, string>();
var t = typeof(TEnum);
var values = (TEnum)Enum.GetValues(t);
var names = Enum.GetNames(t);
for (var i = 0; i < values.Length; i++)
public static string Get(TEnum enm)
public static string AsString<TEnum>(this TEnum enm) where TEnum : struct, IConvertible
Let’s run benchmarks on 50000 iterations (check the gist for the full source code):
Faster and less allocated memory, not so bad. (Note: It’s even a little better than the original CodeProject article but with less possibilities). For sure, a .NET Guru may find a more efficient way, but it’s enough for me: I reached my performance goal. We now have an optimized ready-to-use extension method with less than 30 lines of code, that’s 40 times faster. Very well done!
But, we’re ALL WRONG !
Optimizing code means you have to think differently. Simply create a property (eventually lazy), computed just once per request, and inserted somewhere in the scope. That’s it.
In every project, there are code conventions: this class to manage logging, this one to format date format, this one to translate
string… We should be very careful with these kind of helpers, extension methods, utilities; we all have these kind of classes in our projects to factorize pure-technical stuff. Unsupervised usage may lead to serious performance issues. Never do anything more than once.
Is this micro-optimization a complete waste of time? Not at all. We’ve done a lot of interesting things: explore .NET internals, run profiling sessions & benchmarks. The final solution is still valid but just not so important in our context. But it could be useful in the future…