C++ 加快编译速度的方法 C 的主要优点是高性能但是说起编译速度却只有低调的份了。如果不加以重视编译速度极有可能会成为开发过程中的一个瓶颈。那么为什么C它就编译的这么慢呢我想最重要的一个原因应该是C基本的头文件-源文件的编译模型每个源文件作为一个编译单元可能会包含上百甚至上千个头文件而在每一个编译单元这些头文件都会被从硬盘读进来一遍然后被解析一遍。每个编译单元都会产生一个obj文件然后所以这些obj文件会被link到一起并且这个过程很难并行。这里问题在于无数头文件的重复load与解析以及密集的磁盘操作。下面从各个角度给出一些加快编译速度的做法主要还是针对上面提出的这个关键问题。1. 代码角度1.1. 在头文件中使用前置声明而不是直接包含头文件不要以为你只是多加了一个头文件由于头文件的被包含特性这种效果可能会被无限放大。所以要尽一切可能使头文件精简。很多时候前置申明某个namespace中的类会比较痛苦而直接include会方便很多千万要抵制住这种诱惑类的成员函数参数等也尽量用引用指针为前置声明创造条件。1.2. 使用Pimpl模式Pimpl全称为Private Implementation。传统的C的类的接口与实现是混淆在一起的而Pimpl这种做法使得类的接口与实现得以完全分离。如此只要类的公共接口保持不变对类实现的修改始终只需编译该cpp同时该类提供给外界的头文件也会精简许多。1.3. 高度模块化模块化就是低耦合就是尽可能的减少相互依赖。这里其实有两个层面的意思。一是文件与文件之间一个头文件的变化尽量不要引起其他文件的重新编译二是工程与工程之间对一个工程的修改尽量不要引起太多其他工程的编译。这就要求头文件或者工程的内容一定要单一不要什么东西都往里面塞从而引起不必要的依赖。这也可以说是内聚性吧。以头文件为例不要把两个不相关的类或者没什么联系的宏定义放到一个头文件里。内容要尽量单一从而不会使包含他们的文件包含了不需要的内容。记得我们曾经做过这么一个事把代码中最hot的那些头文件找出来然后分成多个独立的小文件效果相当可观。其实我们去年做过的refactoring把众多DLL分离成UI与Core两个部分也是有着相同的效果的 - 提高开发效率。1.4. 删除冗余的头文件一些代码经过上十年的开发与维护经手的人无数很有可能出现包含了没用的头文件或重复包含的现象去掉这些冗余的include是相当必要的。当然这主要是针对cpp的因为对于一个头文件其中的某个include是否冗余很难界定得看是否在最终的编译单元中用到了而这样又可能出现在一个编译单元用到了而在另外一个编译单元中没用到的情况。之前曾写过一个Perl脚本用来自动去除这些冗余的头文件在某个工程中竟然去掉多达了5000多个的include。1.5. 特别注意inline和template这是C中两种比较先进的机制但是它们却又强制我们在头文件中包含实现这对增加头文件的内容从而减慢编译速度有着很大的贡献。使用之前权衡一下。2. 综合技巧2.1. 预编译头文件 (PCH)把一些常用但不常改动的头文件放在预编译头文件中。这样至少在单个工程中你不需要在每个编译单元里一遍又一遍的load与解析同一个头文件了。2.2. Unity BuildUnity Build做法很简单把所有的cpp包含到一个cpp中 (all.cpp)然后只编译all.cpp。这样我们就只有一个编译单元这意味着不需要重复load与解析同一个头文件了同时因为只产生一个obj文件在链接的时候也不需要那么密集的磁盘操作了,估计能有10x的提高看看这个视频感受一下其做法与速度吧。2.3. ccachecompiler cache通过cache上一次编译的结果使rebuild在保持结果相同的情况下极大的提高速度。我们知道如果是build系统会对比源代码与目标代码的时间来决定是否要重新编译某个文件这个方法其实并不完全可靠比如从svn上拿了上个版本的代码而ccache判断的原则则是文件的内容相对来讲要可靠的多。很可惜的是Visual Studio现在还不支持这个功能 - 其实完全可以加一个新的命令比如cache build介于build与rebuild之间这样rebuild就可以基本不用了。2.4. 不要有太多的Additional Include Directories编译器定位你include的头文件是根据你提供的include directories进行搜索的。可以想象如果你提供了100个包含目录而某个头文件是在第100个目录下定位它的过程是非常痛苦的。组织好你的包含目录并尽量保持简洁。3. 编译资源要提高速度要么减少任务要么加派人手前面两个方面讲得都是减少任务而事实上在提高编译速度这块加派人手还是有着非常重要的作用的。3.1. 并行编译编译的时候指定使用多核买个4核的或者8核的cpu每次一build就是8个文件并行着编那速度看着都爽。3.2. 更好的磁盘我们知道编译速度慢很大一部分原因是磁盘操作那么除了尽可能的减少磁盘操作我们还可以做的就是加快磁盘速度。比如上面8个核一块工作的时候磁盘极有可能成为最大的瓶颈。买个15000转的磁盘或者SSD或者RAID0的总之越快越好。3.3. 分布式编译一台机子的性能始终是有限的利用网络中空闲的cpu资源以及专门用来编译的build server来帮助你编译才能从根本上解决我们编译速度的问题想想原来要build 1个多小时工程的在2分钟内就能搞定你就知道你一定不能没有它 - Incredibuild。3.4. 并行其实还可以这么做这是一个比较极端的情况如果你用了Incredibuild对最终的编译速度还是不满意怎么办其实只要跳出思维的框架编译速度还是可以有质的飞跃的 - 前提是你有足够多的机器假设你有solution A和solution BB依赖于A所以必须在A之后Build B。其中AB Build各需要1个小时那么总共要2个小时。可是B一定要在A之后build吗跳出这个思维框架你就有了下述方案同时开始build A和B。A的build成功这里虽然B的build失败了但都只是失败在最后的link上。重新link B中的project。这样通过让A的build与B的编译并行最后link一下B中的project4. 分拆文件在开发过程中经常遇到这样的情况在某段时间内需要只在某个文件上开发或者调试。如果该文件比较大或者依赖该文件的文件比较多则每次哪怕只改动一行编译时间都可能很长。解决这种问题的方法是把该文件分拆成多个文件让比较稳定的部分独立成一个文件变动比较大的部分独立成一个文件。这种方法有时可能违背代码设计模式的原则所以只能用于调试或者开发中的不稳定阶段。参考文献如何加快C代码的编译速度_weixin_34111819的博客-CSDN博客