C++的混合特性为我们提供了多种实现方式,一方面可以实现性能管理的全自动化,另一方面也可以对性能进行更加精准的控制。正是这些选择方式,使得优化C++程序以满足性能需求成为可能。
C++代码优化策略总结
C++有一些热点代码是性能“惯犯”,其中包括函数调用、内存分配和循环。下面是一份改善C++程序性能的方法的总结。这些优化建议简单得让人震惊,而且所有这些建议都是曾经发表过的。当然,恶魔隐藏在细节中。
用好的编译器并用好编译器
C++编译器是非常复杂的软件构件。每种编译器为C++语句生成的机器码都有差别。它们所看到的优化机会是不同的,会为相同的源代码产生不同的可执行文件。如果打算为代码做出最后一丁点性能提升,那么你可以尝试一下各种不同的编译器,看看是否有一种编译器会为你产生更快的可执行文件。
关于如何选择C++编译器的一条最重要的建议,是使用支持C++11的编译器。C++11实现了右值引用(rvaluereference)和移动语义(movesemantics),可以省去许多在以前的C++版本中无法避免的复制操作。
有时,用好的编译器也意味着用好编译器。例如,如果应用程序非常缓慢,那么你应当检查是否打开了编译器的优化选项。这条建议看似非常明显,但是我已经记不清有多少次我向其他人提出这个建议后,他们都承认在编译时确实忘记打开优化选项了。
多数情况下,只要正确地打开了优化选项,你都不用做额外的优化,因为编译器就可以让程序的运行速度提高数倍。
默认情况下,许多编译器都不会进行任何优化,因为如果不进行优化,编译器就可以稍微缩短一点编译时间。这一点在20世纪90年代曾经非常重要,但是如今的编译器和计算机已经足够快了,以至于因优化而产生的额外的编译时间开销微不足道。
当关闭优化选项时,调试也会变得更加简单,因为程序的执行流程与源代码完全一致。优化选项可能会将代码移出循环、移除一些函数调用和完全移除一些变量。当编译选项打开后,有些编译器将不会生成任何调试符号。
虽然部分编译器可能仍然会生成调试符号,但是开发人员想通过在调试器中观察执行流程来理解程序正在做什么就会变得非常困难。
许多编译器在调试构建时允许打开和关闭个别不会过多干扰调试的优化选项。仅仅是打开函数内联优化选项就可以显著地提升C++程序的性能,因为编写许多小的成员函数去访问各个类的成员变量是一种优秀的C++编码风格。
C++编译器的文档对可用优化选项和预处理指令做了全面说明。这份文档如同你在购买新车时获取的操作手册一样——你完全可以不看操作手册就直接坐上并驾驶你的新车,但是其中可能包含了大量的能够帮助你更高效地使用这个庞大、复杂的工具的信息。
如果你正在Windows或者Linux上开发x86体系结构的应用程序,那么你很幸运,因为有一些非常棒的编译器适合你,而且这些编译器的开发和维护也处于非常活跃的状态。在本书问世的前五年,微软将VisualC++升级了三个版本。每年GCC也会发布不止一个版本。
在年早期,大家一致认为无论是在Linux上还是在Windows上,英特尔的C++编译器编译出来的代码的速度都最快;虽然GNU的C++编译器GCC编译出的代码的速度稍慢,但是非常符合标准;而微软的VisualC++则折中。
虽然我乐于绘制一张图表来说明英特尔C++编译器产生的代码比GCC快很多,以此来帮助你选择编译器,但这取决于你的代码以及哪家厂商或是组织又发布了一个提升效率的升级版本。
虽然购买英特尔C++编译器需要花费多美元,但它有30天的免费试用期。VisualC++Express是免费的。Linux上的GCC是永久免费的。因此,对你的代码进行一项实验,试试各种编译器可以带来多大的性能提升,其成本并不高。