Exceptional Series (2/3): Performance impacts with Exceptions
Saturday, April 04, 2009 12:52 PM
Exceptions are by their very nature, very exceptional (haha pun intentional)! :> There are quite a few different ways of throwing and catching them. I hope you enjoy this short series on exception handling (this is part 2 of 3).
This is a three part Exception handling series on:
1) Throwing/catching in different ways
2) Performance impacts and
3) Alternative error handling mechanism.
This is second blog is about performance impacts with exceptions (sample solution is available).
Performance Impacts of Exceptions
Something I've heart many, Many, MANY times over is exceptions are "TERRIBLE for performance". I usually hear this about web servers and company's using exceptions as their standard way handling errors. I used to think like this with only the local architects word that it was so! :< So, today I thought I would take a quick look into this hypothesis and see for myself what's the truth of the matter.
To focus on pure exception performance, I'm looking at a Console application and using the System.Diagnostics.Stopwatch class (high resolution timer). The project I'm using to illustrate this example is 2-TimingExceptions (from the same exception solution) and has only one class, Program. This class has three modes, a pass or throwing one of two exceptions. The two exceptions being thrown are FileNotFoundExcption and SocketException. The reason for two exceptions is to see if all exceptions (and constructors) are created equal.
So let's look at Main. You will not see the ExceptionTest enum declaration cause it's simply got three members, Pass, FileExeption and SocketException, I certainly hope you can see what their purpose is for?! :> The Stopwatch is a hi-resolution timer and is MUCH better than using DateTime.Now and comparing ticks arithmetically or using TimeSpan, Stopwatch is made for this stuff.
You'll notice we're doing this test 5000 times right? Ya, I'll compare doing it once, 5000 times and then 50,000. If you want more, you'll have to try it out for yourself (and leave me your comments! :>). The TestingException method is simply called with the type of test we want to do and then bingo, it comes back and we loop and loop and loop.......then we spit out the elapsed time. Simple enough IMHO. The focus is to keep things simle to focus on exception times, not our code.
There is some commented code if you want to try 50,000 (or more? :>) to give you a bit of feedback when things haven't just stopped working (like the Sens this year :>).
Next we'll look at that TestingException method.
This method is meant to keep as many things fair as possible while still focusing on exception performance (run your exe in a command prompt and it'll get truer times than debugging in VS!). What I mean by this is, most of the time when you see code that stress tests exceptions, the "pass" case, well, does nothing, literally, it does nothing. I've always wondered at the IL level if the CLR optimizer in the .NET compiler just puts in NO-OP instructions to totally skip over that portion of code. That's why in my example I'm setting the int 'i' and returning to try to force the .NET compiling optimizer to keep it's nose out of things (or at least treat everything here as equal as possible). The test here is also testing two different exceptions via two different types of constructors. I know, I know, I should have broken them out into four separate tests but after you see the numbers I think you'll agree it all works out in the wash.
Here's the results, drum roll please........
||5000 times (ms)
50,000 times (ms)
||0, 0, 0, 0, 0
||0, 0, 0, 0, 0
||1, 1, 1, 1, 1
||0, 0, 0, 0, 0
||200, 199, 200, 198, 197
||1974, 2183, 2003, 1936, 1916
||0, 0, 0, 0, 0
251, 254, 255, 254 ,252
|2457, 2450, 2438, 2454, 2563
OK, so what? Not throwing an exception is the most efficient way of doing things. DUH! "Ya, thanks Peter, thanks for telling me something I already knew! :<<<<" OK, so that much you already figured out (I hope :>), but for throwing an exception once in a while, like in EXCEPTIONAL circumstances, the cost is easily justified by the information and protection exceptions afford you. If you're going to be silly enough to be throwing 50,000 exceptions, then it's gonna cost you a bit of performance. There, two lessons for the price of one. :>
Something I noticed that is interesting, the SocketException takes a bit more time to instantiate, but only a fraction longer. That could be because there is slightly more logic in the constructor. Something to keep in mind.
The conclusions I'd like to draw to your attention is not that exceptions are expensive but used wisely are very informative and therefore are required and justified in our code FOR WHEN EXCEPTIONAL CIRCUMSTANCES ARISE. The question I believe NEEDS to be asked is not whether you should use them or not, but HOW you should use them! For that, you'll have to read the next blog article!
Until then, it's time to go grab a coffee and get coding! :>
Source Code: http://www.pchenry.com:8080/svn/Blog/trunk/2009/ExceptionHandlingDifferentWays
Mauro Sant'Anna: DevTeach session on Exceptions
Mauro Sant'Anna: Interview with Andres Hejlsberg (talking about not using try/catch/finally at the same time)
stackoverflow: Proper use of try...catch