.NET JIT是否优化嵌套的try / catch语句?

时间:2009-07-10 06:46:36

标签: .net optimization exception-handling try-catch jit

我一直在考虑嵌套的try / catch语句,并开始考虑JIT可以在哪些条件下执行编译IL的优化或简化。

为了说明,请考虑以下功能等效的异常处理程序表示。

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

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

假设在嵌套的try语句的堆栈帧中没有额外的变量引用或函数调用,JIT可以断定堆栈帧可能会折叠为线性示例吗?

现在以下示例怎么样?

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

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

我认为JIT没有任何方法可以内联委托调用,因此这个示例不能简化为前一个。但是,如果foo()抛出ExceptionC,与线性示例相比,此解决方案的执行情况是否较差?我怀疑从委托调用中拆除堆栈帧需要额外的成本,即使帧中包含的额外数据很少。

2 个答案:

答案 0 :(得分:8)

值得注意的是,在第一种情况下,当你在catch块中什么都不做的时候,它们只是在功能上等效。否则,请考虑一下:

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");
}

现在在这种情况下差别很明显,但是如果catch块调用了一些任意的方法,那么JIT如何知道可能会抛出什么?最好小心。

这让我们可以选择JIT对空捕获块执行优化 - 这种做法首先是强烈劝阻的。我不希望JIT花时间尝试检测错误的代码并使其运行得更快 - 如果确实首先存在任何性能差异。

答案 1 :(得分:4)

我对try / catch / finally区域的性能理解是这些区域对于代码的常规执行是透明的。也就是说,如果您的代码没有抛出任何异常捕获,那么try / catch / finally区域会对代码执行性能产生 ZERO 影响。

但是,当引发异常时,运行时开始从引发它的站点向上移动堆栈,检查元数据表以查看相关站点是否包含在任何关键的try块中。如果找到一个(并且它有一个符合条件的catch块或finally块),则会识别相关的处理程序并执行到此为止。

从性能角度来看,提出和处理异常的过程非常昂贵。程序员不应该使用异常作为在特殊情况下发出信号或控制程序流的方式(双关语。)