23

I know that exceptions have a performance penalty, and that it's generally more efficient to try and avoid exceptions than to drop a big try/catch around everything -- but what about the try block itself? What's the cost of merely declaring a try/catch, even if it never throws an exception?

Nick T
  • 25,754
  • 12
  • 83
  • 121
Merus
  • 8,796
  • 5
  • 28
  • 41
  • 8
    You are the only person who can answer this question. You are the only person who knows what kind of hardware and software you are using, you are the only person who knows what performance metrics are relevant to your customer, and so on. Write up some realistic benchmarks, try them on realistic hardware, and then you'll know the answer. Anyone else who tries to answer the question for you is either guessing, or describing the performance characteristics on their machines, for their customers, not yours. Either way, that's not the data you need. – Eric Lippert May 15 '09 at 16:26

5 Answers5

31

The performance cost of try is very small. The major cost of exception handling is getting the stack trace and other metadata, and that's a cost that's not paid until you actually have to throw an exception.

But this will vary by language and implementation. Why not write a simple loop in C# and time it yourself?

JSBձոգչ
  • 40,684
  • 18
  • 101
  • 169
  • 1
    Yep. Try is effectively free because it's basically handled by metadata that's really only inspected when the exception is actually thrown. – Curt Hagenlocher May 15 '09 at 04:59
8

Actually, a couple months ago I was creating an ASP.NET web app, and I accidentally wrapped a try / catch block with a very long loop. Even though the loop wasn't generating every exceptions, it was taking too much time to finish. When I went back and saw the try / catch wrapped by the loop, I did it the other way around, I wrapped the loop IN the try / catch block. Performance improved a LOT. You can try this on your own: do something like

int total;

DateTime startTime = DateTime.Now;

for(int i = 0; i < 20000; i++)
{
try
{
total += i;
}
catch
{
// nothing to catch;
}
}

Console.Write((DateTime.Now - startTime).ToString());

And then take out the try / catch block. You'll see a big difference!

Carlo
  • 25,602
  • 32
  • 128
  • 176
  • 12
    Hmmm. I just tried this on .Net 2.0 (using a `Stopwatch`). 50000 trials of 20000 loop iterations takes 4184ms without `try-catch`, 4363ms with `try-catch`. That is an incredibly small difference. Such a difference will be even less visible if each iteration is actually doing something beyond a simple addition operation. I hit similar results with and without debugging. – Brian Oct 26 '10 at 17:13
5

To see what it really costs, you can run the code below. It takes a simple two dimensional array and generates random coordinates which is out of range. If your exception only occurs one time, of course you will not notice it. My example is done to emphasize what it will mean when doing this several thousand times, and what catching an exception vs implementing a simple test will save you.

        const int size = 1000;
        const int maxSteps = 100000;

        var randomSeed = (int)(DateTime.UtcNow - new DateTime(1970,1,1,0,0,0).ToLocalTime()).TotalMilliseconds;
        var random = new Random(randomSeed);
        var numOutOfRange = 0;
        var grid = new int[size,size];
        var stopwatch = new Stopwatch();
        Console.WriteLine("---Start test with exception---");
        stopwatch.Reset();
        stopwatch.Start();
        for (int i = 0; i < maxSteps; i++)
        {
            int coord = random.Next(0, size * 2);
            try
            {
                grid[coord, coord] = 1;
            }
            catch (IndexOutOfRangeException)
            {
                numOutOfRange++;
            }
        }
        stopwatch.Stop();
        Console.WriteLine("Time used: " + stopwatch.ElapsedMilliseconds + "ms, Number out of range: " + numOutOfRange);
        Console.WriteLine("---End test with exception---");

        random = new Random(randomSeed);

        stopwatch.Reset();
        Console.WriteLine("---Start test without exception---");
        numOutOfRange = 0;
        stopwatch.Start();
        for (int i = 0; i < maxSteps; i++)
        {
            int coord = random.Next(0, size * 2);
            if (coord >= grid.GetLength(0) || coord >= grid.GetLength(1))
            {
                numOutOfRange++;
                continue;
            }
            grid[coord, coord] = 1;
        }
        stopwatch.Stop();
        Console.WriteLine("Time used: " + stopwatch.ElapsedMilliseconds + "ms, Number out of range: " + numOutOfRange);
        Console.WriteLine("---End test without exception---");
        Console.ReadLine();

Example output of this code:

---Start test with exception---
Time used: 3228ms, Number out of range: 49795
---End test with exception---
---Start test without exception---
Time used: 3ms, Number out of range: 49795
---End test without exception---
fiLLLipnet
  • 111
  • 1
  • 3
5

A common saying is that exceptions are expensive when they are caught - not thrown. This is because most of the exception metadata gathering (such as getting a stack trace etc.) only really happens on the try-catch side (not on the throw side).

Unwinding the stack is actually pretty quick - the CLR walks up the call stack and only pays heed to the finally blocks it finds; at no point in a pure try-finally block does the runtime attempt to 'complete' an exception (it's metadata etc.).

From what I remember, any try-catches with filters (such as "catch (FooException) {}") are just as expensive - even if they do not do anything with the exception.

I would venture to say that a method (call it CatchesAndRethrows) with the following block:

try
{
    ThrowsAnException();
}
catch
{
    throw;
}

Might result in a faster stack walk in a method - such as:

try
{
    CatchesAndRethrows();
}
catch (Exception ex) // The runtime has already done most of the work.
{
    // Some fancy logic
}

Some numbers:

With: 0.13905ms
Without: 0.096ms
Percent difference: 144%

Here is the benchmark I ran (remember, release mode - run without debug):

    static void Main(string[] args)
    {
        Stopwatch withCatch = new Stopwatch();
        Stopwatch withoutCatch = new Stopwatch();

        int iterations = 20000;

        for (int i = 0; i < iterations; i++)
        {
            if (i % 100 == 0)
            {
                Console.Write("{0}%", 100 * i / iterations);
                Console.CursorLeft = 0;
                Console.CursorTop = 0;
            }

            CatchIt(withCatch, withoutCatch);
        }

        Console.WriteLine("With: {0}ms", ((float)(withCatch.ElapsedMilliseconds)) / iterations);
        Console.WriteLine("Without: {0}ms", ((float)(withoutCatch.ElapsedMilliseconds)) / iterations);
        Console.WriteLine("Percent difference: {0}%", 100 * withCatch.ElapsedMilliseconds / withoutCatch.ElapsedMilliseconds);
        Console.ReadKey(true);
    }

    static void CatchIt(Stopwatch withCatch, Stopwatch withoutCatch)
    {
        withCatch.Start();

        try
        {
            FinallyIt(withoutCatch);
        }
        catch
        {
        }

        withCatch.Stop();
    }

    static void FinallyIt(Stopwatch withoutCatch)
    {
        try
        {
            withoutCatch.Start();
            ThrowIt(withoutCatch);
        }
        finally
        {
            withoutCatch.Stop();
        }
    }

    private static void ThrowIt(Stopwatch withoutCatch)
    {
        throw new NotImplementedException();
    }
Jonathan C Dickinson
  • 7,181
  • 4
  • 35
  • 46
1

You might want to read up on Structured Exception Handling. It's Window's implementation of exceptions and used in .NET.

http://www.microsoft.com/msj/0197/Exception/Exception.aspx

Unknown
  • 45,913
  • 27
  • 138
  • 182