编译器是否允许优化掉异常抛出?

12

今天在工作中我们讨论了这个话题,但是没有人能够给出一个明确的答案。请考虑以下情况:

int foo()
{
  int err;

  err = some_call(1);

  if (err != 0)
    return err;

  err = some_call(2);

  if (err != 0)
    return err;

  err = some_call(3);

  if (err != 0)
    return err;

  err = some_call(4);

  if (err != 0)
    return err;

  bar();

  return err;
}

有很多代码重复。显然,这可以通过宏进行因式分解,但很遗憾不能用模板(因为有返回条款)。或者至少不是直接的。

现在的问题是,如果我们用异常替换那些返回错误代码,并立即捕获这些异常,编译器是否允许并聪明到足以检测该模式并完全避免抛出异常?

这里是我的意思的说明:

int foo()
{
  try
  {
    // some_call now throws a ErrorReturned exception that contains the error code upon failure.
    some_call(1);
    some_call(2);
    some_call(3);
    some_call(4);
  }
  catch (ErrorReturned& ex)
  {
    return ex.error_code();
  }

  bar();

  return 0;
}

现在,没有当前的性能问题,所以是的,我们不需要优化甚至关心它。这更多是为了理解编译器可以做什么。

简而言之,这是一种“好”的做法吗?如果是,编译器是否可以通过根本不抛出异常来进行优化?(假设异常构造没有副作用)


1
这更多是为了理解编译器被允许做什么。你知道 as-if 规则吗,[intro.execution]/1(和/8)? - dyp
2
那么,对于2、3和4,重复使用if ((err = some_call(1))) return err;怎么样? - NPE
2
如果some_call被内联,那么编译器可以用实际上的goto替换throw/catch。至于你的编译器是否这样做,没有什么比盯着(反)汇编更好的替代方法了 :-) 快速查看一下,使用-O3的gcc 4.8.1,在一个带有throw/catch的函数中,生成的代码仍然会分配异常对象并进行解旋。 - Steve Jessop
请注意,大多数合理的编译器在不抛出异常的情况下对使用异常没有任何性能影响,并且在抛出异常时通常也非常快。 - PlasmaHH
1个回答

12
“编译器是否足够聪明”似乎暗示在您的项目中异常处理没有作用,如果是这样,您就不应该使用它们(除非您确实有可能遇到异常)。简短回答:不,编译器不会根据您使用它们的模式删除您的异常/异常处理。当您使用try/catch时,它处理的异常将添加到主异常表中;此表可以被监视、挂钩、添加和删除。仅仅因为您立即捕获了异常并不意味着其他事情也不会发生。附加来源:关于优化C++异常处理的论文Optimizing Away C++ Exception Handling概述了所有(几乎所有)与异常相关的当前优化实现。在整个过程中,它显示出目前它们不会在编译时被剥离,但对它们进行了优化。该论文本身建议增强EH(异常处理)以消除不必要的异常,并且总体上是一篇相当不错的阅读材料。

更新(额外来源)
进一步研究这个话题,GCC编译器似乎不会优化掉异常;但是,它提供了一个选项来实现:-fno-exceptions。此选项将删除所有异常,并直接替换为abort()调用。

另一个来源(在StackOverflow上)没有直接提到“删除异常”,但确实概述了两种对异常进行的优化,setjmp/longjmp和零成本。通过突出显示实际增强而没有提及“完全删除异常”,可以推断出目前(至少使用所提到的编译器)不存在这样的优化。另一个具有更详细信息的来源可以在这里找到。


@Mgetz “标准”并未定义编译器如何优化,每个编译器都有自己的方式。我们可以查看每个编译器的手册,甚至是他们生成的汇编代码,以确定是否剥离立即使用的异常 - 但我认为这有点过度了,除非你对大量研究感兴趣。我链接了一篇论文,概述了当前编译器对异常处理的优化,已经进行了很多研究,希望它能更好地回答你的问题 =] - newfurniturey
1
我在那篇论文中找不到日期,但它引用了C++98标准的草案--看起来相当古老。 - dyp
@newfurniturey,这就是我删除评论的原因,我更好奇的是标准是否明确禁止某些优化。 - Mgetz
无论它是新的还是旧的,那篇论文似乎专注于如何在异常发生时使其更快。现在的问题是编译器是否可以避免异常发生,这是一个不同的问题。虽然说实话,如果任何编译器删除上述代码中的异常,我会感到非常惊讶。 - David Rodríguez - dribeas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接