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?
try catch block does not slow down your program at all and is basically a standard for catching exceptions. Try Catch statements is basically your safe net when it comes to bugs in your code/program.
3. If no exception occurs in try block then the catch blocks are completely ignored.
In general, wrapping your Java code with try/catch blocks doesn't have a significant performance impact on your applications. Only when exceptions actually occur is there a negative performance impact, which is due to the lookup the JVM must perform to locate the proper handler for the exception.
Hi Shola, yes you can use try / catch without a throw. Nevertheless, as try / catch only makes sense if an exception can be thrown, this code would not make much sense.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With