Closed. This question needs details or clarity。它当前不接受答案。












想改善这个问题吗?添加详细信息,并通过editing this post阐明问题。

6年前关闭。



Improve this question




作为一个(可能的)示例,LLVM编码标准禁止使用标准RTTI或异常(exception):
http://llvm.org/docs/CodingStandards.html#do-not-use-rtti-or-exceptions

这是一个好主意,还是大多数程序的编码标准已经过时或不合理?

即使不使用C++,C++中是否还有其他此类功能会严重恶化程序的速度,内存使用量或可执行文件的大小?

最佳答案



RTTI绝对是最臭名昭著的违反零开销原则的原因,因为它会产生与多态类(即具有至少一个虚函数的类)数量成比例的静态成本(可执行大小和初始化代码) ,并且完全不取决于您使用了多少。但是,没有一些每类的开销就无法真正提供RTTI。因此,如果根本不需要RTTI,或者如果您想用RTTI系统替换它,则可以在大多数编译器上禁用RTTI,从而可以更好地控制它们(就像LLVM那样)。但是,如果您启用了RTTI,并且没有使用它,那么开销只会以代码膨胀(更大的可执行文件,所需的更大内存空间,更大的代码散布)和加载/卸载时间等形式出现,运行-时间执行开销几乎不存在。但是在资源匮乏的环境中,或者对于小型实用程序(例如,在shell脚本中重复调用),静态开销可能实在太大。此外,在很多实际情况下,您不需要高性能的RTTI,大多数时候根本不需要它,而在其他时候,您需要在一些通常不是性能的特殊地方使用它-与其他事物相比至关重要。 LLVM是一个异常(exception),因为编写编译器涉及处理抽象语法树和类似的描述性类层次结构,如果不进行大量下调,很难做到这一点,并且由于分析这些结构是编译器执行的核心操作,因此性能下降-casts(或其他RTTI调用)至关重要。因此,不要以“不使用RTTI”为一般规则,只知道它会带来什么开销,并且就其成本/ yield 而言,知道它是否适合您的应用程序。

当然,C++异常可能会比您准备讨价还价的开销大。这是一个更具争议性的问题,尤其是当涉及到实际描述整个异常的开销时。凭经验评估异常的开销是一项非常困难的任务,因为它高度依赖于使用模式,即使用异常的方式不同,严重性级别不同(是否将异常用于错误,致命错误,特殊情况,或者替换每个if语句?),并且对错误处理有不同程度的关注(无论是否使用异常)。然后,当然,不同的编译器可以不同地实现异常。当前的实现方式,即所谓的“零成本异常(exception)”,旨在使正常执行期间的运行时成本为零,但是这会留下相当多的静态开销,并使“逐项捕获”执行路径变慢。 Here是一个很好的概述。至于违反“您只为使用的东西付费”原则的异常(exception),这是真的(除非您禁用它们),但是它们通常是合理的。带有异常的基本假设是,作为程序员,您打算编写健壮的错误处理代码,如果您确实处理了所有错误,则与错误处理代码相比,异常的开销将减少(catch-blocks)和析构函数),与同等数量的错误处理的等效C样式错误代码实现相比,您可能会拥有一个更小更快的程序。但是,如果您不打算进行过多的错误处理(例如,“如果发生任何错误,只会崩溃!”方法),那么异常将招致大量开销。我不确定为什么LLVM禁止异常(exception)(如果我必须直言不讳,我会说这是因为就我从该项目中看到的代码来看,它们并不十分重视错误处理) 。因此,长话短说,指南应该是“如果您打算认真处理错误,则使用异常,否则,则不要”。但是请记住,这是一个热门话题。



您已经命名了两个显而易见的名称,并且毫不奇怪,它们是大多数编译器具有禁用选项的两个主要功能(并且在适当的时候经常被禁用)。当然,还有其他一些较小的违反零开销原则的行为。

一个臭名昭著的例子是标准库中的IO流库(<iostream>)。该库经常因批评大多数人需要和使用它的太多开销而受到批评。 IO流库倾向于引入大量代码,并且需要大量的加载时初始化。然后,它的许多类(如std::ostreamstd::ofstream)都具有过多的运行时开销,无论是构造/销毁还是读写性能。我认为他们在该库中打包了太多功能,并且由于在大多数情况下,IO流任务非常简单,因此这些功能通常不使用,其开销也不合理。但是总的来说,我发现开销通常是可以接受的,尤其是因为大多数IO任务已经非常慢。顺便说一句,LLVM还禁止使用IO流库,这又是因为LLVM的目标是编写精简且卑鄙的命令行实用程序,这些程序执行大量文件IO(例如编译器或其相关工具) 。

在某些特定情况下,可能还有其他一些标准库比某些库具有更多的开销。库代码常常不得不做出妥协,以至于无法取悦所有人。我怀疑某些较新的库(例如线程,计时,正则表达式和随机库)所提供的功能或健壮性保证比许多应用程序所必需的要多,因此会增加一些不必要的开销。但话又说回来,许多应用程序确实从这些功能中受益。这就是折衷的含义。

至于造成不必要开销的语言规则,有许多小问题会带来一些开销。首先,我可以想到该标准在几个地方必须做出妨碍优化的保守假设。一个著名的例子是无法使用restrict指针别名,这迫使编译器假定任何内存都可以被任何指针别名(尽管实际上,指针别名很少见),从而限制了优化的机会。在许多类似的情况下,编译器必须做出“安全”的假设,从而限制了优化。但是其中大多数在范围和潜在 yield 方面都相当小,而且通常在能够保证行为的正确性(以及可重复性,鲁棒性,可移植性等)方面是合理的。另外,请注意,在大多数情况下,它在其他语言中并没有得到任何改善,在C语言中可能略有改善,仅此而已。这些问题中的某些问题也可以通过编译器扩展或特定于平台的功能来解决,或者作为最后一种选择,通过内联汇编代码来解决,也就是说,如果您确实需要进行最优化。

一个不再有效的示例是一个问题,即即使对于永不抛出的函数,也要求编译器生成异常处理(堆栈展开)代码。现在,可以通过在相关函数上指定noexcept来解决。

但是除了那些微观问题之外,我真的想不出C++中除了RTTI和异常之外的任何其他主要开销来源。我的意思是,C++中的某些事物可能会产生不同种类的开销,但它们都是(每次使用)“选择加入”功能,例如虚函数,虚继承,多重继承,模板等,但是这些大多遵守“您只为使用的商品付费”的原则。有关为C++的低开销子集施加的规则的示例,请查看Embedded C++

关于c++ - 零开销原则(如果有)有哪些重要异常(exception)? ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/23594924/

10-13 09:42