写程序最主要的目标就是使它在所有可能的情况下都正确工作。一个运行得很快但是给出错误结果的程序没有任何用处。程序员必须写出清晰简洁的代码,这样做不仅仅是为了程序员能够看懂代码,也是为了在检查代码和今后需要修改代码时,其他人能够读懂和理解代码。
另一方面,在很多情况下,让程序运行得快也是一个重要的考虑因素。如果一个程序要实时地处理视频帧或者网络包,一个运行得很慢的程序就不能提供所需的功能。当一个计算任务的计算量非常大,需要执行数日或者数周,那么哪怕只让它运行得快20%也会产生重大的影响。
编写高效程序需要几类活动:第一,我们必须选择一组合适的算法和结构数据。第二,我们必须编写出编译器能够有效优化以转换成高效可执行代码的源代码。对于第二点,理解优化编译器的能力和局限性是很重要的。编写程序方式中看上去只是一点小小的变动,都会引起编译器优化方式很大的变化。有些编程语言比其他语言容易优化很多。C语言的有些特性,例如执行指针运算和强制类型转换的能力,使得编译器很难对它进行优化。程序员经常能够以一种使编译器更容易产生高效代码的方式来编写他们的程序。第三项技术针对处理运算量特别大的计算,将一个任务分成多个部分,这个部分可以在多核和多处理器的某种组合上并行地计算。
在程序开发和优化的过程中,我们必须考虑代码使用的方式,以及影响它的关键因素。通常,程序员必须在实现和维护程序的简单性与它的运行速度之间做出权衡。在算法级上,几分钟就能编写一个简单的插入排序,而一个高效的排序算法程序可能需要一天或更长的时间来实现和优化。在代码级上,许多低级别的优化往往会降低程序的可读性和模块性,使得程序容易出错,并且更难以修改或扩展。对于在性能重要的环境中反复执行的代码,进行广泛的优化比较合适。一个挑战就是尽管做了广泛的变化,但还是要维护代码一定程序的简洁和可读性。
程序优化的第一步就是消除不必要的内容,让代码尽可能有效地执行它期望的工作。这包括消除不必要的函数调用、条件测试和存储引用。这些优化不依赖于目标机器的任何具体属性。
为了使程序性能最大化,程序员和编译器都需要一个目标机器的模型,指明如何处理指令,以及各种操作的时序特性。现代计算机用复杂的技术来处理机器级程序,并行地执行许多指令,执行顺序还可能不同于它们在程序中出现的顺序。程序员必须理解这些处理器是如何工作的,从而调整它们的程序以获得最大的速度。基于Intel和AMD处理器最近的设计,我们提出了一个这种机器的高级模型。我们还设计了一种图形数据流(data-flow)表示法,可以使处理器对指令的执行形象化,我们还可以利用它预测程序的性能。
了解了处理器的运作,我们就可以进行程序优化的第二步,利用处理器提供的指令级并行(instruction-level parallelism)能力,同步执行多条指令。我们会讲述几个对程序的变化,降低一个计算不同部分之间的数据相关,增加并行度,这样就可以同时执行这些部分了。