Flipkart.com
$1.99/mo Web Hosting
Save some Cold, Hard Cash! $7.49/yr domains

Tuesday, September 6, 2011

Do try-catch really effect runtime performance in .Net application?


Recently, a friend told me that someone had told him that writing a try-catch block in C# resulted in a "huge penalty" in terms of performance compared to if you had not written it.
I was little worrying if that statement is true or not.. I have googled it and read many articles.. also made few small applications.. when no expection occurs and expection occurs.
That is, merely writing such a block actually hurt program performance, even if an exception was never thrown. He didn't believe this was true, and rightly so-it's completely wrong.

Inside a .Net Assembly

A .Net assembly, if we ignore various headers, consists of three things:
  • Bytecode: a sequence of low-level instructions that specify the body of a method
  • Metadata: a set of tables, a little bit like a database, describing higher level constructs such as classes, methods, signatures and so forth
  • Heaps: places where string constants and other such things are stored
That is, bytecode is what we actually execute and the rest is there to describe the extra details. To give you an example of the interplay between them, consider a method call. The bytecode stream contains the call instruction, followed by a method token, which is actually an index into the methods table. The methods table in turn allows us to find out where the bytecode for the method we're calling starts, so we can execute it.

(As an aside, these lookups wouldn't be all that expensive to do per call, though in reality they likely won't actually happen more than once per call site, unless it's done through reflection or a delegate. That's because when the CLR JIT-compiles the bytecode, it can compile the method call down to call instructions at the CPU level that refer directly to the method being called. You don't need to know this bit; it's just here for the curious.)

How Exception Handlers Are Stored

The .Net CLR has a concept known as a "protected region". A protected region is a sequence of instructions that has an associated handler. In C# or VB.NET, the instructions in a protected region correspond to code in a try block. There are various types of handlers, including typed ones (that catch only a certain type of exception) and finally ones. Note that a try...catch...finally will result in two protected regions at the CLR level, one for the catch and one for the finally. The protected regions will cover the exact same sequence of instructions, just have different handler addresses.

Each method that has protected regions comes with a table of them. For each protected region, it contains four entries:
  • The starting instruction in the bytecode for this protected region
  • The number of bytes worth of instructions from the starting point that are protected
  • The type of handler
  • The location in the bytecode of the handler
They are ordered with innermost handlers coming first.

What happens at runtime

Here is the important part when it comes to performance when an exception is not thrown. Since the protected regions are stored in a table and are not in the bytecode, and because the CLR does not need to worry about the exception handlers unless an exception is thrown, then there is no runtime penalty for having a try...catch block. For finally handlers it is a little different, because we do need to run those even when we don't have exceptions. However, since we can compute what we will need to run when statically, the JIT compiler can still emit code that jumps to the finally handler at the appropriate time, making the execution overhead of one of those most likely just a jump and a return.

So that's most of the answer to the question that was asked, but for interest let's take a look at what happens when an exception is thrown. Provided the current method has protected regions, you scan through the table to find if there are any that cover the instruction where the exception was thrown and that are capable of handling it (they are looking for an exception of the correct type, for example). If there are multiple nested handlers that could handle the exception, the ordering of innermost first means that we will find the correct handler.

If we find a handler, we execute it. If we don't find one, we move down to the next method in the call stack and check if it has a suitable handler, keeping going until we find one (or we discover that the exception is user-unhandled and terminate the program). This means that the cost of throwing and catching an exception is dependent on how far down the call stack you have to go to find a handler.

From this we can conclude that .Net is optimized for the case where you do not get exceptions. Since exceptions are intended to happen only in exceptional (that is, unexpected) circumstances, this is a sensible design decision. If you inclined to use exceptions for ordinary flow control, this should give you another reason not to.

And the cost of a try...catch block is...

So what can we conclude? The overall cost of a try...catch block that never handles an exception is a few bytes of memory - or at worst a few words - for the entry in the protected regions table. The only possible runtime penalty is the extra time to load those extra few bytes into memory. Since they are stored way away from the JITted bytecode stream, it's highly unlikely you're going to incur any additional cache-misses at runtime as a result of the handler too. Thus, the cost is essentially nothing.

The cost of not handling an exception that you should have may well be that your program crashes. This results in unhappy customers, a hit to your reputation and development time to go and do a bug-fix, which will almost certainly be much greater than if you had put it in there in the first place. Obviously, protecting code that can not throw an exception under any circumstances is a waste of your development time. But otherwise, it's best to be safe rather than sorry, safe in the knowledge that even if an exception never does occur in that bit of code, it's not really costing anything anyway.

Courtesy:
http://channel9.msdn.com/Forums/TechOff/471678-try-finally-as-costly-as-try-catch-
http://www.programmersheaven.com/user/pheaven/blog/175-Do-trycatch-blocks-hurt-runtime-performance/
http://msmvps.com/blogs/peterritchie/archive/2007/06/22/performance-implications-of-try-catch-finally.aspx

No comments:

Post a Comment