Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Try-catch speeding up my code?

I wrote some code for testing the impact of try-catch, but seeing some surprising results.

static void Main(string[] args) {     Thread.CurrentThread.Priority = ThreadPriority.Highest;     Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;      long start = 0, stop = 0, elapsed = 0;     double avg = 0.0;      long temp = Fibo(1);      for (int i = 1; i < 100000000; i++)     {         start = Stopwatch.GetTimestamp();         temp = Fibo(100);         stop = Stopwatch.GetTimestamp();          elapsed = stop - start;         avg = avg + ((double)elapsed - avg) / i;     }      Console.WriteLine("Elapsed: " + avg);     Console.ReadKey(); }  static long Fibo(int n) {     long n1 = 0, n2 = 1, fibo = 0;     n++;      for (int i = 1; i < n; i++)     {         n1 = n2;         n2 = fibo;         fibo = n1 + n2;     }      return fibo; } 

On my computer, this consistently prints out a value around 0.96..

When I wrap the for loop inside Fibo() with a try-catch block like this:

static long Fibo(int n) {     long n1 = 0, n2 = 1, fibo = 0;     n++;      try     {         for (int i = 1; i < n; i++)         {             n1 = n2;             n2 = fibo;             fibo = n1 + n2;         }     }     catch {}      return fibo; } 

Now it consistently prints out 0.69... -- it actually runs faster! But why?

Note: I compiled this using the Release configuration and directly ran the EXE file (outside Visual Studio).

EDIT: Jon Skeet's excellent analysis shows that try-catch is somehow causing the x86 CLR to use the CPU registers in a more favorable way in this specific case (and I think we're yet to understand why). I confirmed Jon's finding that x64 CLR doesn't have this difference, and that it was faster than the x86 CLR. I also tested using int types inside the Fibo method instead of long types, and then the x86 CLR was as equally fast as the x64 CLR.


UPDATE: It looks like this issue has been fixed by Roslyn. Same machine, same CLR version -- the issue remains as above when compiled with VS 2013, but the problem goes away when compiled with VS 2015.

like image 521
Eren Ersönmez Avatar asked Jan 19 '12 15:01

Eren Ersönmez


People also ask

Does try catch slow down code?

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.

Does code continue after try catch?

If an exception is caught and not rethrown, the catch() clause is executed, then the finally() clause (if there is one) and execution then continues with the statement following the try/catch/finally block.

What is try catch in coding?

The try statement allows you to define a block of code to be tested for errors while it is being executed. The catch statement allows you to define a block of code to be executed, if an error occurs in the try block.

How does try catch affect performance?

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.


1 Answers

One of the Roslyn engineers who specializes in understanding optimization of stack usage took a look at this and reports to me that there seems to be a problem in the interaction between the way the C# compiler generates local variable stores and the way the JIT compiler does register scheduling in the corresponding x86 code. The result is suboptimal code generation on the loads and stores of the locals.

For some reason unclear to all of us, the problematic code generation path is avoided when the JITter knows that the block is in a try-protected region.

This is pretty weird. We'll follow up with the JITter team and see whether we can get a bug entered so that they can fix this.

Also, we are working on improvements for Roslyn to the C# and VB compilers' algorithms for determining when locals can be made "ephemeral" -- that is, just pushed and popped on the stack, rather than allocated a specific location on the stack for the duration of the activation. We believe that the JITter will be able to do a better job of register allocation and whatnot if we give it better hints about when locals can be made "dead" earlier.

Thanks for bringing this to our attention, and apologies for the odd behaviour.

like image 78
Eric Lippert Avatar answered Sep 20 '22 01:09

Eric Lippert