comp.lang.c FAQ 列表·
问题 20.13
Q如何才能让我的程序最高效?
A通过选择好的算法,仔细实现它们,并确保您的程序没有做任何额外的工作。例如,世界上最微优化的字符复制循环将无法与根本不需要复制字符的代码相媲美。
在担心效率时,重要的是要从几个角度来看待问题。首先,尽管效率是一个非常热门的话题,但它并不总是像人们认为的那么重要。大多数程序中的大部分代码都不是时间关键的。当代码不是时间关键的时,编写清晰且可移植的代码通常比编写最大化效率的代码更重要。(请记住,计算机非常、非常快,看似“低效”的代码可能编译得相当高效,并且运行起来没有明显的延迟。)
众所周知,很难预测程序中的“热点”在哪里。当效率是关注点时,重要的是使用性能分析软件来确定程序哪些部分值得关注。通常,实际的计算时间会被 I/O 和内存分配等外围任务所淹没,而这些任务可以通过使用缓冲和缓存技术来加速。
即使是时间关键的代码,最无效的优化技术之一就是纠结于编码细节。许多经常提出的“高效编码技巧”即使是简单的编译器也会自动执行。笨拙的优化尝试会让代码变得臃肿,从而实际降低性能,因为它们会增加页面错误数或溢出指令缓存或流水线。此外,优化技巧很少是可移植的(也就是说,它们可能在一台机器上加速,但在另一台机器上减速)。无论如何,调整编码通常最多只能带来线性性能改进;巨大的回报在于更好的算法。
如果您的代码性能如此重要,以至于您愿意投入编程时间进行源代码级别的优化,请确保您使用的是您能负担得起的最好的优化编译器。(编译器,即使是平庸的编译器,也能执行源代码级别无法实现的优化。)
当效率确实很重要,选择了最佳算法,甚至编码细节也至关重要时,以下建议可能有用。(提及这些仅是为了减少后续提问;出现在这里并不一定代表作者的认可。请注意,其中一些技术是双向的,可能会使情况变得更糟。)
- 在代码中大量使用register声明来定义常用变量;如果适用,将它们放在内部块中。(另一方面,大多数现代编译器都会忽略register声明,因为它们假设自己可以比程序员更好地进行寄存器分析和分配。)
- 仔细检查算法。尽可能利用对称性来减少显式情况的数量。
- 检查控制流:确保常见情况首先被检查并更容易处理。如果涉及&&或||的表达式的某一边通常决定结果,则尽可能将其放在左侧。(另请参见问题 3.6。)
- 使用memcpy来解决这个问题,而不是memmove(如果合适,请参阅问题 11.25)。
- 使用特定于机器和供应商的例程和#pragma。
- 将公共子表达式手动放入临时变量中。(好的编译器会为您完成这项工作。)
- 将关键的内循环代码移出函数并放入宏或内联函数中(如果不变,则移出循环)。如果循环的终止条件是一个复杂但与循环无关的表达式,请预先计算它并将其放入临时变量中。(好的编译器会为您完成这些。)
- 如果可能,将递归转换为迭代。
- 展开小循环。
- 确定while, 对于或do/while循环在您的编译器下是否能产生最佳代码,以及递增或递减循环控制变量哪种效果更好。
- 删除goto语句——有些编译器在存在这些语句时无法很好地进行优化。
- 使用指针而不是数组下标来遍历数组(但请参见问题 20.14)。
- 降低精度。(在 ANSI 编译器下,使用float来解决这个问题,而不是double可能导致更快的单精度算术运算,但旧编译器会将所有内容转换为double,因此使用float也可能更慢。)用您自己为所需范围和精度量身定制的、可能使用查找表的方法替换耗时的三角函数和对数函数。(请务必为您自己编写的版本赋予*不同的*名称;请参见问题 1.29。)
- 缓存或预计算频繁需要的数值表。(另请参见问题 20.12。)
- 优先使用标准库函数而不是您自己编写的函数。(有时编译器会内联或专门优化其自己的函数。)另一方面,如果程序的调用模式特别规律,您自己专门实现的函数可能会比库中的通用版本更优。(同样,如果您确实编写了自己的版本,请给它一个不同的名称。)
- 作为最后的、*最后的*手段,请用汇编语言(或手动调整编译器的汇编语言输出)手动编写关键例程。如果可能,请使用asm指令。
以下是一些*不需要*担心的事情
- 17x. `i++` 是否比 `i = i + 1` 快i++快于i = i + 1
- 18x. `i << 1`(或 `i >> 1`, `i & 1`)是否比 `i * 2`(分别为 `i / 2`, `i % 2`)快i << 1(或i >> 1或i & 1)快于i * 2(分别i / 2, i % 2).
。(这些是编译器经常为您执行的优化示例;请参见问题 20.14 和 20.15。)
此处无意暗示效率可以完全忽略。然而,在大多数情况下,只需注意选择好的算法,干净地实现它们,并避免明显的低效错误(即,确保您最终不会以 O(n**3) 的实现得到 O(n**2) 的算法),就可以达到完全可接受的结果。
有关效率权衡的更多讨论,以及提高效率重要性时如何改进效率的好建议,请参阅 Kernighan 和 Plauger 的《The Elements of Programming Style》第 7 章,以及 Jon Bentley 的《Writing Efficient Programs》。
另请参见问题 17.11。
关于此 FAQ 列表 关于 Eskimo 搜索 反馈 版权由
托管