c++ - 并发编译,串行链接

标签 c++ makefile cmake linker

我有一个大的 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 生成的,则至少有两个选项:

  1. 调整您的 CMake 文件,使 CMake 生成两个不同的 makefile:一个用于编译,一个用于链接。然后编写一个简单的包装器 makefile,例如:

    .PHONY: myAll myCompile
    
    myAll: myCompile
        $(MAKE) -j $(concurrent-linkers) -f Makefile.link
    
    myCompile:
        $(MAKE) -f Makefile.compilation
    
  2. 说服 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/

相关文章:

c++ - g++从具有模板参数的类派生

c++ - 如何从 QWidget 表单中删除此空格(边距)

cmake - 在 CMake 中对数字列表进行数字排序

c++ - 在 solaris 上 boost 库信号量

makefile - 对配置脚本和 Makefile.in 感到困惑

c++ - Coin-CLP 的设置问题

makefile - 如何让 GNU make 删除由隐式规则创建的中间目录?

c++ - CMake如何选择gcc和g++进行编译?

cmake - 是否可以使 CMake 在命令行末尾打印一个变量?

c++ - 如何检查线程中是否调用了析构函数?