Table of Contents
概述
在公司做手机软件研发已有一年多,前前后后接触过三种手机系统:MStar,MTK和Android。他们有一个共同的特点:代码非常庞大,每种系统都有超过1GB的源代码。如此大量的代码,编译起来是相当缓慢的。实际中,如果使用一台普通四核的机器进行编译的话,将至少需要一个小时才能完成一次完整的编译。长时间的编译等待,对于研发来讲是个极大的浪费。
好在事情没有一路悲剧,因为实际中确有一种方法可以提高代码的编译速度,那就是通过分布式编译工具来加速编译工作。何谓分布式编译?简而言之,就是借网络上其他电脑的空闲CPU给自己使用,从而加速编译工作。一般来讲,通过分布式编译工具可以借到的CPU数量是10至20个,这10到20个借来的CPU足以将编译效率提高300%~400%。像MStar系统如果使用分布式编译的话,完整的编译时间将会缩短到15分钟左右!相比单机一个多小时的编译时间来讲,这是非常大的效率提升!
Windows下的Incredibuild就是一款成熟的常用分布式编译软件,它提供编译协同处理,可以很好的协同网络上的其他计算机获取和利用他们的空余计算能力进行代码编译。开发MStar和MTK时,就是利用Incredibuild实现的分布编译。遗憾的是Android的系统开发将平台换成了Linux,Linux上有没有办法使用分布式编译呢?答案是肯定的。本文就将探讨如何在Linux上搭建及使用分布式编译系统。
分布式编译系统的搭建
分布式编译系统,其实分两部分:编译器程序和分布式编译协同程序,像前面说的Windows下的Incredibuild软件其实就是分布式编译协同软件。编译器部分不用担心,Linux下的编译器种类非常丰富,有商业的也有开源的,一般我们使用开源的GCC系列编译器。而分布式协同软件,我们同样选择开源的distcc。
distcc简介
distcc是一款符合GPL协议开源的分布式编译协同软件,它分为两个部分:distcc-client和distcc-server。distcc-client将代码的编译请求发送到distcc-server上,而distcc-server则会对代码按要求进行编译后回传至请求方,从而完成编译代码的动作。client可以和server装在同一台电脑上,也即是说一台电脑可以利用其他电脑进行分布编译,也可以在空闲的时候为其他电脑提供分布编译服务。下面,我们将着重讲解如何安装,配置和使用distcc。
distcc的安装
distcc的项目主页为:http://code.google.com/p/distcc/, 主页上提供源代码以及安装包的下载。安装包默认提供deb和rpm软件包,分别用来适应Debian系和RedHat系的Linux系统,可以直接安装。如果比较追新和在意个性化定制,也可以下载最新的源代码进行编译后使用。本文采用编译源代码的方法进行安装。
从官网下载最新的 3.1
版的distcc软件包,名称为: distcc-3.1.tar.bz2
。
假如软件包下载后存放在 ~/Downloads
目录下,使用命令:
1
|
|
将其解压至个人目录中的临时目录: ~/temp
下。然后执行:
1 2 3 4 |
|
安装时需要输入自己的密码,如果顺利的话,就会看到distcc安装完成了。不过目前这种状态还不能使用,需要对distcc进行配置。
distcc的配置
配置分为两部分,客户端和服务端的配置,主要的配置文件存放在 /etc/distcc
目录下。
服务器端配置
假设当前电脑所在网络IP地址为: 172.16.149.22
,需要 172.16
网段的电脑都能够访问这台分布编译服务器,那么做如下配置:
修改 /etc/distcc/client.allow
,在最后一行添加
#允许172.16网段的所有电脑连接本服务器 172.16.0.0/16
修改 /etc/distcc/commands.allow.sh
,在 allowed_compilers
段里加入需要访问到的编译器程序。注意这里编译器程序可以使用通配符,但是路径需要使用绝对路径,否则会拒绝访问相应的编译器。此例中配置允许访问存放在 /usr/local/
目录下gcc的 arm-eabi-4.4.3
交叉编译器,实际配置中需要根据需要做修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
将 /usr/local/arm-eabi-4.4.3/bin/
加入到访问路径中,修改 ~/.bash_profile
文件,添加以下内容:
1
|
|
修改 /etc/init.d/distcc
,在
1
|
|
下面添加一行
1
|
|
上述文件都改好保存后,退出到命令行界面,重启distcc服务程序:
1
|
|
客户端配置
相对于服务器端的配置来说,客户端的配置非常简单。只需要修改 /etc/distcc/hosts
文件即可。
修改 /etc/distcc/hosts
,加入配置好的服务器IP即可,一行一个IP地址,如:
172.16.149.45 172.16.149.14 172.16.149.60 172.16.149.20 #本机也加入分布编译服务器群组 localhost
然后定义 CROSS_COMPILE
环境变量,以之前配置的 arm-eabi-gcc
交叉编译器为例,如下定义:
1
|
|
验证distcc
在客户端电脑上进入开源代码目录中执行,上执行
1
|
|
1
|
|
Every 2.0s: distccmon-text Mon Oct 24 15:29:40 2011 8836 Compile state.c 172.16.149.45[0] 8808 Connect climasq.c 172.16.149.14[2] 8807 Connect backoff.c 172.16.149.60[3] 8839 Preprocess strip.c 172.16.149.60[0]
如果显示Block之类的信息,请检查对方服务器上的 client.allow
是否正确,同时需要确保服务器上的3632端口没有被防火墙拦住。
使用distcc进行效率对比
本例中使用3台单核2.6G的电脑群组做编译实验,分别对单机编译和分布编译时间进行比较。对比中会分别编译emacs23代码和交叉编译Android的linux内核代码。
编译emacs
下载并解压emacs23代码,目录为: ~/Downloads/emacs-23.3/
首先非分布式编译,使用8个线程。执行
1 2 3 4 |
|
real 3m14.220s user 2m24.740s sys 0m48.610s
然后使用分布式编译,同样使用8个线程。执行
1 2 3 4 |
|
real 2m24.330s user 1m38.630s sys 0m37.600s
可以看到编译时间减少了50秒,cpu的占用也明显减小了不少。
编译Android的Linux内核
假设Android的Linux内核存目录为: ~/Downloads/Android2.3_kernel_v1.01
同样首先非分布式编译,使用8个线程。由于之前设置过 CROSS_COMPILE
变量,现在单机编译需要重新设置该变量。
1 2 3 4 |
|
real 6m32.640s user 4m22.040s sys 2m9.160s
然后分布式编译,使用8个线程。执行
1 2 3 4 |
|
real 3m42.140s user 2m10.630s sys 1m20.240s
可以看到编译时间减少了2分50秒,接近一半的水平了,分布编译的性能提升非常明显。
从以上两个实验可以看出,对于使用纯C程序编写的软件项目,如:Linux内核,distcc提供的分布编译效率非常显著。而对于混合有其他程序语言的项目,如Emacs1,效率提升就不是那么显著了。
Android代码的分布式编译
Android的代码比较特殊,一部分是C程序,而另一部分为JAVA程序。所以分布编译的时候只能对C程序部分生效。同时,Android代码包包含完整的交叉编译工具链,编译时会使用自己代码包中的工具链进行编译,所以我们之前设置的 CROSS_COMPILE
环境变量会失效。好在解决办法也不是没有,做如下修改即可:
修改Android代码目录中的 build/core/combo/select.mk
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
1 2 3 4 5 6 |
|
不开启分布编译 | 开启分布编译第一次 | 开启分布编译第二次 |
---|---|---|
real 37m49.735s | real 34m1.854s | real 31m7.957s |
user 107m13.238s | user 55m57.950s | user 52m28.229s |
sys 9m55.805s | sys 26m40.228s | sys 21m22.468s |
- 磁盘瓶颈。编译过程中需要读写大量中间文件,磁盘的读写速度限制了编译程序的处理能力;
- 编译JAVA时不能分布式。如前面所述,Android代码中有一部分时JAVA代码。而JAVA代码在编译的时候是不能应用到distcc的分布式能力的,所以这方面也拖了编译速度的后腿。
- 没有根据服务器的CPU处理能力进行任务分发优化。distcc默认是没有任务分发优化的,需要配合使用dmucs程序才能实现。据说配合了dmucs后,性能还能提升30%~50%。不过如何配置使用dmucs就不是本文的主题了,也希望通过我这抛出的“砖”能引出配置dmucs的“玉”来。
总结
总体来讲,使用distcc这个工具可以大幅提高编译代码的效率。但这个工具只能针对C++、ObjC、C等C系列语言生效;而对于像Android这种有一半代码是JAVA的系统来说,联编优势就不那么显著了。尽管如此,distcc带来的200%的编译效率提升,还是值得使用的。
Footnotes:
1 Emacs源码中包含不少的elisp程序,这些elisp程序也在make编译阶段进行编译,从而成为elc文件。