潜在的异常会带来开销吗?

Posted

技术标签:

【中文标题】潜在的异常会带来开销吗?【英文标题】:Do potential exceptions carry an overhead? 【发布时间】:2009-12-02 17:14:24 【问题描述】:

如果不引发异常,与不会引发异常的类似代码相比,一段可能引发异常的代码的性能会降低吗?

【问题讨论】:

见***.com/questions/43253/… 我编写的大多数 C++ 代码可能只会抛出 std::bad_alloc (你可以从 C++ 的每一行中得到它),我通常也懒得去捕捉它(我只是一个业余爱好者)。会影响性能吗?我不知道。如果你不在乎,我认为不会。 :) In what ways do C++ exceptions slow down code when there are no exceptions thown?的可能重复 【参考方案1】:

已经证明可以在“正常”(非异常相关)代码中实现零开销的 C++ 异常处理机制。然而,实际上编译器通常坚持更简单的实现,这通常会导致效率较低的“正常”代码。编译器必须考虑到潜在异常通过函数层次结构的可能性,因此生成一些额外的家庭操作以在抛出异常时启用正确的堆栈展开。无论是否抛出异常,这些额外的家庭代码都会影响代码的整体效率。

这完全是一个 QoI(实施质量)问题。它是特定于编译器的。检查您的编译器以获取更多详细信息。一些编译器实际上提供了启用/禁用 C++ 异常的选项,以便在根本不使用异常时生成最有效的代码。

【讨论】:

“已经证明可以在“正常”(与异常无关的)代码中实现零开销的 C++ 异常处理机制”。请参考。据我了解,您需要保存堆栈展开的信息。所以我真的不明白这怎么可能是零开销。 您不需要为堆栈展开保存信息。 C++ 中的自动变量具有完全嵌套的生命周期,这意味着在代码中的任何时候,您都可以确定哪些对象需要展开。无需在运行时存储编译时实际已知的内容。如果抛出异常,您只需在适当的位置输入展开代码。 G++ 有零成本例外。由于这是 Visual Studio 旁边最常见的 C++ 编译器,我不会说“编译器通常坚持更简单的实现”。【参考方案2】:

视情况而定;基于表的实现(我相信现代 g++ 使用,这是 Windows 中用于 x64 二进制文件的策略)对于非抛出异常来说是零处理开销(以稍微多一点的内存使用为代价)。基于函数的异常处理(x86 Windows 使用)即使是非抛出异常也会对性能造成很小的影响。

【讨论】:

【参考方案3】:

尝试很便宜,抓很便宜,扔很贵。显然有一些额外的处理执行代码被包装在 try 中。

对特殊的东西使用异常 - 这样开销就无关紧要了。

【讨论】:

【参考方案4】:

这取决于您的编译器。一些编译器/运行时组合在进入带有 catch 处理程序的块时会做额外的工作。其他人构建静态数据结构,所有工作都发生在 throw 上。在所有情况下,入口成本都会低于 throw,但您要小心内部循环中的 catch 块。使用您关心的编译器衡量时间成本。

【讨论】:

【参考方案5】:

这取决于编译器,但答案几乎肯定是“是”。具体来说,如果作用域包含一个具有非平凡析构函数的对象,那么该对象将需要向运行时注册,以便在异常时调用析构函数。例如:

struct Thing

    ~Thing();
    void Process();
;

for (int i = 0; i < 1000000; ++i)

    Thing thing;
    thing.Process();

除了构造和处理一百万个事物之外,这还将生成一百万个函数调用来注册和注销每个事物,以防对 Process 的调用抛出。

除此之外,进入或离开try 块时会产生少量开销,因为相应的catch 块会添加到异常处理程序堆栈中或从中删除。

【讨论】:

【参考方案6】:

由于编译器需要生成在抛出异常时将内卷堆栈的代码,因此在幕后添加了一些代码。但如果它更多,那就值得商榷了:

在变量超出范围时自动调用析构函数的代码,

以及您必须编写的代码来检查每个调用的退出状态并处理错误。

捕获错误是昂贵的:try ... catch 语句以及抛出和捕获异常时会发生什么:

保留有关添加 try ... catch 的每个位置的信息(也隐式添加,例如在析构函数周围或在异常规范中),

很多堆栈要展开(以及要调用的析构函数),以便看起来像简单的跳转,

匹配异常抛出到 catch() 子句,

复制异常。

【讨论】:

【参考方案7】:

与没有异常处理的代码相比,带有异常处理的代码更慢且更大。

因为在堆栈展开过程中,当引发异常时,它必须为要销毁的对象进行簿记。

【讨论】:

【参考方案8】:

不管它是否“在没有抛出异常时可能实现零开销”以及所有理论讨论,现实情况是对于某些编译器(g++ 4.4),即使使用 -O2 优化,只是你有一个抛出的事实紧循环函数内的子句(即 cpu-bound)将使函数慢 10 到 100 倍之间,这就是问题所在:这是实际上永远不会抛出的情况执行。

所以我的建议是避免像瘟疫一样在 C++ 中进行标准异常处理(除非你证明我错了);如果您想对性能敏感的应用程序进行错误处理,请使用 boost.context

【讨论】:

【参考方案9】:

draft Technical Report on C++ Performance 的第 5.4 节完全致力于异常的开销。

【讨论】:

以上是关于潜在的异常会带来开销吗?的主要内容,如果未能解决你的问题,请参考以下文章

realloc 调用会引入多少开销?

Java 线程数过多会造成什么异常?

Java 线程数过多会造成什么异常?

Java异常知识整理_处理异常时的性能开销

在 XML 中定义资源会导致解析开销吗?

都说不要装箱,那装箱到底带来了什么开销?