为什么异常总是会在具有可破坏堆栈对象的非叶函数中产生开销?

时间:2015-03-29 18:48:26

标签: c++ exception-handling

我在标题中遇到了声明:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2271.html

通过这里:

http://www.boost.org/doc/libs/1_57_0/doc/html/container/exception_handling.html

  
      
  • 异常处理违反了不要使用C ++的设计,因为它会导致任何非叶子函数的开销。   可破坏的堆栈对象,无论它们是否使用异常   处理
  •   

这是指什么?

我认为这个要点意味着在异常情况下正确展开堆栈的任何策略都要求非叶函数存储有关它们放置在堆栈上的可破坏对象的某种信息。如果这是正确的,那么我的具体问题是:

必须存储的信息是什么?

为什么不能正确展开堆栈,只给出发生抛出的指令地址和运行时之前计算的地址范围表?

2 个答案:

答案 0 :(得分:1)

您支付二进制大小。

无论是否使用异常,所有处理异常的代码都需要存在,因为通常编译器无法知道某个函数是否可以抛出,除非它被标记为noexcept({ {1}}主要是出于这个原因而存在。)

如果包含异常处理的代码进入CPU缓存,浪费缓存内存,则增加的二进制大小也可能会损害实际的运行时性能。一个好的编译器应该能够通过将所有执行异常处理的代码尽可能地存储在“热”运行时路径中来避免这个问题。

此外,一些ABI(SJLJ)即使在非异常路径中也会实现具有一些运行时开销的异常。 Itanium和Windows ABI在非异常路径上都有零开销(因此在这些ABI上,您可以预期异常比返回错误代码错误处理更快)。

如果您对各种ABI中的异常处理之间的差异感兴趣,

This llvm doc是一个很好的起点。

答案 1 :(得分:1)

现代异常处理确实是基于表格和零成本的。不幸的是,Windows x86并非如此 - 这是游戏开发最受欢迎的目标之一。最有可能的原因是二元兼容性原因,但即便是Raymond Chen也不是原因。在x64中,他们应该从一开始就implemented