发布于 

加快编译速度

提高 C语言 代码的编译速度的方法

多核编译

gcc 为例,编译器只会使用一个进程去做编译,编译控制脚本 makefile 会在编译器编译了一个源文件后,顺序的控制编译器编译下一个源文件。

大多数情况下,在编译阶段(-c 选项,只编译,不连接),众多的源代码编译顺序是不关联的,现在的计算机大多是双核、4核的,甚至8核,完全可以利用CPU的众多核心实现并行编译。使用 make 程序的 -j 参数可以指定同时编译的进程数量,后面一定要指定一个数字,否则 make 会同时开启最大的进程数量,导致太多的进程调度,反而影响效率。

实验情况:

  • CPU : i5 4590 4C/4T 3.3G
  • 编译工具 : Cygwin / sde-gcc

将编译指令写成一条命令

1
2
3
4
# cat mm
#!/bin/bash
make clean
make -j8

使用 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
2
3
4
5
6
7
# sde-gcc -E -o xxx.pre.c xxx.c

# cat xxx.c | wc -l
49

# cat xxx.pre.c | wc -l
19931

可以看到,这个不到50行的代码,在预编译之后,变成了接近两万行的代码。对于源文件数量、头文件数量很多的代码,这是非常花费时间的。 ccache 就是帮助我们将编译过程拆分,暂存了预编译的一些信息,在不需要重新做预编译的情况下,直接使用缓存中的数据,以减少预编译的行为,达到提高速度的效果。

那么我们使用 ccache 来试试,可以选择去下载源码安装,在 Cygwin 中找到已经有制作好的 ccache 安装包,直接安装就好了,接下来,需要设置一下。

ccache 的使用非常简单,将原本的编译命令行传递给 ccache 即可,比如这样:
ccache gcc -c xxx

我们有几种方式来使用 ccache

  • 修改 makefile ,修改编译工具名称,比如 CC = ccache sde-gcc
  • 不修改代码、脚本,将编译工具指向 ccache
1
2
3
mkdir ~/bin
cd ~/bin
ln -s /usr/bin/ccache sde-gcc

然后将 ~/bin 这个目录加入到 PATH 变量中,并优先搜索 ~/bin,在 ~/.bashrc文件末尾加上 PATH=~/bin:$PATH , 然后重启下 bash , 确保用 which 命令找到的编译工具是我们刚建立的符号链接

1
2
# which sde-gcc
/home/root/bin/sde-gcc

下面我们使用 ccache 来测试一下编译时间,在更换编译进程数的时候,使用命令
ccache -Cz 清除缓存数据,以测试实际花费的时间

进程数量 1# 2# 3#
单进程 25.611 7.065 6.947
-j8 9.321 3.876 3.870

可以看出来,第一次编译的时候,由于缓存没有命中,导致 ccache 需要去进行拆分编译过程,缓存信息,造成时间增加,而后每一次的操作,缩减的时间是非常可观的。


本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

本站由 @刘鹏飞 创建,使用 Stellar 作为主题。