C ++中的exception真的很慢

我正在观察C ++中的系统error handling – Andrei Alexandrescu他声称Exceptions in C++中的exception非常缓慢。

我想知道这对于C++98还是如此

今天用于例外(Itanium ABI,VC ++ 64位)的主要模型是零成本模型例外。

这个想法是,通过build立一个守卫并明确检查是否存在exception,编译器会生成一个副表,将任何可能抛出exception(程序计数器)的点映射到处理程序列表。 当抛出一个exception时,会查询这个列表来select正确的处理器(如果有的话)并且堆栈被解除。

与典型的if (error)策略相比:

  • 顾名思义,零成本模型在没有exception发生时是免费的
  • if发生exception,则花费约10x / 20x

但是,成本并不是微不足道的:

  • 边桌一般是冷的 ,因此从记忆中取出需要很长时间
  • 确定正确的处理程序涉及RTTI:许多RTTI描述符要获取,分散在内存中,复杂的操作运行(基本上每个处理程序的dynamic_casttesting)

所以,大部分caching未命中,因此与纯CPU代码相比并非微不足道。

注意:有关更多详细信息,请阅读TR18015报告章节5.4exception处理(pdf)

所以,是的,例外情况在例外的path上是缓慢 ,但是它们比其他方式快于明确的检查( if策略)。

注意:根据NoSenseEtAl,Andrei Alexandrescu似乎质疑这个“更快”。 我个人在我的程序中测量了一个加速,我还没有看到关于可优化性丧失的certificate。


有关系吗 ?

我会声称它不。 一个程序应该写在可读性的基础上,而不是性能(至less不作为第一标准)。 当有人希望调用者不能或不愿意当场处理失败并将其传递给堆栈时,就会使用exception。 奖金:在C ++中,可以使用标准库在线程之间编组11个exception。

这是微妙的,虽然,我声称map::find不应该抛出,但我很好用map::find返回一个checked_ptr抛出如果试图取消它失败,因为它是空的:在后一种情况下,如Alexandrescu介绍的类, 调用者在显式检查和依赖exception之间进行select 。 给予主叫方而不给他更多的责任通常是良好devise的标志。

当问题发布的时候,我正在去看医生的路上,还有一辆出租车在等,所以我只有时间做一个简短的评论。 但现在已经评论,并提出和downvoted我最好添加我自己的答案。 即使Matthieu的回答已经很不错了。


与其他语言相比,C ++中的exception特别慢吗?

重申索赔

“我在看C ++中的系统error handling – Andrei Alexandrescu他声称C ++中的exception非常缓慢。

如果这就像安德烈所说的那样,那么即使他不是完全错误的话,他也是非常具有误导性的。 与语言中的其他基本操作相比,抛出/抛出的exception总是比较慢, 无论编程语言如何 。 不仅仅是在C ++中,在C ++中比在其他语言中更多,正如声称的声明所表明的那样。

一般来说,大多数情况下与语言无关,两种基本的语言特征比其他语言慢了几个数量级,因为它们转化为处理复杂数据结构的例程调用,

  • 抛出exception,和

  • dynamic内存分配。

在C ++中令人高兴的是,通常可以避免在时间关键的代码中。

不幸的 ,即使C ++的默认效率非常接近,也不会有免费的午餐 。 :-)为避免exception抛出和dynamic内存分配而获得的效率通常是通过在较低的抽象层次进行编码来实现的,使用C ++作为“更好的C”。 而较低的抽象意味着更大的“复杂性”。

复杂度越高意味着花在维护上的时间越多,即使难以估计或测量,代码复用也很less或者没有得到代码重用,这是实际的货币成本。 也就是说,如果需要的话,用C ++就可以交换一些程序员的执行效率。 是否这样做主要是一个工程和直觉的决定,因为在实践中,只有收益,而不是成本,可以很容易地估计和衡量。


有没有客观的措施C ++exception投掷性能?

是的,国际C ++标准化委员会已经发布了关于C ++性能的技术报告TR18015 。


什么意思是例外是“慢”?

主要是这意味着一个throw可以采用Very Long Time™,相比于int赋值,由于search处理程序。

正如TR18015在第5.4节“例外”中讨论的那样,有两个主要的exception处理实施策略,

  • 每个try blockdynamic设置exception捕获的方法,以便在引发exception时执行处理程序的dynamic链search,以及

  • 编译器生成静态查找表的方法,用于确定抛出exception的处理程序。

第一种非常灵活和普遍的方法几乎被迫在32位Windows中,而在64位的土地和* nix-land中,第二种更为有效的方法是常用的。

同样如该报告所讨论的,对于每种方法,exception处理都会影响效率的三个主要方面:

  • try

  • 规则function(优化机会)和

  • throwexpression。

主要是,dynamic处理程序方法(32位Windows)exception处理对try块有影响,大多数情况下与语言无关(因为这是Windows的结构化exception处理scheme所强制的),而静态表方法大致为零try -blocks。 讨论这个需要更多的空间和研究,而不是实际的答案。 所以,看报告的细节。

