作为(可能的)示例,LLVM编码标准禁止使用标准RTTI或例外:http: //llvm.org/docs/CodingStandards.html#do-not-use-rtti-or-exceptions
这是一个好主意,还是大多数程序的编码标准过时或不合理?
C++中是否有任何其他此类功能,即使您不使用它们,也会显着恶化程序的速度,内存使用或可执行文件大小?
Mik*_*son 10
这仍然是一个好主意,还是编码标准过时了?
RTTI绝对是最臭名昭着的零开销原则的违反,因为它产生的静态成本(可执行大小和初始化代码)与多态类的数量成比例(即,具有至少一个虚函数的类) ,如果有的话,它不取决于你使用它多少.但是如果没有一些每类开销,就没有办法真正提供RTTI.这就是为什么你可以在大多数编译器上禁用RTTI,如果你根本不需要它,或者如果你想用一个你有更多控制权的RTTI系统替换它(就像LLVM的人那样).然而,如果你启用了RTTI,并且你没有使用它,那么开销只是代码膨胀(更大的可执行文件,更大的内存空间,更大的代码扩展)和加载/卸载时间的形式,因此,运行时间执行开销几乎不存在.但是在资源匮乏的环境中,或者对于小型实用程序(例如,在shell脚本中重复调用),静态开销可能无法承受.此外,没有那么多需要高性能RTTI的实际情况,大部分时间你根本不需要它,而有时你需要在一些通常不具备性能的特殊地方 - 与其他事物相比至关重要.LLVM在这里是一个例外,因为编写编译器涉及处理抽象语法树和类似的描述性类层次结构,如果没有大量的向下转换很难做到这一点,并且因为分析这些结构是编译器所做的核心操作,所以-casts(或其他RTTI调用)至关重要.因此,不要将"不要使用RTTI"作为一般规则,只要知道它需要的开销,并知道它的成本/收益是否可以接受.
C++异常肯定是可能比你准备讨价还价更多开销的事情列表中的下一个.这是一个更具争议性的问题,特别是在实际描述整体异常的开销时.根据经验评估异常的开销是一项非常困难的任务,因为它高度依赖于使用模式,即,有不同的方法来使用异常,不同的严重程度(您是否使用异常来处理错误,致命错误,异常情况,或者替换每个if语句?),并且对错误处理有不同程度的关注(无论是否使用异常).当然,不同的编译器可以不同地实现异常.当前的实现,即所谓的"零成本异常",是为了在正常执行期间具有零运行时成本,但是这会留下相当多的静态开销并使得throw-to-catch执行路径变慢.这是一个很好的概述.如果例外违反了"你只支付你使用的东西"的原则,那就是真的(除非你禁用它们),但它们通常是合理的.有例外的基本假设是,作为程序员,您打算编写健壮的错误处理代码,如果您确实适当地处理了所有错误,那么与错误处理代码(catch-blocks)相比,异常的开销会很大.和destructors),你可能会有一个比相同数量的错误处理的等效C风格的错误代码实现更小更快的程序.但是如果你不打算做很多错误处理(例如,"如果出现任何问题,只是崩溃!"的方法),那么异常会产生很大的开销.我不确定为什么LLVM禁止异常(如果我不得不直言不讳,我会说这是因为他们对错误处理不是很认真,据我从该项目中看到的代码可以看出) .所以,长话短说,指南应该是"如果你打算认真处理错误,使用例外,否则,不要".但请记住,这是一个激烈争论的话题.
是否有任何其他此类功能违反"您只需支付使用费用"?
你已经命名为两个明显的,并且毫不奇怪,它们是大多数编译器可以选择禁用的两个主要功能(并且在适当的时候通常被禁用).当然还有其他更微小的违反零开销原则的行为.
一个臭名昭着的例子是<iostream>标准库中的IO-stream库().这个库经常被批评为大多数人需要和使用它的开销过多.IO流库倾向于引入大量代码,并且需要相当多的加载时初始化.然后,它的许多类都喜欢std::ostream或者std::ofstream有太多的运行时开销,无论是构造/破坏还是读/写性能.我认为它们在该库中包含了太多的功能,并且由于大多数时候IO流传输任务非常简单,因此这些功能通常不被使用且开销不合理.但一般来说,我发现开销通常是可以接受的,特别是因为大多数IO任务已经很慢了.顺便说一句,LLVM也禁止使用IO流库,这也是因为LLVM的目标是编写执行大量文件IO的精简和平均命令行实用程序(如编译器或其相关工具) .
可能有其他标准库比某些特定情况下可能希望具有的开销更多.图书馆代码通常必须妥协,而不是让每个人都满意.我怀疑一些较新的库,如线程,计时,正则表达式和随机,提供了比许多应用程序所需的更多的功能或强大的保证,因此,会带来一些不必要的开销.但话说回来,许多应用程序确实从这些功能中受益.这就是妥协的意义.
至于带来不必要开销的语言规则,有许多小问题会带来一些开销.首先,我可以想到标准必须做出一些阻止优化的保守假设的几个地方.一个值得注意的例子是无法限制指针别名,这迫使编译器假设任何内存都可能被任何指针别名化(即使实际上,指针别名很少),这限制了优化的机会.有许多类似的情况,编译器必须做出"安全"假设,限制优化.但是大多数这些都在范围和潜在的好处相当小,并且它们通常在能够保证行为的正确性(以及可重复性,稳健性,可移植性等)方面是合理的.另外,请注意,在绝大多数情况下,它在其他语言中并没有真正好转,在C中可能稍微好一点,但这就是它.其中一些问题也可以通过编译器扩展或特定于平台的功能来规避,或者作为最后的手段,使用内联汇编代码,也就是说,如果您确实需要优化到该级别.
一个不再有效的例子是需要编译器生成异常处理(堆栈展开)代码的问题,即使对于永不抛出的函数也是如此.现在,可以通过指定noexcept相关函数来解决这个问题.
但除了那些微观问题之外,我无法真正想到C++中任何其他主要的过度开销(除了RTTI和异常).我的意思是,C++中有些东西可以创建不同类型的开销,但它们都是"选择加入"功能(按用途),如虚函数,虚继承,多继承,模板等等......但是大多数都是遵守"你只为你所用的付出"的原则.有关为C++的低开销子集强加的规则的示例,请查看嵌入式C++.