Android基于distcc的分布式编译及负载均衡的实现
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 878cache miss 11494called for link 691not a C/C++ file 307unsupported compiler option 48files in cache 22988cache size 1.0 Gbytesmax cache size 20.0 Gbytes
ccache 初次编译 2.3.7 统计结果
cache hit 12127cache miss 16called for link 691not a C/C++ file 307unsupported compiler option 48files in cache 23020cache size 1.0 Gbytesmax 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分布式编译获取的益处。
为了解决这个问题,可从以下几个方面入手,进行进一步的改造:
为支持分布式编译,单一编译主机需要进行以下部署步骤:
?
发出编译请求的客户端还需要进行如下部署步骤:
?
?
cd /usr/lib/distccln -s ../../bin/distcc arm-eabi-addr2lineln -s ../../bin/distcc arm-eabi-arln -s ../../bin/distcc arm-eabi-asln -s ../../bin/distcc arm-eabi-c++ln -s ../../bin/distcc arm-eabi-c++filtln -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));fiendef因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
来启用支持负载均衡的分布式编译过程了。
?