-
Notifications
You must be signed in to change notification settings - Fork 561
[coreclr] Initial support of R2R builds #10007
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
Conversation
|
I think I'd rather wait with merging this PR until marshal methods can be used again. Maybe it's a simple matter of ordering? R2R should be called, like Mono AOT, after we're done modifying the assemblies. |
|
|
||
| proj.SetProperty ("RuntimeIdentifier", rid); | ||
| proj.SetProperty ("UseMonoRuntime", "false"); // Enables CoreCLR | ||
| proj.SetProperty ("_IsPublishing", "true"); // Make "dotnet build" act as "dotnet publish" |
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.
Can you update this line for when $(PublishReadyToRun) is true:
Line 34 in 747041d
| <_IsPublishing Condition=" '$(_IsPublishing)' == '' and '$(_AndroidRuntime)' == 'NativeAOT' ">true</_IsPublishing> |
I would expect dotnet build -c Release -p:PublishReadyToRun=true to work by default on Android.
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.
We could make this as a temporary workaround for R2R as well.
However, on the long run we shouldn't force dotnet build -c Release -p:PublishReadyToRun=true to do publishing as that would not match the behavior on other platforms.
On desktop and on iOS, triggering NativeAOT builds only happens with dotnet publish , dotnet build uses "regular" default runtimes/builds -> CoreCLR desktop, Mono iOS
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.
Just for reference, MAUI on Windows uses PublishReadyToRun=true by default on Release builds since .NET 7:
If R2R is what makes the startup performance reasonable, I think we have to use it by default on Android as well? So, dotnet build -c Release on Android would use R2R?
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.
That only applies if you are publishing your app through Visual Studio, or manually through dotnet publish.
dotnet build will not / should not run publish targets when PublishReadyToRun=true.
On a regular console app if you add PublishReadyToRun=true to the project and run dotnet build it will not run R2R AOT compiler. But if you run dotnet publish it will.
So to come back to my previous comment, we should not force _IsPublishing=true for NativeAOT nor R2R just so we would make
dotnet build -p:PublishAOT=true or
dotnet build -p:PublishReadyToRun=true to run/act like dotnet publish, it is against the behavior we have on other platforms (e.g., on macios we solved it with yet another property: https://github.com/dotnet/macios/blob/7cef45367161ca26d3a68e2e096639194ed4df1f/dotnet/targets/Xamarin.Shared.Sdk.props#L39)
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 difference is people can actually ship their app with dotnet build -c Release for Android today (and they are). There is currently no requirement to use dotnet publish on Android.
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.
As per our offline discussion, we will address this in the follow-up work of fully enabling R2R builds
| var b = CreateApkBuilder (); | ||
| Assert.IsTrue (b.Build (proj), "Build should have succeeded."); |
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.
In this test, is there a way to assert the .dll files inside the .apk has a ReadyToRun image inside? Is Mono.Cecil able to detect them? We already have some tests that use Cecil to assert build output.
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.
We could maybe just inspect whether assemblies from obj/Release/netX.0-android/android-X/R2R/ end up in the .apk? I am saying this because the SPC.dll we ship with CoreCLR already includes R2R image in it - even without enabling R2R builds at app build time.
I will look into it.
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.
It might be enough to compare the MVID of the obj/Release/netX.0-android/android-X/R2R/*.dll file and the one inside the .apk?
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.
Inspecting the .apk it self doesn't seem to be doable. As in CoreCLR builds we use assembly store and all assemblies get merged and converted into a single .so file that ends up in the .apk.
Do we have a way of reversing that process and extracting the .dll it self?
If not, we could potentially add a custom MSBuild target to the test project that simply prints out ResolvedUserAssemblies which go into GeneratePackageManagerJava, if those paths include obj/Release/netX.0-android/android-X/R2R/*.dll assemblies the test passes?
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.
For the test, I think you could use:
android/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs
Lines 281 to 290 in b54ec05
| var apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}-Signed.apk"); | |
| FileAssert.Exists (apk); | |
| var helper = new ArchiveAssemblyHelper (apk, useAssemblyStore); | |
| foreach (string abi in proj.GetRuntimeIdentifiersAsAbis ()) { | |
| Assert.IsTrue (helper.Exists ($"assemblies/{abi}/{assemblyName}.dll"), $"{assemblyName}.dll should exist in apk!"); | |
| using var stream = helper.ReadEntry ($"assemblies/{assemblyName}.dll"); | |
| stream.Position = 0; | |
| using var assembly = AssemblyDefinition.ReadAssembly (stream); |
Then assembly you could either check the MVID or some flag that says there is a R2R image.
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs
Show resolved
Hide resolved
@grendello I'm going to look into this, but I don't think we have to block this PR on it. We will need to split up parts of |
…sts/BuildTest2.cs PR feedback Co-authored-by: Jonathan Peppers <jonathan.peppers@microsoft.com>
|
@ivanpovazan This probably isn't the place but any thoughts on the comments about Ready 2 run here https://docs.monogame.net/articles/getting_started/packaging_games.html?tabs=windows#readytorun-r2r. |
|
@dellis1972, yes definitely. The recommendations/concerns you linked are related to steady-state performance and we plan on investigating what is the right tuning of R2R, PGO and tiered JIT, to get best of both worlds - fast startup and steady-state performance on mobile apps. For example, current limitation of ReadyToRun is lacking the support of partial AOT mode - precompiling only startup code hot paths, which is something we are looking into. While this PR enables R2R builds in the SDK and preliminary measurements are showing promising results, I don't think we should make it a default just yet. |
| using var stream = helper.ReadEntry ($"assemblies/{assemblyName}.dll"); | ||
| stream.Position = 0; | ||
| using var peReader = new System.Reflection.PortableExecutable.PEReader (stream); | ||
| Assert.IsTrue (peReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory.Size > 0, |
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.
@jonathanpeppers I used the recommendation from: dotnet/runtime#38440 (comment) to verify that the app main assembly that gets packed into the .apk has R2R image in it.
|
Draft commit message: Context: https://learn.microsoft.com/en-us/dotnet/core/deploying/ready-to-run
Context: https://github.com/dotnet/runtime/blob/c83f92ad7aa7afa152b833a8e8f5ffe36a3287d1/docs/design/coreclr/botr/readytorun-overview.md
Context: https://github.com/dotnet/runtime/blob/main/docs/design/coreclr/botr/readytorun-format.md
Context: https://github.com/xamarin/monodroid/commit/f22cc208374637fea59bef7f849bff29247a3e5a
ReadyToRun (R2R) is a form of ahead-of-time compilation supported by
CoreCLR.It works by adding setting the `ManagedNativeHeader`
CLR header entrypoint to refer to a `READYTORUN_HEADER` section.
R2R is enabled by using `dotnet publish` alongside
`$(PublishReadyToRun)`=true:
dotnet new console
dotnet publish -p:PublishReadyToRun=true
You can see some of the changes that R2R produces by using
[`dumpbin /CLRHEADER`][0]. Consider the original assembly:
> dumpbin /clrheader .\obj\Release\net9.0\win-x64\*.dll
…
clr Header:
…
6000001 entry point token
…
0 [ 0] RVA [size] of ManagedNativeHeader Directory
vs. the R2R assembly:
> dumpbin /clrheader .\obj\Release\net9.0\win-x64\R2R\*.dll
…
clr Header:
…
6000001 entry point token
…
1548 [ 94] RVA [size] of ManagedNativeHeader Directory
The `ManagedNativeHeader` entry point having a non-zero RVA indicates
that the assembly contains native machine code, possibly R2R data.
Enable R2R usage with .NET for Android and CoreCLR, when
`$(UseMonoRuntime)`=false. Additionally, `$(RuntimeIdentifier)` must
be set, via `-r RID`:
dotnet new android
dotnet publish -p:PublishReadyToRun=true -p:UseMonoRuntime=false -r android-arm64
The resulting packaged assemblies will contain R2R data.
## Problem
Android app build runs two assembly post-processing build tasks which
modify assemblies using Mono.Cecil:
`<RewriteMarshalMethods/>` (86260ed36d) and
`<RemoveRegisterAttribute/>` (xamarin/monodroid@f22cc208).
Unfortunately, if this happens *after* R2R image generation, Cecil
will throw a NotSupportedException, as Cecil does not support
modifying assemblies which contain native data:
System.NotSupportedException: Writing mixed-mode assemblies is not supported
at Mono.Cecil.ModuleWriter.Write(ModuleDefinition module, Disposable`1 stream, WriterParameters parameters)
at Mono.Cecil.ModuleWriter.WriteModule(ModuleDefinition module, Disposable`1 stream, WriterParameters parameters)
at Mono.Cecil.ModuleDefinition.Write(Stream stream, WriterParameters parameters)
at Mono.Cecil.ModuleDefinition.Write(WriterParameters parameters)
at Mono.Cecil.ModuleDefinition.Write()
at Mono.Cecil.AssemblyDefinition.Write()
at Xamarin.Android.Tasks.RemoveRegisterAttribute.RunTask() in /Users/runner/work/1/s/xamarin-android/src/Xamarin.Android.Build.Tasks/Tasks/RemoveRegisterAttribute.cs:line 37
at Microsoft.Android.Build.Tasks.AndroidTask.Execute() in /Users/runner/work/1/s/xamarin-android/external/xamarin-android-tools/src/Microsoft.Android.Build.BaseTasks/AndroidTask.cs:line 25
"Fix" this by *disabling* use of the `<RewriteMarshalMethods/>`
and `<RemoveRegisterAttribute/>` tasks when using R2R.
## TODO
Investigate running R2R image generation build task after
`<RewriteMarshalMethods/>` and `<RemoveRegisterAttribute/>` so that
R2R builds could also benefit from the performance improvements these
build tasks bring.
Investigate running R2R image generation as part of the
`$(RuntimeIdentifiers)` inner build, so that `-r RID` is not required.
[0]: https://learn.microsoft.com/en-us/cpp/build/reference/clrheader?view=msvc-170 |
|
I opened #10062 for tracking the follow-up work |
Description
ReadyToRun deployments are enabled when:
<PublishReadyToRun>true</PublishReadyToRun>is added to the project filedotnet publishOnce enabled, the SDK injects necessary build tasks to perform AOT compilation of assemblies after their trimming and creation of R2R images during app publish.
Problem
Currently, Android app build runs two assembly postprocessing build tasks (after R2R image generation):
RewriteMarshalMethodsandRemoveRegisterAttributethat are modifying assemblies withMono.Cecil. However, altering R2R assemblies is not supported with Mono.Cecil and build fails via:Changes
To enable R2R builds, this PR:
RewriteMarshalMethodsandRemoveRegisterAttributebuild tasks in ReadyToRun deploymentsFuture work
Investigate running R2R image generation build task after
RewriteMarshalMethodsandRemoveRegisterAttributeso that R2R builds could also benefit from the performance improvements these build tasks bring.