Skip to content

Cache SDK resolver data process-wide#9335

Merged
rainersigwald merged 5 commits into
dotnet:mainfrom
ladipro:9302-static-resolver-loader
Nov 1, 2023
Merged

Cache SDK resolver data process-wide#9335
rainersigwald merged 5 commits into
dotnet:mainfrom
ladipro:9302-static-resolver-loader

Conversation

@ladipro

@ladipro ladipro commented Oct 17, 2023

Copy link
Copy Markdown
Member

Fixes #9302

Context

Unless a shared evaluation context is used, each project evaluation creates a fresh new SdkResolverLoader, walks the disk to discover resolver manifests, calls Assembly.LoadFrom on resolver assemblies and so on. The cost of this is non-trivial (close to 3% of total evaluation time for solution like OrchardCore) while arguably there is no way how any of this can change while an MSBuild process is running.

Changes Made

  • Refactored the code to clean up unused parameters and changed several types from IList<> to IReadOnlyList<> to codify the fact that they are immutable.
  • Introduced CachingSdkResolverLoader as a subclass of SdkResolverLoader and plugged it into SdkResolverService.

Testing

Existing unit test and an experimental VS insertion.

Notes

Consider reviewing commit by commit.

@JanKrivanek JanKrivanek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good.

I love the extra diligence in ensuring the encapsulation!!

Comment thread src/Build/BackEnd/Components/SdkResolution/CachingSdkResolverLoader.cs Outdated
@ladipro ladipro force-pushed the 9302-static-resolver-loader branch from 9aedf0f to d845e21 Compare October 30, 2023 10:46
@rainersigwald rainersigwald merged commit 34ae4f3 into dotnet:main Nov 1, 2023
MattKotsenas added a commit to MattKotsenas/reproducible-builds that referenced this pull request Jun 4, 2026
Upstream investigation across dotnet/sdk, dotnet/msbuild, microsoft/MSBuildLocator
identified the actual mechanism for issue dotnet#79 and one real correctness fix:

