Mat*_* M. 145
今天用于例外的主要模型(Itanium ABI,VC++ 64位)是零成本模型例外.
我们的想法是,通过设置一个保护并明确检查各处是否存在异常而不是浪费时间,编译器会生成一个边表,将可能引发异常的任何点(程序计数器)映射到处理程序列表.抛出异常时,会查询此列表以选择正确的处理程序(如果有)并解除堆栈.
与典型if (error)策略相比:
if当发生异常时,它的成本约为10倍/ 20倍然而,成本并非无足轻重:
dynamic_cast对每个处理程序的测试)因此,主要是缓存未命中,因此与纯CPU代码相比并不是微不足道的.
注意:有关更多详细信息,请阅读TR18015报告,第5.4章异常处理(pdf)
所以,是的,异常路径上的异常很慢,但它们通常比明确的检查(if策略)更快.
注意:根据NoSenseEtAl,Andrei Alexandrescu似乎质疑这个"更快".我个人测量了我的程序中的加速,我还没有看到关于失去可优化性的证据.
有关系吗 ?
我会声称它没有.程序的编写应考虑到可读性,而不是性能(至少不是第一个标准).当人们期望呼叫者不能或不希望当场处理故障并将其传递到堆栈时,将使用例外.额外奖励:在C++ 11中,可以使用标准库在线程之间编组异常.
这虽然很微妙,我声称不map::find应该扔但是如果尝试取消引用它会失败因为它是null,我会很好地map::find返回一个checked_ptr抛出:在后一种情况下,如在Alexandrescu引入的类的情况下,调用者选择在显式检查和依赖异常之间.赋予呼叫者权力而不给予他更多责任通常是良好设计的标志.
Che*_*Alf 56
当问题发布后,我正在去医生的路上,有一辆出租车在等,所以我只有时间做一个简短的评论.但现在评论,投票和投票,我最好添加自己的答案.即使Matthieu的答案已经很好了.
重申索赔
"我正在观看C++中的系统错误处理 - Andrei Alexandrescu他声称C++中的异常非常缓慢."
如果这确实是安德烈所说的话,那么一旦他非常误导,即使不是彻头彻尾的错误.对于引发/抛出的异常,与语言中的其他基本操作相比总是很慢,无论编程语言如何.正如声称的声明所表明的那样,在C++中,不仅仅是在C++中,而是在其他语言中.
一般来说,无论语言多少,两种基本语言特征都比其他语言慢几个数量级,因为它们转换为处理复杂数据结构的例程调用,
例外投掷,和
动态内存分配.
令人高兴的是,在C++中,人们通常可以避免使用时间要求严格的代码.
不幸的是,即使C++的默认效率非常接近,也不会有免费午餐.:-)为了通过避免异常抛出和动态内存分配获得的效率通常通过在较低的抽象级别进行编码来实现,使用C++作为"更好的C".较低的抽象意味着更大的"复杂性".
更高的复杂性意味着更多的时间花在维护上,代码重用很少或没有好处,即使难以估计或衡量,也是真正的货币成本.也就是说,使用C++,如果需要的话,可以将一些程序员效率交换为执行效率.是否这样做主要是工程和直觉决定,因为在实践中,只能获得收益,而不是成本,可以轻松估算和衡量.
是的,国际C++标准化委员会已经发布了关于C++性能的技术报告TR18015.
主要意味着,由于搜索处理程序,a throw可以采用与例如int分配相比的非常长时间.
正如TR18015在5.4节"异常"中讨论的那样,有两个主要的异常处理实现策略,
每个try-block动态设置异常捕获的方法,以便在抛出异常时执行动态处理程序链的搜索,并且
编译器生成静态查找表的方法,该查找表用于确定抛出异常的处理程序.
第一种非常灵活和通用的方法几乎是在32位Windows中强制实现的,而在64位平台和*nix-land中,通常使用第二种更有效的方法.
此报告也讨论过,对于每种方法,有三个主要方面,例外处理对效率的影响:
try-blocks,
常规功能(优化机会),以及
throw-expressions.
主要是,使用动态处理程序方法(32位Windows)异常处理会对try块产生影响,主要是与语言无关(因为这是Windows的结构化异常处理方案所强制的),而静态表方法的成本大致为零try-块.讨论这将需要更多的空间和研究,而不是实际的答案.因此,请参阅报告以获取详细信息.
不幸的是,从2006年开始,这份报告在2012年底已经有点过时了,据我所知,没有任何可比性更新的报道.
另一个重要的观点是,使用例外对绩效的影响与支持语言特征的孤立效率有很大不同,因为正如报告所述,
"在考虑异常处理时,必须将其与处理错误的其他方法进行对比."
例如:
由于编程风格不同(正确性)导致的维护成本
冗余呼叫站点if故障检查与集中式故障检查try
缓存问题(例如,较短的代码可能适合缓存)
该报告有一个不同的方面列表需要考虑,但无论如何,获取有关执行效率的硬性事实的唯一实用方法可能是使用异常实现相同的程序,而不是使用异常,在开发时间的决定上限内,以及开发人员熟悉每种方式,然后测量.
正确性几乎总是胜过效率.
没有例外,很容易发生以下情况:
一些代码P用于获取资源或计算一些信息.
调用代码C应检查成功/失败,但不检查.
在C之后的代码中使用不存在的资源或无效信息,导致一般混乱.
主要问题是第(2)点,其中使用通常的返回码方案,不强制调用代码C.
有两种主要方法可以强制进行此类检查:
P在失败时直接抛出异常的地方.
其中P返回C 在使用其主值之前必须检查的对象(否则是异常或终止).
第二种方法是AFAIK,首先由Barton和Nackman在他们的书" 科学与工程C++:高级技术与实例的介绍"中描述,他们在这本书中引入了一个称为Fallow"可能"功能结果的类.optional现在,Boost库提供了一个类似的类.并且您可以Optional自己轻松地实现一个类,使用std::vectoras值载体作为非POD结果的情况.
使用第一种方法,调用代码C别无选择,只能使用异常处理技术.但是,对于第二种方法,调用代码C本身可以决定是进行if基于检查还是进行一般异常处理.因此,第二种方法支持使程序员与执行时间效率之间的权衡取舍.
"我想知道这对C++ 98来说仍然如此"
C++ 98是第一个C++标准.对于异常,它引入了异常类的标准层次结构(遗憾的是相当不完美).对性能的主要影响是异常规范的可能性(在C++ 11中删除),然而主要的Windows C++编译器Visual C++从未完全实现:Visual C++接受C++ 98异常规范语法,但只是忽略例外规范.
C++ 03只是C++ 98的技术勘误.C++ 03中唯一真正的新功能是值初始化.这与例外无关.
随着C++ 11标准的一般异常规范被删除,并替换为noexcept关键字.
C++ 11标准还增加了对存储和重新抛出异常的支持,这对于跨C语言回调传播C++异常非常有用.此支持有效地限制了当前异常的存储方式.但是,据我所知,这对性能没有影响,除了在较新的代码中,异常处理可能更容易在C语言回调的两端使用.
Phi*_*ipp 11
这取决于编译器.
例如,GCC在处理异常时表现非常糟糕,但在过去几年中这种情况要好得多.
但请注意,处理异常应该 - 正如名称所示 - 是例外,而不是软件设计中的规则.当你的应用程序每秒抛出如此多的异常会影响性能时,这仍然被视为正常操作,那么你应该考虑采取不同的方式.
通过将所有笨重的错误处理代码排除在外,异常是使代码更具可读性的好方法,但只要它们成为正常程序流程的一部分,它们就变得非常难以理解.请记住,a throw几乎是goto catch伪装的.
| 归档时间: |
|
| 查看次数: |
39297 次 |
| 最近记录: |