views:

1765

answers:

13

I've heard the advice that you should avoid try catch blocks if possible since they're expensive.

My question is specifically about the .NET platform: Why are try blocks expensive?

Summary of Responses:

There are clearly two camps on this issue: those that say that try blocks are expensive, and those that say "maybe a tiny little bit".

Those that say try blocks are expensive normally mention the "high cost" of unwinding the call stack. Personally, I'm not convinced by that argument - specially after reading about how exceptions handlers are stored here.

Jon Skeet sits on the "maybe a tiny little bit" camp, and has written two articles on exceptions and performance which you can find here.

There was one article that I found extremely interesting: it talked about "other" performance implications of try blocks (not necessarily memory or cpu consumption). Peter Ritchie mentions that he found that code inside try blocks is not optimized as it'd otherwise be by the compiler. You can read about his findings here.

Finally, there's a blog entry about the issue from the man that implemented exceptions in the CLR. Go take a look at Chris Brumme's article here.

+18  A: 

The following articles should shed some light on the subject:

Do try...catch blocks hurt runtime performance?

Performance Implications of try/catch/finally

DaveK
good literature DaveK.
smink
see http://stackoverflow.com/questions/161942/how-slow-are-net-exceptions#162027 for more information on .net exceptions
gbjbaanb
+11  A: 

It's not the block itself that's expensive, and it's not even catching an exception, per se, that's expensive, it's the runtime unwinding the call stack until it finds a stack frame that can handle the exception. Throwing an exception is pretty light weight, but if the runtime has to walk up six stack frames (i.e. six method calls deep) to find an appropriate exception handler, possibly executing finally blocks as it goes, you may see a noticeable amount of time passed.

Bob King
Also, scanning the debug data to resolve the method names and line numbers for print a stack trace adds a lot of overhead.
sk
very true. You'll see a large difference depending on the presence or absence of a .pdb file.
Bob King
+1  A: 

I doubt if they are particularly expensive. A lot of times, they are necessary/required.

Though I strongly recommend using them only when necessary and at the right places / level of nesting instead of rethrowing the exception at every call return.

I would imagine the main reason for the advice was to say that you shouldnt be using try-catches where if---else would be a better approach.

Mostlyharmless
A: 

This is not specific to .NET (sorry), but I think it's a great explanation to have a better understanding on what's going on behind the scenes when an exception is thrown.

http://channel9.msdn.com/shows/Going+Deep/Ale-Contenti-and-Louis-Lafreniere-Understanding-Exceptions-and-WhenHow-to-Handle-Them/

Juan Pablo Califano
+8  A: 

You shouldn't avoid try/catch blocks as that generally means you aren't properly handling exceptions that might occur. Structured Exception Handling (SEH) is only expensive when an exception actually occurs as the runtime must walk the call stack looking for a catch handler, execute that handler (and there may be more than one), then execute the finally blocks, and then return control back to the code at the right location.

Exceptions are not intended to be used to control program logic, but rather to indicate error conditions.