1. The reason ValidateGlobalJsonSdkVersion's [DllImport("hostfxr")] fails
   inside VS 2022+'s IDE-hosted MSBuild (but not in dotnet build or
   command-line MSBuild.exe) is that VS's in-process MSBuild engine is
   compiled for .NET (8/9/10) and its SdkResolverService.ResolveSdk()
   contains a #if NET fast path (enabled by ChangeWave 17.10, see
   PR dotnet/msbuild#9335) that resolves in-box SDKs like Microsoft.NET.Sdk
   directly from MSBuildSDKsPath via DefaultSdkResolver. The plugin chain
   never runs, Microsoft.DotNet.MSBuildSdkResolver is never loaded, and the
   Interop.PreloadWindowsLibrary("hostfxr") call that would otherwise
   bring hostfxr into the process never fires. Standalone netfx MSBuild.exe
   lacks the fast path (it's net472), so its plugin chain runs and the
   preload happens; that's why CLI succeeds and VS IDE fails.

   Update HostFxrResolver's class remarks and the netfx-branch comment to
   describe this mechanism accurately rather than the previous
   "safety net" hand-wave.

2. Switch from LoadLibraryW to LoadLibraryExW with
   LOAD_WITH_ALTERED_SEARCH_PATH (0x8). hostfxr.dll has its own transitive
   dependencies (e.g. hostpolicy.dll in the same versioned directory).
   LOAD_WITH_ALTERED_SEARCH_PATH makes the loader resolve those deps from
   hostfxr's directory rather than the process default DLL search path -
   matching what Microsoft.DotNet.NativeWrapper.Interop does for the same
   reason.

3. Document the StubTaskHarness x64 coverage gaps in the csproj comment
   (VS 2019 x86 MSBuild, MSBuildTaskHost.exe, arm64-native MSBuild on
   arm64 Windows). All are acceptable misses for a regression-prevention
   test focused on the issue dotnet#79 scenario.
MattKotsenas added a commit to MattKotsenas/reproducible-builds that referenced this pull request Jun 4, 2026
Upstream investigation across dotnet/sdk, dotnet/msbuild, microsoft/MSBuildLocator
identified the actual mechanism for issue dotnet#79 and one real correctness fix:

1. The reason ValidateGlobalJsonSdkVersion's [DllImport("hostfxr")] fails
   inside VS 2022+'s IDE-hosted MSBuild (but not in dotnet build or
   command-line MSBuild.exe) is that VS's in-process MSBuild engine is
   compiled for .NET (8/9/10) and its SdkResolverService.ResolveSdk()
   contains a #if NET fast path (enabled by ChangeWave 17.10, see
   PR dotnet/msbuild#9335) that resolves in-box SDKs like Microsoft.NET.Sdk
   directly from MSBuildSDKsPath via DefaultSdkResolver. The plugin chain
   never runs, Microsoft.DotNet.MSBuildSdkResolver is never loaded, and the
   Interop.PreloadWindowsLibrary("hostfxr") call that would otherwise
   bring hostfxr into the process never fires. Standalone netfx MSBuild.exe
   lacks the fast path (it's net472), so its plugin chain runs and the
   preload happens; that's why CLI succeeds and VS IDE fails.

   Update HostFxrResolver's class remarks and the netfx-branch comment to
   describe this mechanism accurately rather than the previous
   "safety net" hand-wave.

2. Switch from LoadLibraryW to LoadLibraryExW with
   LOAD_WITH_ALTERED_SEARCH_PATH (0x8). hostfxr.dll has its own transitive
   dependencies (e.g. hostpolicy.dll in the same versioned directory).
   LOAD_WITH_ALTERED_SEARCH_PATH makes the loader resolve those deps from
   hostfxr's directory rather than the process default DLL search path -
   matching what Microsoft.DotNet.NativeWrapper.Interop does for the same
   reason.

3. Document the StubTaskHarness x64 coverage gaps in the csproj comment
   (VS 2019 x86 MSBuild, MSBuildTaskHost.exe, arm64-native MSBuild on
   arm64 Windows). All are acceptable misses for a regression-prevention
   test focused on the issue dotnet#79 scenario.
MattKotsenas added a commit to MattKotsenas/reproducible-builds that referenced this pull request Jun 12, 2026
Upstream investigation across dotnet/sdk, dotnet/msbuild, microsoft/MSBuildLocator
identified the actual mechanism for issue dotnet#79 and one real correctness fix:

1. The reason ValidateGlobalJsonSdkVersion's [DllImport("hostfxr")] fails
   inside VS 2022+'s IDE-hosted MSBuild (but not in dotnet build or
   command-line MSBuild.exe) is that VS's in-process MSBuild engine is
   compiled for .NET (8/9/10) and its SdkResolverService.ResolveSdk()
   contains a #if NET fast path (enabled by ChangeWave 17.10, see
   PR dotnet/msbuild#9335) that resolves in-box SDKs like Microsoft.NET.Sdk
   directly from MSBuildSDKsPath via DefaultSdkResolver. The plugin chain
   never runs, Microsoft.DotNet.MSBuildSdkResolver is never loaded, and the
   Interop.PreloadWindowsLibrary("hostfxr") call that would otherwise
   bring hostfxr into the process never fires. Standalone netfx MSBuild.exe
   lacks the fast path (it's net472), so its plugin chain runs and the
   preload happens; that's why CLI succeeds and VS IDE fails.

   Update HostFxrResolver's class remarks and the netfx-branch comment to
   describe this mechanism accurately rather than the previous
   "safety net" hand-wave.

2. Switch from LoadLibraryW to LoadLibraryExW with
   LOAD_WITH_ALTERED_SEARCH_PATH (0x8). hostfxr.dll has its own transitive
   dependencies (e.g. hostpolicy.dll in the same versioned directory).
   LOAD_WITH_ALTERED_SEARCH_PATH makes the loader resolve those deps from
   hostfxr's directory rather than the process default DLL search path -
   matching what Microsoft.DotNet.NativeWrapper.Interop does for the same
   reason.

3. Document the StubTaskHarness x64 coverage gaps in the csproj comment
   (VS 2019 x86 MSBuild, MSBuildTaskHost.exe, arm64-native MSBuild on
   arm64 Windows). All are acceptable misses for a regression-prevention
   test focused on the issue dotnet#79 scenario.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Performance]: 2.7% of evaluation is spent initializing SDK resolvers

4 participants