我有一个脚本,可以编译我的解决方案的所有项目(大约50个),比如下面的
msbuild "myProjName.dproj" /t:build /p:config="Release" /fileLogger /flp:ErrorsOnly /nologo
这工作得很好,但需要很长时间才能编译。为了加快构建速度,我一直在尝试利用我们现代多核机器的所有潜力,使用此处解释的"/maxcpucount"开关:http://msdn.microsoft.com/library/bb651793.aspx
在我的4核CPU开发机器上,我得到了大约相同的编译时间。没有绩效收益。
显然,只有当项目需要建立依赖关系时,这才能起作用。然后,其他"工作者"将并行地构建这些依赖项项目作为主项目。所以我试着在delphi中建立一个项目组,并将我所有的项目添加到其中,然后在这个.groupproj上运行msbuild命令,但它仍然像往常一样慢。
你们中有人用msbuild同时建造了多个项目吗?如果是,你能给我一个解释吗?
谢谢!
以下内容适用于RAD Studio XE4,但也可能适用于早期或更高版本。此外,.groupproj中定义的依赖项将不会被此方法所接受。我试图并行化的.groupproj没有项目间的依赖关系,所以我不知道如何处理它
当使用Build
、Clean
或Make
目标使用MSBuild生成.groupproj文件时,生成不会并行运行,因为这些目标使用CallTarget
任务执行其他目标,但CallTarget
不会并行执行其目标。
为了并行生成单独的项目,MSBuild项目必须使用单个MSBuild
任务同时生成多个项目。目标必须这样定义:
<Target Name="Build">
<MSBuild Projects="@(Projects)" BuildInParallel="true"/>
</Target>
<Target Name="Clean">
<MSBuild Targets="Clean" Projects="@(Projects)" BuildInParallel="true"/>
</Target>
<Target Name="Make">
<MSBuild Targets="Make" Projects="@(Projects)" BuildInParallel="true"/>
</Target>
将这些指令添加到.groupproj,然后删除其他<Target>
指令和<Import>
指令。(当您要求只生成项目的子集时,CodeGear.Group.Targets
定义了一些目标以按正确的顺序生成项目,并生成依赖项,但它会覆盖.groupproj中定义的Build
、Clean
和Make
目标。)请注意,这只允许您生成所有项目,而不仅仅是一个子集。
BuildInParallel
已添加到MSBuild 3.5中。但是,由于.groupproj文件没有指定ToolsVersion
属性,MSBuild将使用2.0版中定义的MSBuild
任务,该版本不支持BuildInParallel
。有两个选项可以解决此问题:
- 将
ToolsVersion="3.5"
(或更高版本)添加到.groupproj文件的根<Project>
元素中 - 使用
/toolsversion:3.5
(或简称/tv:3.5
)命令行参数运行MSBuild(/toolsversion
覆盖所有项目文件中指定的ToolsVersion
)
完成此操作后,使用/maxcpucount
(或/m
)参数运行MSBuild,您的项目应该并行生成。但是,RAD Studio无法正确处理这个转换后的项目组,因此您可能需要为文件提供不同的扩展名,以明确它不是标准的RAD Studio项目组(任何以proj
结尾的扩展名都可以)。
下面的XSLT样式表执行上面描述的转换:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
exclude-result-prefixes="msbuild"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
xmlns:msbuild="http://schemas.microsoft.com/developer/msbuild/2003"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="//msbuild:Project">
<xsl:copy>
<xsl:attribute name="ToolsVersion">3.5</xsl:attribute>
<xsl:apply-templates select="@* | node()"/>
<Target Name="Build">
<MSBuild Projects="@(Projects)" BuildInParallel="true"/>
</Target>
<Target Name="Clean">
<MSBuild Targets="Clean" Projects="@(Projects)" BuildInParallel="true"/>
</Target>
<Target Name="Make">
<MSBuild Targets="Make" Projects="@(Projects)" BuildInParallel="true"/>
</Target>
</xsl:copy>
</xsl:template>
<xsl:template match="//msbuild:Target">
<!-- Do not copy -->
</xsl:template>
<xsl:template match="//msbuild:Import">
<!-- Do not copy -->
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
您可以使用以下项目文件(其中groupproj2parallel.xslt
是上面的XSLT文件)将此样式表应用于MSBuild(4.0或更高版本:XslTransformation
已添加到MSBuild 4.0中):
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Build" Inputs="$(InputPaths)" Outputs="$(OutputPaths)">
<XslTransformation
XmlInputPaths="$(InputPaths)"
XslInputPath="groupproj2parallel.xslt"
OutputPaths="$(OutputPaths)" />
</Target>
</Project>
您需要使用/p:InputPaths="..." /p:OutputPaths="..."
在命令行中显式指定InputPaths
和OutputPaths
,或者在MSBuild
任务的Properties
参数中指定它们。(或者,您也可以对项目文件中的文件名进行硬编码。)
MSBuild为C#和Visual Basic项目提供的目标定义通过使用项目文件中定义的<ProjectReference>
项来处理依赖项,而不是在解决方案文件中定义依赖项。Delphi.droj文件和C++Builder.cbproj文件不支持这一点,因为底层的CodeGear.Common.Targets
不重用Microsoft.Common.Targets
中为<ProjectReference>
定义的机制。
有两种方法可以构建Delphi项目:MSBuild
或DCC32.exe
。建议使用MSBuild
,因为项目文件(dproj
和groupproj
)封装了所有配置设置。
然而,与普通的旧DCC32.exe
相比,使用MSBuild
有额外的开销。此外,使用MSBuild
构建Delphi项目组(.groupproj
)并没有给多核CPU带来任何好处。构建性能与单核CPU相同。
以下是我在一个groupproj
:中构建290个dproj
文件的统计数据
MSBuild a `groupproj` contains 290 `dproj` on 2C/4T CPU: ~100s
MSBuild a `groupproj` contains 290 `dproj` on 4C/8T CPU: ~100s
MSBuild 290 `dproj` run in multi-threads on 2C/4T CPU: ~121s
MSBuild 290 `dproj` run in multi-threads on 4C/8T CPU: ~50s
DCC 290 `dproj` run in multi-threads on 2C/4T CPU: ~37s
DCC 290 `dproj` run in multi-threads on 4C/8T CPU: ~24s
从阅读中,我们可以得出结论,与DCC32
相比,MSBuild
引入了额外的开销。为了充分利用可用的CPU内核和线程,DCC32
是牺牲.DPROJ
项目配置封装设计方便的方法。
并行构建Delphi groupproj
的msbuild
脚本可在https://github.com/ccy/msbuild.delphi.parallel
有点偏离主题:您可以尝试IDE修复包的fastdcc部分以获得更快的构建:http://andy.jgknet.de/blog/ide-tools/ide-fix-pack/例如,我的构建时间从1分钟降到了22秒!