购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

引言

我们所说的“卓越代码”到底为何意?不同程序员对此有不同的观点。因而没法提供一个公认的定义让所有人都满意。本书采用以下的定义:

“卓越代码”是用一种恒定且优先顺序明确的良好软件风格写出的软件。特别是,“卓越代码”遵循一套规则,该规则指导程序员在通过源代码实现算法时做出各种决定。

然而正如我在《编程卓越之道(卷1)》(简称为 WGC1 )中写的那样,基本上所有人都会认可“卓越代码”有一些特性,尤其是以下几点:

•高效地使用CPU,即运行速度快

•高效地使用内存,即占用内存少

•高效地使用系统资源

•容易看懂和维护

•遵守一套恒定的风格原则

•采用已有软件工程习惯的明确设计

•容易增强功能

•经过仔细测试,健壮,即能够正常工作

•有详细的说明文档

我们还能在这个清单上列出几十条内容。例如,有的程序员认为“卓越代码”必须可移植,必须遵循一整套编程风格指导,或者必须用某种语言写成,抑或一定不能用某种语言写成。有些程序员觉得“卓越代码”必须写得尽可能简洁,还有些程序员相信“卓越代码”应该很快能写出来,也有些程序员则认为“卓越代码”应该按时、在预算内实现。

卓越代码涵盖太多方面的要素,远不是一本书能够包容的。所以本书作为《编程卓越之道》系列图书的第2卷,将关注卓越代码的一个重要组成部分—性能。尽管效率并不总是软件开发努力的首要目标,也不是必须要求“质量卓越”,但人们通常都会赞同这样一种观点:效率欠佳的代码算不上卓越代码。对于现代应用程序而言,效率欠佳是主要问题之一,所以这是要强调的重要话题。

卓越代码的性能特征

随着计算机系统的速度已经从原先的兆赫级增长到几百兆赫级,又至吉赫级,软件获得了广阔的性能施展空间。时至今日,软件工程师声称“代码根本无须优化!”已是司空见惯的现象。有趣的是,使用计算机应用程序的人却很少说这样的话。

尽管本书讲述如何编写高效代码,但它并非一本关于优化的书。优化指的是在软件开发生命周期(Software Development Life Cycle,SDLC)几近结束时,由软件工程师判断其代码为何不能符合性能要求,并为达到要求而修改代码的过程。然而不幸的是,倘若直到优化阶段才想到应用程序的性能,优化将无从实施。在软件开发生命周期的前期,即设计与实现阶段,就该确保应用程序具备合理的性能特征。优化措施可以调校系统性能,但对编写糟糕的代码是无力回天的。

Tony Hoare最早说过:“不成熟的优化乃万恶之源。”而Donald Knuth让这句话流行开来。它成了长期以来有些软件工程师的战斗口号,这些工程师不到软件开发生命周期行将收尾,就总是一味地不考虑应用程序的性能问题,即便最后也往往以经济性或销售时限为借口而将优化不了了之。然而,Hoare可没说“在应用开发的早期阶段,关注应用程序的性能就是万恶之源”。他强调的是不成熟的优化,在当时这意味着注意汇编语言代码的周期数和指令条数,而不是在初始程序设计期间要操心编码的类型,毕竟此时程序的骨架尚未定型。故而Hoare的话是切中要害的。

下面这段话摘自Charles Cook在网址链接2上的一篇短文,其中谈到了某些人对上述说法断章取义的问题。

我经常在想,这句话老是导致软件设计者犯严重错误,因为不同立场的人对其有不同的解读。

这句引语的完整版本是“我们应当在97%的时间里忘掉琐碎的效率问题:不成熟的优化乃万恶之源”。我赞同这种说法。在性能没有明显成为瓶颈时,花大量时间去精雕细刻代码是不值得的。但相反,要是在系统级设计软件,着手时就应考虑性能问题。出色的软件开发者会自觉这样做,他们已经形成了“性能问题终将导致麻烦”的意识;而没有经验的开发人员却不这么想,他们自以为后期调整一丁点儿,就能够将以前遗留的问题统统排除。

Hoare真正的意思是,软件工程师应当把心操在诸如精良的算法设计、算法的恰当实现等方面,然后再关注传统的优化措施,例如执行特定语句要花费多少CPU周期之类的问题。

我们当然可以将本书的概念用到优化阶段,但多数技术其实都需要在初始编码时运用。有着丰富经验的软件工程师大概会争辩说,这些技术都只会对性能起到微不足道的改善作用。在有些情况下,这种评价是对的。但我们必须知道,这些微小的改良措施具有累加效应。如果在程序行将完工时才付诸实施,那么这些技术很可能在软件中已无立足之地。因为在既成事实面前,履行这些思路要做的工作量太大了,何况对已经工作起来的代码做这些修改,面临的风险太大。

本书的目标

本书和卷1( WGC1 )尝试填充当代程序员的教育空白,让他们能够写出高质量的代码。特别是,本书将涵盖以下概念:

