-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Support for multi-folder composite builds #37130
Conversation
src/coreclr/src/vm/nativeimage.cpp
Outdated
LoaderAllocator *pLoaderAllocator, | ||
AllocMemTracker *pamTracker) | ||
AssemblyLoadContext *pAssemblyLoadContext, | ||
LoaderAllocator *pLoaderAllocator) | ||
{ | ||
STANDARD_VM_CONTRACT; | ||
|
||
NewHolder<PEImageLayout> peLoadedImage = PEImageLayout::LoadNative(fullPath); |
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.
Does this work on non-Windows? I do not think that PEImageLayout::LoadNative
will keep returning the same address for multiple loads of the same image.
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.
Resolved in 2nd commit by changing the dictionary to be keyed by simple file names of the composite images. This is also related to DavidWr's feedback - I have reverted encoding the reverse relative paths in the OwnerCompositeExecutable R2R sections of the component assemblies and instead I added a new COMPlus variable NativeImageSearchPaths
to let the runtime locate the native image in an arbitrary location (in addition to the containing folder of the MSIL assembly).
src/coreclr/src/vm/nativeimage.cpp
Outdated
return image.Extract(); | ||
NewHolder<NativeImage> image = new NativeImage(pAssemblyLoadContext, peLoadedImage.Extract(), nativeImageFileName); | ||
image->Initialize(pHeader, pLoaderAllocator); | ||
pExistingImage = AppDomain::GetCurrentDomain()->SetNativeImage(peLoadedImage->GetBase(), image); |
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.
The regular R2R images use READYTORUN_HELPER_Module
fixup to claim the image. Look for AcquireImage
in readytoruninfo.cpp. Would be make sense to use the same scheme here instead of maintaining a hashtable?
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.
Hmm, I'm afraid that, according to your other comment, we still need some table on Linux to cater for the fact that repeated dll loads allocate separate instances. In combination with David's suggestion it seems to me that it would be easiest to keep the dictionary in AppDomain (to let us detect and fail fast upon an attempt to load the composite DLL into a secondary ALC on Linux), just key it off the composite image simple name and apply the path search when opening the dll for the first time.
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.
Resolved in 2nd commit as described in my other comment.
Do we have confirmation that the relative paths are really required to make the workload work? |
Based on our recent discussions with partner teams I got the impression that, at least short-term, it can speed up our related perf and other work if we allow the folder hierarchy. Thanks for pointing out the Module fixup in AcquireImage, that's a great idea, I'll do that. I'll look into the executable image unification on Unix, thanks for pointing that out. |
I think we should do something much simpler. Add a COMPLUS variable that provides a path to look up native image files. So the workflow would go from:
to
This has the side benefit of if we tweak native image generation to generate a naturally 2MB aligned PE we could load the native image using huge pages out of the hugetlbfs on Linux. |
Thanks @davidwrighton, that sounds reasonable; I can easily modify the change as you suggest. For the remaining problem regarding multiple ALC's, that would be probably solvable via AcquireImage as @jkotas proposed. |
In my opinion multiple ALCs simply should not be supported from one composite image file. That should be a hard fail, and we should not attempt to make it work. |
One other aspect is the Crossgen2 part of the change - the |
There is side-data in each package folder. It seems to all be loaded by the main engine and looked up by string and resolved centrally. I suspect with the current implementation we'll need this ability to generate the composite-finder DLLs into a matching output tree. |
c073ba7
to
c7f2119
Compare
@davidwrighton - I believe I have addressed the gist of your feedback by reverting the relative path manipulation related to locating the composite image and by introducing a new |
@@ -270,6 +274,8 @@ public sealed class ReadyToRunCodegenCompilation : Compilation | |||
_dependencyGraph.AddRoot(new Import(NodeFactory.EagerImports, instructionSetSupportSig), "Baseline instruction set support"); | |||
} | |||
|
|||
private readonly static string s_folderUpPrefix = ".." + Path.DirectorySeparatorChar; |
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.
Will this work on Unix? Should we choose DirectorySeparatorChar
based on our target platform?
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.
Actually, I think you can disregard this. I was thinking about the relative path we embed in the non-composite images.
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.
The Path.DirectorySeparatorChar is platform specific.
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.
Yeah I was more worried about future cross-platform bug if we generate a Linux composite image from Windows. The change description indicates we burn relative paths in the component assemblies to the composite image but it seems like we actually just put the composite image name and look it up by simple name instead. If that's the case, then we should be fine.
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.
The first version of my change used reverse relative paths to go from the component assemblies to the composite native image. Based on David's PR feedback I simplified this logic by throwing away the relative path magic and just allowing explicit composite image path lookup which seems more general for additional reasons.
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.
Sigh, that's what I get for reviewing in a 2 day old browser tab 🙄 Thanks for clarifying
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.
LGTM 👍
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.
With the move to the search path approach for finding the composite image, I think this change is good. I'm not entirely convinced that the approach of building a directory tree of output is great, but I'm willing to see if it improves the workflow.
src/coreclr/src/vm/nativeimage.cpp
Outdated
m_pImageLayout = pImageLayout; | ||
m_fileName = imageFileName; | ||
m_eagerFixupsHaveRun = false; | ||
} | ||
|
||
void NativeImage::Initialize(READYTORUN_HEADER *pHeader, LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker) | ||
void NativeImage::Initialize(READYTORUN_HEADER *pHeader, LoaderAllocator *pLoaderAllocator) |
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.
Why did you remove the memory tracking here? It opens up a possibility for a memory leak if there are racing loads of the native image in NativeImage::Open
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.
Hmm, you're obviously right, I have apparently messed this up in my third refactoring of the native image dictionary during the review, will fix.
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.
Fixed in 5th commit.
0561844
to
e48f27e
Compare
2af8710
to
aeb2004
Compare
d74f7c0
to
938511b
Compare
This change add support for Crossgen2 input files residing in a directory structure; the same directory structure gets created under the output path and the output composite image sits at its root. The component R2R headers store reverse relative paths to the composite image, e.g. for a composite image "composite-r2r.dll" the standalone MSIL file FolderA/FolderA.dll would have "../composite-r2r.dll" in the OwnerCompositeImage section of its R2R header. To avoid having to implement homebrew path normalization and to make the entire mechanism more robust, I dropped the composite image name comparisons. Instead we just construct the path and open the composite image. In case of multiple components the OS guarantees unification of the openend DLL's on the same path. We can also use this mechanism for AssemblyLoadContext checking. When we load a component assembly into two different ALC's, for the second ALC (along the execution timeline) we cannot reuse the composite image that got loaded into the first ALC and we cannot load it a second time so we just pretend there's no image. Manish suggested we might want some diagnostics that would crash instead of silently returning null when loading a component assembly into a second ALC with the rationale that using composite images is a conscious decision and we don't want to lose perf just because we accidentally loaded an assembly into two different ALC's. I'll welcome thoughs on how to implement this - using a COMPlus variable, perhaps? Thanks Tomas
I have temporarily disabled the new multifolder test with an issue on Windows ARM64 to unblock merging this change and using it for X64 development. Thanks Tomas
f0549a5
to
92778e3
Compare
d909ca5
to
2949446
Compare
This change add support for Crossgen2 input files residing in a
directory structure; the same directory structure gets created
under the output path and the output composite image sits at its
root. The component R2R headers store reverse relative paths to
the composite image, e.g. for a composite image "composite-r2r.dll"
the standalone MSIL file FolderA/FolderA.dll would have
"../composite-r2r.dll" in the OwnerCompositeImage section of its
R2R header.
To avoid having to implement homebrew path normalization and to
make the entire mechanism more robust, I dropped the composite image
name comparisons. Instead we just construct the path and open the
composite image. In case of multiple components the OS guarantees
unification of the openend DLL's on the same path.
We can also use this mechanism for AssemblyLoadContext checking.
When we load a component assembly into two different ALC's, for the
second ALC (along the execution timeline) we cannot reuse the
composite image that got loaded into the first ALC and we cannot
load it a second time so we just pretend there's no image.
Manish suggested we might want some diagnostics that would crash
instead of silently returning null when loading a component
assembly into a second ALC with the rationale that using composite
images is a conscious decision and we don't want to lose perf
just because we accidentally loaded an assembly into two different
ALC's. I'll welcome thoughs on how to implement this - using
a COMPlus variable, perhaps?
Thanks
Tomas
/cc: @dotnet/crossgen-contrib