Visual Studio - 并行构建不会提高性能

3
我一直在使用一些大型的Visual Studio C++项目,似乎花费在构建预编译头文件上的时间比单独的源文件还要多。
我已经对项目本身进行了一些更改(启用了/MP标志并设置了“工具==〉选项”中的最大作业数),构建速度似乎快了约10%,但是与同一项目的Linux版本相比,后者在指定make中的-j选项时运行速度几乎快了4-5倍。
首先,是否需要设置其他选项以利用多核系统来提高构建速度,特别是生成预编译头文件?
其次,通过启用多处理器支持,似乎无法再执行“增量构建”。如果我理解正确,每个“构建”将与完整的“重建”或“清除,构建”操作相同。是这样吗?据我所知,如果makefile编写正确,GNU makefile项目不会受到此限制,因此,像Visual Studio这样的现代且昂贵的工具遇到这样的问题似乎很奇怪。
谢谢。

1
根据我的个人经验,通常是链接器占用的时间最长。因此,我通常禁用IPO、整个程序优化和链接时代码生成。这将把所有“昂贵”的步骤移到高度可并行化的主要编译中。仅在生产构建时才打开所有这些东西。 - Mysticial
2个回答

4
我已经调查了这个问题一周了。事实证明,启用并行构建("/m:njobs")的基础“msbuild”工具可以并行构建项目,但项目中的单个任务始终是串行的。鉴于项目之间的依赖关系,这通常意味着并行化机会相当有限。
我使用CMake生成解决方案和项目文件,这意味着可以使用不同构建系统的生成器来比较相同的构建。我一直在使用Ninja构建工具,它可以更好地利用并行化。使用资源监视器监视所有CPU核心的使用情况显示,MSBuild使用1个核心,有时使用2个核心。对于我的工作站上的大部分构建,Ninja将所有8个核心都用到极限。对于我的代码,在一个24核构建节点上,这意味着使用msbuild需要125分钟,而使用Ninja只需要45分钟。事实上,它并没有实现24倍的加速,因为单元测试占用了大部分时间,而且没有并行化,但我确实看到它在构建期间使用了所有24个核心,而msbuild没有。
因此,总的结论是,Visual Studio/msbuild对于有效的并行化支持相当有限,如果您想使构建尽可能快,您必须寻找其他更好的工具。我发现CMake/Ninja是一个有效的替代方案。

CMake/Ninja工具在Windows上能够正常工作吗?请问我可以得到它们的链接吗? - Cloud
说实话,最近我听说Visual Studio 2015具有在项目内并行构建文件的能力。 - TheUndeadFish
关于CMake,请参见http://www.cmake.org/ 关于Ninja,请参见https://martine.github.io/ninja/,下载请访问https://github.com/martine/ninja/releases - Roger Leigh
Visual Studio 已经很长时间具备了可以与项目并行构建的能力。但是,有许多选项使其默认处于禁用状态。因此,您需要调整设置才能让它正常工作。根据我的经验,这涉及打开 /MP、禁用整个程序优化和禁用链接时代码生成。 - Mysticial
@Mysticial,这似乎只影响Roger在他的答案中提到的并行项目数量,而不是单个项目中的文件。 - xaxxon

2
Visual Studio有两种类型的并行构建:项目内和项目间。
在项目内,当启用“多处理器编译”选项(/MP)时,它将运行多个C++编译器副本(一旦预编译头文件构建完成)。链接器是单线程运行的,但我还没有尝试过链接时代码生成以了解它的作用。如果您的项目文件很少,您将看不到太多好处。
它进行的另一个并行构建是同时构建多个项目,并可以通过“工具”=>“选项”进行设置。只要项目依赖关系允许(即,如果它们彼此不依赖,则可以同时构建两个项目),它就会同时构建多个C++、C#和其他类型的项目。
我成功地使用了其中之一,具体取决于我的解决方案的内容和大小。启用两者可能会有益,但您也可能会超额预订可用的CPU线程。如果您的解决方案由全部或大部分C++项目组成,请选择其中之一。在8核系统上构建8个项目,每个项目都尝试使用8个进程,可能会有点吃力。
为了减少预编译头文件的构建时间,在包含Windows头文件之前,可以定义WIN32_LEAN_AND_MEAN宏来排除很少使用的内容。Windows.h中还定义了一些NOxxx宏来排除API的其他部分。(请参阅定义WIN32_LEAN_AND_MEAN会排除什么?)
回答您的第二个问题,/Gm增量重建选项将检查修改的头文件中的类定义是否已更改,以确定是否需要重新构建源文件。这是与VS(和make)执行的正常时间戳检查不同的额外检查。重新构建或清理/构建将删除所有编译器生成的文件并重新构建所有内容,无论文件是否更改。虽然/MP标志不能与/Gm一起使用(如果两者都给出,编译器将忽略/MP),但它不会阻止make使用基于时间戳的依赖关系检查。

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