Exceptions are common enemies of Web Apps performance
(Update: Thanks to Avi Pinto for pointing out at a stupid mistake regarding catching exceptions in every method. What I meant was that it is a bad practice catching exceptions in every method. Don’t catch exceptions if you have nothing to do with them)
Yes, exceptions are one of most common sources of performance issues of any .NET application, and especially of ASP.NET Web Applications. Without going into details, every thrown exception, either handled or not, introduces a serious penalty on the code execution time.
Consider the following two simplistic functions:
private static int Divide(int a, int b)
{
if (b == 0) return int.MaxValue;
return a / b;
}
private static int DivideEx(int a, int b)
{
try
{
return a / b;
}
catch (DivideByZeroException ex)
{
return int.MaxValue;
}
}
The first one checks the input, and returns the maximum value of integer in case of invalid denominator. The second one, relies on the user input, and handles the exception. Unfortunately, in my practice I’ve seen much more of the second type of function. Now let’s see what this does to the execution performance.
I’m gong to use the following method to benchmark the performance of the above functions:
private static long ProfileDivideFunc(Func<int, int, int> func)
{
Random rnd = new Random(DateTime.Now.Millisecond);
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 10000; i++)
{
func(rnd.Next(0, i), 0);
}
sw.Stop();
return sw.ElapsedMilliseconds;
}
Nothing fancy here. The only “interesting” thing is that the method accepts a delegate and invokes it. Inside the method, I’m just running the tested function 10,000 times, passing a random integer as nominator, and zero as denominator. Practically, I’m measuring the performance penalty of throwing and catching 10,000 exceptions.
The results are pretty much as expected. On my D-Core/4G/Win7 x64 laptop, the first function test returns 0ms, whereas the second function (the one with exceptions), takes 422ms to complete.
It sounds like nothing, however, consider what happens, in ASP.NET app, when 500 or 1,000 concurrent requests are being handled. Also take into consideration the fact that usually we don’t catch exceptions in every method (catching exception in every method is a very bad practice), so it bubbles all the way to some top-level-exception-catching-mechanism. In addition, it has to be logged, which also takes some CPU cycles.
I’ve recently seen a web app, that thrown and logged 3 exceptions on each and every request. Needless to mention how the performance was improved after eliminating the sources for those exceptions.
So here are couple of recommendations:
- Never, and I repeat, never, throw exceptions. Use response error codes if you need to indicate and error.
- Verify user input. Don’t handle exceptions following bad input.
- Try eliminating usage of functions that throw exceptions in case of invalid input, like Parse(). Use TryParse() instead.
- Watch for exceptions, log them and eliminate their sources ASAP.
- There is nothing worst than unhandled exceptions in ASP.NET application. Catch everything in some top-level mechanism, like Application_Error of global.asax, or connect to AppDomain.CurrentDomain.UnhandledException event to catch and log them all.
The most important this is: remember, Exceptions are EXCEPTIONS from the normal, so they should be treated accordingly. They are not NORMAL at runtime.
Have fun.
Want to know why you should use IIS7 ? Come to see me at Developers Academy 4.
