GNU make的-j选项
自从我了解到-j我已经很快地使用了-j8。 有一天,我正在编译一个地图集安装和制作失败。 最终,我把它追踪到了无序的事情上 – 而且一旦我回到单线程构造中,它就运行良好。 这让我紧张。 在编写我自己的make文件时,我需要注意什么样的条件,以避免使用make -j做一些意想不到的事情?
我认为make -j会尊重您在Makefile中指定的依赖关系; 即如果你指定objA依赖于objB和objC,那么在objB和objC完成之前,make将不会开始在objA上工作。
很可能你的Makefile并没有严格的指定必要的操作顺序,而且在单线程的情况下它恰好适合你。
总之 – 确保你的依赖是正确和完整的。
如果你使用单线程make,那么你可以盲目地忽略目标之间的隐式依赖。 当使用并行make时,你不能依赖隐式依赖。 他们都应该明确。 这可能是最常见的陷阱。 特别是如果使用.phony目标作为依赖关系。
这个链接是关于并行make的一些问题的一个很好的入门。
这是我开始使用并行构build时遇到的一个问题的示例。 我有一个名为“新鲜”的目标,我用它从头开始重build目标(一个“新鲜”的构build)。 过去,我通过简单地指出“干净”,然后“build立”作为依赖性来编码“新鲜”目标。
build: ## builds the default target clean: ## removes generated files fresh: clean build ## works for -j1 but fails for -j2
这工作得很好,直到我开始使用并行构build,但与并行构build,它试图同时做“干净”和“构build”。 所以我改变了“新鲜”的定义如下,以保证正确的操作顺序。
fresh: $(MAKE) clean $(MAKE) build
这基本上只是正确指定依赖关系的问题。 诀窍是并行构build比单线程构build更严格。 我的示例演示了给定目标的依赖关系列表不一定表示执行顺序。
如果你有一个recursion的东西,事情可以很容易地打破。 如果你没有做recursion构造,那么只要你的依赖是正确和完整的,你不应该遇到任何问题(除了make中的bug)。 请参阅recursion使用考虑有害recursion使问题更彻底的描述。
有一个自动化testing来testing所有make文件的-j选项是一个好主意。 即使是最好的开发者也会遇到make的-j选项的问题。 最常见的问题是最简单的。
myrule: subrule1 subrule2 echo done subrule1: echo hello subrule2: echo world
在正常情况下,你会看到你好 – >世界 – >完成。 用make -j 4,你可能会看到world – > hello – > done
我发现这种情况发生的最多的是创build输出目录。 例如:
build: $(DIRS) $(OBJECTS) echo done $(DIRS): -@mkdir -p $@ $(OBJECTS): $(CC) ...
只是以为我会添加到subsetbrew的答案,因为它不显示效果清晰。 但是,添加一些睡眠命令。 那么它适用于Linux。
然后运行使显示差异:
- 使
- make -j4
all: toprule1 toprule1: botrule2 subrule1 subrule2 @echo toprule 1 start @sleep 0.01 @echo toprule 1 done subrule1: botrule1 @echo subrule 1 start @sleep 0.08 @echo subrule 1 done subrule2: botrule1 @echo subrule 2 start @sleep 0.05 @echo subrule 2 done botrule1: @echo botrule 1 start @sleep 0.20 @echo "botrule 1 done (good prerequiste in sub)" botrule2: @echo "botrule 2 start" @sleep 0.30 @echo "botrule 2 done (bad prerequiste in top)"