不幸的是,从2006年开始的报告已经有点过时了,截至2012年底,据我所知,没有什么比较新的。

另一个重要的观点是, 例外使用对绩效的影响与支持性语言特征的孤立效率是截然不同的,因为正如报告所指出的那样,

“在考虑exception处理时,必须将其与处理错误的其他方式进行对比。”

例如:

  • 由于不同的编程风格而导致的维护成本(正确性)

  • 冗余呼叫站点if失败检查与集中try

  • caching问题(例如较短的代码可能适合caching)

报告有不同的要考虑的方面列表,但无论如何,获得有关执行效率的重要事实的唯一可行的方法可能是在开发时间的决定上限内以及与开发人员一起使用exception而不使用exception的相同程序熟悉每一个方法,然后测量


避免exception开销的好方法是什么?

正确性几乎总是胜过效率。

没有例外,以下情况很容易发生:

  1. 一些代码P意味着获得资源或计算一些信息。

  2. 调用代码C应该检查成功/失败,但不是。

  3. 在C之后的代码中使用不存在的资源或无效的信息,从而引起普遍的混乱。

主要的问题是点(2),在通常的返回码scheme中,调用码C不被强制检查。

有两种主要方法可以强制进行这种检查:

  • P在失败时直接抛出exception。

  • 其中P返回一个C在使用其主值之前必须检查的对象(否则为exception或终止)。

第二种方法是AFAIK,首先由Barton和Nackman在其“ 科学与工程C ++:高级技术与实例介绍”一书中介绍了一个名为Fallow的类,用于“可能的”函数结果。 Boost库现在提供了一个类似的optional类。 你可以很容易地实现一个Optional类,使用std::vector作为非POD结果的情况下的值载体。

通过第一种方法,调用代码C别无select,只能使用exception处理技术。 然而,使用第二种方法,调用代码C本身可以决定是否执行基于检查或一般exception处理。 因此,第二种方法支持使程序员与执行时间效率权衡。


各种C ++标准对exception性能的影响是什么?

“我想知道这对C ++ 98来说还是如此”

C ++ 98是第一个C ++标准。 对于例外情况,它引入了一个标准的exception类层次(不幸的是相当不完善)。 对性能的主要影响是exception规范的可能性(在C ++ 11中删除),但是Windows C ++编译器Visual C ++从未完全实现这些规范:Visual C ++接受C ++ 98exception规范语法,但忽略exception规范。

C ++ 03只是C ++ 98的技术勘误。 在C + + 03中唯一真正新的是值初始化 。 这与exception无关。

随着C ++ 11标准的一般exception规范被删除,并被replace为noexcept关键字。

C ++ 11标准还增加了对存储和重新抛出exception的支持,这对于在C语言callback中传播C ++exception非常有用。 这种支持有效地限制了当前exception如何被存储。 不过,据我所知,不影响性能,除了在新的代码exception处理可能更容易在C语言callback的两边使用的程度。

这取决于编译器。

例如,海湾合作委员会以处理例外情况的performance非常差着称,但在过去几年里这种情况有了很大的改善。

但请注意,处理exception应该像名称所说的那样是例外,而不是软件devise中的规则。 当你有一个应用程序每秒抛出这么多的exception,这会影响性能,这仍然被认为是正常的运行,那么你应该考虑做不同的事情。

例外是通过将所有笨重的error handling代码排除在外使代码更易读的好方法,但是一旦它们成为正常程序stream的一部分,它们就变得非常难以遵循。 请记住, throw几乎是一个变相的转移。

就像在硅片上说,它的实现依赖,但一般来说,exception对于任何实现来说被认为是慢的,不应该用在性能密集的代码中。

编辑:我不是说根本不使用它们,但对于性能密集的代码,最好避免它们。

是的,但没关系。 为什么?
读这个:
https://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx

基本上说,使用像Alexandrescu这样的例外描述(50倍放缓,因为他们使用catch作为else )是错误的。 对于那些喜欢这样做的人来说,我希望C ++ 22 :)会添加如下内容:
(注意,这必须是核心语言,因为它基本上是编译器从现有的代码生成代码)

 result = attempt<lexical_cast<int>>("12345"); //lexical_cast is boost function, 'attempt' //... is the language construct that pretty much generates function from lexical_cast, generated function is the same as the original one except that fact that throws are replaced by return(and exception type that was in place of the return is placed in a result, but NO exception is thrown)... //... By default std::exception is replaced, ofc precise configuration is possible if (result) { int x = result.get(); // or result.result; } else { // even possible to see what is the exception that would have happened in original function switch (result.exception_type()) //... } 

PS还注意到,即使exception是缓慢的…如果在执行过程中不花费大量的时间在代码中,这不是一个问题…例如,如果float division是缓慢的,并且使它成为4x如果你花费0.3%的时间进行FP部门,则速度更快