私は109のプロジェクトで解決策を持っています。すべての組み合わせ - .NET、Silverlight、MVC、Web アプリケーション、コンソール アプリケーション。
次に、msbuild を使用してコンソール上でビルドします。時間がかかります。理解できる - たくさんのプロジェクト、たくさんのファイル。
しかし、最初の直後に 2 回目にビルドすると、実際には何もビルドされていなくても、まだ時間がかかります。diag msbuild ログで確認できます。
たとえば、最初の完全なビルドのタスク パフォーマンスの概要は次のとおりです。
Task Performance Summary:
4 ms GetSilverlightItemsFromProperty 1 calls
13 ms Move 1 calls
20 ms GetReferenceAssemblyPaths 27 calls
28 ms GetFrameworkPath 190 calls
29 ms ValidateSilverlightFrameworkPaths 163 calls
72 ms AssignCulture 192 calls
75 ms ResolveKeySource 179 calls
79 ms MakeDir 200 calls
95 ms CreateProperty 260 calls
100 ms CreateCSharpManifestResourceName 122 calls
102 ms Delete 442 calls
112 ms GenerateResource 3 calls
123 ms CopyFilesToFolders 1 calls
177 ms ReadLinesFromFile 190 calls
179 ms CreateHtmlTestPage 31 calls
181 ms CallTarget 190 calls
184 ms GetSilverlightFrameworkPath 163 calls
211 ms Message 573 calls
319 ms CreateSilverlightAppManifest 97 calls
354 ms FileClassifier 119 calls
745 ms ConvertToAbsolutePath 190 calls
868 ms PackagePlatformExtensions 94 calls
932 ms AssignProjectConfiguration 190 calls
1625 ms CategorizeSilverlightReferences 163 calls
1722 ms ResourcesGenerator 60 calls
2467 ms WriteLinesToFile 118 calls
5589 ms RemoveDuplicates 380 calls
8207 ms FindUnderPath 950 calls
17720 ms XapPackager 97 calls
38162 ms Copy 857 calls
38934 ms CompileXaml 119 calls
40567 ms Exec 14 calls
55275 ms ValidateXaml 119 calls
65845 ms AssignTargetPath 1140 calls
83792 ms Csc 108 calls
105906 ms ResolveAssemblyReference 190 calls
1163988 ms MSBuild 471 calls
msbuild クレームTime Elapsed 00:08:39.44
罰金。ここで、同じコマンド ラインを再度実行すると、次の結果が得られます。
Task Performance Summary:
1 ms GetSilverlightItemsFromProperty 1 calls
11 ms WriteLinesToFile 1 calls
17 ms GetReferenceAssemblyPaths 27 calls
24 ms GetFrameworkPath 190 calls
32 ms ValidateSilverlightFrameworkPaths 163 calls
43 ms CopyFilesToFolders 1 calls
47 ms GenerateResource 3 calls
60 ms ResolveKeySource 179 calls
66 ms MakeDir 200 calls
69 ms AssignCulture 192 calls
70 ms PackagePlatformExtensions 94 calls
76 ms Delete 432 calls
89 ms CreateProperty 260 calls
98 ms CreateCSharpManifestResourceName 122 calls
136 ms GetSilverlightFrameworkPath 163 calls
156 ms CallTarget 190 calls
182 ms CreateHtmlTestPage 31 calls
207 ms XapPackager 97 calls
215 ms ReadLinesFromFile 190 calls
217 ms Message 573 calls
271 ms CreateSilverlightAppManifest 97 calls
350 ms FileClassifier 119 calls
526 ms ConvertToAbsolutePath 190 calls
795 ms AssignProjectConfiguration 190 calls
1658 ms CategorizeSilverlightReferences 163 calls
2237 ms Exec 2 calls
5703 ms RemoveDuplicates 380 calls
6948 ms Copy 426 calls
7550 ms FindUnderPath 950 calls
17126 ms CompileXaml 119 calls
54495 ms ValidateXaml 119 calls
78953 ms AssignTargetPath 1140 calls
97374 ms ResolveAssemblyReference 190 calls
603295 ms MSBuild 471 calls
msbuild クレームTime Elapsed 00:05:25.70
。
これにより、次の疑問が生じます。
ResolveAssemblyReference
2 番目のビルドに時間がかかるのはなぜですか? 最初のビルドで作成されたキャッシュ ファイルはすべて残っています。何も変わっていません。97 秒と 106 秒というように、以前とほぼ同じ時間がかかっているのはなぜでしょうか。- なぜ
ValidateXaml
とCompileXaml
はまったく実行されているのですか? フルビルドから何も変わっていないということです!
今度は同じ実験を繰り返しますが、今回はdevenv
ではなくコマンド ラインでビルドしmsbuild
ます。msbuild と同様に、並列ビルドは使用されず、ログ レベルは diag です。
devenv
は最後にこのような素晴らしい要約を提示しないため、プロジェクトごとの要約から手動で集計する必要があります。
結果は私を驚かせました。次の PowerShell スクリプトを使用して、経過時間を集計しました。
Param(
[Parameter(Mandatory=$True,Position=1)][string]$log
)
[timespan]::FromMilliseconds((sls -SimpleMatch "Time Elapsed" $log |% {[timespan]::Parse(($_ -split ' ')[2]) } | measure -Sum TotalMilliseconds).Sum).ToString()
devenv
コマンド ラインでまったく同じ立場からまったく同じソリューションをビルドするには00:06:10.9000000
、最初のビルドと00:00:03.1000000
2 番目のビルドが必要でした。わずか3秒です!!!.
統計を集計するための powershell スクリプトも作成しました。
Param(
[Parameter(Mandatory=$True,Position=1)][string]$log
)
$summary=@{}
cat $log |% {
if ($collect) {
if ($_ -eq "") {
$collect = $false;
} else {
$tmp = ($_ -replace '\s+', ' ') -split ' ';
$cur = $summary[$tmp[3]];
if (!$cur) {
$cur = @(0, 0);
$summary[$tmp[3]] = $cur;
}
$cur[0] += $tmp[1];
$cur[1] += $tmp[4];
}
} else {
$collect = $_ -eq "Task Performance Summary:"
}
}
$summary.Keys |% {
$stats = $summary[$_];
$ms = $stats[0];
$calls = $stats[1];
[string]::Format("{0,10} ms {1,-40} {2} calls", $ms,$_,$calls);
} | sort
最初の (完全な) ビルドのログで実行すると、次の出力が生成されます。
5 ms ValidateSilverlightFrameworkPaths 82 calls
7 ms Move 1 calls
9 ms GetFrameworkPath 108 calls
11 ms GetReferenceAssemblyPaths 26 calls
14 ms AssignCulture 109 calls
16 ms ReadLinesFromFile 108 calls
18 ms CreateCSharpManifestResourceName 61 calls
18 ms ResolveKeySource 97 calls
23 ms Delete 268 calls
26 ms CreateProperty 131 calls
41 ms MakeDir 118 calls
66 ms CallTarget 108 calls
70 ms Message 326 calls
75 ms ResolveNonMSBuildProjectOutput 104 calls
101 ms GenerateResource 1 calls
107 ms GetSilverlightFrameworkPath 82 calls
118 ms CreateHtmlTestPage 16 calls
153 ms FileClassifier 60 calls
170 ms CreateSilverlightAppManifest 49 calls
175 ms AssignProjectConfiguration 108 calls
279 ms ConvertToAbsolutePath 108 calls
891 ms CategorizeSilverlightReferences 82 calls
926 ms PackagePlatformExtensions 47 calls
1291 ms ResourcesGenerator 60 calls
2193 ms WriteLinesToFile 108 calls
3687 ms RemoveDuplicates 216 calls
5538 ms FindUnderPath 540 calls
6157 ms MSBuild 294 calls
16496 ms Exec 4 calls
19699 ms XapPackager 49 calls
21281 ms Copy 378 calls
28362 ms ValidateXaml 60 calls
29526 ms CompileXaml 60 calls
66846 ms AssignTargetPath 654 calls
81650 ms Csc 108 calls
82759 ms ResolveAssemblyReference 108 calls
さて、2 番目のビルドの結果は次のとおりです。
1 ms AssignCulture 1 calls
1 ms CreateProperty 1 calls
1 ms Delete 2 calls
1 ms ValidateSilverlightFrameworkPaths 1 calls
3 ms AssignTargetPath 6 calls
3 ms ConvertToAbsolutePath 1 calls
3 ms PackagePlatformExtensions 1 calls
3 ms ReadLinesFromFile 1 calls
3 ms ResolveKeySource 1 calls
4 ms ResolveNonMSBuildProjectOutput 1 calls
5 ms CreateCSharpManifestResourceName 1 calls
5 ms GetFrameworkPath 1 calls
10 ms CategorizeSilverlightReferences 1 calls
11 ms CallTarget 1 calls
11 ms FileClassifier 1 calls
11 ms FindUnderPath 5 calls
11 ms MakeDir 1 calls
13 ms Copy 2 calls
17 ms GetSilverlightFrameworkPath 1 calls
17 ms RemoveDuplicates 2 calls
30 ms AssignProjectConfiguration 1 calls
32 ms Message 25 calls
239 ms ResolveAssemblyReference 1 calls
351 ms MSBuild 2 calls
687 ms CompileXaml 1 calls
1413 ms ValidateXaml 1 calls
ここでは、まったく同じソリューションについて話しています。
最後に、ソリューションで構築するために使用したスクリプトを次に示します。
msbuild:
@setlocal
set SHELFSET=msbuild
set MSBUILDLOGVERBOSERARSEARCHRESULTS=true
set AppConfig=app.config
set Disable_CopyWebApplication=true
set MvcBuildViews=false
call \tmp\undo.cmd
del /a:-R /s/q *.*
tf unshelve %SHELFSET% /recursive /noprompt
msbuild DataSvc.sln
msbuild Main.sln /v:diag > \tmp\00.Main.msbuild.full.log
msbuild Main.sln /v:diag > \tmp\01.Main.msbuild.incr.log
msbuild Main.sln /v:diag > \tmp\02.Main.msbuild.incr.log
@endlocal
開発者:
@setlocal
set SHELFSET=msbuild
set MSBUILDLOGVERBOSERARSEARCHRESULTS=true
set AppConfig=app.config
set Disable_CopyWebApplication=true
set MvcBuildViews=false
call \tmp\undo.cmd
del /a:-R /s/q *.*
tf unshelve %SHELFSET% /recursive /noprompt
msbuild DataSvc.sln
devenv Main.sln /build > \tmp\00.Main.devenv.full.log
devenv Main.sln /build > \tmp\01.Main.devenv.incr.log
devenv Main.sln /build > \tmp\02.Main.devenv.incr.log
@endlocal
私のテストでmsbuild
は、これはがらくたの一部であり、コマンド ラインで使用して C# ソリューションを構築するべきではないことがわかりました。https://connect.microsoft.com/VisualStudio/feedback/details/586358/msbuild-ignores-projectsection-projectdependencies-in-sln-file-and-attempts-to-build-projects-in-wrong-orderがこれに追加されますフィーリング。
しかし、結局のところ、私は間違っている可能性があり、単純な微調整により、2番目のビルドで msbuild がそのまま効率的になるでしょうdevenv
。
2番目のビルドでmsbuildを正常に動作させる方法はありますか?
編集1
このCompileXaml
タスクは、 C:\Program Files (x86)\MSBuild\Microsoft\Silverlight\v5.0\Microsoft.Silverlight.Common.targetsMarkupCompilePass1
にあるターゲットの一部です。
<Target Name="MarkupCompilePass1"
DependsOnTargets="$(CompileXamlDependsOn)"
Condition="'@(Page)@(ApplicationDefinition)' != '' " >
<CompileXaml
LanguageSourceExtension="$(DefaultLanguageSourceExtension)"
Language="$(Language)"
SilverlightPages="@(Page)"
SilverlightApplications="@(ApplicationDefinition)"
ProjectPath="$(MSBuildProjectFullPath)"
RootNamespace="$(RootNamespace)"
AssemblyName="$(AssemblyName)"
OutputPath="$(IntermediateOutputPath)"
SkipLoadingAssembliesInXamlCompiler="$(SkipLoadingAssembliesInXamlCompiler)"
TargetFrameworkDirectory="$(TargetFrameworkDirectory)"
TargetFrameworkSDKDirectory="$(TargetFrameworkSDKDirectory)"
ReferenceAssemblies ="@(ReferencePath);@(InferredReference->'$(TargetFrameworkDirectory)\%(Identity)')"
>
<Output ItemName="Compile" TaskParameter="GeneratedCodeFiles" />
<!-- Add to the list list of files written. It is used in Microsoft.Common.Targets to clean up
for a next clean build
-->
<Output ItemName="FileWrites" TaskParameter="GeneratedCodeFiles" />
<Output ItemName="_GeneratedCodeFiles" TaskParameter="GeneratedCodeFiles" />
</CompileXaml>
<Message Text="(Out) GeneratedCodeFiles: '@(_GeneratedCodeFiles)'" Condition="'$(MSBuildTargetsVerbose)'=='true'"/>
</Target>
ご覧のとおり、入力も出力もありません。
次に、2 番目のビルドの diag msbuild ログには、「再構築」などの疑わしい単語は含まれていません。
最後に、msbuild と devenv の両方がまったく同じ状況で実行され、マルチスレッド ビルドを使用していないことに注意してください。それでも、違いはひどいです - 5 分以上 (msbuild) 対 3 秒 (devenv、コマンド ライン)。
私にはまだ完全な謎です。
編集2
devenvビルドがどのように機能するかについての詳細がわかりました。ヒューリスティックを使用して、現在のプロジェクトを最初に msbuild に引き渡す必要があるかどうかを判断します。DisableFastUpToDateCheck
このヒューリスティックは既定で有効になっていますが、 msbuild プロパティを に設定することで無効にすることができますtrue
。
現在、コマンド ライン devenv build が msbuild を実行する必要があるかどうかを判断するには、実際には 3 秒以上かかります。全体として、私のようなソリューションでは、msbuild に何も渡す必要がないと判断するのに 20 秒または 30 秒かかる場合があります。
このヒューリスティックが、この大きな時間差の唯一の理由です。Visual Studio チームは、標準のビルド スクリプト (MarkupCompilePass1 のようなタスクが入力と出力によって駆動されない) の品質の低さを認識し、最初に msbuild をスキップする方法を考え出すことにしたと思います。
しかし、問題があります。ヒューリスティックは csproj ファイルのみを検査し、インポートされたターゲット ファイルは検査されません。さらに、他の TypeScript ファイルから参照される TypeScript ファイルのように、暗黙的な依存関係については何も知りません。そのため、TypeScript ファイルが別のプロジェクトに属し、プロジェクト ファイルから明示的にリンクされていない他の TypeScript ファイルを参照している場合、ヒューリスティックはそれらについて認識しないため、DisableFastUpToDateCheck = true
. ビルドは遅くなりますが、少なくとも正しくはなります。
結論-msbuildを修正する方法がわかりません。明らかにdevenvの人も修正できません。それが彼らがヒューリスティックを発明した理由のようです。