Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
769 views
in Technique[技术] by (71.8m points)

optimization - Does the .NET JIT optimize nested try/catch statements?

I've been thinking about nested try/catch statements and started to think about under which conditions, if any, the JIT can perform an optimization or simplification of the compiled IL.

To illustrate, consider the following functionally-equivalent representations of an exception handler.

// Nested try/catch
try
{
  try
  {
    try
    {
      foo();
    }
    catch(ExceptionTypeA) { }
  }
  catch(ExceptionTypeB) { }
}
catch(ExceptionTypeC) { }

// Linear try/catch
try
{
  foo();
}
catch(ExceptionTypeA) { }
catch(ExceptionTypeB) { }
catch(ExceptionTypeC) { }

Assuming there are no additional variable references or function calls within the stack frames of the nested try statement, can the JIT conclude that the stack frames may be collapsed to the linear example?

Now how about the following example?

void Try<TException>(Action action)
{
  try
  {
    action();
  }
  catch (TException) { }
}

void Main()
{
  Try<ExceptionC>(Try<ExceptionB>(Try<ExceptionA>(foo)));
}

I don't think there is any way for the JIT to inline the delegate invocations, so this example can't be reduced to the previous one. However in the event of foo() throwing ExceptionC, does this solution perform poorer when compared to the linear example? I suspect there is an extra cost to tear down the stack frames from the delegate invocations, even though the extra data contained in the frames is minimal.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

It's worth noting that in the first case they're only functionally equivalent when you're doing nothing within the catch block. Otherwise, consider this:

try
{
    foo();
}
catch (IOException)
{
    throw new ArgumentException(); // Bubbles up to caller
}
catch (ArgumentException)
{
    Console.WriteLine("Caught");
}

vs

try
{
    try
    {
        foo();
    }
    catch (IOException)
    {
        throw new ArgumentException(); // Caught by other handler
    }
}
catch (ArgumentException)
{
    Console.WriteLine("Caught");
}

Now in this case the difference is obvious, but if the catch block calls some arbitrary method, how is the JIT meant to know what might be thrown? Best to be cautious.

That leaves us with the option of the JIT performing optimisations for empty catch blocks - a practice which is strongly discouraged in the first place. I don't want the JIT to spend time trying to detect bad code and make it run very slightly faster - if indeed there's any performance difference in the first place.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...