•对于我们的高级语言程序,为何考虑其底层的执行很重要?

•编译器怎样对高级语言(HLL)语句生成机器码?

•编译器如何使用底层的原始数据类型来表示高级语言数据类型?

•怎样编写高级语言代码,帮助编译器生成更好的机器码?

•如何利用编译器的优化机制?

•怎样在编写高级语言代码时,从底层的汇编语言角度“思考”?

本书意在教授怎样选择适当的高级语言语句,以便让现代的优化型编译器生成有效率的机器码。大多数情况下,不同的高级语言语句可提供多种途径来达到给定结果;而在机器层级,有些方法天生就比另一些方法强。尽管舍弃高效的语句,选用低效的语句序列可能有充分理由—例如,出于可读性考虑—但实际情况是,多数软件工程师并不了解高级语言语句的运行开销。没有这些知识,他们在选择语句时就无法做出明智的决定。本书的目标就在于改变这种面貌。

再次说明,本书并不是讲在任何情况下都要选择最高效的语句;而在于了解不同高级语言语句的开销,从而在面临多个选项时,我们有翔实的理由来选择哪个序列是最恰当的那个。

章节组织

尽管我们不必成为汇编语言专家就能写出高效的代码,但至少得懂一些汇编语言的知识,以理解编译器的输出。第1章和第2章探讨学习汇编语言的若干方面,涵盖常见的误区、围绕编译器的考虑和有效资源等内容。第3章提供80x86汇编语言的快速入门知识。网上附录(参见链接1)给出了PowerPC、ARM、Java字节码和“通用中间语言”(Common Intermediate Language,CIL)汇编语言的启蒙教程。

在第4章和第5章中,我们将学习通过检查编译器输出来确定所用高级语言语句的代码质量。这两章描述反汇编器、目标代码转储工具、调试器、高级语言编译器显示汇编代码的各种选项,以及一些有用的软件工具。

本书的其余部分,从第6章到第15章,说明编译器为不同高级语言语句、数据类型生成机器码的原理。有了这些知识来武装头脑,我们就能选取最适当的数据类型、常量、变量和控制结构,以便生成高效的应用程序。

假设与前提条件

本书对你具备的知识有若干假定条件。如果你的个人技能匹配下列要求,就会从本书获得最大收益:

•应当至少熟练掌握一种命令式(过程)语言或面向对象的编程语言,包括C与C++、Pascal、Java、Swift、BASIC、Python和汇编语言,还包括Ada、Modula-2和FORTRAN。

•应当能够领会一些小问题的描述,并可完成对问题的软件解决方案从设计到实现的过程。通常半个或一个学期的大学课程,或者几个月的自行实践就能为此准备充分。

•对机器组织和数据表示有基本的掌握。你应该知道十六进制、二进制编码系统,理解计算机在内存里是如何表示有符号整数、字符和字符串等高层数据类型的。接下来的几章会介绍机器语言的一些入门知识,如果你已经掌握了这些知识,就会锦上添花。倘若你感觉自己在机器组织方面的知识薄弱,可以阅读 WGC1 ,该书完整讲解了这些内容。

本书的环境

尽管本书提供一般性知识,但我们的讨论还是有必要特定于某些系统的。由于Intel架构的个人计算机(PC)在当代最常见,因此我将用它来探讨依赖于特定系统的概念。然而,这些概念依然适用于其他系统和CPU,例如早期Power Macintosh系统上的PowerPC CPU,手机、平板电脑和单板计算机(single-board computer,SBC;例如Raspberry Pi、更高端的Arduino板)上的ARM CPU及UNIX机器上的其他精简指令集CPU;若实在没办法用其他方案写出卓越的代码时,才去执行特定于某操作系统的调用。

本书的大部分示例运行在macOS、Windows和Linux下。当创作这些示例时,只要可能,我会尽量遵从操作系统的标准库接口;只有在替代方案中只能写出“欠缺卓越”的代码时,才去执行特定于某操作系统的调用。

本书的大多数示例代码可以运行于最新Intel 架构(包括AMD在内)的CPU中,计算机的操作系统是Windows、macOS或Linux,有一定容量的RAM及其他现在个人计算机上该有的外设。这些概念倘若不是软件本身的,就也适用于Mac计算机、UNIX主机、单板计算机、嵌入式系统,甚至大型机。

获取更多信息

•Rico Mariani编写的 Designing for Performance ,参见网址链接3。

•维基百科(Wikipedia)上的 Program Optimization ,参见网址链接4。

读者服务

微信扫码回复:45074

•获取书中的链接地址

•加入本书读者交流群,与更多同道中人互动

•获取【百场业界大咖直播合集】(持续更新),仅需1元 F80lLQraVxkVm6zJ9/mpX7Riqr8FG4UMazvoqUClJOcTcC8aiSQFt4m2ybTZzgLZ

点击中间区域
呼出菜单
上一章
目录
下一章
×