15

私は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

これにより、次の疑問が生じます。

  1. ResolveAssemblyReference2 番目のビルドに時間がかかるのはなぜですか? 最初のビルドで作成されたキャッシュ ファイルはすべて残っています。何も変わっていません。97 秒と 106 秒というように、以前とほぼ同じ時間がかかっているのはなぜでしょうか。
  2. なぜValidateXamlCompileXamlはまったく実行されているのですか? フルビルドから何も変わっていないということです!

今度は同じ実験を繰り返しますが、今回は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.10000002 番目のビルドが必要でした。わずか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の人も修正できません。それが彼らがヒューリスティックを発明した理由のようです。

4

1 に答える 1

4

まず、生成している診断ログを見てください。実際には、最初にコンソール オペレータではなくファイル ロガーを使用して、コンソール出力をログにパイプし、次にそれらのログを見てみましょう。

実際には、代わりに/v:diag >msbuild.logこれを使用します。

/v:min /fl3 /flp3:warningsonly;logfile=msbuild.wrn /fl4 /flp4:errorsOnly;logfile=msbuild.err /fl5 /flp5:Verbosity=diag;logfile=msbuild.log

これで、コンソール バッファーはあなたに感謝します。開発者も、デバッグ用にエラーのみのログと警告のみのログを別々に保持するための先見の明を含めて感謝しています。

次に、その診断 MsBuild ログを調べ、実行に時間がかかっているターゲットについて CTRL+F を調べます。何も変更されていないにもかかわらず、ターゲットが再び実行されていることを示す言葉遣いが表示されますか? ビルドをスキップするには、ターゲットに入力と出力が定義されている必要があります。入力 (.cs) が出力 (.dll、.pdb) よりも新しい場合、何かが変更されたに違いないことを認識し、新しいビルドをトリガーします。

これらの CompileXaml ターゲットは WinFx ターゲットにあり、入力と出力が定義されていると思われます。これらの長期実行ケースの出力テキストを見つけて、エラーが原因で再構築されたかどうかを判断できますか? 「Y が見つからなかったので、X を完全に再構築する」ということですか?

最後に、コマンド ラインからビルドを高速化するための楽しいトリックを紹介します。

msbuild.exe /m

これにより、複数のスレッドにわたってプロジェクトが個別にビルドされます。

于 2014-03-28T20:55:12.300 に答える