【问题标题】:MSVC10 /MP builds not multicore across folders in a projectMSVC10 /MP 不会跨项目中的文件夹构建多核
【发布时间】:2011-08-11 22:46:04
【问题描述】:

我希望有人指出我们遇到的问题或解决方法。

使用 /MP 编译项目时,似乎只会同时编译同一文件夹中的文件。我使用进程资源管理器滑动命令行并确认行为。

项目过滤器似乎对同时编译的内容没有任何影响。

磁盘上的项目结构:

Folder\
  project.vcxproj  
  source\  
    foo.cpp  
    foo1.cpp  
  other_folder\  
    bar.cpp
    bar1.cpp
    bar3.cpp

初始进程树:

MSBuild.exe
  cl.exe  ( passed: source\foo.cpp source\foo1.cpp )
    cl.exe  ( passed: source\foo.cpp )
    cl.exe  ( passed: source\foo1.cpp )

在 cl.exe 的 2 个子实例完成后,父级关闭并出现以下进程树:

MSBuild.exe
  cl.exe  ( passed: other_folder\bar.cpp other_folder\bar1.cpp other_folder\bar2.cpp )
    cl.exe  ( passed: other_folder\bar.cpp )
    cl.exe  ( passed: other_folder\bar1.cpp )
    cl.exe  ( passed: other_folder\bar2.cpp )

我们的源代码很好地组织在许多级别的嵌套文件夹中,这些文件夹与磁盘上标题的布局相匹配 - 我不想放弃它来利用 /MP。

【问题讨论】:

  • 这可能不是你能看到的。基于并发构建项目,而不是基于源文件。您应该看到 cl.exe 的 one 实例构建了两个 .cpp 文件。如果项目之间完全没有依赖关系(不太可能),那么只会同时构建这两个项目。
  • 你试过了吗?你熟悉 /MP 吗?我知道同时构建项目,这不是我要问的。
  • 您的 MSBuild 日志中显示了什么?我刚刚在我的机器上创建了一个新的 VS 2010 C++ 项目,使用上面列出的目录结构,将所有 C++ 文件添加到项目中,并使用 MSBuild 从命令行构建。 MSBuild 记录了 CL.exe 的一次执行,并使用 /MP 选项在同一命令行上传递了所有文件(来自两个目录)。
  • @Bilal,我会在星期一试一试,看看我得到了什么。

标签: c++ visual-studio-2010 visual-c++ msbuild cl.exe


【解决方案1】:

在“对象文件名”(在 vcxproj XML 中,CL.exe 命令行上的 /Fo)项目中使用 %(RelativeDir) 会导致 msbuild 按目录将 cpp 文件批处理到 cl.exe。这会对使用 /MP 获得的好处产生重大影响。

请注意,如果您的项目将 %(RelativeDir) 用于目标文件,则配置可能会尝试避免冲突来自不同文件夹中同名 cpp 文件的 .obj 文件。

/Fo 命令行参数通常是编译器将 obj 文件转储到的文件夹 - 只有一个传递,因此给定目录的所有 cpp 文件一次只能传递给 CL.exe。

那很痛苦 - 但我很高兴有一个原因和一个解决方案。希望对您有所帮助。


更新

一位队友发现,只要将 MSBuild 参数发送到 CL.exe,它似乎就会破坏或严重限制 /MP。这很可能是因为 /MP 要正常工作,顶级 CL.exe 需要有一组 cpp 文件。

我们的解决方案是不使用任何 msbuild 参数(我认为它是 %params% )作为“对象文件名”。这要求我们重命名一些 cpp 文件,以免它们发生冲突。

希望这在 VS2012 或 VS2013 中有所改变。

【讨论】:

  • 有趣的是,我对自动生成的项目文件也有同样的问题,并且它还为所有将 ObjectFileName 设置为相对于 $(IntDir) 的文件的文件按顺序编译。它们也都位于不同的文件夹中,并且总是有一些同名(因此 XML 中的 ObjectFileName 规范)。似乎没有简单的解决方案,即使目标文件名不重叠......
  • 我也有同样的问题。我们将源文件重构为子文件夹以获得清晰的文件夹层次结构。现在构建时间慢了 3 倍。有没有办法解决这个问题?
