41

链接指出,

要捕获异常,我们必须将一部分代码置于异常检查之下。这是通过将那部分代码包含在 try 块中来完成的。当该块内出现异常情况时,将引发异常,将控制权转移到异常处理程序。如果没有抛出异常,代码将正常继续,所有处理程序都将被忽略。

这是否意味着由于在运行时“检查”的额外任务,有一个 try 块会降低性能?

4

5 回答 5

87

TL;DR NO ,与错误代码处理相比,非异常路径上的异常通常更快。


好吧,显而易见的评论与什么相比?

与不处理错误相比,它明显降低了性能;但是性能是否值得缺乏正确性?我认为不是,所以让我们假设您的意思是与使用if语句检查的错误代码相比。

在这种情况下,这取决于。有多种机制用于实现异常。实际上,它们可以使用与语句非常接近的机制来实现,if以至于它们最终具有相同的成本(或略高)。

不过,在 C++ 中,所有主要编译器(gcc 在 4.x 系列中引入它,MSVC 将它用于 64 位代码)现在都使用零成本异常模型。如果您阅读 Need4Sleep 链接到的这篇技术论文,它被列为表驱动方法。这个想法是,对于程序的每个点,可能会让您在边表中注册一些可以让您找到正确的 catch 子句的点点滴滴。老实说,它在实现方面比旧的策略要复杂一些,但是零成本的名称源于它是免费的,不应该抛出异常。将此与 CPU 上的分支错误预测进行对比。另一方面,当抛出异常时,惩罚是巨大的,因为该表存储在冷区(很可能需要往返 RAM 或更糟)......但例外是例外,对吗?

总而言之,现代 C++ 编译器的异常比错误代码更快,但代价是更大的二进制文件(由于静态表)。


为了详尽起见,还有第三种选择:堕胎。不是通过状态或异常传播错误,而是可以中止进程。这仅适用于有限数量的情况,但它比任何一种替代方案都优化得更好。

于 2013-05-28T06:29:47.757 回答
6

看一下关于 C++ 性能的技术报告草案的第 5.4 节, 它专门关于 c++ 中 try-catch 语句的开销。

部分摘录:

5.4.1.1.2 “代码”方法的时间开销

• On entry to each try-block
    ♦ Commit changes to variables enclosing the try-block
    ♦ Stack the execution context 
    ♦ Stack the associated catch clauses 
• On exit from each try-block
    ♦ Remove the associated catch clauses 
    ♦ Remove the stacked execution context 
• When calling regular functions 
    ♦ If a function has an exception-specification, register it for checking 
• As local and temporary objects are created 
    ♦ Register each one with the current exception context as it is created 
• On throw or re-throw 
    ♦ Locate the corresponding catch clause (if any) – this involves some runtime check (possibly resembling RTTI checks) 
    If found, then: 
    destroy the registered local objects 
    check the exception-specifications of the functions called in-between 
    use the associated execution context of the catch clause 
    Otherwise: 
    call the terminate_handler6
于 2013-05-28T05:42:04.910 回答
2

It really depends on the specific compiler.

If the compiler prefers to consider exception throwing a really exceptional condition then you can implement a scheme where in case of no exception you have zero-overhead but that, in turn, will cost more time in case of an exception and/or more code size.

To implement a zero-overhead approach you can notice that in C++ you cannot dynamically change code so once you know what is the stack frame and the return address it's fixed what are the objects that must be destroyed in case of unwinding or if there is an exception handling code section. The code for throwing an exception could therefore check a global table of all function call sites to decide what should be done.

On the other side you can make exception throwing faster by preparing the list of objects to be explicitly destroyed and the address of the exception handling code during normal execution. This will make regular code slower, but exception handling faster and also I'd say code a bit smaller.

Unfortunately there is no standard way in C++ to completely give up exception support, so something has to be paid to this possibility: the standard library throws exceptions and any code that calls unknown code (e.g. using a function pointer or calling a virtual method) must be prepared to handle an exception.

于 2013-05-28T06:32:56.760 回答
2

这取决于。对于异常处理,编译器必须做一些事情 - 否则它无法进行堆栈展开等。这意味着是的,异常处理会降低性能——即使没有抛出异常。多少 - 这取决于您的编译器实现。

另一方面,您必须质疑自己:如果您自己插入错误处理代码,它真的会更快吗(测量它 - 不要猜测它)。它可以做与异常相同的事情吗(客户端不能忽略异常-错误代码可以-并且它们可以进行堆栈展开而错误代码不能)。此外,可以将代码编写成更易于维护的异常情况。

简短:除非您的代码非常非常非常非常非常时间紧迫,否则请使用异常。即使您决定反对他们:首先衡量。规则的一个例外:跨模块边界或在析构函数中抛出异常是一个坏主意。

于 2013-05-28T05:49:11.163 回答
1

我建议在执行内存分配、删除、调用另一个复杂函数等的函数中添加 try catch。实际上,性能方面的 try catch 会增加一点开销。

但考虑到捕获未知异常的优点,它非常有帮助。良好的编程实践总是建议在代码中添加某种异常处理,除非您是一位出色的程序员。

我想知道为什么你如此关心小的性能问题而不是整个程序被异常卡住。

于 2013-05-28T05:59:42.607 回答