加快编译速度
提高 C语言 代码的编译速度的方法
多核编译
以 gcc
为例,编译器只会使用一个进程去做编译,编译控制脚本 makefile
会在编译器编译了一个源文件后,顺序的控制编译器编译下一个源文件。
大多数情况下,在编译阶段(-c
选项,只编译,不连接),众多的源代码编译顺序是不关联的,现在的计算机大多是双核、4核的,甚至8核,完全可以利用CPU的众多核心实现并行编译。使用 make
程序的 -j
参数可以指定同时编译的进程数量,后面一定要指定一个数字,否则 make
会同时开启最大的进程数量,导致太多的进程调度,反而影响效率。
实验情况:
- CPU : i5 4590 4C/4T 3.3G
- 编译工具 :
Cygwin
/sde-gcc
将编译指令写成一条命令
1 | # cat mm |
使用 time
命令统计运行时间
1 | # time ./mm |
以下是在不同进程数量情况下的编译时间,可以看出并行的进程数量在CPU核心的1~2倍时,能达到最高的效率,再多也无用,反而增加了进程调度的次数。
进程数量 | 1# | 2# | 3# |
---|---|---|---|
单进程 | 16.714 | 16.269 | 16.051 |
-j2 | 9.230 | 9.314 | 9.280 |
-j4 | 6.540 | 6.571 | 6.895 |
-j8 | 6.792 | 6.318 | 6.305 |
-j16 | 6.580 | 6.355 | 6.375 |
ccache
ccache
是 complier cache 的缩写。他的原理是缓存编译器使用的中间文件,以达到提高速度的效果。他适用于支持预编译的编译器
编译器的工作流程:
-
预编译
-E
参数,预编译阶段主要是将#include
引用的文件载入到实际代码中,将#define
宏定义展开 -
编译、优化
-c
-o
参数,将源文件编译成目标文件,并进行优化 -
链接
将众多目标文件、库文件整合在一起,修正符号地址,形成一个可执行程序
通常,我们在编译代码的时候都是将预编译、编译阶段一起进行了,编译器帮助我们完成了很多的事情,我们的代码会引用很多的头文件,包括SDK的头文件,这些头文件自己还会引用非常多的头文件,这样我们一个很短的源文件就会被扩充成一个很长很长的代码,我们来看一下例子:
1 | # sde-gcc -E -o xxx.pre.c xxx.c |
可以看到,这个不到50行的代码,在预编译之后,变成了接近两万行的代码。对于源文件数量、头文件数量很多的代码,这是非常花费时间的。 ccache
就是帮助我们将编译过程拆分,暂存了预编译的一些信息,在不需要重新做预编译的情况下,直接使用缓存中的数据,以减少预编译的行为,达到提高速度的效果。
那么我们使用 ccache
来试试,可以选择去下载源码安装,在 Cygwin
中找到已经有制作好的 ccache
安装包,直接安装就好了,接下来,需要设置一下。
ccache
的使用非常简单,将原本的编译命令行传递给 ccache
即可,比如这样:
ccache gcc -c xxx
我们有几种方式来使用 ccache
- 修改
makefile
,修改编译工具名称,比如CC = ccache sde-gcc
- 不修改代码、脚本,将编译工具指向
ccache
1 | mkdir ~/bin |
然后将 ~/bin
这个目录加入到 PATH
变量中,并优先搜索 ~/bin
,在 ~/.bashrc
文件末尾加上 PATH=~/bin:$PATH
, 然后重启下 bash
, 确保用 which 命令找到的编译工具是我们刚建立的符号链接
1 | # which sde-gcc |
下面我们使用 ccache
来测试一下编译时间,在更换编译进程数的时候,使用命令
ccache -Cz
清除缓存数据,以测试实际花费的时间
进程数量 | 1# | 2# | 3# |
---|---|---|---|
单进程 | 25.611 | 7.065 | 6.947 |
-j8 | 9.321 | 3.876 | 3.870 |
可以看出来,第一次编译的时候,由于缓存没有命中,导致 ccache
需要去进行拆分编译过程,缓存信息,造成时间增加,而后每一次的操作,缩减的时间是非常可观的。