我有一个大的 cmake/c++/linux 项目:很多小的静态库,一些大的和相互依赖的静态库,一些大的可执行二进制文件。带有调试符号的单个二进制文件是几个 GB。大约有 10 个这样的二进制文件(worker、testA、testB、testC...)。编译通常比我们希望的花费更多的时间,但是我们有快速构建服务器并且我们使用 make -j20
。最糟糕的是链接。单链接大约需要 60 秒和 4GB 内存。但是当所有最终二进制文件同时链接时(经常发生在修改 1 个小子库时,很少重新编译,很多重新链接),10 个链接器使用 40GB RAM(对于 1 个开发人员,可能有更多)并且时间很长. IO 很可能是瓶颈。
我们有很多开发人员在一台强大的服务器上,每个人都使用 make -j20 -l30
这样我们就不会使 CPU 过载。但是我们没有限制并发链接器数量的方法。限制服务器上全局工作链接器的数量会很好,但每次 make 调用也会有所帮助。理想情况下 make -j20 -l30 --concurrent-linkers=2
。可能吗?
我们使用黄金链接器。我们正在分离更小的独立模块,但这需要很长时间。
最佳答案
你可以尝试这样的事情:
$ cat Makefile
OBJS := foo bar baz...
EXES := qux quux quuz...
.PHONY: all
all: $(OBJS)
$(MAKE) -j $(concurrent-linkers) $(EXES)
$(OBJS): ...
<compile>
$(EXES): ...
<link>
并调用它:
$ make -j20 -l30 concurrent-linkers=2
基本上,它将构建分为两个 make 调用,一个用于编译,一个用于链接,具有不同的 -j
选项。主要缺点是所有编译必须在第一个链接开始之前完成。更好的解决方案是设计一个简单的链接作业服务器(一个简单的 shell 脚本,带有一些 flock
和标记文件就可以了)并将链接作业委托(delegate)给它。但是如果你能忍受这个......
带有虚拟 Makefile 的演示:
$ cat Makefile
OBJS := a b c d e f
EXES := u v w x y z
.PHONY: all
all: $(OBJS)
$(MAKE) -j $(concurrent-linkers) $(EXES)
$(OBJS) $(EXES):
@printf 'building $@...\n' && sleep 2 && printf 'done\n' && touch $@
$ make -j20 -l30 concurrent-linkers=2
building a...
building d...
building b...
building c...
building e...
building f...
done
done
done
done
done
done
make -j 2 u v w x y z
make[1]: warning: -jN forced in submake: disabling jobserver mode.
make[1]: Entering directory 'foobar'
building u...
building v...
done
done
building w...
building x...
done
done
building y...
building z...
done
done
make[1]: Leaving directory 'foobar'
如您所见,所有 $(OBJS)
目标都是并行构建的,而 $(EXES)
目标是一次构建 2 个(最多)目标。
编辑 如果您的 makefile 是由 CMake 生成的,则至少有两个选项:
调整您的 CMake 文件,使 CMake 生成两个不同的 makefile:一个用于编译,一个用于链接。然后编写一个简单的包装器 makefile,例如:
.PHONY: myAll myCompile myAll: myCompile $(MAKE) -j $(concurrent-linkers) -f Makefile.link myCompile: $(MAKE) -f Makefile.compilation
说服 CMake(如果尚未如此)生成定义两个 make 变量的 makefile:一个 (
OBJS
) 设置为所有目标文件的列表,另一个 (EXES
) 设置为所有可执行文件的列表。然后编写一个简单的包装器 makefile,例如:.DEFAULT_GOAL := myAll include CMake.generated.Makefile .PHONY: myAll myAll: $(OBJS) $(MAKE) -j $(concurrent-linkers) $(EXES)
如果 CMake 生成两个伪目标,一个用于所有目标文件,另一个用于所有可执行文件,则存在非常相似的解决方案:
.DEFAULT_GOAL := myAll include CMake.generated.Makefile .PHONY: myAll myAll: cmake-target-for-compilation $(MAKE) -j $(concurrent-linkers) cmake-target-for-link
关于c++ - 并发编译,串行链接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55039722/