What’s a micro-optimization, you may ask? It’s applying a local-in-scope rule about optimization to your code without actually testing to see if it makes any difference to the performance of your application as a whole. Micro-optimizations are generally cast as never-to-be-broken rules, or even as items in some coding standards document.
Examples are easy to find: always using for instead of foreach, appending to a StringBuilder object instead of string concatenation, avoiding small methods to minimize the call time. Or, if you are a Delphi Win32 developer, never using TCollection, not using interfaces because of the reference counting, using other optimization tricks and tips.
Now, before you start huffing and puffing, I’m not advocating abandoning these optimizations, or saying that they are inherently evil, or anything like that. All I’m saying is that you should initially write code as clearly as possible. That implies that your code should be readable, simple and understandable. After all, your code gets written just the once, and then will be read many, many times.
Concentrate, therefore, on writing the clearest, simplest code that satisfies the requirements. Writing clear code gives you the best chance of creating provably correct software and of creating code that is easier to optimize. Writing clear code provides the best opportunity for enhancing maintainability. Far more money is spent on software maintenance than is ever spent on the original code. Clear code is generally more flexible and better able to adapt to future needs and changes. Consider using refactorings guided by software metrics to simplify your code. Consider using your skills as a developer to identify and remove code smells.
And it’s only after your code is written that you get to profile it to test its performance within the application. Only then do you get to analyze the hot spots in your application as a whole. Only then do you get to formulate a thesis ("replacing this simple-to-read foreach with a slightly harder to read for loop will improve the performance of my application as a whole") and then test it with the real application. Only then should you accept code changes that have a measurable change in your application’s performance. Only then should you reject failed theses and revert your code to the simpler-to-read version.
Of course, when you profile, don’t just profile the speed performance of your application. Profile the space requirements too, the memory pressure on the heap (the number and sizes of objects created and destroyed), the stress on the virtual memory system.
And remember that in order to refactor your code you must have unit tests to ensure that you don’t break functionality as you polish the performance.