2

20 年前,(几乎)没有任何编译器优化。因此,我们开始使用一些技巧,例如:

  1. 使用指针,而不是数组索引。
  2. 不要使用小函数(如swap()),使用宏或直接编写代码。

今天,我们有复杂的编译器优化。数组索引和指针是相同的。如果我们使用-O3(我知道,这很危险),编译器将删除除main().

那么,旧书(Programming PearlsThe C Programming Language)中的小技巧在今天没有用了吗?他们只是让代码更难读?

4

4 回答 4

6

Programming Pearls是关于算法级别的优化,而不是代码级别的优化,因此它在今天仍然具有高度相关性。

但是,代码微优化是另一回事,许多旧技巧现在要么是多余的,要么是有害的。今天仍有一​​些重要的技术可以应用于性能关键代码,但这些技术在未来的某个时候也可能变得多余/有害。你需要跟上 CPU 微架构和编译器技术的最新进展,并且只使用合适的东西(当然,只有在绝对需要的时候——过早的优化是万恶之源。)

于 2012-11-29T13:30:04.920 回答
1

“使用指针,而不是数组索引。”

这从未像现在这样高效。甚至ANSI-C 的旧草案也指定它们是等价的:

3.3.2.1 数组下标

下标运算符[]的定义是E1[E2]等同于(*(E1+(E2)))

“不要使用小函数(比如swap()),使用宏或者直接写代码。”

这已经过时了很长一段时间。C99 引入了inline关键字,但在此之前,编译器可以自由地内联部分代码。出于效率原因,今天编写这种类似函数的宏是没有意义的。

“那么,旧书(Programming Pearls, The C Programming Language)中的小技巧在今天毫无用处?它们只是让代码更难读?”

注意,以下只是我的个人观点,并非世界程序员社区的共识:我个人认为这两本书不仅无用,而且有害。与其说是因为各种优化技巧,不如说是因为可怕的、不可读的编码风格以及对定义不明确的行为的严重依赖。这两本书也充满了错误和错别字,所以如果没有勘误表,你甚至无法阅读它们。

于 2012-11-29T14:04:58.617 回答
0

基本上,是的。但是,如果您确实发现了一个错过优化机会的特别荒谬的例子,那么您应该向开发人员报告!

Braindead 源代码总是会产生 Braindead 机器代码:在某种程度上,编译器仍然必须按照您所说的,而不是按照您的意思去做,尽管许多常见的习语都被识别和“固定”(规则是它必须不使用调试器就不可能知道它已被更改)。

还有一些新的和旧的技巧,至少在某些架构上是有用的。

例如,如果您有一个从 0 计数到 100 的循环并对数组执行某些操作,则某些编译器可能会反转计数器并使其从 100 降至零(因为与零进行比较比与另一个常数进行比较便宜),但是如果你循环有副作用,他们就不能这样做。如果您不关心副作用以相反的顺序发生,那么如果您自己反转计数器,您可以获得更好的代码。

GCC 的另一个有用技巧是__builtin_expect(expr, bool),您可以使用它告诉编译器expr可能是trueor false,因此它可以相应地优化分支。同样,__builtin_unreachable()可以告诉 GCC 某事不可能发生,因此它不必考虑它发生的情况。

不过总的来说,编译器已经足够好了,除非你的程序将 90% 的运行时间花在那个小函数上,否则你真的不需要关心。(例如,memcpy仍然通常用汇编程序编写)。

于 2012-11-29T13:38:33.220 回答
0

如果您因任何原因不允许打开优化,这些技巧仍然很有用。有时编译器也无法优化代码,因为他不知道某段代码的预期和意外副作用。

这真的取决于你有什么要求。根据我的经验,您仍然可以以更好的方式表达一些东西,以使编译器更好地理解您的意图。为了获得更好的编译结果,牺牲可读性总是一种权衡。

于 2012-11-29T13:24:09.767 回答