【问题标题】:Does the .NET JIT optimize nested try/catch statements?.NET JIT 是否优化嵌套的 try/catch 语句?
【发布时间】:2009-07-10 06:46:35
【问题描述】:

我一直在考虑嵌套的 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,与线性示例相比,此解决方案的性能是否较差?我怀疑从委托调用中拆除堆栈帧会产生额外的成本,即使帧中包含的额外数据很少。

【问题讨论】:

  • 前两种情况的IL你看过了吗?

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


【解决方案1】:

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

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

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

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

这让我们可以选择让 JIT 对空的 catch 块执行优化——这种做法一开始就强烈反对。我不希望 JIT 花时间尝试检测错误代码并使其运行得稍微快一点——如果确实存在任何性能差异的话。

【讨论】:

  • 感谢乔恩的分析。在我看来,空的 catch 处理程序正在执行不抛出操作。但是,您的分析仍然适用,因为 foo() 之外的原因可能会发生 OutOfMemoryException 和 ThreadAbortExcetpion 等异常。
【解决方案2】:

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

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

从性能角度来看,引发和处理异常的过程非常昂贵。程序员不应将异常用作在异常情况(双关语)以外的任何情况下发出信号或控制程序流的一种方式。

【讨论】:

  • ZERO impact:不完全是。处理程序被压入堆栈,catch 的对象是预先分配的。当您对不抛出的代码进行计时时,有和没有 try/catch 块有明显的区别。这在 2009 年是正确的,今天几乎在所有情况下都是如此。另见:stackoverflow.com/questions/1308432/…
猜你喜欢
  • 1970-01-01
  • 2011-08-12
  • 2010-10-14
  • 2017-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多