全部博文(465)
分类: C/C++
2012-11-16 14:49:24
提高C++性能的编程技术
[美]Dov Bulka(多夫·布尔卡) David Mayhew(大卫·梅休)著
左飞 薛佟佟 高阳 译
ISBN 978-7-121- 12937-7
2011年3月出版
定价:59.00元
16开
292 页
宣传语
传承大师智慧 领悟技术本真
经典名著 翻译良品 典藏不二之选
内 容 简 介
很多程序员及软件设计师都认为,用C++开发意味着放弃程序性能提升的可能。在很多人眼里,使用C++来开发那些效率至上的应用无疑将导致一场空前的浩劫。因此,在许多性能敏感型领域,诸如网络协议、操作系统内核、移动设备驱动等等,C++都常常处于被冷落的境地。
而本书正是对这种错误观念的最有力回击。本书揭示了C++开发高效应用的潜力,向广大读者展示了大量实用的C++面向对象编程技术。通过改善普遍藏匿于设计编码过程暗处的缺陷,这些技术无一不为C++的性能提升带来最为强劲的动力。
本书详细讨论了临时对象、内存管理、继承、虚函数、内联、引用计数以及STL等一切有可能提升C++效率的细节内容。最终,该书将C++性能提升的各种终极利器,完美地呈现在广大读者的面前!无论你是相关领域的从业人员,还是C++程序设计爱好者,或者是渴望突破编程瓶颈、大幅提升自我修为的程序设计爱好者,本书都必将使你获益良多。
作者简介
Dov Bulka在软件开发以及向市场交付大型软件产品方面拥有超过15年的实战经验。他曾是IBM DominoGo Web服务器的性能设计师,一些曾出现在Internet上的最大型网站使用了这种服务器,其中包括1996年亚特兰大奥运会的网站。Dov Bulka在杜克大学获得了计算机科学博士学位。
David Mayhew是StarBridge Technologies,Inc.的首席设计师。他主要从事互连构造、对等处理和PCI总线研发等方面的工作,他曾就职于IBM的网络软件部。David Mayhew在弗吉尼亚理工大学获得了计算机科学博士学位。
译者简介
左飞,技术作家、译者。著有《C语言参悟之旅》 《C++数据结构原理与经典问题求解》 《数字图像处理原理与实践:基于Visual C++实现》 《代码揭秘》等书。他在CSDN学生大本营上开设的主页非常受欢迎,多篇关于程序设计学习的系列文章在网络上广为流传。
薛佟佟,研究兴趣主要集中在无线传感网与分布式系统等领域,参与了多项国防基金项目的研究攻关。曾和左飞合作翻译了Charles Petzold的名作《编码——隐匿在计算机软硬件背后的语言》。
中文版前言
在岁末年初、万象更新之际,电子工业出版社隆重推出了引进版图书大系——《传世经典书丛》,而我们有幸参与了这项浩繁而极富意义的工作。作为该系列中的一员,《提高C++性能的编程技术》(Efficient C++: Performance Programming Techniques)是一本指导C++程序员如何写出高性能程序的经典书籍,曾畅销欧美,可以说是一本不折不扣的经典之作。
该书的两位作者Dov Bulka和David Mayhew拥有丰富的实践经验和深厚的编程功底,他们将自己工作与学习中的宝贵经验,汇聚成本书,旨在告诉人们这样一个长期被忽略的事实——C++也能写出高效的程序!这对长久以来存在于很多程序员和软件设计师脑中的一种“偏见”构成了极大的挑战,人们总是习惯于认为C++天生与高效就是对立的。Dov Bulka和David Mayhew所写的这本书成功地否定了这一观点。
Dov Bulka曾经在杜克大学获得计算机科学博士学位,他在软件开发以及向市场交付大型软件产品方面拥有超过15年的经验。他还曾经是IBM DominoGo Web服务器的性能设计师。David Mayhew在弗吉尼亚理工大学获得计算机科学博士学位,并曾担任StarBridge Technologies的首席设计师。由于两位作者在商业应用程序开发中对于最佳性能方面的积极探索与实践,掌握了第一手资料,他们借由本书说明了C++在开发高效程序方面的潜力,同时提出了在实际开发中获得大幅度性能提升的C++编程技术。本书重点讨论C++开发中程序性能与可移植能力的提升,通过各种高效技术及精确测控,本书证明C++在这两方面都可以臻于完美。另外,本书详细讨论了临时对象、内存管理、继承、虚函数、内联、引用计数以及STL等一切有可能提升C++效率的细节内容。本书还指出了在设计和编码中产生隐含操作代价的一些常见错误。
通过本书,读者可以了解C++程序设计中关于性能提升的主要技术。因此,本书对于渴望提高C++程序性能的读者来说将大有裨益,而且更重要的是,读者通过本书可以更深入地探讨C++高级程序设计思路与方法。
参与本书翻译工作的还有南京航空航天大学计算机科学与技术学院研究生宋通、刘彧、张锐恒和殷科科。他们的谦逊与协作精神,以及对于学术问题的孜孜以求和扎实的技术功底都给我们留下了深刻的印象。在此对他们的辛勤付出表示最诚挚的谢意!
计算机程序设计其实是一门妙不可言的艺术。但是真正能将自己从埋头苦干的工匠变成收放自如的设计师,领会程序设计之美,却非易事。无论何时,在使自己变得更加优秀的过程中,一本好书的作用永远不能被忽视!笔者真诚地希望本书能够在这个过程中帮到各位读者。为此,我们始终以审慎、严谨的态度对待此书的翻译工作,力求最大程度地贴合中国读者的阅读习惯,并且不丧失原作的风采。然而,翻译和出版终究是留有缺憾的艺术,纰漏和欠缺往往在所难免,我们真诚地希望广大读者朋友不吝赐教与指正,这将成为我们将此书不断完善的最强大动力,联系信箱:beckham@vip.163.com,或访问笔者的个人博客http://baimafujinji.blog.51cto.com/。
译 者
2010年冬
序 言
如果就C++的性能问题对软件开发者进行一次非正式调查,您会毫无疑问地发现,他们中绝大多数人把性能问题视为C++语言相较于其他优秀语言的阿特琉斯之踵。自从C++诞生以来,我们多次听到这样的话:开发性能要求严格的程序尽量不选择C++。在开发人员看来,编写这一类的程序首选的语言通常是标准C,有时甚至是汇编语言。
作为软件社区的一分子,我们有机会目睹了这种神话般语言的发展和壮大。几年前,我们充满热情地加入了投入C++怀抱的浪潮中,我们身边的许多开发项目也都很积极地投身于其中。一段时间过后,以C++实现的软件解决方案开始发生变动。人们发现C++的性能不太理想,所以就逐渐放弃了它。在对性能要求严格的领域,人们对于C++的热情冷却了下来。当时我们正在为别人提供网络软件,这些软件的运行速度是不容协商的,也就是说速度必须放在第一位。由于网络软件位于软件层级链中相对低的层次,而且大量的应用程序位于网络软件之上并且依赖于它,所以网络软件的性能要求比较严格。较低层级链上的程序性能不佳会对较高层级链上的程序产生影响。
并不是我们才有这样的经验。在我们周围,不少骨灰级的C++使用者也很难用C++取得他们所期盼的性能。没有人认为问题在于面向对象开发模式有多难学,反而怪罪C++语言本身。尽管C++编译器本质上还处于“婴儿期”,但C++语言已经被打上了“天生就慢”的标签。这种看法迅速传播并被广泛地当做事实接受。不使用C++的软件企业经常说他们这么做是因为性能是他们考虑的主要因素。这种考虑源于人们认为C++语言无法达到与之相对的C语言所达到的性能。因此,C++很少在一些对性能要求很严格的软件领域取得成功,如操作系统内核、设备驱动程序、网络系统(路由器、网关、协议栈)等。
我们花费了很多年时间用来剖析用C和C++代码写成的大型系统,以充分发挥这些代码的性能。在整个过程中我们竭尽全力寻找用C++产生高效程序的潜力。我们发现事实上C++确实是有这个潜力的。在本书中,我们试着与您分享这些经历,以及在剖析C++效率的过程中所得到的一些经验教训。编写高效的C++代码,并非轻而易举,也不是比登天还难。要做到这一点,需要理解性能规律,以及掌握一些有关C++性能陷阱和缺陷的知识。
80-20法则是软件结构领域的一个重要原则。在本书的写作中我们同样认同它的存在:20%的性能缺陷将会占用我们80%的时间。因此我们把精力集中在最有价值的地方。我们主要对那些经常在工业化代码中出现,并有显著影响的性能问题进行剖析。本书不对各种可能出现的性能缺陷及其解决方法进行详尽的讨论,因此,将不讨论我们认为深奥而不常见的性能缺陷。
毫无疑问,我们的观点来自于我们作为程序员在开发服务器端、对性能要求严格的通信程序中获取的实际经验。由此形成的观点会在以下方面影响本书:
在实践中所遇到性能问题与在科学计算、数据库程序和其他领域中所遇问题在本质上稍有不同。这没什么大不了的。普遍的性能原则适用于各个领域(包括在网络软件以外的领域)。
尽管我们尽量避免,但有时候还是会人为地虚构一些例子来佐证一些观点。以前我们犯过足够多的编码错误,所以能提供足够多的来自于我们所编写的真实的产品代码中的例子。我们的经验得来不易——是从自己和同事所犯的错误中学习而来的。我们将尽可能使用实际范例来证明我们的观点。
我们将不对算法渐近复杂性、数据结构,以及数据的访问、排序、搜索及压缩的最新和最优技术进行深入的研究。这些确实是重要的论题,但是其他地方已给出大量的论述[Knu73、BR95、KP74]。我们将着重论述那些简单、实用、常见的、会大幅提高性能的编码和设计原则。也许您在不知情的状态下使用了会导致隐含性高消耗的语言特性,也许您违反了敏感(或是不太敏感)的性能原则,这些会导致性能降低的常见的设计和编码方法,我们都将一一指出。
我们如何区分传说与现实呢?C++的性能真的比C语言的要差么?笔者认为人们通常所持的C++性能差的观点是不正确的。确实,在一般情况下,如果把C语言和看起来与C语言相同的C++版本相比,前者通常要快一些。但同时笔者也认为两种语言在表面上的相似性通常是基于它们的数据处理功能,而不是它们的正确性、健壮性和易维护性。我们的观点是如果让C语言程序在上述方面达到C++程序的级别,则速度差别就会消失,甚至可能是C++版本的程序更快。
C++不是天生就较慢或较快,这两者都是有可能的,关键要看怎样使用它以及想从它那里得到什么。这与如何使用C++有关系:运用得当的话,C++不仅可以让软件系统具备可接受的性能,甚至还可以获得出众的性能。
在此我们向那些对本书做出过贡献的人表示感谢。万事开头难,我们的编辑Marina Lang对本书写作项目的启动给予了帮助。Julia Sime为早期的书稿做出了不小的贡献,Yomtov Meged也给我们提供了很多有价值的建议。他还指出了我们的想法与客观情况之间的细微差别。尽管有时它们碰巧会一致,但对我们而言还是有必要加以区分的。
十分感谢Addison-Wesley聘请的两位评审,他们的反馈信息非常有价值。
同时感谢对原稿进行了检查的朋友和同事,他们是(排名不分先后):Cyndy Ross、Art Francis、Scott Snyder、Tricia York、Michael Fraenkel、Carol Jones、Heather Kreger、Kathryn Britton、Ruth Willenborg、David Wisler、Bala Rajaraman、Don “Spike” Washburn和Nils Brubaker。
最后但并非最不重要,还要感谢我们各自的妻子:Cynthia Powers Bulka和Ruth Washington Mayhew。
Dov Bulka
David Mayhew
目 录
导读 1
第1章 跟踪实例 10
1.1 初步跟踪的实现 12
1.2 要点 18
第2章 构造函数和析构函数 20
2.1 继承 20
2.2 复合 32
2.3 缓式构造 34
2.4 冗余构造 37
2.5 要点 41
第3章 虚函数 43
3.1 虚函数的构造 43
3.2 模板和继承 46
3.3 要点 51
第4章 返回值优化 52
4.1 按值返回机制 52
4.2 返回值优化 54
4.3 计算性构造函数 57
4.4 要点 58
第5章 临时对象 59
5.1 对象定义 59
5.2 类型不匹配 60
5.3 按值传递 63
5.4 按值返回 64
5.6 使用op=()消除临时对象 66
5.7 要点 67
第6章 单线程内存池 69
6.1 版本0:全局函数new()和delete() 70
6.2 版本1:专用Rational内存管理器 71
6.3 版本2:固定大小对象的内存池 76
6.4 版本3:单线程可变大小内存管理器 80
6.5 要点 87
第7章 多线程内存池 88
7.1 版本4:实现 88
7.2 版本5:快速锁定 91
7.3 要点 95
第8章 内联基础 96
8.1 什么是内联? 96
8.2 方法调用的代价 100
8.3 因何内联? 105
8.4 内联详述 105
8.5 虚方法的内联 107
8.6 通过内联提升性能 108
8.7 要点 109
第9章 内联——站在性能的角度 110
9.1 调用间优化 110
9.2 何时避免内联? 115
9.3 开发阶段及编译期的内联考虑 118
9.4 基于配置的内联 119
9.5 内联规则 123
9.6 要点 125
第10章 内联技巧 126
10.1 条件内联 126
10.2 选择性内联 127
10.3 递归内联 129
10.4 对静态局部变量进行内联 134
10.5 与体系结构有关的注意事项:多寄存器集 136
10.6 要点 137
第11章 标准模板库 138
11.1 渐近复杂度 138
11.2 插入 139
11.3 删除 146
11.4 遍历 149
11.5 查找 150
11.6 函数对象 152
11.7 比STL更好? 154
11.8 要点 157
第12章 引用计数 158
12.1 实现细节 160
12.2 已存在的类 172
12.3 并发引用计数 175
12.4 要点 179
第13章 编码优化 180
13.1 缓存 182
13.2 预先计算 183
13.3 降低灵活性 184
13.4 80-20法则:加快常用路径的速度 185
13.5 延迟计算 189
13.6 无用计算 191
13.7 系统体系结构 192
13.8 内存管理 193
13.9 库和系统调用 194
13.10 编译器优化 197
13.11 要点 198
第14章 设计优化 200
14.1 设计灵活性 200
14.2 缓存 204
14.3 高效的数据结构 208
14.4 延迟计算 208
14.5 getpeername() 209
14.6 无用计算 212
14.7 失效代码 213
14.8 要点 214
第15章 可扩展性 215
15.1 对称多处理器架构 217
15.2 Amdahl定律 218
15.3 多线程和同步 220
15.4 将任务分解为多个子任务 221
15.5 缓存共享数据 222
15.6 无共享 224
15.7 部分共享 226
15.8 锁粒度 228
15.9 伪共享 230
15.10 惊群现象 231
15.11 读/写锁 233
15.12 要点 234
第16章 系统体系结构相关话题 235
16.1 存储器层级 235
16.2 寄存器:存储器之王 237
16.3 磁盘和内存结构 241
16.4 缓存效应 244
16.5 缓存抖动 246
16.6 避免跳转 247
16.7 使用简单计算代替小分支 248
16.8 线程化的影响 249
16.9 上下文切换 251
16.10 内核交叉 254
16.11 线程化选择 255
16.12 要点 257
参考文献 258
索引 260