使用g++编译时如何利用多核心?

199

快速问题:允许g++生成多个实例以便更快地编译大型项目的编译器标志是什么(例如,对于具有多核CPU的情况,每次编译4个源文件)?


这真的有用吗?我的所有编译工作都是I/O绑定,而不是CPU绑定。 - Brian Knoblauch
5
即使它们受限于I/O,当CPU繁重的任务发生时(只有一个g++实例会出现停滞),你可能仍然可以保持更高的I/O负载,并且如果调度程序可以更自由地选择下一个要从磁盘读取的内容,则可能获得I/O效率的提升。我的经验是,明智地使用make -j几乎总会带来一些改善。 - Flexo
1
@BrianKnoblauch 但是在我的机器上(真实的或者在VirtualBox中),它是CPU绑定的,我发现当编译时,通过'top'命令可以看到CPU很忙。 - superK
1
即使它们是I/O绑定的,我们也可以使用gcc的标志“-pipe”来减少痛苦。 - superK
刚在谷歌上看到这个链接: https://gcc.gnu.org/onlinedocs/libstdc++/manual/parallel_mode_using.html#parallel_mode.using.prereq_flags - Jim Michaels
显示剩余2条评论
9个回答

265
你可以使用 make 命令完成此操作,如果是 GNU 版本的 make,则需要使用 -j 标志(这也可在单处理器计算机上提高效率)。
例如,如果你想要让 make 并行运行 4 个作业:
make -j 4

你也可以通过管道运行gcc:

gcc -pipe

这将使编译阶段进行流水线处理,也有助于保持核心的繁忙程度。

如果您还有其他可用的机器,也可以尝试使用distcc,它可以将编译任务分发给其他机器来处理。


47
你的 -j 数量应该是你拥有的核心数的1.5倍。 - Mark Beckwith
2
谢谢。我一直试图通过CFLAGS/CPPFLAGS/CXXFLAGS将“-j#”传递给gcc。我完全忘记了“-j#”是GNU make的参数(而不是GCC的参数)。 - chriv
46
为什么 GNU Make 的 -j 选项需要是 CPU 核心数的 1.5 倍? - Alex Bitek
37
“1.5”这个数字是由于被称为“I/O bound”的问题而得出的。这是一个经验法则。大约三分之一的工作将等待I/O操作,因此剩下的工作将使用可用的核心。比核心数更高的数字更好,你甚至可以提高到“2x”。参见:Gnu make -j arguments - artless noise
4
可能是因为您的项目中依赖关系设置有误(即使其依赖项尚未准备好,目标仍开始构建),因此只有按顺序构建才能成功。 - Antonio
显示剩余6条评论

46

并没有这样的标志,拥有这样的标志违背了Unix哲学,即每个工具只执行一个功能并且表现良好。生成编译器进程在概念上是构建系统的任务。你可能正在寻找的是GNU make的-j(作业)标志,例如:

make -j4

或者你可以使用pmake或类似的并行构建系统。


请参阅以下网页: http://www.gnu.org/software/make/manual/html_node/Parallel.html http://www.gnu.org/software/make/manual/html_node/Options-Summary.html#Options-Summary - Jim Michaels
4
“Unix pedantry is not helpful” 的意思是“Unix的迂腐教条主义并没有帮助”。匿名编辑说幸好不是因为迂腐教条主义才出现这种情况,已经被撤回了。请审核人员更加注意自己在做什么。 - Lightness Races in Orbit
1
尽管声称不拘泥于细节,GCC仍然会使用-fparallel-jobs=N标志。 最好告诉GCC开发人员他们做错了。 - Spike0xff

13

如果使用make,-j存在问题。来自man make:

  -j [jobs], --jobs[=jobs]
       Specifies the number of jobs (commands) to run simultaneously.  
       If there is more than one -j option, the last one is effective.
       If the -j option is given without an argument, make will not limit the
       number of jobs that can run simultaneously.
而且,特别值得注意的是,如果你想编写脚本或者查看可用的处理器核数(这取决于你的环境和运行的不同环境,可能会有所变化),你可以使用Python函数cpu_count()

https://docs.python.org/3/library/multiprocessing.html#multiprocessing.cpu_count

像这样:

make -j $(python3 -c 'import multiprocessing as mp; print(int(mp.cpu_count() * 1.5))')

