-
Notifications
You must be signed in to change notification settings - Fork 541
[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
Outdated
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 <[email protected]>
@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 publish
Once 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):
RewriteMarshalMethods
andRemoveRegisterAttribute
that 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:
RewriteMarshalMethods
andRemoveRegisterAttribute
build tasks in ReadyToRun deploymentsFuture work
Investigate running R2R image generation build task after
RewriteMarshalMethods
andRemoveRegisterAttribute
so that R2R builds could also benefit from the performance improvements these build tasks bring.