2

I know the following catch and throw block is redundant, I am curious what kind of damage it can do ?

Can compiler optimizes it away in release mode? Or it will just catch the exception anyway and rethrow it ? If it is the latter case, what kind of performance penalty it will cause ?

try
{
  //...
}
catch {
  throw;
}
3
  • 1
    I would compile an example, and look at the disassembly using ILSpy or another .NET disassembler. Commented Nov 12, 2013 at 3:01
  • 1
    possible duplicate of Can I remove empty catch with throw? Commented Nov 12, 2013 at 3:03
  • 2
    I don't agree that this is a duplicate. The OP states that he already knows that the code is redundant and could be removed. Instead, he wishes to know what the actual impact is of leaving the code in place. Commented Nov 12, 2013 at 3:37

1 Answer 1

7

Optimization

The compiler will not optimize this out, even in a Release build.

Take the following test application:

public class Program {
    public static void Main(string[] args) {
        try {
            CallsThrow();
        }
        catch(Exception ex) {
            Console.WriteLine("Main() caught exception: " + ex.Message);
        }
        Console.Read();
    }

    private static void CallsThrow() {
        try {
            Throw();
        }
        catch {
            throw;
        }
    }

    private static void Throw() {
        throw new Exception("Here's my exception.");
    }
}

Using ILSpy we can look at the output binary at the IL level. We see that the try/catch in CallsThrow is still there in our Release binary:

.method private hidebysig static 
    void CallsThrow () cil managed 
{
    // Method begins at RVA 0x2094
    // Code size 11 (0xb)
    .maxstack 1

    .try
    {
        IL_0000: call void JunkCSharpConsole.Program::Throw()
        IL_0005: leave.s IL_000a
    } // end .try
    catch [mscorlib]System.Object
    {
        IL_0007: pop
        IL_0008: rethrow
    } // end handler

    IL_000a: ret
} // end of method Program::CallsThrow

Benchmark

Code:

public class Program
{
    public static void Main(string[] args) {
        const int N = 100000000;
#if DEBUG
        const string mode = "Debug";
#else
        const string mode = "Release";
#endif

        Console.WriteLine("Testing {0} iterations in {1} mode:", N, mode);

        // Attempt to JIT / cache
        CallsThrowWithTryCatch(false);
        CallsThrowWithoutTryCatch(false);

        // Test with try/catch+throw
        var s1 = Stopwatch.StartNew();
        for (int i = 0; i < N; i++ )
            CallsThrowWithTryCatch(false);
        s1.Stop();
        Console.WriteLine("  With try/catch: {0} ms", s1.ElapsedMilliseconds);

        // Test without try/catch+throw
        var s2 = Stopwatch.StartNew();
        for (int i = 0; i < N; i++)
            CallsThrowWithoutTryCatch(false);
        s2.Stop();
        Console.WriteLine("  Without try/catch: {0} ms", s2.ElapsedMilliseconds);

        var pct = (s1.ElapsedMilliseconds - s2.ElapsedMilliseconds) / (double)s1.ElapsedMilliseconds * 100.0;
        Console.WriteLine("No try/catch faster by {0:.02}%", pct);

        // Just show that it works
        try {
            CallsThrowWithTryCatch(true);
        }
        catch (Exception ex) {
            Console.WriteLine("Main() caught exception: " + ex.Message);
        }

        // Wait to exit
        Console.WriteLine("Press ENTER to exit.");
        Console.Read();
    }

    private static void CallsThrowWithTryCatch(bool doThrow) {
        try {
            Throw(doThrow);
        }
        catch {
            throw;
        }
    }

    private static void CallsThrowWithoutTryCatch(bool doThrow) {
        Throw(doThrow);
    }

    private static void Throw(bool doThrow) {
        if (doThrow)
            throw new Exception("Here's my exception.");
    }
}

Results:

Testing 100000000 iterations in Debug mode:
  With try/catch: 1492 ms
  Without try/catch: 1474 ms
No try/catch faster by 1.22%
Main() caught exception: Here's my exception.
Press ENTER to exit.

Testing 100000000 iterations in Release mode:
  With try/catch: 598 ms
  Without try/catch: 458 ms
No try/catch faster by 23.42%
Main() caught exception: Here's my exception.
Press ENTER to exit.

We can see that yes, there is a performance penalty associated with even the empty try/catch. In Debug builds it is not so significant, but a Release build showed a substantial 23.42% improvement by removing the try/catch.

Sign up to request clarification or add additional context in comments.

9 Comments

The overhead of one more throw would be minimal, second dumping the IL is a trivial exercise left to the reader, so where is the value add in answering a question like this at all?
The overhead is clearly not minimal. Did you look at the numbers? Also, it may be trivial to a more experienced developer, but I'm certain there is a large percentage of .NET developers that have no idea you can look at the generated binary at the IL level.
Microsoft says handle only those exceptions that need to be handled either because an action needs to be performed or a fix up could take place, otherwise let the next level of exception handler deal with it, in this case it would be the last chance exception handler, functionally in that regard the code is the same as without a try catch block. Do I recommend it? No, are the performance implications disastrous No. Worse a throw at this point impedes debugging.
I completely understand the behavioral implications of including and excluding the handler in question. I believe the OP does as well. He asked about the low-level implications of including the handler, and I did my best to answer his specific questions. I think your concerns are going to go largely unheard here.
actually the above empty catch, throw code was copied from a project written by our architect, I knew it stinks from the first impression, your answer helped me to understand why it smells. should I tell him or not, that is another question :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.