One of the biggest misconceptions about exceptions is that they are for “exceptional conditions.” The reality is that they are for communicating error conditions. From a framework design perspective, there is no such thing as an “exceptional condition”. Whether a condition is exceptional or not depends on the context of usage, --- but reusable libraries rarely know how they will be used. For example, OutOfMemoryException might be exceptional for a simple data entry application; it’s not so exceptional for applications doing their own memory management (e.g. SQL server). In other words, one man’s exceptional condition is another man’s chronic condition. [http://blogs.msdn.com/kcwalina/archive/2008/07/17/ExceptionalError.aspx]

Scott Dorman
A: 

It's not try blocks you need to worry about as much as catch blocks. And then, it's not that you want to avoid writing the blocks: it's that you want as much as possible to write code that will never actually use them.

Joel Coehoorn
A: 

Slightly O/T, but...

There is fairly good design concept that says you should never require exception handling. This means simply that you should be able to query any object for any conditions that might throw an exception before that exception would be thrown.

Like being able to say "writable()" before "write()", stuff like that.

It's a decent idea, and if used, it makes checked exceptions in Java look kind stupid--I mean, checking for a condition and right after that, being forced to still write a try/catch for the same condition?

It's a pretty good pattern, but checked exceptions can be enforced by the compiler, these checks can't. Also not all libraries are made using this design pattern--it's just something to keep in mind when you are thinking about exceptions.

Bill K
There are many situations where a race condition would not allow such pre-checks to work.
erickson
Possibly, but I can't come up with an example that doesn't involve an outside system.
Bill K
A: 

Every try needs to record a lot of information, e.g. stack pointers, values of CPU register, etc. so it can unwind the stack and bring back the state it has been when passing the try block in case an exception is thrown. Not only that every try needs to record a lot of information, when an exception is thrown, a lot of values needs to be restored. So a try is very expensive and a throw/catch is very expensive, too.

That doesn't mean you shouldn't use exceptions, however, in performance critical code you should maybe not use too many tries and also not throw exceptions too often.

Mecki
But I did a benchmark test where I invoked a Java method 1 million times that had no try/catch block. And then I invoked an exact method, also 1 million times but it had a try/catch block in it (but never thrown). When measured at the nanosecond, I couldn't observe any significant differences. I've concluded that to enter a try block, if it does grab a snapshot of the current state, it is negligible. Only the throwing part is expensive. Why do you say "try is very expensive [...]"?
Jeach
@Jeach: Because it is. See my answer to this question, where I post benchmark code myself and the benchmark results. http://stackoverflow.com/questions/299068/how-slow-are-java-exceptions/299315#299315 - it is not very expensive anymore today, because CPUs got faster and up-to-date JVMs have a highly optimized way to eliminate tries for exceptions never thrown at runtime - but try this whith a very old JVM on an old CPU and you will see how expensive they are. Java is crossplatform, you never know what target machine will one day run your code or what JVM version (unless you force latest).
Mecki
+1  A: 

This is not something i would ever worry about. I would rather the clarity and safety of a try ... finally block over concerning myself with how "expensive" it is. I personal dont use a 286, nor does anyone using .Net/java either. Move on. Worry about writing good code that will affect your users and other developers instead of the underlying framework that is working fine for 99.999999% of the people using it. This is probably not very helpful, and i dont mean to be scathing but just highlighting perspective.

RhysC
I second that RhysC... especially if you consider where that try/catch block is! For example, if it is in a piece of server code, it would be totally different than if it was in client code running on a single PC.
Jeach
+4  A: 

I think people really overestimate the performance cost of throwing exceptions. Yes, there's a performance hit, but it's relatively tiny.

I ran the following test, throwing and catching a million exceptions. It took about 20 seconds on my C2D, 2.8GHz. That's about 50K exceptions a second. If you're throwing even a small fraction of that, you've got some architecture problems.

Here's my code:

using System;
using System.Diagnostics;

namespace Test
{
    class Program
    {
     static void Main(string[] args)
     {
      Stopwatch sw = Stopwatch.StartNew();
      for (int i = 0; i < 1000000; i++)
      {
       try
       {
        throw new Exception();
       }
       catch {}
      }
      Console.WriteLine(sw.ElapsedMilliseconds);
      Console.Read();
     }
    }
}
Michael Petrotta
This is kind of misleading. Almost always, thrown exceptions are not handled in the very same method but bubbling up a few levels before reaching catch {}. Then you would see real-world performance impact which is very variable by the way.
lubos hasko
Right. My point is, people worry way too much about the impact of throwing exceptions. If you're seeing a significant performance impact, re-architect your code; you're throwing *way* too many exceptions. If you're using exceptions as intended, to reflect exceptional conditions, you're ok.
Michael Petrotta
+2  A: 

The compiler emits more IL when you wrap code inside a try/catch block; Look, for the following program :

using System;
public class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("abc");
    }
}

The compiler will emit this IL :

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       13 (0xd)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "abc"
  IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_000b:  nop
  IL_000c:  ret
} // end of method Program::Main

While for the slightly modified version :

using System;
public class Program
{
    static void Main(string[] args)
    {
        try { Console.WriteLine("abc"); }
        catch { }
    }
}

emits more :

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       23 (0x17)
  .maxstack  1
  IL_0000:  nop
  .try
  {
    IL_0001:  nop
    IL_0002:  ldstr      "abc"
    IL_0007:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000c:  nop
    IL_000d:  nop
    IL_000e:  leave.s    IL_0015
  }  // end .try
  catch [mscorlib]System.Object 
  {
    IL_0010:  pop
    IL_0011:  nop
    IL_0012:  nop
    IL_0013:  leave.s    IL_0015
  }  // end handler
  IL_0015:  nop
  IL_0016:  ret
} // end of method Program::Main

All these NOPs and others cost.

Andrei Rinea
The nops are for debugging, and will disappear from release code. The other additional IL is associated with the catch block, not the try block.
RoadWarrior
+1  A: 

A try block is not expensive at all. Little or no cost is incurred unless an exception is thrown. And if an exception has been thrown, that's an exceptional circumstance and you don't care about performance any more. Does it matter if your program takes 0.001 seconds or 1.0 seconds to fall over? No, it does not. What matters is how good the information reported back to you is so you can fix it and stop it happening again.

Anthony
A: 

IMO this whole discussion is like saying "wow lops are expensive because I need to increment a counter... i'm not going to use them any more", or "wow creating an object takes time, i'm not going to create a ton of objects any more."

Bottom line is your adding code, presumably for a reason. If the lines of code didn't then incur some overhead, even if its 1 CPU cycle, then why would it exist? Nothing is free.

The wise thing to do, as with any line of code you add to your application, is to only put it there if you need it to do something. If catching an exception is something you need to do, then do it... just like if you need a string to store something, create a new string. By the same means, if you declare a variable that isn't ever used, you are wasting memory and CPU cycles to create it and it should be removed. same with a try/catch.

In other words, if there's code there to do something, then assume that doing something is going to consume CPU and/or memory in some way.

rally25rs