Board logo

标题: 通过 ccache 改善协同构建时间 [打印本页]

作者: look_w    时间: 2018-5-7 21:00     标题: 通过 ccache 改善协同构建时间

在标准的编译过程中,在 UNIX 下使用 C/C++ 开发应用程序通常需要用到一个编译器(如 gcc)以及一个编译工具,比如 make。make 和所有的 C 编译器的问题在于 C 预处理程序(preprocessor)和头文件的工作方式。观察一个典型的 C 源文件,您会发现其中有很多由         #include 所引用的各种头文件。      
每一次编译一个文件时,C 预处理程序(cpp)都会解析并引入每个头文件以及这些头文件引用到的任何文件。通过对内容进行解析,ccp 可以将一个相当基本的 1-KB 大小的源文件转化为一个 8-KB 大小的源文件,在这个过程中,会合并入几十个甚至几百个头文件。在典型的开发项目中,有很多与项目相关的头文件可能会在不同的源文件中多次被引入,而且每个头文件本身也可能引用很多其他头文件。
在典型的编译过程中,make 工具只编译自上次编译后发生修改的文件,这样就在很大程度上简化了编译过程。例如,清单 1 中的目录表明,         foo.o 对象比相应的 foo.c 源文件的最后修改日期更新。同时,bar.o 比 bar.c 更新。使用一个经过适当配置的 Makefile,将只会从源文件编译         foo.o 。      
make 将必须被编译的文件限制在经过修改的那些源文件范围之内,但是即使是使用 make,仍然有相当可观的浪费。每一次编译项目时,源文件在编译为汇编语言和最终的机器代码之前,都要通过 cpp 进行解析。对每一个文件来说,每一次可能都要重新解析头文件。从编译的全过程来看,您最后可能多次解析了相同的头文件,浪费了处理器周期,更重要的是浪费了开发者的时间,因为他们要等待这一过程的完成。在一个团队中,这一影响可能会更为明显,因为多名开发者可能都会反复多次重复这一过程,在某一天甚至可能会同时进行。
清单 1. 一个示例源文件环境
1
2
3
4
5
6
7
total 808
-rw-------  1 mc  mc    5123 24 Jul 14:17 bar.c
-rw-------  1 mc  mc   39474 24 Jul 14:19 bar.o
-rw-------  1 mc  mc    7856 24 Jul 14:17 foo.c
-rw-------  1 mc  mc   28443 24 Jul 14:19 foo.o
-rwx--x--x  1 mc  mc  319742 24 Jul 14:19 foobar*
-rw-------  1 mc  mc    1045 24 Jul 14:21 foobar.h




使用 ccacheccache(“compiler cache”的缩写)工具会高速缓存编译生成的信息,并在编译的特定部分使用高速缓存的信息,比如头文件,这样就节省了通常使用 cpp 解析这些信息所需要的时间。如果您编译清单 2 中的文件,假定         foobar.h 中包含对其他头文件的引用,ccache 会用那个文件的 cpp-parsed 版本来取代         include 声明。就那么简单。不是真正去读取、理解并解释其内容,ccache 只是将最终的文本拷贝到文件中,使得它可以立即被编译。      
清单 2. 源文件内容
1
2
3
4
#include "foobar.h"
void main(void)
{
}




安装安装和使用 ccache 并不像您可能想像的那样复杂。它不会取代或者以任何方式影响您原来的使用编译器的方式,而是担当了您与您的编译器之间的一个接口,所以您可以根据需要选择是否使用它。要安装 ccache,需要从 Samba 小组或者一个本地镜像(参阅本文最后的 )直接下载源文件。解压出文件的内容:      
$ bunzip2 -c ccache-2.3.tar.bz2|tar xf -
切换到那个目录:
$ cd ccache-2.3
配置:
$ ./configure
编译:
$ make
最后,安装 ccache:
$ make install
您就准备好开始使用了!
部署如上所述,ccache 在您与您的普通编译器之间进行工作。以 gcc 为第一个参数调用 ccache,而不是调用 gcc。例如,要在命令行中编译一个文件,您通常会使用:
$ gcc foo.c
要使用 ccache,您应该输入:
$ ccache gcc foo.c
像这样对一个文件进行单独的编译,尤其是第一次使用 ccache 编译那个文件时,您将不会看到有任何的帮助,因为编译信息还没有被高速缓存。所以,配置 ccache 永久地取代主要编译器通常来说更为有效。设置 CC 环境变量的值来完成这一任务:
$ export set CC='ccache gcc'
如果您只是想为一个项目启用 ccache,比如说编译 Perl 等第三方工具时,那么您或者可以使用环境变量,或者可以告知配置脚本或 make 命令使用哪个 C 编译器。
控制高速缓存默认情况下,ccache 使用当前用户主目录中的一个目录($HOME/.ccache)来保持高速缓存信息。在团队环境中,您应该使用一个集中的位置来进行高速缓存,这样在编译过程中每个人都可以使用高速缓存的信息。另一个环境变量         CCACHE_DIR 指定了高速缓存目录的位置。在单机环境中,将这个环境变量设置为一个每一个需要它的人都可以访问的目录。使用通过 tmpfs 挂载的目录可以获得更高的速度,前提是您得有支持这一功能的存储器。您的速度可能会再提高 10% 到 25%。      
如果您是在网络中多台机器上使用 ccache,那么要确保您共享的目录要通过 NFS 导出(export)并挂载到每一个客户机上。如果您希望获得额外的加速,同样可以使用 tmpfs 文件系统。
另外的一些选项让您可以更深入地控制高速缓存设置:        
清单 3. ccache 高速缓存统计数据
1
2
3
4
5
6
7
8
cache hit                             44
cache miss                           152
called for link                      107
compile failed                        11
no input file                          2
files in cache                       304
cache size                           8.8 MB
max cache size                     976.6 MB




