-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change Load Behavior for NuGet Components #6126
Conversation
…o avoid multiple loads of the same assembly in different contexts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
Thanks for the review @Forgind. Can you merge at your convenience? I don't appear to have permissions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
This introduced regressions in our build tasks. This is sharing NuGet unconditionally regardless of version which will pin NuGet to the version in the SDK. It’s also causing problems because NewtonsoftJson is not pinned but is a dependency of NuGet. This causes Newtonsoft to load in the tasks ALC and it doesn’t unify with the one used by NuGet even if the same version. This results in Type loading issues (method not found in the case we saw). Why not instead remove NuGet from the tasks which are carrying extra copies to achieve this perf improvement? That will have the added benefit of reducing disk footprint, assuming this unification is indeed legitimate. Barring that it seems like some additional work needs to be done to selectively reuse based on some version rules and ensure the entire assembly closure will be reused, including Newtonsoft.Json. |
Fixes #6289 Context #6126 changes the way that NuGet.Frameworks.dll is loaded to be used by NuGetFrameworkWrapper from a call to LoadFile to a call to LoadFrom. The path passed in to the Load*` call is the same in both cases, but the behavior is different when running on .NET Framework. On .NET Framework, calling LoadFile results in IJW loading rules being followed, and a different copy of NuGet.Frameworks.dll than the one requested being loaded. It also appears to match the one loaded into the LOAD context. Calling LoadFrom results in the specified assembly being loaded, but it doesn't match the copy of the assembly loaded into the LOAD context. Thus, it remains in the LOADFROM context instead of being promoted to the LOAD context. Later on, there is a collision between code from the two instances of NuGet.Frameworks.dll that are loaded into the LOAD context and the LOADFROM context, and this is where the MissingMethodException is thrown. Note, this does not happen on the .NET Core bootstrap build because the loader behavior is significantly simpler. Changes Made Choose the Load* API based on the target framework at build time. On .NET Core, use LoadFrom and on .NET Framework, use LoadFile. This type of precedent already exists in MSBuild where there is different load behavior for .NET Framework and .NET Core.
Contributes to dotnet/sdk#15558.
Context
In a single MSBuild process, most of the NuGet binaries are being loaded into more than one
AssemblyLoadContext
. The ready to run code associated with these binaries can only be used in a singleAssemblyLoadContext
, which means that usage of the assembly in all other contexts must be jitted. This shows up as an extra 736 methods that get jitted (measured as 147.6ms).Changes Made
NuGetFrameworkWrapper
to useAssembly.LoadFrom
instead ofAssembly.LoadFile
when using reflection to load NuGet.Frameworks.dll. This ensures that the assembly is not loaded into the NULL loader context, and unifies the load with later loads of the same assembly.MSBuildLoadContext
, forcing them to all be loaded in the defaultAssemblyLoadContext
. This is an important part of the fix because these loads occur after the load that occurs inNuGetFrameworkWrapper
, and so by MSBuild taking a dependency on these NuGet assemblies, they should be treated as part of MSBuild itself, and not as part of a task using the task loader behavior.Testing
AssemblyLoadContexts
.Notes
This PR contains the following performance wins:
cc: @stephentoub, @DamianEdwards, @marcpopMSFT