1.每个源文件作为一个编译单元,可能会包含上百甚至上千个头文件,而在每一个编译单元,这些头文件都会被从硬盘读进来一遍,然后被解析一遍。
2.每个编译单元都会产生一个obj文件,然后所以这些obj文件会被link到一起,并且这个过程很难并行。
这里,问题在于无数头文件的重复load与解析,以及密集的磁盘操作。
下面从各个角度给出一些加快编译速度的做法,主要还是针对上面提出的这个关键问题。
一、代码角度
1、在头文件中使用前置声明,而不是直接包含头文件。
不要以为你只是多加了一个头文件,由于头文件的“被包含”特性,这种效果可能会被无限放大。所以,要尽一切可能使头文件精简。很多时候前置申明某个namespace中的类会比较痛苦,而直接include会方便很多,千万要抵制住这种诱惑;类的成员,函数参数等也尽量用引用,指针,为前置声明创造条件。
2、使用Pimpl模式
Pimpl全称为Private Implementation.传统的C++的类的接口与实现是混淆在一起的,而Pimpl这种做法使得类的接口与实现得以完全分离。如此,只要类的公共接口保持不变,对类实现的修改始终只需编译该cpp;同时,该类提供给外界的头文件也会精简许多。
3、高度模块化
模块化就是低耦合,就是尽可能的减少相互依赖。这里其实有两个层面的意思。一是文件与文件之间,一个头文件的变化,尽量不要引起其他文件的重新编译;二是工程与工程之间,对一个工程的修改,尽量不要引起太多其他工程的编译。这就要求头文件,或者工程的内容一定要单一,不要什么东西都往里面塞,从而引起不必要的依赖。这也可以说是内聚性吧。
以头文件为例,不要把两个不相关的类,或者没什么联系的宏定义放到一个头文件里。内容要尽量单一,从而不会使包含他们的文件包含了不需要的内容。记得我们曾经做过这么一个事,把代码中最“hot”的那些头文件找出来,然后分成多个独立的小文件,效果相当可观。
其实我们去年做过的refactoring,把众多DLL分离成UI与Core两个部分,也是有着相同的效果的 - 提高开发效率。
4、删除冗余的头文件
一些代码经过上十年的开发与维护,经手的人无数,很有可能出现包含了没用的头文件,或重复包含的现象,去掉这些冗余的include是相当必要的。当然,这主要是针对cpp的,因为对于一个头文件,其中的某个include是否冗余很难界定,得看是否在最终的编译单元中用到了,而这样又可能出现在一个编译单元用到了,而在另外一个编译单元中没用到的情况。
之前曾写过一个Perl脚本用来自动去除这些冗余的头文件,在某个工程中竟然去掉多达了5000多个的include.
5、特别注意inline和template
这是C++中两种比较“先进”的机制,但是它们却又强制我们在头文件中包含实现,这对增加头文件的内容,从而减慢编译速度有着很大的贡献。使用之前,权衡一下。
二、综合技巧
1、预编译头文件(PCH)
把一些常用但不常改动的头文件放在预编译头文件中。这样,至少在单个工程中你不需要在每个编译单元里一遍又一遍的load与解析同一个头文件了。
2、Unity Build
Unity Build做法很简单,把所有的cpp包含到一个cpp中(all.cpp) ,然后只编译all.cpp.这样我们就只有一个编译单元,这意味着不需要重复load与解析同一个头文件了,同时因为只产生一个obj文件,在链接的时候也不需要那么密集的磁盘操作了,估计能有10x的提高,看看这个视频感受一下其做法与速度吧。
3、ccache
compiler cache, 通过cache上一次编译的结果,使rebuild在保持结果相同的情况下,极大的提高速度。我们知道如果是build,系统会对比源代码与目标代码的时间来决定是否要重新编译某个文件,这个方法其实并不完全可靠(比如从svn上拿了上个版本的代码),而ccache判断的原则则是文件的内容,相对来讲要可靠的多。
很可惜的是,Visual Studio现在还不支持这个功能 - 其实完全可以加一个新的命令,比如cache build,介于build与rebuild之间,这样,rebuild就可以基本不用了。
4、不要有太多的Additional Include Directories
编译器定位你include的头文件,是根据你提供的include directories进行搜索的。可以想象,如果你提供了100个包含目录,而某个头文件是在第100个目录下,定位它的过程是非常痛苦的。组织好你的包含目录,并尽量保持简洁。
三、编译资源
要提高速度,要么减少任务,要么加派人手,前面两个方面讲得都是减少任务,而事实上,在提高编译速度这块,加派人手还是有着非常重要的作用的。
1、并行编译
买个4核的,或者8核的cpu,每次一build,就是8个文件并行着编,那速度,看着都爽。 要是你们老板不同意,让他读读这篇文章:Hardware is Cheap, Programmers are Expensive
2、更好的磁盘
我们知道,编译速度慢很大一部分原因是磁盘操作,那么除了尽可能的减少磁盘操作,我们还可以做的就是加快磁盘速度。比如上面8个核一块工作的时候,磁盘极有可能成为最大的瓶颈。买个15000转的磁盘,或者SSD,或者RAID0的,总之,越快越好。
3、分布式编译
一台机子的性能始终是有限的,利用网络中空闲的cpu资源,以及专门用来编译的build server来帮助你编译才能从根本上解决我们编译速度的问题,想想原来要build 1个多小时工程的在2分钟内就能搞定,你就知道你一定不能没有它 - Incredibuild.
4、并行,其实还可以这么做。
这是一个比较极端的情况,如果你用了Incredibuild,对最终的编译速度还是不满意,怎么办?其实只要跳出思维的框架,编译速度还是可以有质的飞跃的 - 前提是你有足够多的机器:
假设你有solution A和solution B,B依赖于A,所以必须在A之后Build B.其中A,B Build各需要1个小时,那么总共要2个小时。可是B一定要在A之后build吗?跳出这个思维框架,你就有了下述方案:
● 同时开始build A和B .
● A的build成功,这里虽然B的build失败了,但都只是失败在最后的link上。
● 重新link B中的project.
这样,通过让A的build与B的编译并行,最后link一下B中的project,整个编译速度应该能够控制在1个小时15分钟之内。
分享到:
相关推荐
### 如何优化C语言代码(程序员必读) 在IT领域,特别是对于从事嵌入式系统开发的工程师来说,代码优化是提升程序性能的关键步骤之一。本文将基于标题、描述及部分给定内容来深入探讨如何优化C语言代码,并提供实用...
### C语言代码优化方案详解 #### 一、选择合适的算法和数据结构 1. **算法选择**:在处理大量数据时,高效的算法可以显著提高程序的执行效率。例如,在进行排序时,快速排序通常比冒泡排序更快。合理地选择算法...
- 游戏速度会根据玩家吃掉的食物数量而加快,每吃掉10个食物,蛇的速度就会提高,增加了游戏的挑战性。 - 蛇的身体长度会随着吃掉食物而增长,这也意味着玩家需要更加小心地操作,避免蛇头撞到自己的身体或游戏边界...
因此,理解FDTD算法的这些基础知识对于编写和优化C语言代码至关重要。通过阅读和理解这本书,读者应能够掌握FDTD算法的基本原理和入门级编程技能,为进一步学习更高级的仿真技术和方法打下坚实的基础。
2. 预编译头文件(stdafx.h):在Visual Studio中,stdafx.h是一个预编译头文件,用于加快编译速度,节省时间。它包含了MFC标准头文件,例如Windows.H和Afxwin.H等。 3. stdafx.cpp和stdafx.h的关系:stdafx.cpp是...
这种方法不仅实现了FSK调制的基本功能,而且简化了硬件设计,加快了信号处理速度。 #### 四、C语言在DSP编程中的应用 传统的DSP编程主要依赖于汇编语言,虽然能够优化代码效率和资源占用,但编写过程复杂,可读性...
在C语言中,`#pragma omp` 是一种编译预处理指令,用于开启OpenMP(Open Multi-Processing)功能,从而让程序支持多线程执行。OpenMP 是一个跨平台的共享内存并行编程模型,它允许程序员通过添加简单的API...
### 8051单片机编程中C语言代码优化的关键知识点 #### 一、引言 8051单片机作为一种广泛应用的8位微控制器,在工业测量控制领域扮演着重要角色。随着技术的发展,高级语言如C语言逐渐成为单片机编程的首选工具。...
本文将探讨8051单片机编程中的C语言代码优化,主要围绕变量定位、数据和变量类型的选取等关键点展开。 首先,我们必须认识到单片机与桌面计算机系统在硬件资源上的巨大差异。由于单片机受限于功耗、成本和体积等...
"C语言打字练习typing.zip"这个压缩包显然设计用于帮助学习者提高在编写C语言代码时的打字速度和准确性,这对于任何程序员来说都是至关重要的技能。在C语言的学习过程中,熟悉语法、掌握常用函数以及能够快速准确地...
在这个爬虫中,每个线程可能负责下载不同的网页或者解析网页内容,使得爬取过程并行化,从而加快了数据抓取的速度。 【标签】"C语言"是编程的基础,其简洁的语法和强大的性能使得它在系统级编程中广泛应用。"抓取...
2. "下"键:加快方块下落速度。 3. "左右"键:移动方块左右位置。 4. "回车"键:立即让方块落到最底部。 5. "空格"键:暂停或继续游戏。 6. "q"键:退出游戏。 【标签】"Linux"表明这款游戏只能在Linux系统中运行,...
C语言编译器的一个显著特点是编译速度快,这意味着开发者在编写和测试代码时,可以快速地获得编译结果,及时发现并修正错误。而“小巧耗内存少”这一特性,让编译器在各种计算机硬件配置上都能轻松运行,不仅优化了...
例如,register关键字用于建议编译器将变量存储在CPU寄存器中,以加快访问速度,但它只是一个建议,并非强制。static关键字则有多种用途,可以用来修改变量的存储周期、连接属性以及函数的作用域。sizeof关键字常常...
7. **编译与调试**:在开发过程中,使用编译器(如GCC)将源代码编译成可执行文件,然后通过调试工具(如GDB)进行调试,找出并修复程序中的错误,这是整个开发流程中的重要环节。 8. **项目管理与文档编写**:虽然...
随着游戏难度的增加,方块下落速度会逐渐加快,考验玩家的反应速度和策略布局能力。 源码中包含了详细的注释和说明文档,帮助开发者快速理解程序结构和实现原理。此外,源码还支持多种编译环境,如VC++、GCC等,...
综合上述高级技巧,我们可以总结出编写高效C语言代码的几个关键点:1. 以空间换时间,通过预分配内存或使用常量字符串等方式来减少运行时的计算开销。2. 利用数学方法,寻找问题的数学规律,从而简化计算步骤,提升...
在Visual Studio 2012环境下开发C语言程序,你需要熟悉IDE的使用,包括创建项目、编写源代码、编译和运行。Visual Studio提供了丰富的调试工具,可以帮助你检查程序的运行状态,找出潜在的问题。 在处理万亿级别的...
- `#include "stdafx.h"`:预编译头文件,用于加快编译速度,是Visual Studio特有的一个预定义头文件。 ### 三、程序结构和逻辑 - **main函数**:C/C++程序的入口点,文档中所有习题的解答都以`int main()`函数开始...
5. **速度控制**:蛇的速度随着食物的积累而逐渐加快,增加了游戏的挑战性。 6. **用户输入**:通过读取键盘输入,控制蛇的移动方向,通常包括上、下、左、右四个方向。 7. **图形界面**:虽然C语言本身不支持图形...