views:

3296

answers:

10

During a code review with a Microsoft employee we came across a large section of code inside a try{} block. She and an IT representative suggested this can have effects on performance of the code. In fact, they suggested most of the code should be outside of try/catch blocks, and that only important sections should be checked. The Microsoft employee added and said an upcoming white paper warns against incorrect try/catch blocks.

I've looked around and found it can affect optimizations, but it seems to only apply when a variable is shared between scopes.

I'm not asking about maintainability of the code, or even handling the right exceptions (the code in question needs re-factoring, no doubt). I'm also not referring to using exceptions for flow control, this is clearly wrong in most cases. Those are important issues (some are more important), but not the focus here.

How do try/catch blocks affect performance when exceptions are not thrown?

EDIT: I'm adding a bounty. There are interesting responses, but I would like to get some more input.

+18  A: 

Quite comprehensive explanation of the .NET exception model.

Rico Mariani's Performance Tidbits: Exception Cost: When to throw and when not to

The first kind of cost is the static cost of having exception handling in your code at all. Managed exceptions actually do comparatively well here, by which I mean the static cost can be much lower than say in C++. Why is this? Well, static cost is really incurred in two kinds of places: First, the actual sites of try/finally/catch/throw where there's code for those constructs. Second, in unmanged code, there's the stealth cost associated with keeping track of all the objects that must be destructed in the event that an exception is thrown. There's a considerable amount of cleanup logic that must be present and the sneaky part is that even code that doesn't itself throw or catch or otherwise have any overt use of exceptions still bears the burden of knowing how to clean up after itself.

Dmitriy Zaslavskiy:

As per Chris Brumme's note: There is also a cost related to the fact the some optimization are not being performed by JIT in the presence of catch

arul
Thing about C++ is that a very large chunk of the standard library will throw exceptions. There is nothing optional about them. You have to design your objects with some sort of exception policy, and once you've done that there is no more stealth cost.
David Thornley
Thnx for the link! Nice one.
Henri
+22  A: 

No. If the trivial optimizations a try/finally block precludes actually have a measurable impact on your program, you probably should not be using .NET in the first place.

John Kugelman
That's an excellent point - compare to the other items on our list, this one should be minuscule. We should trust basic language features to behave correctly, and optimize what we can control (sql, indexes, algorithms).
Kobi
+58  A: 

Check it.

