0

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

4

2 回答 2

8

值得注意的是,在第一种情况下,它们在您在 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 花时间尝试检测错误代码并使其运行得稍微快一点——如果确实存在任何性能差异的话。

于 2009-07-10T07:16:09.700 回答
4

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

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

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

于 2009-07-13T07:21:11.847 回答