C++ 中的异常真的很慢吗
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13835817/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Are Exceptions in C++ really slow
提问by Avinash
I was watching Systematic Error Handling in C++—Andrei Alexandrescuhe claims that Exceptions in C++ are very very slow.
我正在看C++ 中的系统错误处理——Andrei Alexandrescu他声称 C++ 中的异常非常非常慢。
Is this still true for C++98?
这对于 C++98 仍然适用吗?
回答by Matthieu M.
The main model used today for exceptions (Itanium ABI, VC++ 64 bits) is the Zero-Cost model exceptions.
今天用于异常(Itanium ABI、VC++ 64 位)的主要模型是零成本模型异常。
The idea is that instead of losing time by setting up a guard and explicitly checking for the presence of exceptions everywhere, the compiler generates a side table that maps any point that may throw an exception (Program Counter) to the a list of handlers. When an exception is thrown, this list is consulted to pick the right handler (if any) and stack is unwound.
这个想法是,编译器生成一个侧表,将任何可能抛出异常的点(程序计数器)映射到处理程序列表,而不是通过设置保护和显式检查无处不在的异常来浪费时间。当抛出异常时,会参考此列表以选择正确的处理程序(如果有)并展开堆栈。
Compared to the typical if (error)
strategy:
与典型if (error)
策略相比:
- the Zero-Cost model, as the name implies, is free when no exceptions occur
- it costs around 10x/20x an
if
when an exception does occur
- 零成本模型,顾名思义,在没有异常发生时是免费的
if
当异常发生时,它的成本约为 10 倍/20 倍
The cost, however, is not trivial to measure:
然而,成本的衡量并非微不足道:
- The side-table is generally cold, and thus fetching it from memory takes a long time
- Determining the right handler involves RTTI: many RTTI descriptors to fetch, scattered around memory, and complex operations to run (basically a
dynamic_cast
test for each handler)
- 边桌通常是冷的,因此从内存中获取它需要很长时间
- 确定正确的处理程序涉及 RTTI:要获取许多 RTTI 描述符,分散在内存中,以及要运行的复杂操作(基本上是
dynamic_cast
对每个处理程序的测试)
So, mostly cache misses, and thus not trivial compared to pure CPU code.
因此,主要是缓存未命中,因此与纯 CPU 代码相比并非微不足道。
Note: for more details, read the TR18015 report, chapter 5.4 Exception Handling (pdf)
注意:有关更多详细信息,请阅读TR18015 报告,第 5.4 章异常处理 (pdf)
So, yes, exceptions are slow on the exceptional path, but they are otherwise quicker than explicit checks (if
strategy) in general.
所以,是的,异常在异常路径上很慢,但它们通常比显式检查(if
策略)更快。
Note: Andrei Alexandrescu seems to question this "quicker". I personally have seen things swing both ways, some programs being faster with exceptions and others being faster with branches, so there indeed seems to be a loss of optimizability in certain conditions.
注意:Andrei Alexandrescu 似乎对此“更快”提出了质疑。我个人已经看到事情是双向的,有些程序在异常情况下更快,而其他程序在分支时更快,因此在某些情况下确实似乎失去了可优化性。
Does it matter ?
有关系吗 ?
I would claim it does not. A program should be written with readabilityin mind, not performance (at least, not as a first criterion). Exceptions are to be used when one expects that the caller cannot or will not wish to handle the failure on the spot, and pass it up the stack. Bonus: in C++11 exceptions can be marshalled between threads using the Standard Library.
我会声称它没有。编写程序时应该考虑可读性,而不是性能(至少,不是作为首要标准)。当期望调用者不能或不想当场处理故障并将其向上传递到堆栈时,将使用异常。奖励:在 C++11 中,可以使用标准库在线程之间编组异常。
This is subtle though, I claim that map::find
should not throw but I am fine with map::find
returning a checked_ptr
which throws if an attempt to dereference it fails because it's null: in the latter case, as in the case of the class that Alexandrescu introduced, the caller choosesbetween explicit check and relying on exceptions. Empowering the caller without giving him more responsibility is usually a sign of good design.
虽然这是微妙的,但我声称不map::find
应该抛出,但我可以map::find
返回一个checked_ptr
抛出,如果尝试取消引用它失败,因为它是空的:在后一种情况下,就像 Alexandrescu 引入的类一样,调用者选择在显式检查和依赖异常之间。赋予呼叫者权力而不给他更多责任通常是良好设计的标志。
回答by Cheers and hth. - Alf
When the question was posted I was on my way to the doctor, with a taxi waiting, so I only had time then for a short comment. But having now commented and upvoted and downvoted I’d better add my own answer. Even if Matthieu’s answeralready is pretty good.
当问题发布时,我正在去看医生的路上,有一辆出租车在等着,所以我当时只有时间做一个简短的评论。但是现在已经评论、赞成和反对,我最好添加我自己的答案。即使马修的回答已经很好了。
Are exceptions especially slow in C++, compared to other languages?
与其他语言相比,C++ 中的异常是否特别慢?
Re the claim
重新索赔
“I was watching Systematic Error Handling in C++—Andrei Alexandrescuhe claims that Exceptions in C++ are very very slow.”
“我在看C++ 中的系统错误处理——Andrei Alexandrescu他声称 C++ 中的异常非常非常慢。”
If that’s literally what Andrei claims, then for once he’s very misleading, if not downright wrong. For a raised/thrown exceptions is always slow compared to other basic operations in the language, regardless of the programming language. Not just in C++ or more so in C++ than in other languages, as the purported claim indicates.
如果这确实是安德烈所说的,那么这一次他非常具有误导性,如果不是完全错误的话。对于引发/抛出的异常,与语言中的其他基本操作相比,无论编程语言如何,总是很慢。正如声称的声明所示,不仅仅是在 C++ 中,或者在 C++ 中比在其他语言中更是如此。
In general, mostly regardless of language, the two basic language features that are orders of magnitude slower than the rest, because they translate to calls of routines that handle complex data structures, are
一般来说,无论语言如何,两个基本语言特征比其他语言慢几个数量级,因为它们转换为处理复杂数据结构的例程调用,是
exception throwing, and
dynamic memory allocation.
异常抛出,以及
动态内存分配。
Happily in C++ one can often avoid both in time-critical code.
令人高兴的是,在 C++ 中,通常可以在时间关键的代码中避免这两种情况。
Unfortunately There Ain’t No Such Thing As A Free Lunch, even if the default efficiency of C++ comes pretty close. :-) For the efficiency gained by avoiding exception throwing and dynamic memory allocation is generally achieved by coding at a lower level of abstraction, using C++ as just a “better C”. And lower abstraction means greater “complexity”.
不幸的是,没有免费午餐之类的东西,即使 C++ 的默认效率非常接近。:-) 对于通过避免异常抛出和动态内存分配获得的效率,通常通过在较低抽象级别进行编码来实现,将 C++ 用作“更好的 C”。较低的抽象意味着更大的“复杂性”。
Greater complexity means more time spent on maintenance and little or no benefit from code reuse, which are real monetary costs, even if difficult to estimate or measure. I.e., with C++ one can, if so desired, trade some programmer efficiency for execution efficiency. Whether to do so is largely an engineering and gut-feeling decision, because in practice only the gain, not the cost, can be easily estimated and measured.
更大的复杂性意味着更多的时间花在维护上,而代码重用几乎没有好处,即使难以估计或衡量,这是真正的货币成本。即,如果需要的话,使用 C++ 可以用一些程序员效率来换取执行效率。是否这样做在很大程度上是一个工程和直觉决定,因为在实践中只有收益,而不是成本,可以很容易地估计和衡量。
Are there any objective measures of C++ exception throwing performance?
是否有任何客观的 C++ 异常抛出性能衡量标准?
Yes, the international C++ standardization committee has published a Technical Report on C++ performance, TR18015.
是的,国际 C++ 标准化委员会发布了关于 C++ 性能的技术报告,TR18015。
What does it meanthat exceptions are “slow”?
异常“慢”是什么意思?
Mainly it means that a throw
can take a Very Long Time™ compared to e.g. an int
assignment, due to the search for handler.
这主要意味着throw
与例如int
分配相比,由于搜索处理程序,a可能需要很长的时间™ 。
As TR18015 discusses in its section 5.4 “Exceptions” there are two principal exception handling implementation strategies,
正如 TR18015 在其第 5.4 节“异常”中讨论的那样,有两种主要的异常处理实现策略,
the approach where each
try
-block dynamically sets up exception catching, so that a search up the dynamic chain of handlers is performed when an exception is thrown, andthe approach where the compiler generates static look-up tables that are used to determine the handler for a thrown exception.
每个
try
-block 动态设置异常捕获的方法,以便在抛出异常时执行动态处理程序链的搜索,以及编译器生成静态查找表的方法,用于确定抛出异常的处理程序。
The first very flexible and general approach is almost forced in 32-bit Windows, while in 64-bit land and in *nix-land the second far more efficient approach is commonly used.
第一种非常灵活和通用的方法在 32 位 Windows 中几乎被强制使用,而在 64 位和 *nix-land 中,通常使用第二种更有效的方法。
Also as that report discusses, for each approach there are three main areas where exception handling impacts on efficiency:
同样正如该报告所讨论的,对于每种方法,异常处理都会影响三个主要领域的效率:
try
-blocks,regular functions (optimization opportunities), and
throw
-expressions.
try
- 块,常规功能(优化机会),以及
throw
-表达式。
Mainly, with the dynamic handler approach (32-bit Windows) exception handling has an impact on try
blocks, mostly regardless of language (because this is forced by Windows' Structured Exception Handlingscheme), while the static table approach has roughly zero cost for try
-blocks. Discussing this would take a lot more space and research than is practical for an SO answer. So, see the report for details.
主要是,动态处理程序方法(32 位 Windows)异常处理对try
块有影响,主要与语言无关(因为这是由 Windows 的结构化异常处理方案强制的),而静态表方法的成本大致为零try
-块。讨论这个将需要更多的空间和研究,而不是 SO 答案的实际情况。因此,请参阅报告了解详情。
Unfortunately the report, from 2006, is already a little bit dated as of late 2012, and as far as I know there’s not anything comparable that’s newer.
不幸的是,这份 2006 年的报告到 2012 年底已经有点过时了,据我所知,没有任何可比的更新。
Another important perspective is that the impact of use of exceptionson performance is very different from the isolated efficiency of the supporting language features, because, as the report notes,
另一个重要的观点是,异常的使用对性能的影响与支持语言特性的孤立效率有很大不同,因为正如报告指出的那样,
“When considering exception handling, it must be contrasted to alternative ways of dealing with errors.”
“在考虑异常处理时,它必须与其他处理错误的方法形成对比。”
For example:
例如:
Maintenance costs due to different programming styles (correctness)
Redundant call site
if
failure checking versus centralizedtry
Caching issues (e.g. shorter code may fit in cache)
由于不同的编程风格(正确性)导致的维护成本
冗余呼叫站点
if
故障检查与集中式try
缓存问题(例如较短的代码可能适合缓存)
The report has a different list of aspects to consider, but anyway the only practical way to obtain hard facts about the execution efficiency is probably to implement the same program using exception and not using exceptions, within a decided cap on development time, and with developers familiar with each way, and then MEASURE.
该报告有不同的方面需要考虑,但无论如何,获得有关执行效率的确凿事实的唯一可行方法可能是使用异常而不使用异常,在确定的开发时间上限内,并与开发人员一起实现相同的程序熟悉每一种方式,然后MEASURE。
What is a good way to avoid the overhead of exceptions?
避免异常开销的好方法是什么?
Correctnessalmost always trumps efficiency.
正确性几乎总是胜过效率。
Without exceptions, the following can easily happen:
无一例外,以下情况很容易发生:
Some code P is meant to obtain a resource or compute some information.
The calling code C should have checked for success/failure, but doesn't.
A non-existent resource or invalid information is used in code following C, causing general mayhem.
某些代码 P 旨在获取资源或计算某些信息。
调用代码 C 应该检查成功/失败,但没有。
在 C 之后的代码中使用了不存在的资源或无效信息,从而导致普遍的混乱。
The main problem is point (2), where with the usual return codescheme the calling code C is not forced to check.
主要问题是第 (2) 点,使用通常的返回码方案,调用代码 C 不会被强制检查。
There are two main approaches that do force such checking:
有两种主要方法可以强制进行此类检查:
Where P directly throws an exception when it fails.
Where P returns an object that C has to inspectbefore using its main value (otherwise an exception or termination).
其中 P 失败时直接抛出异常。
其中 P 返回一个对象,C在使用其主值之前必须检查该对象(否则为异常或终止)。
The second approach was, AFAIK, first described by Barton and Nackman in their book *Scientific and Engineering C++: An Introduction with Advanced Techniques and Examples, where they introduced a class called Fallow
for a “possible” function result. A similar class called optional
is now offered by the Boost library. And you can easily implement an Optional
class yourself, using a std::vector
as value carrier for the case of non-POD result.
第二种方法是 AFAIK,由 Barton 和 Nackman 在他们的书《科学与工程 C++:高级技术和示例介绍》中首次描述,他们在其中介绍了一个调用Fallow
“可能”函数结果的类。optional
Boost 库现在提供了一个类似的类。并且您可以轻松地Optional
自己实现一个类,std::vector
在非 POD 结果的情况下使用一个作为值载体。
With the first approach the calling code C has no choice but to use exception handling techniques. With the second approach, however, the calling code C can itself decide whether to do if
based checking, or general exception handling. Thus, the second approach supports making the programmer versus execution time efficiency trade-off.
对于第一种方法,调用代码 C 只能使用异常处理技术。然而,使用第二种方法,调用代码 C 可以自己决定是进行if
基于检查还是一般异常处理。因此,第二种方法支持使程序员与执行时间效率权衡。
What is the impact of the various C++ standards, on exception performance?
各种 C++ 标准对异常性能有何影响?
“I want to know is this still true for C++98”
“我想知道 C++98 是否仍然如此”
C++98 was the first C++ standard. For exceptions it introduced a standard hierarchy of exception classes (unfortunately rather imperfect). The main impact on performance was the possibility of exception specifications(removed in C++11), which however were never fully implemented by the main Windows C++ compiler Visual C++: Visual C++ accepts the C++98 exception specification syntax, but just ignores exception specifications.
C++98 是第一个 C++ 标准。对于异常,它引入了异常类的标准层次结构(不幸的是相当不完美)。对性能的主要影响是异常规范的可能性(在 C++11 中删除),但是它从未被主要的 Windows C++ 编译器完全实现 Visual C++:Visual C++ 接受 C++98 异常规范语法,但只是忽略异常规范。
C++03 was just a technical corrigendum of C++98. The only really new in C++03 was value initialization. Which has nothing to do with exceptions.
C++03 只是 C++98 的技术勘误表。C++03 中唯一真正新的是值初始化。这与异常无关。
With the C++11 standard general exception specifications were removed, and replaced with the noexcept
keyword.
随着 C++11 标准的通用异常规范被删除,并替换为noexcept
关键字。
The C++11 standard also added support for storing and rethrowing exceptions, which is great for propagating C++ exceptions across C language callbacks. This support effectively constrains how the current exception can be stored. However, as far as I know that does not impact on performance, except to the degree that in newer code exception handling may more easily be used on both sides of a C language callback.
C++11 标准还增加了对存储和重新抛出异常的支持,这对于跨 C 语言回调传播 C++ 异常非常有用。这种支持有效地限制了当前异常的存储方式。但是,据我所知,这不会影响性能,除非在较新的代码中异常处理可能更容易在 C 语言回调的两侧使用。
回答by Philipp
It depends on the compiler.
这取决于编译器。
GCC, for example, was known for having very poor performance when handling exceptions, but this got considerably better in the past few years.
例如,GCC 以处理异常时的性能很差而著称,但在过去几年中它变得更好了。
But note that handling exceptions should - as the name says - be the exception rather than the rule in your software design. When you have an application which throws so many exceptions per second that it impacts performance and this is still considered normal operation, then you should rather think about doing things differently.
但请注意,处理异常应该——顾名思义——是软件设计中的例外而不是规则。当您的应用程序每秒抛出如此多的异常以致影响性能并且这仍被视为正常操作时,那么您应该考虑以不同的方式做事。
Exceptions are a great way to make code more readable by getting all that clunky error handling code out of the way, but as soon as they become part of the normal program flow, they become really hard to follow. Remember that a throw
is pretty much a goto catch
in disguise.
异常是让代码更具可读性的好方法,它可以将所有笨重的错误处理代码排除在外,但是一旦它们成为正常程序流程的一部分,它们就变得非常难以理解。请记住, athrow
几乎是goto catch
伪装的 a。
回答by Arash
You can never claim about performance unless you convert the code to the assembly or benchmark it.
除非您将代码转换为程序集或对其进行基准测试,否则您永远无法声称性能。
Here is what you see: (quick-bench)
这是你看到的:(快速工作台)
The error code is not sensitive to the percentage of occurrence. Exceptions have a little bit overhead as long as they are never thrown. Once you throw them, the misery starts. In this example, it is thrown for 0%, 1%, 10%, 50% and 90% of the cases. When the exceptions are thrown 90% of the time, the code is 8 times slower than the case where the exceptions are thrown 10% of the time. As you see, the exceptions are really slow. Do not use them if they are thrown frequently. If your application has no real-time requirement, feel free to throw them if they occur very rarely.
错误代码对发生的百分比不敏感。只要从不抛出异常,就会有一点开销。一旦你扔掉它们,痛苦就开始了。在这个例子中,它在 0%、1%、10%、50% 和 90% 的情况下被抛出。当 90% 的时间抛出异常时,代码比 10% 的时间抛出异常的情况慢 8 倍。如您所见,异常非常缓慢。如果经常扔它们,不要使用它们。如果您的应用程序没有实时性要求,如果它们很少发生,请随意丢弃它们。
You see many contradictory opinions about them. But finally, are exceptions are slow? I don't judge. Just watch the benchmark.
你会看到很多关于他们的相互矛盾的观点。但最后,异常是否很慢?我不判断。只看基准。
回答by NoSenseEtAl
Yes, but that doesn't matter.
Why?
Read this:
https://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx
是的,但这没关系。为什么?
阅读:https:
//blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx
Basically that says that using exceptions like Alexandrescu described (50x slowdown because they use catch
as else
) is just wrong.
That being said for ppl who like to do it like that
I wish C++22 :) would add something like:
(note this would have to be core language since it is basically compiler generating code from existing one)
基本上就是说使用像 Alexandrescu 描述的异常(50 倍减速,因为它们使用catch
as 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())
//...
}
P.S. also note that even if the exceptions are that slow... it is not a problem if you dont spend a lot of time in that part of the code during execution... For example if float division is slow and you make it 4x faster that doesnt matter if you spend 0.3% of your time doing FP division...
PS还请注意,即使异常很慢……如果您在执行期间不在该部分代码上花费大量时间,那也不是问题……例如,如果浮点除法很慢并且您将其设为4x如果你把 0.3% 的时间花在 FP 除法上,那么更快也没关系……
回答by Chris McCabe
Like in silico said its implementation dependent, but in general exceptions are considered slow for any implementation and shouldn't be used in performance intensive code.
就像 in silico 说它的实现依赖一样,但一般来说异常对于任何实现都被认为是缓慢的,不应在性能密集型代码中使用。
EDIT: I'm not saying don't use them at all but for performance intensive code it is best to avoid them.
编辑:我并不是说根本不要使用它们,但对于性能密集型代码,最好避免使用它们。