static public void Main(string[] args)
{
    Stopwatch w = new Stopwatch();
    double d = 0;

    w.Start();
    for (int i = 0; i < 10000000; i++)
    {
        try
        {
            d = Math.Sin(1);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }
    w.Stop();
    Console.WriteLine(w.Elapsed);
    w.Reset();
    w.Start();
    for (int i = 0; i < 10000000; i++)
    {
        d = Math.Sin(1);
    }
    w.Stop();
    Console.WriteLine(w.Elapsed);
}

Output:

00:00:00.4269033  // with try/catch
00:00:00.4260383  // without.

In milliseconds:

449
416

New code:

    for (int j = 0; j < 10; j++)
    {
        Stopwatch w = new Stopwatch();
        double d = 0;
        w.Start();
        for (int i = 0; i < 10000000; i++)
        {
            try
            {
                d = Math.Sin(d);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {
                d = Math.Sin(d);
            }
        }
        w.Stop();
        Console.Write("   try/catch/finally: ");
        Console.WriteLine(w.ElapsedMilliseconds);
        w.Reset();
        d = 0;
        w.Start();
        for (int i = 0; i < 10000000; i++)
        {
            d = Math.Sin(d);
            d = Math.Sin(d);
        }
        w.Stop();
        Console.Write("No try/catch/finally: ");
        Console.WriteLine(w.ElapsedMilliseconds);
        Console.WriteLine();
    }

New results:

   try/catch/finally: 382
No try/catch/finally: 332

   try/catch/finally: 375
No try/catch/finally: 332

   try/catch/finally: 376
No try/catch/finally: 333

   try/catch/finally: 375
No try/catch/finally: 330

   try/catch/finally: 373
No try/catch/finally: 329

   try/catch/finally: 373
No try/catch/finally: 330

   try/catch/finally: 373
No try/catch/finally: 352

   try/catch/finally: 374
No try/catch/finally: 331

   try/catch/finally: 380
No try/catch/finally: 329

   try/catch/finally: 374
No try/catch/finally: 334
Ben M
Can you try them in reverse order as well to be sure that JIT compilation hasn't had an effect on the former?
JoshJordan
Programs like this hardly seem like good candidates for testing the impact of exception handling, too much of what would be going on in normal try{} catch{} blocks is going to be optimized out. I may be out to lunch on that...
LorenVS
+1 For measuring. I wonder if a try/finally would have the same relative stats as a try/catch?
Joseph
This is a debug build. The JIT doesn't optimize those.
Ben M
Note that this doesn't seem to be the same as what the question is about. The question states that some of the code should be moved outside and only some pieces checked with exceptions. In other words, it's not about getting rid of the try/catch altogether, it's whether code inside try/catch runs slower *by itself* than outside.
Lasse V. Karlsen
I'm not really looking for empirical results, but this is interesting: I tried not to share d between scopes (`try {d = Math.Sin(d); d = Math.Sin(d); }`), and I actually get better results *with* try/catch.
Kobi
This is not true at all, Think of it. How many times that you use try catch in a loop? Most of the time you will use loop in a try.c
Jonathan Shepherd
-1, agree with Lasse V. Karlsen, this doesn't seem to be the question.
John M Gant
Really? "How do try/catch blocks affect performance when exceptions are not thrown?"
Ben M
The way I see it, yes, really. No offense intended, but the original question was about the placement of the try/catch blocks, not their existence. Your tests address the performance impact of their existence, not their placement.
John M Gant
Actually, if I may, that is also a part of the question. Should we worry about try/catch blocks, and will it help if that are moved/removed (though, clearly, they are important and would not be removed - you're right about that jmgant). Again, the scientist in me isn't not crazy about benchmarking (unless done on a huge scale), so I won't accept this answer, but Ben did get my +1.
Kobi
After careful thought I've decided to accept this answer. Although the benchmarking is iffy, I think this answer best relate to my question (Eric Lippert has an important point, but we already knew that). This approach, to my tests, shows inconclusive results - sometimes it runs faster with try/catch - and this is probably the best proof.
Kobi
OK, I stand corrected, +1.
John M Gant
+6  A: 

I tested the actual impact of a try..catch in a tight loop, and it's too small by itself to be a performance concern in any normal situation.

If the loop does very little work (in my test I did an x++), you can measure the impact of the exception handling. The loop with exception handling took about ten times longer to run.

If the loop does some actual work (in my test I called the Int32.Parse method), the exception handling has too little impact to be measurable. I got a much bigger difference by swapping the order of the loops...

Guffa
+4  A: 

try catch blocks have a negligible impact on performance but exception Throwing can be pretty sizable, this is probably where your coworker was confused.

RHicke
+78  A: 

Here's a good algorithm for knowing whether to make this change or not.

First, set meaningful customer-based performance goals.

Second, write the code to be both correct and clear first.

Third, test it against your goals.

Fourth, if you meet your goals, knock off work early and go to the beach.

Fifth, if you do not meet your goals, use a profiler to find the code that is too slow.

Sixth, if that code happens to be too slow because of an unnecessary exception handler, only then remove the exception handler. If not, fix the code that is actually too slow. Then go back to step three.

Eric Lippert
+1 for going to the beach early ;)
Steffen Opel
-1 for missing step 4a "go to the beach anyway". (Kidding :-))
Stephen C
+1 for using the Profiler
Alfred Myers
{Insert standard Knuth quote about premature optimization}
Shea Daniels
-1: This is a statement of philosophy, not an answer. Worse, it patronizes the curiosity of the questioner.
Ben M
This is not a statement of philosophy, this is a description of the only sound engineering discipline which leads obtaining an answer. It is impossible for anyone to answer a performance question -- a question about reality -- without measurement. We reading this do not know what the questioner's hardware, software, or realistic customer scenarios are. The only person who knows that is the questioner, so they are the only person who can answer the question! I would much rather point out that the questioner is the only one who can answer the question than present a guess posing as an answer.
Eric Lippert
The OP specifically asks that we discount his introductory anecdote when considering our answers: there is no customer-based scenario with which to evaluate your algorithm. I read his question as an analog to "how expensive is reflection?", which is usually asked in relative terms. For similar quantities of data, we have that answer: _slower_ than static property access, _faster_ than file I/O. And we know why: metadata lookup. Can't we likewise consider try/catch blocks? If so--and you would know better than most--I think that would be a more valuable answer than either yours or mine.
Ben M
+7  A: 

The structure is different in the example from Ben M. It will be extended overhead inside the inner for loop that will cause it to not be good comparison between the two cases.

The following is more accurate for comparison where the entire code to check (including variable declaration) is inside the Try/Catch block:

        for (int j = 0; j < 10; j++)
        {
            Stopwatch w = new Stopwatch();
            w.Start();
            try { 
                double d1 = 0; 
                for (int i = 0; i < 10000000; i++) { 
                    d1 = Math.Sin(d1);
                    d1 = Math.Sin(d1); 
                } 
            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString()); 
            }
            finally { 
                //d1 = Math.Sin(d1); 
            }
            w.Stop(); 
            Console.Write("   try/catch/finally: "); 
            Console.WriteLine(w.ElapsedMilliseconds); 
            w.Reset(); 
            w.Start(); 
            double d2 = 0; 
            for (int i = 0; i < 10000000; i++) { 
                d2 = Math.Sin(d2);
                d2 = Math.Sin(d2); 
            } 
            w.Stop(); 
            Console.Write("No try/catch/finally: "); 
            Console.WriteLine(w.ElapsedMilliseconds); 
            Console.WriteLine();
        }

When I ran the original test code from Ben M, I noticed a difference both in Debug and Releas configuration.

This version, I noticed a difference in the debug version (actually more than the other version), but it was no difference in the Release version.

Conclution:
Based on these test, I think we can say that Try/Catch does have a small impact on performance.