【解决方案2】:

According it MSDN,文件应该在有线程处理它们时被编译,同时它不能保证文件被编译的顺序:

源文件的编译顺序可能与它们的编译顺序不同 出现在命令行上。虽然编译器创建了一组 包含编译器、操作系统副本的进程 安排每个进程执行的时间。因此,您不能 保证源文件将以特定的方式编译 顺序。

当一个进程可用于编译源文件时,它就会被编译。 如果文件多于进程,则第一组文件是 由可用进程编译。处理剩余的文件 当一个进程完成处理前一个文件并且可用于 处理剩余的文件之一。

它还声明创建的进程数将受命令行中的线程数和文件数的限制:

该值是您在命令行中指定的源文件数量中的较小者

将这些结合起来,我们可以看到编译器以增量方式(文件方式)处理编译,以便它可以正确地将工作分派给子级,它逐个文件夹地处理这个文件夹。

如果您生成了一个自定义的 make 文件,您或许能够解决这个问题,您应该能够同时处理多个文件夹(或尝试使用 MSBUILD.exe 工具)。

【讨论】:

  • 我得出的结论是,msbuild 实际上是如何将 cpp 文件分组并慢慢将它们分发给编译器的。
【解决方案3】:

我在这里用一种避免 .obj 破坏冲突的新方法解决了这个问题

https://stackoverflow.com/a/26935613/4253427

考虑到上述内容,我可以详细说明“任何时候将 MSBuild 参数发送到 CL.exe,它似乎会破坏或严重限制 /MP”。 /MP 一次调用一个 CL.exe。当您“发送一个 msbuild 参数”时,您实际上所做的是创建多个 CL.exe 调用,并使用为每个源文件量身定制的命令行。这些不能成批。上面链接的解决方案试图通过将命令行定制到最小的输出目录集来解决这个问题。

这应该可以通过防止破坏同时保持高度批处理来解决您的问题,只要您的项目不包含 100 个在不同目录中都名为 x.cpp 的文件...通常根据我的经验只有几个在一个更大的项目中碰撞 .obj。

【讨论】:

    【解决方案4】:

    可以确认问题,而且原因也不好找,所以我们再多输入一些关键词,让其他用户遇到这个问题。

    我一直注意到,我最新的 MSVC 项目的重建耗时太长。特别是 CPU 内核几乎没有被利用,任务管理器中 cl.exe 进程的数量变化很大,输出窗口显示正在编译的源文件,看起来像是批量编译的。一次编译 1 到 16 个文件,稍作停顿,然后是另一组文件,依此类推。相比之下,在我的旧项目中,CPU 几乎得到了充分利用,并且“输出”窗口显示正在编译的源文件的连续流。

    现在,我的新项目的最大不同是更好地使用具有匹配目录结构的命名空间,这意味着最终会得到一些具有相同名称的类,并由于指向同一目录的不同 .obj 文件而导致冲突,这会导致更改 C/C++ -> 输出文件中的对象文件名。

    对输出窗口的更新也与目录结构相匹配。如果有一个名称空间/目录,其中包含一个源文件,则 VS 一次只显示一个正在编译的文件。下一个目录有 10 个源文件,VS 显示所有 10 个正在同时编译。

    解决方案并不多。避免使用相同名称的类并且不要更改对象文件名,或者使用the workaround posted by zeromus,它的功能很好。我的重建时间从 03:15 到 01:20,这是一个相当大的差异,并且在大多数编译过程中,CPU 使用率从 ~35% 变为 100%。

    VS 2015、2017 和 2019 都是这种行为,所以改变的希望不大。

    【讨论】:

      猜你喜欢
      • 2010-09-18
      • 2013-04-26
      • 1970-01-01
      • 2018-08-18
      • 2014-11-26
      • 1970-01-01
      • 2020-06-10
      • 1970-01-01
      • 2015-03-14
      相关资源
      最近更新 更多