I am very much in favour of performance awareness, as previous posts should have shown (optimize it, cache as cache can, performance is king, …), nobody question that. But I repeatedly stumble over advice that I find … questionable.
So, with this post, I thought I might pick up some of the more common hints and tell you why you should not (have to) apply them for performance reasons. Yes, at the price of processor cycles. Here are some links that contain that advice (not the only ones, though):
- “Improving .NET Application Performance and Scalability” ff (refered to as msdn)
- “ASP.NET Best Practices for High Performance Applications“
- “Tips to improve Performance of Web Application“
- “Improving Application Performance in .Net“
Please note that my statements are meant for developers of ordinary line-of-business or web-applications. If you write real time software controlling atomic plants, this article is not for you. (Neither is it for the guys at Microsoft working on the .NET Framework or SQL Server.)
Please also note that I left out things related to garbage collection and finalization on purpose.
Optimization techniques that adversely affect class design
Some optimization techniques address the way the CLR is working and are aiming to help the JIT compiler to produce better (read “faster”) code.
Consider Using the sealed Keyword. The rationale behind this recommendation is “Sealing the virtual methods makes them candidates for inlining and other compiler optimizations.” (msdn)
The only candiates for this advice are derived classes of your application. Declare them as sealed won’t hurt the class design. But won’t the virtual methods usually be called via the base class? No very much to gain then anyway. Then why bother?
Consider the Tradeoffs of Virtual Members? Consider the Benefits of Virtual Members, I’d rather say! “Use virtual members to provide extensibility.” (msdn). One should avoid making a method virtual if it’s not intended to be overwritten. But that’s a question of class design and design for extensibility rather than a performance related one. Avoiding a virtual declaration for performance reasons? No way.
These are just examples, there is other advice, e.g. regarding voilatile or properties. All in all, I personally have dismissed this category of performance related advice. It’s either unnecessary (i.e. should be done for reasons far more important, like “know what you do and design things right”) or of adverse effect (like scarifying good design for small gains of performance).
Optimization techniques that adversely affect code
Some techniques aim at eliminating unnecessary repetitive calls:
- Avoid Repetitive Field or Property Access
- Inline procedures, i.e. copy frequently called code into the loop.
- Use for loops rather than foreach
These … hints… are what I call developer abuse. Wasn’t inlining, loop unrolling and detection of loop invariants one of the better known optimization techniques of C++ compilers? And now I shall do that manually? “Optimizing compiler” is not part of my job description, sorry.
My special attention goes to … Mr. String. Mr. String, could you please come up to the stage… Applause for Mr. String!
StringBuilder abuse, i.e. forgetting about +/String.Concat/String.Format, is very common. Take the following code snippet as example:
string t1 = a + b + c + d;
string t2 = e + f;
string t3 = t1 + t2;
Quite complex, don’t you think? Wouldn’t you be using a StringBuilder instead? NO! Don’t fall or that StringBuilder babble. I cannot say that all the given advice is wrong; it just plainly forgets to mention String.Concat too often and leads one to a wrong impression.
How many temporary strings do you count? 3? (t1, t2, and t3) Or 5? (a+b put into a temporary, in turn added to c.) Well, the answer is 3, as the c# compiler will translate all appearances of + in one single expression into one call to String.Concat. If you have a straight forward string concatenation use + (but use it in one expression!). If it gets slightly more complex, using String.Format (which uses StringBuilder.AppendFormat internally) might be another option.
Use StringBuilder if you have to accumulate string contents over method calls, iterations, or recursions. Use it if you cannot avoid multiple expressions for your concatenation and to avoid memory shuffling. And please read “Treat StringBuilder as an Accumulator” (msdn) in order not to spoil the effect.
ASP.NET related things
My favourite category :-)
Disable Session State If You Do Not Use It. That’s sound advice. You may do that for the application. But don’t do it just for one page. If you need session state, chances are you need it for all pages. That particular page is the exception? Well, it can’t be doing very much then and disabling it will hardly improve the performance of your application very much. If you stumble over it go ahead, but don’t waste your time looking for these pages. Spend that time on the majority of your pages that actually need session state; spend it on managing session state efficiently. This way your whole application will profit.
Disable View State If You Do Not Need It. Don’t. You don’t want to do it for the page, as view state is a feature of the controls. You don’t want to do it declaratively for the controls, for that is tedious and error prone work. And you certainly don’t want to do it for every control, for some of them rely in view state to work correctly.
Managing view state is the sensible approach. Find ways to avoid sending large view states back and forth to the client. Check if there is unintended view state usage. The view state is small? Well, why bother? Empty view state is not that expensive. If you worry about that, use derived controls that switch view state off by default.
Using Server.Transfer to avoid the roundtrip caused by Response.Redirect is a bit like penny-wise but pound-foolish. The user requests page A but you decide he should see page B. Rather than letting him know that you just gave him what he did not ask for. If he does a page refresh (not to mention setting a bookmark), you will always get a request for page A and always have to transfer to page B. But rest assured, the last transfer definitely is more efficient than redirecting. Oh, and by the way, you just lied to the user. Telling him he is on the Get_This_Gift_For_Free.aspx page when he actually was on the Buy_Overpriced_Stuff_You_Dont_Need.aspx page. Interesting business model, though.
Use Response.Redirect(…, false) instead of Response.Redirect(… [, true]). Now that’s an example of a half understood issue being made a common recommendation. (Am I becoming polemic? Sorry, could not resist :evil: .)
Redirect throws a ThreadAbortException. “An exception! — Oh my, what can we do about that?” “Oh, don’t worry, we can go through all the data binding, page rending, event handling, and whatever else is left of the page life cycle, we will fight any opponent, such as this perkily grid control that refuses to bind against null. And at the end of the day we will have slain the dragon and it won’t fly again.” OK, now I am being polemic. Anyway. The problem with not throwing the exception should have become clear. If you want to avoid that, try to do it client side.
What to conclude?
Now I’m going to contradict myself: None of the above advice is actually wrong (well, …). Not if you look only on performance in terms of processor cycles. But they are far too expensive in terms of brain cycles and won’t get you the benefit you expect. They trade small gains in performance for adversely affected design, workload on the developers side and additional chances for errors. And they are far too fine grained and restricted to the code level to matter all that much. Usually the interaction patterns between different components or the chosen algorithms have more impact. Not the virtual call to that method, but this non-virtual, non-suspicious method that is called 500 times during one request. Not the fact that 5 strings are concatenated with +, but the fact that those strings result from 10 database calls.
I’m contradicting myself even further: I didn’t say, don’t do it at all. I said don’t do it for performance reasons. There may be other reasons to follow the advice. (E.g. I would “Avoid Repetitive Field or Property Access” in order to have more comprehensible code, code that better supports debugging sessions. “Consider the Tradeoffs of Virtual Members” is no bad advice either.)
If you want to follow some rule upfront, I strongly recommend a sound design and understanding of what you do. And once you’ve got past the broad scenarios, let the profiler tell you what parts of your application to optimize. This way you will spend the work where it matters.
Oh, by the way: Please note that I did not reject every advice. There is a lot of good advice which tells you how to improve performance simply by writing good code (in terms of design, readability, etc.). And quite a few hints for doing things efficiently without adverse effects. Just don’t do it blindly.