我已经读到,对于异常处理使用C++异常有一些开销,而不是检查返回值。我只是在讨论没有异常情况下产生的开销。我还假设您需要实现代码来实际检查返回值并执行适当的操作,无论什么操作都等同于catch块所做的操作。而且,将抛出异常对象的代码与内部有45个状态变量的代码进行比较,也不公平,因为代码会为每个错误返回一个负整数。
我并不是试图建立一个针对或排除C++异常的案例,完全基于哪一个可能执行得更快。我听说最近有人提出这样一种情况,即使用异常的代码应该和基于返回代码的代码运行一样快,一旦考虑到检查返回值和处理错误所需的所有额外的簿记代码。我错过了什么?
- stackoverflow.com/questions/691168/…等的重复
- @尼尔:我认为另一个问题更多是关于内存占用(即内存/可执行文件大小开销),因为这是关于执行速度的问题。
- 我的答案至少是关于性能的,其他的一些也是。可能他们不应该这样做,但是现在改变他们有点晚了。
- @尼尔:哦,是的,我只看了这个问题,但是这个问题的一些答案几乎比它们所附的问题更与这个问题相关。
- C++中异常的可能复制真的很慢
在某些平台上和某些编译器上,与异常处理相关的成本是存在的。
也就是说,在构建32位目标时,Visual Studio将在每个具有局部变量的函数中注册一个处理程序,该函数具有非平凡的析构函数。基本上,它设置了一个try/finally处理程序。
gcc和Visual Studio采用的另一种技术,目标是64位,只在抛出异常时产生开销(该技术涉及遍历调用堆栈和表查找)。在很少抛出异常的情况下,这实际上可以导致更有效的代码,因为不必处理错误代码。
- 关于Visual Studio导致开销而其他编译器没有的声明-4年后的今天仍然是这样吗?
- @是的,不会改变的。C++异常是建立在Windows结构化异常之上的(除了所有的构造都不会停止捕获C++异常),它不会改变,结构化异常是Windows X86 ABI的一部分。
- @avakar是否取决于您是使用Yes (/EHsc)还是Yes with SEH exceptions (/EHa)作为编译器选项?
只有Try/Catch和Try/Except块接受一些设置说明。在任何情况下,开销都应该可以忽略不计,除了最短的循环。但是通常不会使用try/catch/except,除非在内部循环中使用。
我建议您不要担心这一点,并在需要时使用分析器来优化代码。
- 这是真的吗?为什么try/catch不同于具有析构函数的局部变量或临时变量?
- GNU C++编译器默认使用零成本模型,即在不发生异常时没有时间开销。
它完全依赖于实现,但许多最近的实现在不抛出异常时几乎没有或没有性能开销。事实上你是对的。在不使用异常的代码中,正确检查所有函数的返回代码比对使用异常的代码不执行任何操作要慢。
当然,您需要根据您的特定需求来衡量性能,以确保这一点。
- 大概检查返回代码的主要成本是if分支。在基于异常的代码中,您不需要在其他地方使用if语句,以便throw使用正确的异常吗?关键是,如果事情可能出错,那么在某个时候,您可能会陷入if语句的境地。但是,如果下面的库抛出异常(如std::vector::at()),并且您也进行了边界检查,那么您将使错误检查成本加倍。
- @bobobobo:但是如果你有一个函数a调用100个其他函数,那么在每个函数调用之间,带有错误代码的版本必须有一个if分支,而带有异常的版本则根本没有额外的if分支。
- 的确,在对A()、B()、C()的调用之间,try { A() ; B() ; C() ; } catch { ( ExceptionType ) }没有if,而在无异常代码中,则有if( !A() ){handle;} if(!B()){ handle}。所以我确实看到如果不使用异常的话,你会有一点双倍的收益。但是,如果比上面提到的臭名昭著的catch解析成本(依赖于编译器)更糟糕的话,是双倍的成本。
有一些开销和例外(如其他答案所指出的)。
但你现在没有太多选择。请尝试在项目中禁用异常,并确保所有依赖代码和库都可以在没有异常的情况下编译和运行。
它们是否与禁用的异常一起工作?
让我们假设他们这样做!然后对一些情况进行基准测试,但请注意,您必须设置一个"禁用异常"编译开关。如果没有这个开关,您仍然有开销——即使代码从不抛出异常。
唯一的开销是~6条指令,它们在函数开始时添加2个SEH,并将其保留在末尾。不管你在一个线程中有多少次尝试/捕获,都是相同的。
局部变量是什么?我听说人们在使用Try/Catch时总是抱怨他们。我不明白,因为解构主义者最终会被称为任何人。另外,你不应该让一个异常超过1-3个电话。
- 我认为局部变量的意义在于,与正常情况相反,当异常正在运行时,需要进行更多的修改/工作来找出要销毁的内容。至少我听说过。(我想他们可能想减小代码大小,但我不确定)
我把chip uni的测试代码扩展了一点。我将代码分成两个源文件(一个例外,一个不例外)。我让每个基准运行1000次,并使用clock_gettime()和CLOCK_REALTIME记录每个迭代的开始和结束时间。然后我计算了数据的平均值和方差。我用64位版本的g++5.2.0和clang++3.7.0在一个带有16GB RAM的Intel Core i7设备上运行了这个测试,该设备运行带有内核4.2.5-1-arch的ArchLinux。您可以在这里找到扩展的代码和完整的结果。
G+无例外
- 平均:30022994纳秒
- 标准偏差:1.25327E+06纳秒
例外情况
- 平均:30025642纳秒
- 标准偏差:1.83422e+06纳秒
铿锵+无例外
- 平均:20954657纳秒
- 标准偏差:426662纳秒
例外情况
- 平均:23916638纳秒
- 标准偏差:1.72583e+06纳秒
C++异常只会用CLAN++产生一个不平凡的性能惩罚,甚至惩罚也只有14%。
- 投反对票是因为你链接到的Github上的测试套件并不能真正测试OP想要知道什么。它基本上只测量包含一个throw(从未到达)的函数是否比不包含throw的代码慢。如果try catch块是否比none慢,您应该测量什么。