try-catch块会降低性能吗?

Does try-catch block decrease performance

本文关键字:性能 try-catch      更新时间:2023-10-16

此链接表示,

要捕获异常,必须将一部分代码置于exception下检查。这是通过在try中封装该部分代码来实现的块。当在该块中出现异常情况时异常引发,并将控制权转移给该异常处理程序。如果没有抛出异常,代码将继续正常运行所有处理程序都被忽略。

这是否意味着有一个try块会因为在运行时额外的"检查"任务而降低性能?

TL;DR NO,异常通常在非异常路径上比错误码处理更快


那么,最明显的问题是与什么相比?

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

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

在c++中,所有主要的编译器(gcc在4中引入了它)。x系列,MSVC将其用于64位代码)现在使用零成本异常模型。如果您阅读了Need4Sleep所链接的这篇技术论文,就会发现它被列为表驱动方法。其思想是,对于程序的每个点,可能会将您的寄存器扔到边表中,这些点将允许您找到正确的catch子句。老实说,它在实现方面比旧的策略稍微复杂一些,但是零成本名称的派生是由于它是自由的不应该抛出异常。将此与CPU上的分支错误预测进行对比。另一方面,当抛出异常时,代价是巨大的,因为表存储在冷区(因此可能需要往返RAM或更糟)…但是例外就是例外,对吧?

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


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

请看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

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

另一方面,你必须问自己:如果你自己插入错误处理代码,它真的会更快吗(测量它-不要猜测它)。它能像异常一样吗(异常不能被客户端忽略——错误代码可以——并且它们可以做错误代码不能做的堆栈展开)。此外,代码可以编写成更易于维护的异常。

简短:除非你的代码非常非常非常非常非常时间关键,否则使用异常。即使你决定反对它们:先衡量一下。该规则有一个例外:跨模块边界或在析构函数中抛出异常是一个坏主意。

这取决于具体的编译器。

如果编译器倾向于认为异常抛出是一个真正异常的条件,那么你可以实现一个方案,在没有异常的情况下,你的开销为零,但反过来,在发生异常和/或更大的代码大小的情况下,将花费更多的时间。

要实现零开销的方法,你可以注意到,在c++中,你不能动态更改代码,所以一旦你知道什么是堆栈帧和返回地址,它就固定了在展开的情况下必须销毁的对象,或者如果有异常处理代码段。因此,抛出异常的代码可以检查所有函数调用站点的全局表,以决定应该做什么。

另一方面,通过准备要显式销毁的对象列表和在正常执行期间异常处理代码的地址,可以使异常抛出更快。这将使常规代码变慢,但异常处理更快,而且我想说代码更小。

不幸的是,c++中没有完全放弃异常支持的标准方法,因此必须为这种可能性付出一些代价:标准库抛出异常,任何调用未知代码的代码(例如使用函数指针或调用虚方法)必须准备好处理异常。

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

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

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