distcc源起于著名开源项目samba,是一款有着较长历史的跨平台开源分布式编译解决方案。
对于大多数c语言及其衍生语言来说,编译过程主要分为三个步骤:
distcc的作用就是将第二步的编译(3.0版本后通过pump支持部分第一步)过程采用网格计算的模式,将编译任务分配至其它主机,并在编译结束后回传,以供第三步链接使用。并由此降低了发起编译的机器的负载,并提升了编译效率。
distcc本身事实上并不参与任何编译过程,而只是一个编译器(gcc等)的前端。为编译器加入分布式特性,并参与部分管理和简单的负载均衡的功能。根据官方说法,其性能提升的极限阈值是3倍速度,即根据编译池的扩大,无限接近但无法达到原始编译速度的三分之一。
截至目前为止,distcc仅支持c语言及其衍生的c++,Objective-c等的编译。无法支持Java的分布式编译。
自distcc版本3.0开始,加入了基于python的新工具pump。其功能是将头文件也随同源码一起发送至编译服务器。将部分预编译工作也进行分布式处理。从而进一步的提升了编译效率。官方给出的数字是对于文件传输、编译过程有10倍的效率提升。
但pump模式的局限之处在于:在整个编译过程中,源代码(尤其是头文件)不能进行改动,否则会造成错误的编译结果。
ccache同样产生于samba项目,其作用是将编译过程中的中间文件根据预编译结果,通过hash表索引进行缓存。Ccache具有以下特性:
dmucs是为distcc服务的分布式负载均衡解决方案。原始的distcc仅根据服务器指定的顺序来分配编译任务,无法根据编译池中服务器的负载情况 进行动态的编译任务分配。而dmucs则会将编译池中主机的负载情况通知到dmucs服务主机。并由其根据负载及编译机承担能力,动态的对编译任务进行分 配。
图1 展示了不同编译条件下的编译时间。其中,单机的编译参数使用了-j8, 而双机、三机的编译参数则分别为: -j12,-j15
由此图,大致可以总结出以下结论:
cache hit 878 cache miss 11494 called for link 691 not a C/C++ file 307 unsupported compiler option 48 files in cache 22988 cache size 1.0 Gbytes max cache size 20.0 Gbytes
ccache 初次编译 2.3.7 统计结果
cache hit 12127 cache miss 16 called for link 691 not a C/C++ file 307 unsupported compiler option 48 files in cache 23020 cache size 1.0 Gbytes max cache size 20.0 Gbytes
ccache 二次编译 2.3.7 统计结果
由此可见,在第二次编译时(代码未改动),cache的命中率超过了90%。而速度也因此得到大幅度的提升,基本接近初始速度的三倍。对android 4.0版本的速度提升尤为显著。但此数据对平时开发过程中代码有变化的情况仅能起到参考作用。
图2展示了在三机分布式条件下编译android 4.0代码,job数目与编译时间的大致关系。
由此可知,单纯增加job数目并不能获取更好的编译时间。具体原因如下述。
由于android系统的编译过程中同时存在java的编译和c的编译,而distcc又仅支持c类语言的分布式编译。故对于android的分布式编译 获得的效果不如普通纯c类语言项目明显。随着jobs数目的增加,仅能在本机处理的java编译过程反而由于机器硬件机能的限制而拖慢了整体的编译速度, 甚至抵消了由distcc分布式编译获取的益处。
为了解决这个问题,可从以下几个方面入手,进行进一步的改造:
为支持分布式编译,单一编译主机需要进行以下部署步骤:
1 |
apt-get install distcc distcc-pump dumcs |
发出编译请求的客户端还需要进行如下部署步骤:
cd / usr/ lib/ distcc ln -s ../ ../ bin/ distcc arm-eabi-addr2line ln -s ../ ../ bin/ distcc arm-eabi-ar ln -s ../ ../ bin/ distcc arm-eabi-as ln -s ../ ../ bin/ distcc arm-eabi-c++ ln -s ../ ../ bin/ distcc arm-eabi-c++filt ln -s ../ ../ bin/ distcc arm-eabi-cpp ……
define transform- d- to- p $ ( hide ) if [ - e $ ( @:%. o=%. d) ] ; then cp $ ( @:%. o=%. d) $ ( @:%. o=%. P) ; \ sed - e 's/#.*//' - e 's/^[^:]*: *//' - e 's/ *\\ $$//' \ - e '/^$$/ d' - e 's/$$/ :/' < $ ( @:%. o=%. d) >> $ ( @:%. o=%. P) ; \ rm - f $ ( @:%. o=%. d) ; else cp $ ( notdir $ ( @:%. o=%. d) ) $ ( @:%. o=%. P) ; \ sed - e 's/#.*//' - e 's/^[^:]*: *//' - e 's/ *\\ $$//' \ - e '/^$$/ d' - e 's/$$/ :/' < $ ( notdir $ ( @:%. o=%. d) ) >> $ ( @:%. o=%. P) ; \ rm - f $ ( notdir $ ( @:%. o=%. d) ) ; fi endef
因android官方已经在构建环境中加入了ccache支持,故可以很简单的打开ccache功能以提高编译效率。具体方法为:
$ export USE_CCACHE =1 $ export CCACHE_DIR =/ path_of_your_choice/ .ccache $ prebuilt/ linux-x86/ ccache/ ccache -M 20G
其中,20G为推荐值,事实上可以根据编译机器的具体情况进行调整。
在编译期或编译结束后,可以用“ccache -s ”命令查看缓存命中情况。
选中某台主机作为dmucs服务器。进行配置:
此时,即可以在发起编译时使用
make -j8 CC=gethost distcc
来启用支持负载均衡的分布式编译过程了。