一旦设置了初始化选项并配置了期望的目录和高速缓存大小,就不需要再做任何改动。没有必要执行任何日常的维护。
组合 ccache 和 distcc您可能已经想到了 distcc 这一来自 Samba 小组的另一个工具,它让您可以将编译过程分布到多台机器上。只需要为 make 添加多任务选项(使用         -j 命令行选项),它就可以有效地提高同步编译的数目。distcc 系统的工作方式是,每台主机上有一个后台进程,接收最终预解析格式的源文件,然后在本地进行编译,返回生成的对象文件。      
如果使用得当,在每加入一个新的同样节点时,编译时间通常应该会以稍微低于线性的比率下降,不过您将只会在那些远不只一个源文件的项目上看到这样的影响,因为 distcc 只是分布全部源文件。
由于 distcc 所分布的是解析过的文件,所以您可以组合 ccache,它可以加速 C 预处理过程部分,同时distcc 可以完成到对象代码的实际编译。要以这种方式来使用 distcc 和 ccache,需要在主机上配置 distcc,在主要的开发机器上配置 distcc 和 ccache。
现在在希望要编译项目的机器上设置环境变量,如清单 4 所示。
清单 4. 使用 ccache 和 distcc 所需要的环境变量
1
2
3
4
5
export set DISTCC_HOSTS='localhost atuin nautilus pteppic kernel'
export set CCACHE_DIR=/Data/Cache/CCache
export set CCACHE_PREFIX=distcc
export set CCACHE_LOGFILE=/Data/Cache/CCache.log
export set CC='ccache gcc'




环境变量定义如下:        
现在,当运行 make 时,如果使用了         -j 选项来指定要执行的同步编译的数目,则首先使用 ccache 解析文件(如果需要,使用高速缓存),然后将其分布到某个 distcc 主机。      
尽管 distcc 加速了编译过程,但是它没有改变环境的基本限制。例如,您不应该将 make 执行的同步作业的数目设置得大于可用 CPU 数目的两倍。例如,如果您有四台两路机器,那么将作业值设置为超过 16 的值时将不再会观察到有多大改善。
统计数据既然一切都已就绪,现在可以观察它带来了多大的差别。在这里,我已经运行了一系列编译 Perl 的测试。我们需要编译一个足够大的项目,因为 ccache 在高速缓存了解析过的头文件时运转效果最好。这正是在完成了标准配置(使用 configure.gnu)以后的 make 阶段,它包括所有的步骤,甚至那些与编译代码无关的步骤。这些与编译器无关的操作不会影响整体上的统计数据。
如前所述,在第一次编译时,ccache 的影响不会为人所察觉。不同之处在于重新编译时,在这个过程中您会重用先前的预处理程序。通过简单地接触(touch)主要 Perl 源文件目录中的 C 源文件,得到了表 1 中的重新编译时间。其中包括使用普通 gcc、ccache+gcc、ccache+distcc+gcc 所需的编译时间,编译在一个四节点网络中进行并选用了不同的并发 distcc 作业数目值。
表 1. 重新编译时间环境
时间gcc(第一次运行)
8m02.273sgcc(重新编译)
3m30.051sccache+gcc(第一次运行)
8m54.714sccache+gcc(重新编译)
0m45.455sccache+distcc+gcc -j4
4m14.546sccache+distcc+gcc -j4(重新编译)
0m38.985sccache+distcc+gcc -j8
3m13.020sccache+distcc+gcc -j8(重新编译)
0m34.380s
哇!只是使用了 ccache,编译 Perl 就节省几乎 3 分钟(2 分 45 秒)的时间,所有的原因只是,ccache已经保持了头文件的预编译版本,因而不需要再为每个源文件不断重复运行 cpp。将 distcc 引入这一过程所得的结果是,整体上速度提高,重新编译的时间快了一点点。
结束语在本文中,您已经了解了通过 ccache 等相当直观而且易用的工具可以获得怎样的速度上的提高。组合使用 ccache和 distcc,您可以更进一步地改进编译时间。在团队环境中使用这些工具时,您每天就可以节省几个小时的编译时间。这就意味您的开发职员喝咖啡休息的理由会更少,但是也会减少应用程序的开发时间。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0