当问题发布时,我正在去看医生的路上,出租车在等着,所以我只有时间来做一个简短的评论。但是现在已经评论、赞成和反对,我最好添加我自己的答案。就算Matthieu’s answer已经不错了。
与其他语言相比,C++ 中的异常是否特别慢?
重新声明
“我在看 Systematic Error Handling in C++—Andrei Alexandrescu 他声称 C++ 中的异常非常慢。”
如果这就是安德烈所说的,那么这一次他是非常具有误导性的,如果不是完全错误的话。与语言中的其他基本操作相比,引发/抛出的异常总是很慢,不管编程语言是什么。正如所声称的那样,不仅仅是在 C++ 中,或者在 C++ 中比在其他语言中更是如此。
一般来说,无论语言如何,两个基本的语言特性都比其他语言慢几个数量级,因为它们转换为处理复杂数据结构的例程调用,是
在 C++ 中,人们通常可以避免在时间要求严格的代码中同时避免这两种情况。
不幸的是 没有免费午餐这样的东西,即使 C++ 的默认效率非常接近。 :-) 对于通过避免异常抛出和动态内存分配而获得的效率通常是通过在较低抽象级别进行编码来实现的,将 C++ 用作“更好的 C”。较低的抽象意味着更大的“复杂性”。
更高的复杂性意味着花费更多的时间进行维护,而代码重用的好处很少或根本没有,这是真正的金钱成本,即使难以估计或衡量。即,如果需要,使用 C++ 可以用一些程序员效率换取执行效率。是否这样做在很大程度上是一个工程和直觉决定,因为在实践中,只有收益而不是成本可以很容易地估计和衡量。
有 C++ 异常抛出性能的客观衡量标准吗?
是的,国际 C++ 标准化委员会已经发布了Technical Report on C++ performance, TR18015。
异常“慢”是什么意思?
这主要意味着 throw 与例如相比可能需要很长时间™ int 分配,由于搜索处理程序。
正如 TR18015 在其第 5.4 节“异常”中所讨论的,有两种主要的异常处理实现策略,
第一种非常灵活和通用的方法几乎在 32 位 Windows 中被强制使用,而在 64 位域和 *nix 域中通常使用第二种效率更高的方法。
正如该报告所讨论的,对于每种方法,异常处理影响效率的三个主要领域:
try-blocks,
常规函数(优化机会),以及
throw-表达式。
主要是,使用动态处理程序方法(32 位 Windows)异常处理会对 try 块产生影响,主要与语言无关(因为这是由 Windows 的 结构化异常处理 方案强制执行的),而静态表方法对于try-blocks 的成本大致为零。与 SO 答案相比,讨论这一点需要更多的空间和研究。因此,请参阅报告了解详情。
不幸的是,从 2006 年开始,到 2012 年底,这份报告已经有点过时了,据我所知,没有任何可比的更新。
另一个重要的观点是,使用异常对性能的影响与支持语言功能的孤立效率有很大不同,因为正如报告所述,
“在考虑异常处理时,必须将其与其他处理方式进行对比
处理错误。”
例如:
不同编程风格导致的维护成本(正确性)
冗余呼叫站点 if 故障检查与集中式 try
缓存问题(例如较短的代码可能适合缓存)
报告有不同的方面需要考虑,但无论如何,获得关于执行效率的确凿事实的唯一实用方法可能是在确定的开发时间上限内使用异常而不使用异常来实现相同的程序,与熟悉每种方式的开发人员一起,然后MEASURE。
什么是避免异常开销的好方法?
正确性几乎总是胜过效率。
无一例外,很容易发生以下情况:
某些代码 P 用于获取资源或计算某些信息。
调用代码 C 应该检查成功/失败,但没有。
在 C 之后的代码中使用了不存在的资源或无效信息,造成了一般性混乱。
主要问题是第 (2) 点,在通常的 返回码 方案中,调用代码 C 不会被强制检查。
有两种主要方法可以强制进行此类检查:
第二种方法是 AFAIK,首先由 Barton 和 Nackman 在他们的书 *Scientific and Engineering C++: An Introduction with Advanced Techniques and Examples 中进行了描述,他们在其中引入了一个名为 Fallow 的类以获得“可能的”函数结果。 Boost 库现在提供了一个名为 optional 的类似类。并且您可以自己轻松实现Optional 类,使用std::vector 作为非POD 结果情况下的值载体。
使用第一种方法,调用代码 C 只能使用异常处理技术。然而,使用第二种方法,调用代码 C 可以自己决定是进行基于if 的检查,还是进行一般的异常处理。因此,第二种方法支持在程序员与执行时间效率之间进行权衡。
各种 C++ 标准对异常性能有何影响?
“我想知道 C++98 是否仍然如此”
C++98 是第一个 C++ 标准。对于异常,它引入了异常类的标准层次结构(不幸的是相当不完善)。对性能的主要影响是异常规范(在 C++11 中被删除)的可能性,但是它从未被主要的 Windows C++ 编译器完全实现 Visual C++:Visual C++ 接受 C++98异常规范语法,但忽略异常规范。
C++03 只是 C++98 的技术勘误。 C++03 中唯一真正的新功能是值初始化。这与异常无关。
随着 C++11 标准的一般异常规范被删除,并替换为 noexcept 关键字。
C++11 标准还增加了对存储和重新抛出异常的支持,这对于在 C 语言回调中传播 C++ 异常非常有用。这种支持有效地限制了当前异常的存储方式。但是,据我所知,这不会影响性能,除非在较新的代码中,异常处理可能更容易在 C 语言回调的两侧使用。