dotnet / msbuild

The Microsoft Build Engine (MSBuild) is the build platform for .NET and Visual Studio.
https://docs.microsoft.com/visualstudio/msbuild/msbuild
MIT License
5.15k stars 1.34k forks source link

Item operation perf in _GetCopyToOutputDirectoryItemsFromTransitiveProjectReferences #10104

Open KirillOsenkov opened 3 weeks ago

KirillOsenkov commented 3 weeks ago

I have an unconfirmed hunch that we can speed up some item operations in the _GetCopyToOutputDirectoryItemsFromTransitiveProjectReferences target.

Consider for example this snippet: https://github.com/dotnet/msbuild/blob/dbf8d12deab2aee74f1bd574c1ecad2b39f552b4/src/Tasks/Microsoft.Common.CurrentVersion.targets#L5108-L5109

My understanding is that this can be collapsed in a single line with the condition Always or PreserveNewest.

This pattern is used quite a lot: https://github.com/dotnet/msbuild/blob/dbf8d12deab2aee74f1bd574c1ecad2b39f552b4/src/Tasks/Microsoft.Common.CurrentVersion.targets#L5108-L5178

I'd be curious to measure the performance of a reasonable large build with a lot of project references and copy to output, and see if we can see any difference. You can use this query in the binlog viewer: $target GetCopyToOutputDirectoryItems $time

KirillOsenkov commented 3 weeks ago

On a large build with 900 projects this target consumes 28 minutes cumulatively (wall clock time is less because the build is massively parallel)

For the largest project it takes 15 seconds: image

KirillOsenkov commented 3 weeks ago

Is AssignTargetPath called twice for @(_CompileItemsToCopy)? Once for transitive, once for this project? I suppose it's benign and idempotent?

KirillOsenkov commented 3 weeks ago

To clarify, out of 15 seconds for that target, 12 seconds is calling the MSBuild task on the project references, so the remaining 3.3 seconds must be the overhead, perhaps partially attributable to item manipulation: image

I might be totally wrong though, need to measure, and perhaps in a tight loop in a unit-test using BenchmarkDotNet.