EDIT:
I tried to increase the loop value from 10000000 to 1000000000, and ran again in Release to get some differences in the release, and the result was this:

   try/catch/finally: 509
No try/catch/finally: 486

   try/catch/finally: 479
No try/catch/finally: 511

   try/catch/finally: 475
No try/catch/finally: 477

   try/catch/finally: 477
No try/catch/finally: 475

   try/catch/finally: 475
No try/catch/finally: 476

   try/catch/finally: 477
No try/catch/finally: 474

   try/catch/finally: 475
No try/catch/finally: 475

   try/catch/finally: 476
No try/catch/finally: 476

   try/catch/finally: 475
No try/catch/finally: 476

   try/catch/finally: 475
No try/catch/finally: 474

You see that the result is inconsequent. In some cases the version using Try/Catch is actually faster!

awe
I've noticed this too, sometimes it's faster with try/catch. I've commented it on Ben's answer. However, unlike 24 voters, I don't like this sort of benchmarking, I don't think it's a good indication. The code is faster in this case, but will it always be?
Kobi
Doesn't this prove that your machine was doing a variety of other tasks at the same time? Elapsed time is never a good measure, you need to use a profiler that records processor time, not elapsed time.
Colin Desmond
@Kobi: I aggree that this is not the best way to benchmark if you are going to publish it as a proof that your program runs faster than other or something, but can give you as a developer an indication of one method performing better than another. In this case, I think we can say that the differences (at least for Release configuration) is ignorable.
awe
+1  A: 

The try/catch HAS impact on the performance.

But it's not a huge impact. it's complexity is O(1) (just like an assignment)

Here is a reference about try/catch performance (not explain the complexity of it, but implied). Look at the Throw Fewer Exceptions section

Isaac
I'd appreciate some reading material.
Kobi
+12  A: 

After seeing all the stats for with try/catch and without try/catch, curiosity forced me to look behind to see what is generated for both the cases. Here is the code:

C#:

private static void TestWithoutTryCatch(){
    Console.WriteLine("SIN(1) = {0} - No Try/Catch", Math.Sin(1)); 
}

MSIL:

.method private hidebysig static void  TestWithoutTryCatch() cil managed
{
  // Code size       32 (0x20)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "SIN(1) = {0} - No Try/Catch"
  IL_0006:  ldc.r8     1.
  IL_000f:  call       float64 [mscorlib]System.Math::Sin(float64)
  IL_0014:  box        [mscorlib]System.Double
  IL_0019:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                object)
  IL_001e:  nop
  IL_001f:  ret
} // end of method Program::TestWithoutTryCatch

C#:

private static void TestWithTryCatch(){
    try{
        Console.WriteLine("SIN(1) = {0}", Math.Sin(1)); 
    }
    catch (Exception ex){
        Console.WriteLine(ex);
    }
}

MSIL:

.method private hidebysig static void  TestWithTryCatch() cil managed
{
  // Code size       49 (0x31)
  .maxstack  2
  .locals init ([0] class [mscorlib]System.Exception ex)
  IL_0000:  nop
  .try
  {
    IL_0001:  nop
    IL_0002:  ldstr      "SIN(1) = {0}"
    IL_0007:  ldc.r8     1.
    IL_0010:  call       float64 [mscorlib]System.Math::Sin(float64)
    IL_0015:  box        [mscorlib]System.Double
    IL_001a:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                  object)
    IL_001f:  nop
    IL_0020:  nop
    IL_0021:  leave.s    IL_002f //JUMP IF NO EXCEPTION
  }  // end .try
  catch [mscorlib]System.Exception 
  {
    IL_0023:  stloc.0
    IL_0024:  nop
    IL_0025:  ldloc.0
    IL_0026:  call       void [mscorlib]System.Console::WriteLine(object)
    IL_002b:  nop
    IL_002c:  nop
    IL_002d:  leave.s    IL_002f
  }  // end handler
  IL_002f:  nop
  IL_0030:  ret
} // end of method Program::TestWithTryCatch

I'm not an expert in IL but we can see that an local exception object is created on fourth line .locals init ([0] class [mscorlib]System.Exception ex) after that things are pretty same as for method without try/catch till the line seventeen IL_0021: leave.s IL_002f. If an exception occurs the control jumps to line IL_0025: ldloc.0 otherwise we jump to label IL_002d: leave.s IL_002f and function returns.

I can safely assume that if no exceptions occur then it is the overhead of creating local variables to hold exception objects only and a jump instruction.

TheVillageIdiot
+1 this is the best answer. The differences between the two are very clear (and mild).
HuBeZa
Well, the IL includes a try/catch block in the same notation as in C#, so this does not really show how much overhead a try/catch means behind the scenes! Just that the IL does not add much more, does not mean the same as it is not added something in the compiled assembly code. The IL is just a common representation of all .NET languages. It is NOT machine code!
awe
+1  A: 

See discussion on try/catch implementation for a discussion of how try/catch blocks work, and how some implementations have high overhead, and some have zero overhead, when no exceptions occur. In particular, I think the Windows 32 bit implementation has high overhead, and the 64 bit implementation does not.

Ira Baxter