本书是《编程卓越之道》系列书的第二卷,将探讨怎样用高级语言(而非汇编语言)编程得到高效率机器代码。在书中,您可以学到如何分析编译器的输出,以便检验代码的所作所为,从而得到高质量的机器码;了解编译器为常见控制结构生成的典型机器指令,以便在编写高级语言程序时选用恰当的语句;掌握编译器将各种常量和变量类型转换成机器数据的方法,以便于使用这些数据写出又快又短的程序。\r\n 书中的理论超出了特定的编程语言和CPU架构,以各种处理器平台进行开发的高级语言程序员都能从中汲取到卓越编程的营养。
致谢\r\n引言\r\n第1章 以底层语言思考,用高级语言编程\r\n 1.1 关于编译器质量的误区\r\n 1.2 最好还是学学汇编语言\r\n 1.3 为何学习汇编语言并非绝对必要\r\n 1.4 以底层语言思考\r\n 1.5 编程用高级语言\r\n 1.6 假设条件\r\n 1.7 不特定于某种语言的方法\r\n 1.8 卓越代码的特征\r\n 1.9 本卷涉及的环境\r\n 1.10 获取更多信息\r\n第2章 要不要学汇编语百\r\n 2.1 学习汇编语言的障碍\r\n 2.2 向《编程卓越之道》第二卷求援\r\n 2.3 向高层汇编器求援\r\n 2.4 HLA\r\n 2.5 以高级语言思考,用底层语言编程\r\n 2.6 汇编语言的编程范型——在底层思考\r\n 2.7 《汇编语言编程艺术》及其他资源\r\n第3章 高级语言程序员应具备的80x86汇编知识\r\n 3.1 学一种汇编语言很好,能学几种更好\r\n 3.2 80x86汇编语言的语法\r\n 3.3 80x86基本架构\r\n 3.4 文字常量\r\n 3.5 汇编语言中的字面(符号)常量\r\n 3.6 80x86的寻址模式\r\n 3.7 汇编语言的数据声明\r\n 3.8 在汇编语言中指定操作数尺寸\r\n 3.9 80x86最简指令集\r\n 3.10 获取更多信息\r\n第4章 高级语言程序员应具备的PowerPC汇编知识\r\n第5章 编译器的操作与代码生成\r\n第6章 分析编译器输出的工具\r\n第7章 常量与高级语言\r\n第8章 变量\r\n第9章 数组\r\n第10章 字符串\r\n第11章 指针\r\n第12章 记录、联合和类\r\n第13章 算术与逻辑表达式\r\n第14章 控制结构与程序判定\r\n第15章 迭代控制结构\r\n第16章 函数与过程\r\n软件工程学\r\n附录 80x86和PowerPC处理器家族的概要对比\r\n网上附录\r\n索引
比起老前辈来说,我们这一代程序员要幸福得多——我们可以从几百种高级编程语言中挑选自己顺手的那种来用,可以坐在显示器前操作键盘、摆弄鼠标,使用各式先进的输入输出设备。机器的处理和联网速度如此之快、硬盘如此之海量、文本编辑器的功能如此之强大、集成开发环境又是如此方便,以至于我们只需用鼠标指指点点,偶尔敲几下键盘,就能得到一个像模像样的应用程序。大行其道的高级语言一方面屏蔽了底层硬件工作的细节,降低了编程的难度,大大加快了程序的生成速度;另一方面也使得大多数新一代的程序员不了解代码的底层实现,很容易忽略代码的效率问题。而汇编语言程序员由于以机器指令形式与硬件打交道,所以他们对指令的执行效率了解得都很透彻。可是众所周知,汇编语言是一种底层语言,效率虽高但可读性差,编写、维护都很不容易,且没有可移植性。怎样才能既利用高级语言开发周期短、维护便捷的优势,又不过多地丧失效率,逼近汇编语言程序的性能呢?这是每个专业程序员都应认真思考的问题。
如今计算机的资源是如此地丰富,让许多程序员觉得程序效率低一些也没什么大不了的。这种想法对于编写简单程序的非专业程序员尚能说得过去,但实际上,商品级软件的功能在越来越强的同时也在变得越来越复杂,必须耗用更多的计算机资源才能运行。我们经常感到恼火,计算机(硬件)性能已经够强了,为什么(软件)运行却更慢了呢?那就是因为许多时候软件运行了一大堆不必要的进程或功能,或者算法不够精良,耗用了太多的资源所致。
《编程卓越之道》系列书旨在讲解卓越编程的方法。具体到第二卷,则是探讨怎样用高级语言(而非汇编语言)编程,得到高效率机器代码。它将引导我们在确保实现设计目标的前提下,学习如何尽量少占用系统资源,又充分发挥代码的性能。该卷将使我们鱼与熊掌兼得,既让我们免于用汇编语言编程之苦,免于牺牲程序的可读性、可维护性和可移植性,又能通过高级语言得到尽量高效的机器码。其核心思想就是“Thinking Low-Level, Writing High-Level”,即用高级语言编程的同时,思考其底层机器码会是怎样的。它不是一本入门级的编程教材,而是我们用高级语言编程获得了一些经验后,希望能再提高自己水平的用书。尽管其中的例子多数都采用C/C++、Pascal语言,汇编用的是HLA等汇编语言,但书中的理论超出了特定的编程语言和CPU架构,以各种处理器平台开发的高级语言程序员都能从中汲取到卓越编程的营养。
本书为中文版《编程卓越之道》第二卷,基于原著此卷第一次印刷的版本(2003017502)翻译而成。翻译尽量承袭第一卷的风格,但力图更上一层楼:为了方便读者查阅书中提到的参考资料,我特意搜索了这些原版书籍在国内的引进版,以“译者注”的形式标出了其中文版或影印版的书名、译者、出版社和书号。有引进版的书仅在第一次出现时标注原英文书名并辅以译者注,此后所有出现此书的地方都标识最新版的中文书名;没有查到引进版的书则始终以英文书名标识。读者可据此了解该书的引进情况。本卷也将索引做了翻译,并按拼音排序,以便用户查找书中内容。另外,若干术语的译法与第一卷有所区别,主要包括:padding byte(s)在第一卷中译作“补齐字节”,本卷则译为“填充字节”;architecture在第一卷中译作“体系结构”,本卷则主要译为“架构”;object在第一卷中大都译作“对象”,尽管该单词确为此意,但考虑到这样容易让读者与“面向对象编程”中的对象混淆,因此除了第12章在讲述类时把object译作“对象”或目标(码、文件...)之外,其余位置均根据上下文语境改译为“数据”、“变量”等。
原书中出现单词“teach”、“you”的地方比比皆是,在翻译过程中我觉得这种语气对读者不够妥当,因此在译文中尽量以“我们”代替“you”,而将“teach”改译为“研究、探讨、研讨、讨论、学习”一类的词汇。另外原文的叙述用了大量的括号,严重影响了阅读的流畅性,因此在翻译过程中我设法除去了大部分括号,将括号内的信息融入正文。所有对原著的改动都已经过作者确认或听从作者建议。比较重要的修改则以“译者注”标于所在页面;至于单词拼错等细微错误,则直接纠正到正文中。还有,原著第3、9、12章的目录结构不尽合理,所以本书应作者要求调整了这些章的目录,对此不再另加“译者注”。
翻译本书的过程也是我的学习过程,它使我对编译器的优化原理以及编程时的注意事项有了更加理论化的认识,相信对渴望提高编程水平的程序员来说本书会是一部很好的学习资料,必然能从中收获甚丰。
在翻译本书的过程中我遇到了一些困惑,曾试图直接向Randall Hyde先生和No Starch出版社求教,但很久都没有回音,后在电子工业出版社刘皎老师的帮助下才与之取得了联系,消除了翻译遗留的问题。在此向刘皎老师致敬,并向原著作者和
卓越代码涵盖很多方面的要素,远不是一本书能够包容的。所以本书作为《编程卓越之道》系列书的第二卷,将关注卓越代码的一个重要组成部分——性能。随着计算机系统的速度从原先的兆赫级增长到几百兆赫,又到上吉赫兹,软件获得了广阔的性能施展空间。时至今日,软件工程师声称“代码根本无需优化”已是司空见惯的现象。有趣的是,使用计算机应用程序的人却很少说这样的话。
尽管本书讲述的是如何编写高效代码,但它并非一本关于优化的书。优化是在软件开发周期几近结束时,由软件工程师判断其代码为何不能符合性能要求,并为达到要求而修改代码的过程。然而不幸的是,倘若直到优化阶段才想到应用程序的性能,优化将无从实施。在软件开发周期的设计与实现阶段,就该确保应用程序具备合理的性能特征。优化措施可以调校系统性能,但对编写糟糕的代码也是无力回天。
Tony Hoare最早说过:“不成熟的优化乃万恶之源”,而Donald Knuth让这句话流行开来。它成了长期以来有些软件工程师的战斗口号,这些工程师不到软件开发周期行将收尾,就总是一味逃避考虑应用程序的性能,即便最后也往往以经济或销售时限为借口而不了了之。然而,Hoare可没说“在应用开发早期阶段,关注应用程序的性能就是万恶之源”。他强调的是“不成熟的优化”,在当时这意味着注意汇编语言代码的周期数和指令条数,而不是在初始程序设计期间要操心编码的类型,毕竟此时程序骨架尚未定型,故而Hoare的话是切中要害的。下面这段话摘自Charles Cook在http:// www.cookcomputing.com/blog/archives/000084.html的一篇短文,其中谈到了某些人对上面说法断章取义的问题。
我经常在想,这句话老是导致软件设计者犯严重错误,因为不同立场的人对其有不同的解读。
这句引语的完整版本是“我们应当在97%的时间里忘掉琐碎的效率问题:不成熟的优化乃万恶之源。”我赞同这种说法。在性能没有明显成为瓶颈时,花大量时间去精雕细刻代码是不值得的。但相反,要是在系统级进行软件设计,着手时就应考虑性能问题。出色的软件开发者会自动这样做,他们已经形成了“性能问题终将导致麻烦”的意识;而没有经验的开发人员却不这么想,他们自以为后期调整能够将以前遗留的问题统统排除。
Hoare真正的意思是,软件工程师应当把心操在诸如精良的算法设计、算法的恰当实现等方面,然后再注重传统的优化措施,例如执行特定语句要花费多少CPU周期之类的问题。
我们当然可以将本书的概念用到优化阶段,但多数技术其实都需要在初始编码时运用。如果在程序行将完工时才付诸实施,那么很可能在软件中已无其立足之地,因为在既成事实面前,履行这些思路要做的工作量太大了。
本书意在探究怎样选择适当的高级语言语句,以便让现代的优化型编译器生成有效率的机器码。我们在用高级语言编程时,达到某种结果可以采取许多种途径,而在机器级有些方法天生就比另一些强。尽管舍弃高效的语句,选用欠效率的语句序列可能有充分理由——例如出于可读性考虑——但实际情况是,多数软件工程师并不了解高级语言语句的运行开销。没有这些知识,他们在选择语句时就无从作出明智的决定。本书的目标就在于改变这种面貌。
有着丰富经验的软件工程师大概会争辩说,这些技术都只会对性能起到微不足道的改善作用。有些情况下这种评价是对的。但我们必须知道,这些微小的改良措施具有累加效应。人们当然能乱用本书建议的技术,生成难以看懂和维护的代码。但本书的真正意义在于,从系统设计角度看,当我们有两种等效的代码序列可选时,就该选择其中更有效率的那个。遗憾的是,当今的许多软件工程师都不知道哪种实现方案能够产生更高效的机器码。
通过研究编译器输出——在本书里就是这么做的——我们不必成为汇编语言专家就能写出高效的代码,但我们至少得懂一些汇编语言的知识。第3章和第4章分别给出了80x86、PowerPC汇编语言的入门教程。
在第5章和第6章中,我们将学习通过检查编译器输出,来确定所用高级语言语句的代码质量。这两章描述反汇编器、目标代码转储工具、调试器、高级语言编译器显示汇编代码的各种选项,以及一些有用的软件工具。
本书的其余部分,从第7章到第16章,说明了编译器对不同高级语言语句、数据类型生成机器码的原理。有了这些知识来武装头脑,我们就能选取最适当的数据类型、常量、变量和控制结构,以便生成高效率的应用程序。
当我们看这本书的时候,要牢记Hoare博士的话——“不成熟的优化乃万恶之源”。倘若你不在乎本书里的信息,生成了难以看懂和维护的代码,当然可能也会工作。但在项目设计和实现的早期,代码还处于漂忽不定、可塑性很大的阶段,这么做是极其危险的。不过要知道,本书并非不分青红皂白地选取最高效的语句序列,它讲解了若干高级语言程序构建方案的开销,在我们有选择余地时,从而可以做出该用哪种序列的明智决定。有些时候出于恰当的理由,会选择欠效率的语句序列。然而,倘若我们连给定语句的开销都不清楚,又怎能选择较高效的方案呢。
有兴趣的读者还可以阅读其他有关“万恶之源”的文章,请查看下列网页:
http://blogs.msdn.com/ricom/archive/2003/12/12/43245.aspx
http://en.widipedia.org/wiki/Software_optimization
很抱歉这些网址可能并不一直有效。
无封面