如果你想知道为什么是1.5,我会引用上面的评论中用户artless-noise的话:

数字1.5是因为已知存在I/O限制问题。这只是一个经验法则。大约有三分之一的作业会等待I/O,所以其余作业将使用可用的内核。大于内核数量的数字更好,你甚至可以使用高达2倍。


11
大多数Linux用户可能更喜欢更短的命令:make -j `nproc`,其中的nproc来自于GNU Coreutils。 - Ciro Santilli OurBigBook.com
如果您正在使用 SSD,I/O 不会是一个太大的问题。为了进一步完善 Ciro 上面的评论,您可以这样做:make -j $(( $(nproc) + 1 ))(请确保将空格放在我标记的位置)。 - Ed K
使用python提出了不错的建议,在无法访问nproc的系统上,例如manylinux1容器中,通过避免运行yum update/yum install来节省额外的时间。 - hoefling

12

有人提到了 make,但是 bjam 也支持类似的概念。 使用 bjam -jx 命令指示 bjam 构建最多 x 个并发命令。

我们在 Windows 和 Linux 上使用相同的构建脚本,并使用此选项可以将构建时间减少一半。不错。


11

make 命令可以帮你完成这个任务。请查阅 man 手册中的 -j-l 参数。我认为 g++ 不能进行并行计算。


1
如果提到-l选项(除非所有先前的作业都终止,否则不会启动新作业),则加1分。否则,似乎链接器作业始于并非所有对象文件都已构建(因为某些编译仍在进行中),因此链接器作业失败。 - NGI

7

distcc还可以用于在农场中安装了distcc的其他计算机上分发编译,而不仅仅是在当前计算机上分发编译。


+1,distcc是进行大型构建时必备的有用工具。 - Flexo
看起来还有一些像distcc一样工作的东西:https://dev59.com/w1XTa4cB1Zd3GeqP1WnJ#13770116 - rogerdpack

5

我不确定 g++ 是否适用,但如果你使用 GNU Make,那么 "make -j N"(其中 N 是 make 可以创建的线程数)将允许 make 同时运行多个 g++ 作业(只要文件彼此不依赖)。


2
不,N 不是线程数!很多人误解了这一点,但 -j N 告诉 make 同时应该生成多少个进程,而不是线程。这就是为什么它不像 MS 的 cl -MT(真正的多线程)那样高效的原因。 - Sebi2020
如果 N 太大会发生什么呢?例如 -j 100 可能会破坏系统,还是 N 只是一个上界,不一定需要实现? - mercury0114

4
您可以使用 make -j$(nproc) 命令来构建一个项目,多个作业会并行运行。这个命令用于使用make构建系统构建一个项目。
例如,如果您的系统有4个CPU核心,运行 make -j$(nproc) 将指示 make 并发地运行 4 个任务,每个任务在一个 CPU 核心上运行,从而加快构建过程。
您也可以通过运行以下命令来查看您有多少个核心:echo $(nproc)

请注意,nproc 并不总是能够提供最佳性能。我的机器有四个核心,但是使用 -j2 参数编译速度更快。 - undefined

2

GNU并行处理

我正在进行一个合成编译基准测试,但不想编写Makefile,所以我使用了:

sudo apt-get install parallel
ls | grep -E '\.c$' | parallel -t --will-cite "gcc -c -o '{.}.o' '{}'"

解释:

  • {.} 接收输入参数并删除其扩展名
  • -t 打印出正在运行的命令,让我们了解进度
  • --will-cite 如果您使用该软件发布结果,则不需要引用该软件...

parallel 如此方便,甚至可以自己进行时间戳检查:

ls | grep -E '\.c$' | parallel -t --will-cite "\
  if ! [ -f '{.}.o' ] || [ '{}' -nt '{.}.o' ]; then
    gcc -c -o '{.}.o' '{}'
  fi
"

xargs -P 可以并行运行作业,但它不太方便进行扩展操作或使用多个命令: 通过xargs调用多个命令

并行链接的问题被问到:gcc在链接时可以使用多个核心吗?

待办事项:我记得在某个地方读到编译可以简化为矩阵乘法,所以也许可以加速大文件的单个文件编译。但是我现在找不到参考资料。

在Ubuntu 18.10中测试。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接