全民K歌后台编译优化:从40分钟到30秒
问题背景:
随着全民K歌功能越来越多,整个后台代码库越来越大,编译速度也与日俱慢,编译一下整个工程需要30-40分钟,严重影响了平时的开发和发布效率
方案思路:
参考和对比KM和网络上的一些解决方案后,考虑到现有的工程都是makefile来管理的,从改动尽可能小的角度,选择的方案是distcc+ccache,另外考虑到要尽可能减少磁盘的使用,所以把代码和ccache的ccache目录都设置到了tmpfs上
实施过程:
在五台开发机上分别安装ccache+distcc
1.下载安装ccache
#!/bin/sh
tar -xvf ccache-3.2.4.tar.gz
cd ccache-3.2.4
./configure && make && make install
cd /usr/local/bin
ln -s ccache /usr/local/bin/gcc
ln -s ccache /usr/local/bin/g++2.安装distcc(需要先安装python)
#!/bin/sh
[ -d distcc-distcc-3.1 ] || unzip distcc-distcc-3.1.zip
cd distcc-distcc-3.1/
./autogen.sh
./configure&&make&&make install
#启动:
distccd --daemon -a 192.168.1.1 -a 192.168.1.2 -a 192.168.1.3 -a192.168.1.4 -a 192.168.1.53.在一台主编译机器上(这里假如是192.168.1.1)mouttmpfs并做一些配置
#mount tmpfs
mkdir–p /data/home/user_00/AutoBuild
mount tmpfs /data/home/user_00/AutoBuild -t tmpfs -o size=10g
#设置环境变量,可以直接设置到运行用户的.profile中
export DISTCC_HOSTS="localhost/16 --localslots=12 192.168.1.2/5192.168.1.3/5 192.168.1.4/5 192.168.1.5/5"
export CCACHE_PREFIX=distcc
export MAKEFLAGS="-j28"
#设置ccache的cache目录到tmpfs
echo “cache_dir = /data/home/user_00/AutoBuild/.ccache”>>/data/home/user_00/.ccache/ccache.conf遇到的问题:
1.不并行编译ok,并行编译就出错
目标依赖关系设置有问题,导致各个目标都并行在编译,比如有comm和cgi两个目标,实际上cgi是必须在comm编译完之后才能编译的,但是在makefile里没有让cgi依赖于comm,导致在cgi目标在comm目标编译完之前就在并发执行了。解决办法:梳理依赖关系如下:

2.warning: -jN forced in submake: disabling jobserver mode
这个原因是由于递归调用的make进程里自己加了 –j选项,而不是直接使用MAKE变量,比如设置了QMAKE=make –j 10, 在子进程里直接使用$(QMAKE) –f makefile来调用就会出现此问题,解决方法也很简单,去掉QMAKE变量,直接使用$(MAKE) –f makefile就可以了
3.warning: jobserver unavailable: using -j1. Add `+' to parent make rule.
原因是递归调用的makefile里面直接使用了make –f makefile这样的形式,而没有使用MAKE变量,直接修改成$(MAKE) –f makefile就可以了。深层的原因其实就是MAKE变量会把make父进程的MAKEFLAGS也带给make子进程,而直接使用make则不会传递这个变量
4.使用ccache–s观察发现有很多unsupported compiler option
因为使用了-MM先生成了依赖文件.d, 而ccache不支持-MM选项。依赖文件的作用主要是在修改了某个文件后可以不用make clean 直接make, 现在编译速度这么快了,没有必要使用这个,所有都make clean 后再make就可以了,更加的安全可靠
5.使用ccache –s观察发现有很多cache miss
设置CCACHE_LOGFILE环境变量,分析ccache的log发现是使用了__TIME__宏导致,修改方案有两种,一是直接去掉__TIME__, __DATE__之类的调用,二是如果不影响代码实际功能,可以在ccache.conf中设置sloppiness = time_macros
6.设置--localslots不生效,在本地永远只有4个ld进程,导致链接过程速度比较慢
查阅了网上的资料,也有一些用户反馈同样的问题,反馈是distcc代码bug。网上暂时无解。看了下代码,直接修改lock.c里的默认值,重新编译,搞定!

7.编译的时候make clean all有时会失败,有时会成功,但是make clean;make all就没有问题
原因是在并发的时候clean all这两个目标并发执行了,没有先make clean ,再make all,导致刚刚make出来的文件被clean掉了。目前想到的解决办法有三种:
a.使用make clean ;make all而不是make clean all,但是在cis上好像不支持
b.设置.NOTPARALLEL:来变成不并行,但实际测试发现全部都不能并行了。放弃。

c.设置一个目标为clean_all,在makefile中把这个目标分成两个步骤,目前使用这个方案

8.在cis系统中willParallelExec 设置的-j默认值是4,而且不能修改,解决办法是把这个参数设置了false, 直接在下面的环境设置里设置MAKEFLAGS
9.直接使用distcc ccache g++的效果会比使用ccache g++然后再设置环境变量CCACHE_PREFIX=distcc 的效果要差。推测这里的原因是由于ccache g++是执行ccache的时候会先用本地的cache,ccahe不能使用需要编译的时候才会调用distcc, 而直接使用distcc ccache g++则会把很多原本可以在本地ccache的文件分发到其它机器去编译了
优化结果:
完全没有ccache的情况下第一次编译:2分30秒左右

有ccache的情况下编译:30秒左右

超越了之前定的在1分钟之内全部编译完的目标!
后续优化:
1.工程内同名文件清理
2.多余的include清理(include越多会导致预编译出来的文件越大,编译速度越慢)
3.32位-》64位
参考资料:
后台构建利器 ccache和distcc介绍
Linux中利用distcc和ccache加速项目编译
[转]内存文件系统使用及示例:ramdisk, ramfs, tmpfs
http://code.google.com/p/distcc/
https://ccache.samba.org/
http://stackoverflow.com/questions/8496135/parallel-makefile-require-depency-ordering