技术开发 频道

C++项目在VS2010中的并行构建调优

  【IT168资讯】在多处理器计算机上提高构建速度的一个极好方法是充分利用其并行处理能力,如果你在Visual Studio 2010中有一个C++项目,有两种不同类型的并行构建配置方法供你选择。

  有哪些参数可以调整?

  项目级并行构建是由MSBuild控制的,它是在Visual Studio的解决方案级进行设置的(实际上Visual Studio是为每个用户都保存了设置,与你想象的可能有点不一样,你可能认为不同解决方案有不同的设置,但UI却不允许你这么做),默认情况下,Visual Studio选取你机器上的处理器数量作为最大并行构建项目的数量,如图1所示,你可以将这个数字调大调小找出一个并行构建速度最快的合适值,有些人可能喜欢将其调小,以便在构建期间还可以做点其它工作。

  图1  并行构建项目的最大数量

  虽然MSBuild从Visual Studio接管了部分功能,但这里的设置仍然保持和Visual Studio 2008一样。

  如果你正在构建C++或C++/CLI项目,还有一个地方你可以设置并行构建参数,CL编译器支持/MP参数,它告诉编译器使用自身的一个单独实例同时构建它的子集,默认的并行数仍然使用了CPU的数量,但你可以指定一个值,如/MP5,注意现在情况发生了一点变化,因此我要告诉你如何找到这个值,以及在MSBuild格式项目文件中看起来是什么样子。

  打开项目的“属性”窗口,转到“C/C++”*“常规”窗口,我建议你选择“所有配置”和“所有平台”,在后面你才有更多的可选项。

  图2  项目属性设置

  象往常一样,通过转储,你可以看到项目文件中有什么内容,在“解决方案资源管理器”中的节点上点击右键,选择“编辑”。

  图3

  下图显示了项目文件的一部分代码。

  图4  项目文件代码示例

  在这里,所有类型为“ClCompile”的项目都自动拥有元数据MultiProcessorCompilation,默认值为true,除非明确指定了一个不同的值。

  

  顺便说一下,MSBuild项目通常都是一个文件,它们的子元素是元数据,下面是一个例子,注意它们被放在一个“ItemGroup”中。

  图5  MSBuild项目文件示例

  因为这是一个元数据,如果是高手,完全可以直接修改每个文件,你需要为使用了#import的文件禁用/MP,因为它不支持/MP,其它不支持/MP的特性是/Gm,/Gm表示渐进式编译,更多参数请参考http://msdn.microsoft.com/en-us/library/bb385193.aspx。

  回到多处理器CL,如果你想明确告诉CL有多少并行编译执行,Visual Studio可通过/MP实现,它出现在全局设置中。

  图6  C++编译最大并行任务数设置

  Visual Studio通过一个全局属性CL_MPCount进行设置,这意味着在Visual Studio外构建时将不受任何影响。

  如果你选择一个更细粒度的值,你就不能使用图形界面进行设置了,因为你根本看不到它的设置项,这时就必须打开项目文件直接进行修改。在CLICompile项目上这是一个完全不同的元数据块,叫做“ProcessorNumber”,你可以设定一个从1到你认为合理的一个值,然后在/MP后也追加一个同样的值,如果没有,它就会被忽略。

  图7  ProcessorNumber和MultiProcessorCompilation设置

  图中出现的波浪线是一个小小的bug,直接忽略它。

  如何在命令行上构建?

  /MP设置来自项目文件,因此在命令行上进行设置作用是一样的,它是整个MSBuild的一部分,在命令行上构建和在Visual Studio中构建是一样的效果吗?在Visual Studio中设置的全局并行设置不会影响到命令行,你必须亲自给msbuild.exe传递/m参数,这个参数是可选的,如果你不设置,它就使用CPU的数量,但和Visual Studio开箱即用的特性不一样,在命令行中如果不指定/m参数,它只会使用1颗CPU,这个问题可能在将来的版本中会得到修正。

  图8  命令行构建参数

  如果想给/MP选任意的值,你可以设置一个环境变量,或象Visual Studio那样传递一个属性CL_MPCount。

  在每个项目上都设置/MP是很烦人的,怎么才能提高设置效率?

  你可能想在多个项目上使用/MP,但你又不想在每个项目上都设置一遍,Visual Studio解决这类问题的办法是使用属性表。首先从“视图”菜单打开“属性管理器”,根据你使用的设置它的确切位置可能不一样,下面是一个C++项目设置的位置。

  图9  视图菜单中的属性管理器

  在一个项目上点击右键,选择“添加新的属性表”:

  图10  给项目添加新的属性表

  我给它取了一个名字叫做“MultiprocCpp.props”,你将会看到该项目的所有配置都将添加这个属性表,在它上面点击右键,你将看到与项目相同的属性窗口,但这个时候你编辑的是属性表,再次将“Multi-processor Compilation”设为“YES”。关闭属性窗口,在属性管理器中选择属性表,然后点击“保存”。

  

  现在可以在编辑器中打开新建的MultiprocCpp.props文件,我的看起来如下:

  图11  在编辑器中打开MultiprocCpp.props

  仔细查看这个项目文件,你可以看到属性表通过一个Import标签应用到每个配置中了,这一点和C++中的#include非常类似。

  图12  通过Import引用属性表

  现在我们就可以重用之前在项目文件中的定义了,于是我可以在属性管理器中选中多个项目,然后点击右键,选择“添加现有属性表”。

   图13  为多个项目同时指定属性表

  OK!现在所有项目编译时都带有/MP参数了。

  在某些情况下,你可能想要更简单一点,例如,你可能想要删除大量的属性表,幸运的是,MSBuild 4.0有一个强大的,完整的对象模型,你可以使用它,再编写几行代码就可以搞定这种工作了。

  如果你不想通过图形界面进行设置,完全可以自己手动进行编辑,例如,在VS自己的构建中,我们在每个项目的优异都设置了一套属性。

  图14  手动设置项目优异属性

  在这里我们定义了所有类型的全局设置,并导入了其它设置,我将在以后的文章中介绍组织大型构建树的方法。

  并行数量太多了也不好

  一般来说,利用完所有处理器或处理器核心就已经足够了,否则可能会导致机器变慢甚至崩溃,下图就显示了这样一个例子。

  图15  太多的并行构建进程很容易让机器崩溃

  我是在一台8 CPU的机器上做的这个实验,我把解决方案中的所有项目全部开启/MP了,然后使用msbuild.exe /m进行构建(我使用命令行进行构建不会出现这个问题,但在Visual Studio中进行构建就会出现),如果相关依赖不能阻止它,MSBuild将立即启动8个项目,每个CL将会一次运行自己的8个实例,因此总共会有64个CL运行考验我们的处理器核心和磁盘,这样做不但不能提升速度,反倒会使性能急剧下降。

  你可能希望有一天系统能够实现自我调整,但如果现在遇到这样的问题,你不得不手工调整。下面是一些建议:

  ◆将全局值设小一点

  例如将/m:4减少到/m:3,或使用属性表将/MP修改为/MP2,如果你的构建中还有其它问题,如有许多的并行项目,但并行的CL不够,反之亦然,这个时候你都应该将全局并行构建参数调小。

  ◆为每个项目和配置调整/MP

  有些时候使用/MP可能不是非常好的的办法,你也可以通过配置进行调整,Retail配置可能会使速度变得更慢,因为编译器要做的优化更多了,为Retail开启/MP而不是为Debug开启/MP可能更有意义。

  ◆获得超级定制

  在你的团队中,你可能有一系列硬件,也许你的开发人员使用的是双CPU机器,但夜间构建是在一台8 CPU的机器上进行的,两者构建时需要的来源是一样的,你希望两者的速度都不能太慢,在这种情况下,你可以使用环境变量,或是在MSBuild标签上设置条件,几乎所有MSBuild标签都可以设置条件。

  下面是一个例子,当“MultiprocCLCount”有一个大于零的值时,就可以使用这个值启用/MP。

  图16  通过环境变量调整处理器数量

  MSBuild启动时将所有环境变量的值作为初始属性值,因此在我更快速的机器上,我将MultiprocCLCount的值设为8,而在我的开发用机上,我将其设为2。

  类似的方法还可以应用到MSBuild.exe的/m参数中,如/m:%MultiprocMSBuildCount%,

  在外来条件中还有其它属性可能很有用,如$(Number_Of_Processors)表示逻辑处理核心数量,它来自环境变量。$(MSBuildNodeCount)是传递给msbuild.exe /m参数的值,在Visual Studio中,这个值是通过“工具”*“选项”进行设置的。

  最后,我希望你能有效利用/m和/MP。希望你对我介绍的MSBuild功能能进一步深入学习,最好自己动手配置一次。

0
相关文章