make学习笔记之优化makefile篇
2011-11-23 22:03 by hackerzhou以前都只是用make,一直没好好研究makefile的写法。最近由于忍受不了每次3小时的编译过程,于是主动请缨对makefile进行了些优化来提高编译速度,试验了下效果很好,可以节省1-1.5小时。makefile可以简单的认为是make所识别的规则文件,描述了每个target之间的依赖关系以及编译方法,语法又和Shell Script类似,它也可以检查依赖关系,因此make被广泛的应用于编译源代码。写一些小项目的makefile都不会有太大的问题,而随着项目越来越大,其中的部件越来越多,编译过程越来越复杂。每个人都往里面加自己需要编译的东西,时间一长,理清楚各个makefile之间的调用以及依赖关系就变得越来越困难,因此每个小改动都得谨小慎微,改不好就是大灾难。很难想象一个脉络不清楚的体系能够有比较好的效率,本着不怕折腾的原则我这次就客串了下清道夫。由于是make的初学者,本文所述难免有所瑕疵,请各位多多包涵。
makefile通过指定target之间的依赖关系构造了一颗编译树,树的叶子节点最先编译,而后一层层向上,直到根节点被编译。因此,这棵树的构造方法会显著的影响到编译的效率。下面我们来看一个例子,看看一个简单的优化,假设你有两个子项目B和C,编译B和C都比较耗时而且它们互相不依赖,但它们都有一个共同的依赖项目A。
目录结构如下所示:
|-A | |-makefile |-B | |-makefile |-C | |-makefile |-makefile
B的makefile内容大致如下,C和A也类似,sleep时间分别为1s和10s:
all: @echo "Target B sleep 5s" sleep 5 @echo "Target B complete"
一个未经优化的./makefile内容可能是:
all: A cd B && $(MAKE) cd C && $(MAKE) @echo "Complete" A: cd A && $(MAKE) .PHONY: A all
这样构造的makefile将会耗时(1+5+10)秒,make过程输出如下:
cd A && make make[1]: Entering directory `/home/hackerzhou/test/A' Target A sleep 1s sleep 1 Target B complete make[1]: Leaving directory `/home/hackerzhou/test/A' cd B && make make[1]: Entering directory `/home/hackerzhou/test/B' Target B sleep 5s sleep 5 Target B complete make[1]: Leaving directory `/home/hackerzhou/test/B' cd C && make make[1]: Entering directory `/home/hackerzhou/test/C' Target C sleep 10s sleep 10 Target C complete make[1]: Leaving directory `/home/hackerzhou/test/C' Complete
大家可以发现,此时make的执行是顺序的,就算使用make –jobs=2也不能使用多job并行编译,究其原因是因为make的并行编译是依靠依赖关系来进行的,因此cd B && $(MAKE)和cd C && $(MAKE)两句不会被并行执行。于是我们就可以把该makefile改写成:
all: publish publish: B C @echo "Complete" C: A cd C && $(MAKE) B: A cd B && $(MAKE) A: cd A && $(MAKE) .PHONY: A B C publish all
此时make编译的输出如下:
cd A && make make[1]: Entering directory `/home/hackerzhou/test/A' Target A sleep 1s sleep 1 Target B complete make[1]: Leaving directory `/home/hackerzhou/test/A' cd B && make cd C && make make[1]: Entering directory `/home/hackerzhou/test/C' make[1]: Entering directory `/home/hackerzhou/test/B' Target C sleep 10s sleep 10 Target B sleep 5s sleep 5 Target B complete make[1]: Leaving directory `/home/hackerzhou/test/B' Target C complete make[1]: Leaving directory `/home/hackerzhou/test/C' Complete
可以看出稍稍修改一下就可以使用–jobs=2使得编译时间缩短到(10+1)s,B和C可以并行的完成,这只是我一个抛砖引玉的例子。
诚然通过–jobs来优化make编译也有其局限性:
- 比如–jobs的数量过大(超过cpu核的个数的时候性能就会直线下降)
- 子makefile不一定能很好的支持–jobs,可能也需要修改,会使得工程量变大
- 如果编译过程中牵涉到大量的IO操作,并发的IO会成为瓶颈使得性能反而更慢
因此,优化make的方法应该因地制宜,分析出哪里是瓶颈才能对症下药。
2013-04-19 10:56
受教了,今天回去学一下make的写法。哈哈