Skip to content
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

[One .NET] AOT support #5539

Merged
merged 2 commits into from Jul 28, 2021
Merged

[One .NET] AOT support #5539

merged 2 commits into from Jul 28, 2021

Conversation

jonathanpeppers
Copy link
Member

@jonathanpeppers jonathanpeppers commented Jan 22, 2021

Fixes: #6052

Helpful reading:

To make this work, I moved the existing <Aot/> MSBuild task calls to
a new _AndroidAot MSBuild target in Xamarin.Android.Legacy.targets.

In the .NET 6 targets, there is a different _AndroidAot MSBuild
target that runs the new <MonoAOTCompiler/> MSBuild task. The
_AndroidAot target runs per $(RuntimeIdentifier) after the linker
completes. Native libraries are added to the
@(ResolvedFileToPublish) item group to be included in the final
.apk.

To follow convention with Blazor WASM:

https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-4/#blazor-webassembly-ahead-of-time-aot-compilation

The $(RunAOTCompilation) MSBuild property enables AOT. To help with
backwards compatibility with "legacy" Xamarin.Android, I kept the
$(AotAssemblies) MSBuild property intact.

Unfortunately, we have to manually import things to support
$(AotAssemblies):

<ImportGroup Condition=" '$(MonoAOTCompilerTasksAssemblyPath)' == '' and '$(AotAssemblies)' == 'true' ">
  <Import Project="Sdk.props" Sdk="Microsoft.NET.Runtime.MonoAOTCompiler.Task" />
  <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-x86" />
  <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-x64" />
  <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-arm" />
  <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-arm64" />
</ImportGroup>

Since, the default Mono workload does not support $(AotAssemblies):

https://github.com/dotnet/runtime/blob/69711860262e44458bbe276393ea3eb9f7a2192a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in#L20-L25

I think this is reasonable for now.

Limitations

  • Profiled AOT is not implemented yet:

#6053

  • $(EnableLLVM) fails when locating opt:

dotnet/runtime#56386

  • AndroidClientHandler usage causes runtime crash:

dotnet/runtime#56315

  • Use of folder names like Build AndÜmläüts fails:

dotnet/runtime#56163

Results

All tests were running on a Pixel 5.

Using the HelloAndroid app:

https://github.com/dotnet/maui-samples/tree/main/HelloAndroid

Defaults to two architectures: arm64 and x86

Average of 10 runs with -c Release and no AOT:

Activity: Displayed     00:00:00.308

Apk size: 8367969

Average of 10 runs with -c Release -p:RunAOTCompilation=true:

Activity: Displayed     00:00:00.209

Apk size: 12082123

Using the HelloMaui app:

https://github.com/dotnet/maui-samples/tree/main/HelloMaui

Defaults to two architectures: arm64 and x86

Average of 10 runs with -c Release and no AOT:

Activity: Displayed     00:00:01.117

Apk size: 16272964

Average of 10 runs with -c Release -p:RunAOTCompilation=true:

Activity: Displayed     00:00:00.568

Apk size: 42869016

@pjcollins
Copy link
Member

pjcollins commented Jan 26, 2021

Should we be repacking the content of the Microsoft.NET.Runtime.MonoAOTCompiler.Task and Microsoft.NetCore.App.Runtime.AOT* .nupkgs in our SDK pack? I was thinking that the AOT components would be included as workload packs; declared in WorkloadManifest.json, and conditionally imported via WorkloadManifest.targets based on project settings.

Here's a rough example/outline:

WorkloadManifest.json

  "packs": {
    ...
    "Microsoft.NET.Runtime.MonoAOTCompiler.Task": {
      "kind": "sdk",
      "version": "$(MonoAOTCompilerVersion)"
    },
    "Microsoft.NetCore.App.Runtime.AOT.arm": {
      "kind": "sdk",
      "version": "$(MonoAOTCompilerVersion)",
      "alias-to": {
        "osx-x64": "Microsoft.NetCore.App.Runtime.AOT.osx-x64.Cross.android-arm",
        "win-x64": "Microsoft.NetCore.App.Runtime.AOT.win-x64.Cross.android-arm"
      }
    },
    "Microsoft.NetCore.App.Runtime.AOT.arm64": {
      "kind": "sdk",
      "version": "$(MonoAOTCompilerVersion)",
      "alias-to": {
        "osx-x64": "Microsoft.NetCore.App.Runtime.AOT.osx-x64.Cross.android-arm64",
        "win-x64": "Microsoft.NetCore.App.Runtime.AOT.win-x64.Cross.android-arm64"
      }
    },
    ...
  }

WorkloadManifest.targets

  ...
  <Import Project="Sdk.targets" Sdk="Microsoft.NET.Runtime.MonoAOTCompiler.Task"
      Condition=" '$(AotAssemblies)' == 'true' " />
  <Import Project="Sdk.targets" Sdk="Microsoft.NetCore.App.Runtime.AOT.arm"
      Condition=" '$(AotAssemblies)' == 'true' and '$(RuntimeIdentifier)' == 'android.21-arm' " />
  ...

However maybe this won't work with the current version of these packages? I think for this to work these packs would need to include that Sdk\Sdk.[props][targets] file that we can import in our WorkloadManifest.targets, which contain properties pointing to the relevant tooling.

In the case of Microsoft.NetCore.App.Runtime.AOT*, it looks like we'd need an Sdk file containing at least this:

  <PropertyGroup>
    <AotCompilerBinaryPath>$(MSBuildThisFileDirectory)..\tools\mono-aot-cross</AotCompilerBinaryPath>
  </PropertyGroup>

In the case of Microsoft.NET.Runtime.MonoAOTCompiler.Task, this package does have a build\Microsoft.NET.Runtime.MonoAOTCompiler.Task.props:

  <PropertyGroup>
    <MonoAOTCompilerTaskAssemblyPath>$(MSBuildThisFileDirectory)..\tools\net5.0\MonoAOTCompiler.dll</MonoAOTCompilerTaskAssemblyPath>
  </PropertyGroup>

However this file may not be in the right location that would allow us to import it via our WorkloadManifest.targets file?

The Microsoft.NetCore.App.Runtime.AOT* may also need to be renamed for the aliasing mentioned in the .json above to work? I am not sure.

@akoeplinger what are your thoughts on this? Should we be able to consume these .nupkgs as workload packs? It looks like the Microsoft.NetCore.App.Runtime.AOT* components are packaged as "runtime" packs, and should perhaps be "sdk" packs? I think this is the only way to conditionally install the AOT pieces that each individual Android project may need, as per the workload design.

@jonathanpeppers
Copy link
Member Author

Yes, I think we could easily make Microsoft.NET.Runtime.MonoAOTCompiler.Task a workload pack as things are currently. We can use <Import Project="Sdk.targets" Sdk="Microsoft.NET.Runtime.MonoAOTCompiler.Task" /> to use it.

It would be helpful if the Microsoft.NetCore.App.Runtime.AOT* packs had a .targets file with some way to locate the command-line tooling within. However, it would need to be based per-RID, so maybe they would have to be item groups?

<ItemGroup>
  <AotCompilerTool Include="$(MSBuildThisFileDirectory)..\tools\mono-aot-cross" RuntimeIdentifier="android-arm" />
</ItemGroup>

Each pack would have a different value for %(RuntimeIdentifier)?

@akoeplinger
Copy link
Member

@akoeplinger what are your thoughts on this? Should we be able to consume these .nupkgs as workload packs? It looks like the Microsoft.NetCore.App.Runtime.AOT* components are packaged as "runtime" packs, and should perhaps be "sdk" packs? I think this is the only way to conditionally install the AOT pieces that each individual Android project may need, as per the workload design.

TBH I don't have a lot of context around what the best approach is, so if you or @jonathanpeppers think we should do something differently then sure :)

@jonathanpeppers
Copy link
Member Author

I reworked this to use all of the MonoAOTCompiler packages as SDK workload packs.

Building app fails as before, but here is an updated log: msbuild.binlog.zip

👀

    547976 Warning(s)
    10 Error(s)

Time Elapsed 00:47:07.24

@akoeplinger
Copy link
Member

@jonathanpeppers it looks like that binlog is corrupt, I can't open it. Would you mind creating a new one?

@jonathanpeppers
Copy link
Member Author

@akoeplinger did you try the latest one: #5539 (comment) I can open it with the Windows viewer at least.

I think the first one was partial because the process was killed.

@akoeplinger
Copy link
Member

@jonathanpeppers weird, for some reason downloading the .zip on macOS makes it corrupt. I can open when I download it on Windows, taking a look now :)

AotAdditionalArguments="$(AndroidAotAdditionalArguments)"
AotOutputDirectory="$(_AndroidAotBinDirectory)"
RuntimeIdentifier="$(RuntimeIdentifier)"
EnableLLVM="$(EnableLLVM)"
Copy link
Contributor

Choose a reason for hiding this comment

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

IIRC we ship LLVM only mode

/cc @akoeplinger

Copy link
Member Author

Choose a reason for hiding this comment

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

Turning on $(EnableLLVM) does not appear to work quite yet: dotnet/runtime#56386

We could make it the default value when it's working, if it makes sense to do so.

@jonathanpeppers
Copy link
Member Author

To test this currently, you would need: MonoAOTCompiler.zip

Copy MonoAOTCompiler.dll to ~\android-toolchain\dotnet\packs\Microsoft.NET.Runtime.MonoAOTCompiler.Task\6.0.0-preview.7.*\tasks\net6.0. This will be needed until the changes land in dotnet/runtime and flow to us.

@jonpryor
Copy link
Member

Some of the tests are failing, e.g. BuildBasicApplicationAndAotProfileIt, because they're invoking aprofutil, which we removed from the net6 installers in commit 8ad7f52:

Task "Exec" (TaskId:50)
  Task Parameter:Command="/Users/runner/Library/Android/dotnet/packs/Microsoft.Android.Sdk.Darwin/30.0.100-ci.pr.gh5539.94/tools/Darwin/aprofutil"  -s -v -p 9260 -o "custom.aprof" (TaskId:50)
  "/Users/runner/Library/Android/dotnet/packs/Microsoft.Android.Sdk.Darwin/30.0.100-ci.pr.gh5539.94/tools/Darwin/aprofutil"  -s -v -p 9260 -o "custom.aprof" (TaskId:50)
  /var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/tmp775e33529bfa4d3fbd9e131a1d463088.exec.cmd: line 2: /Users/runner/Library/Android/dotnet/packs/Microsoft.Android.Sdk.Darwin/30.0.100-ci.pr.gh5539.94/tools/Darwin/aprofutil: No such file or directory (TaskId:50)

@jonathanpeppers jonathanpeppers force-pushed the dotnet-aot branch 2 times, most recently from 9b0c324 to 8124229 Compare July 22, 2021 17:00
@jonathanpeppers
Copy link
Member Author

After adding some new categories, I realized this breaks legacy due to:

Target Name=_AndroidAot Project=UnnamedProject.csproj
    Skipping target "_AndroidAot" because it has no outputs.
    Though the target has declared its outputs, the output specification only references empty properties and/or empty item lists.

Looking into it...

@jonathanpeppers jonathanpeppers force-pushed the dotnet-aot branch 6 times, most recently from a9275ad to 0fccb69 Compare July 26, 2021 18:37
@jonpryor
Copy link
Member

Fixes: #6052

Fixes: #6052

Helpful reading:

* https://github.com/dotnet/runtime/blob/15dec9a2aa5a4236d6ba70de2e9c146867b9d2e0/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
* https://github.com/dotnet/runtime/blob/15dec9a2aa5a4236d6ba70de2e9c146867b9d2e0/src/mono/netcore/nuget/Microsoft.NET.Runtime.MonoAOTCompiler.Task/README.md

To make this work, I moved the existing `<Aot/>` MSBuild task calls to
a new `_AndroidAot` MSBuild target in `Xamarin.Android.Legacy.targets`.

In the .NET 6 targets, there is a *different* `_AndroidAot` MSBuild
target that runs the new `<MonoAOTCompiler/>` MSBuild task. The
`_AndroidAot` target runs per `$(RuntimeIdentifier)` after the linker
completes. Native libraries are added to the
`@(ResolvedFileToPublish)` item group to be included in the final
`.apk`.

To follow convention with Blazor WASM:

https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-4/#blazor-webassembly-ahead-of-time-aot-compilation

The `$(RunAOTCompilation)` MSBuild property enables AOT. To help with
backwards compatibility with "legacy" Xamarin.Android, I kept the
`$(AotAssemblies)` MSBuild property intact.

Unfortunately, we have to manually import things to support
`$(AotAssemblies)`:

    <ImportGroup Condition=" '$(MonoAOTCompilerTasksAssemblyPath)' == '' and '$(AotAssemblies)' == 'true' ">
      <Import Project="Sdk.props" Sdk="Microsoft.NET.Runtime.MonoAOTCompiler.Task" />
      <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-x86" />
      <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-x64" />
      <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-arm" />
      <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-arm64" />
    </ImportGroup>

Since, the default Mono workload does not support `$(AotAssemblies)`:

https://github.com/dotnet/runtime/blob/69711860262e44458bbe276393ea3eb9f7a2192a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in#L20-L25

I think this is reasonable for now.

~~ Limitations ~~

* Profiled AOT is not implemented yet:

#6053

* `$(EnableLLVM)` fails when locating `opt`:

dotnet/runtime#56386

* `AndroidClientHandler` usage causes runtime crash:

dotnet/runtime#56315

* Use of folder names like `Build AndÜmläüts` fails:

dotnet/runtime#56163

~~ Results ~~

All tests were running on a Pixel 5.

Using the HelloAndroid app:

https://github.com/dotnet/maui-samples/tree/main/HelloAndroid

Defaults to two architectures: arm64 and x86

Average of 10 runs with `-c Release` and no AOT:

    Activity: Displayed     00:00:00.308

Apk size: 8367969

Average of 10 runs with `-c Release -p:RunAOTCompilation=true`:

    Activity: Displayed     00:00:00.209

Apk size: 12082123

Using the HelloMaui app:

https://github.com/dotnet/maui-samples/tree/main/HelloMaui

Defaults to two architectures: arm64 and x86

Average of 10 runs with `-c Release` and no AOT:

    Activity: Displayed     00:00:01.117

Apk size: 16272964

Average of 10 runs with `-c Release -p:RunAOTCompilation=true`:

    Activity: Displayed     00:00:00.568

Apk size: 42869016
@jonathanpeppers
Copy link
Member Author

This only has a couple runtime test failures:

07-27 15:40:11.583  3847  3867 I NUnit   : 1) System.NetTests.NetworkInterfacesTest.DotNetInterfacesShouldEqualJavaInterfaces (Mono.Android.NET-Tests)
07-27 15:40:11.583  3847  3867 I NUnit   : System.Net.NetworkInformation.NetworkInformationException : No such file or directory
07-27 15:40:11.583  3847  3867 I NUnit   :    at System.Net.NetworkInformation.LinuxNetworkInterface.GetLinuxNetworkInterfaces() in System.Net.NetworkInformation.dll:token 0x600009b+0x40
07-27 15:40:11.583  3847  3867 I NUnit   :    at System.Net.NetworkInformation.NetworkInterfacePal.GetAllNetworkInterfaces() in System.Net.NetworkInformation.dll:token 0x6000098+0x0
07-27 15:40:11.583  3847  3867 I NUnit   :    at System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces() in System.Net.NetworkInformation.dll:token 0x600004e+0x0
07-27 15:40:11.583  3847  3867 I NUnit   :    at System.NetTests.NetworkInterfacesTest.DotNetInterfacesShouldEqualJavaInterfaces() in Mono.Android.NET-Tests.dll:token 0x6000133+0x0
07-27 15:40:11.583  3847  3867 I NUnit   :    at System.Reflection.RuntimeMethodInfo.Invoke(Object , BindingFlags , Binder , Object[] , CultureInfo ) in System.Private.CoreLib.dll:token 0x600283b+0x6a

Then:

07-27 15:40:11.583  3847  3867 I NUnit   : 2) SystemTests.AppDomainTest.DateTime_Now_Works (Mono.Android.NET-Tests)
07-27 15:40:11.583  3847  3867 I NUnit   : System.PlatformNotSupportedException : PlatformNotSupported_AppDomains
07-27 15:40:11.583  3847  3867 I NUnit   :    at System.AppDomain.CreateDomain(String ) in System.Private.CoreLib.dll:token 0x6000656+0x13
07-27 15:40:11.583  3847  3867 I NUnit   :    at SystemTests.AppDomainTest.DateTime_Now_Works() in Mono.Android.NET-Tests.dll:token 0x600007b+0xa
07-27 15:40:11.583  3847  3867 I NUnit   :    at System.Reflection.RuntimeMethodInfo.Invoke(Object , BindingFlags , Binder , Object[] , CultureInfo ) in System.Private.CoreLib.dll:token 0x600283b+0x6a

https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=5024995&view=results

I have a mistake where the test results file is wrong, looking into that:

##[warning]No test result files matching TestResult-Mono.Android.NET_Tests-Release-Aot.xml were found.

@jonathanpeppers jonathanpeppers marked this pull request as ready for review July 27, 2021 17:38
@@ -418,17 +425,20 @@ public void HybridAOT ([Values ("armeabi-v7a;arm64-v8a", "armeabi-v7a", "arm64-v
}

[Test]
public void NoSymbolsArgShouldReduceAppSize ([Values (true, false)] bool enableHybridAot)
[Category ("LLVM")]
public void NoSymbolsArgShouldReduceAppSize ([Values ("", "Hybrid")] string androidAotMode)
Copy link
Member

Choose a reason for hiding this comment

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

Should it be "Normal" instead of ""?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think the test was previously leaving it blank, so I kept the behavior the same:

--			if (enableHybridAot)
--				proj.SetProperty ("AndroidAotMode", "Hybrid");
++			if (!string.IsNullOrEmpty (androidAotMode))
++				proj.SetProperty ("AndroidAotMode", androidAotMode);

* had the wrong file path for `TestResults-*.xml`
* `$(ExcludeCategories)` should include `DotNetIgnore` and `InetAccess`
@jonpryor
Copy link
Member

For review:

Fixes: https://github.com/xamarin/xamarin-android/issues/6052

Helpful reading:

  * https://github.com/dotnet/runtime/blob/15dec9a2aa5a4236d6ba70de2e9c146867b9d2e0/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
  * https://github.com/dotnet/runtime/blob/15dec9a2aa5a4236d6ba70de2e9c146867b9d2e0/src/mono/netcore/nuget/Microsoft.NET.Runtime.MonoAOTCompiler.Task/README.md

To make this work, I moved the existing `<Aot/>` MSBuild task calls to
a new `_AndroidAot` MSBuild target in `Xamarin.Android.Legacy.targets`.

In the .NET 6 targets, there is a *different* `_AndroidAot` MSBuild
target that runs the new `<MonoAOTCompiler/>` MSBuild task. The
`_AndroidAot` target runs once per `$(RuntimeIdentifier)`, after the
linker completes.

Native libraries are added to the `@(ResolvedFileToPublish)` item
group to be included in the final `.apk`.

To follow [convention with Blazor WASM][0], the `$(RunAOTCompilation)`
MSBuild property enables AOT.

To help with backwards compatibility with "legacy" Xamarin.Android, I
kept the `$(AotAssemblies)` MSBuild property intact.

Unfortunately, we have to manually import things to support
`$(AotAssemblies)`:

	<ImportGroup Condition=" '$(MonoAOTCompilerTasksAssemblyPath)' == '' and '$(AotAssemblies)' == 'true' ">
	  <Import Project="Sdk.props" Sdk="Microsoft.NET.Runtime.MonoAOTCompiler.Task" />
	  <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-x86" />
	  <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-x64" />
	  <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-arm" />
	  <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-arm64" />
	</ImportGroup>

as the [default Mono workload does not support `$(AotAssemblies)`][1].
I think this is reasonable for now.


~~ Limitations ~~

Profiled AOT is not implemented yet:

  * https://github.com/xamarin/xamarin-android/issues/6053

`$(EnableLLVM)` fails when locating `opt`:

  * https://github.com/dotnet/runtime/issues/56386

`$(AndroidClientHandler)` usage causes runtime crash:

  * https://github.com/dotnet/runtime/issues/56315

Use of folder names like `Build AndÜmläüts` fails:

  * https://github.com/dotnet/runtime/issues/56163


~~ Results ~~

All tests:

 1. Were running on a [Google Pixel 5][2], and
 2. Enabled two architectures, arm64 and x86, and
 3. **JIT time** was average of 10 runs with `-c Release`, with the
    `Activity: Displayed` time, and
 4. **AOT time** was average of 10 runs with
    `-c Release -p:RunAOTCompilation=true` with the
    `Activity: Displayed` time.
 5. Δ values are (AOT / JIT)*100.

| Test                |      JIT time |      AOT time |  Δ time |  JIT apk size |  AOT apk size | Δ size |
| ------------------- | ------------: | ------------: | ------: | ------------: | ------------: | -----: |
| [HelloAndroid][3]   |  00:00:00.308 |  00:00:00.209 |     68% |     8,367,969 |    12,082,123 | 144.4% |
| [HelloMaui][4]      |  00:00:01.117 |  00:00:00.568 |     51% |    16,272,964 |    42,869,016 | 263.4% |

[0]: https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-4/#blazor-webassembly-ahead-of-time-aot-compilation
[1]: https://github.com/dotnet/runtime/blob/69711860262e44458bbe276393ea3eb9f7a2192a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in#L20-L25
[2]: https://store.google.com/us/product/pixel_5_specs?hl=en-US
[3]: https://github.com/dotnet/maui-samples/tree/714460431541f40570e91225e8ba4bc1fe08025f/HelloAndroid
[4]: https://github.com/dotnet/maui-samples/tree/714460431541f40570e91225e8ba4bc1fe08025f/HelloMaui

@jonpryor
Copy link
Member

Fixes: #6052

Helpful reading:

To make this work, I moved the existing <Aot/> MSBuild task calls to
a new _AndroidAot MSBuild target in Xamarin.Android.Legacy.targets.

In the .NET 6 targets, there is a different _AndroidAot MSBuild
target that runs the new <MonoAOTCompiler/> MSBuild task. The
_AndroidAot target runs once per $(RuntimeIdentifier), after the
linker completes.

Native libraries are added to the @(ResolvedFileToPublish) item
group to be included in the final .apk.

To follow convention with Blazor WASM, the $(RunAOTCompilation)
MSBuild property enables AOT.

To help with backwards compatibility with "legacy" Xamarin.Android, I
kept the $(AotAssemblies) MSBuild property intact.

Unfortunately, we have to manually import things to support
$(AotAssemblies):

<ImportGroup Condition=" '$(MonoAOTCompilerTasksAssemblyPath)' == '' and '$(AotAssemblies)' == 'true' ">
  <Import Project="Sdk.props" Sdk="Microsoft.NET.Runtime.MonoAOTCompiler.Task" />
  <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-x86" />
  <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-x64" />
  <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-arm" />
  <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-arm64" />
</ImportGroup>

as the default Mono workload does not support $(AotAssemblies).
I think this is reasonable for now.

~~ Limitations ~~

Profiled AOT is not implemented yet:

$(EnableLLVM) fails when locating opt:

$(AndroidClientHandler) usage causes runtime crash:

Use of folder names like Build AndÜmläüts fails:

~~ Results ~~

All tests:

  1. Were running on a Google Pixel 5, and
  2. Enabled two architectures, arm64 and x86, and
  3. JIT time was average of 10 runs with -c Release, with the
    Activity: Displayed time, and
  4. AOT time was average of 10 runs with
    -c Release -p:RunAOTCompilation=true with the
    Activity: Displayed time.
  5. Δ values are (AOT / JIT)*100.
Test JIT time AOT time Δ time JIT apk size AOT apk size Δ size
HelloAndroid 00:00:00.308 00:00:00.209 68% 8,367,969 12,082,123 144.4%
HelloMaui 00:00:01.117 00:00:00.568 51% 16,272,964 42,869,016 263.4%

@jonpryor jonpryor merged commit c929289 into main Jul 28, 2021
@jonpryor jonpryor deleted the dotnet-aot branch July 28, 2021 15:08
jonpryor pushed a commit that referenced this pull request Jul 28, 2021
Fixes: #6052

Helpful reading:

  * https://github.com/dotnet/runtime/blob/15dec9a2aa5a4236d6ba70de2e9c146867b9d2e0/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
  * https://github.com/dotnet/runtime/blob/15dec9a2aa5a4236d6ba70de2e9c146867b9d2e0/src/mono/netcore/nuget/Microsoft.NET.Runtime.MonoAOTCompiler.Task/README.md

To make this work, I moved the existing `<Aot/>` MSBuild task calls to
a new `_AndroidAot` MSBuild target in `Xamarin.Android.Legacy.targets`.

In the .NET 6 targets, there is a *different* `_AndroidAot` MSBuild
target that runs the new `<MonoAOTCompiler/>` MSBuild task. The
`_AndroidAot` target runs once per `$(RuntimeIdentifier)`, after the
linker completes.

Native libraries are added to the `@(ResolvedFileToPublish)` item
group to be included in the final `.apk`.

To follow [convention with Blazor WASM][0], the `$(RunAOTCompilation)`
MSBuild property enables AOT.

To help with backwards compatibility with "legacy" Xamarin.Android, I
kept the `$(AotAssemblies)` MSBuild property intact.

Unfortunately, we have to manually import things to support
`$(AotAssemblies)`:

	<ImportGroup Condition=" '$(MonoAOTCompilerTasksAssemblyPath)' == '' and '$(AotAssemblies)' == 'true' ">
	  <Import Project="Sdk.props" Sdk="Microsoft.NET.Runtime.MonoAOTCompiler.Task" />
	  <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-x86" />
	  <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-x64" />
	  <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-arm" />
	  <Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Runtime.AOT.Cross.android-arm64" />
	</ImportGroup>

as the [default Mono workload does not support `$(AotAssemblies)`][1].
I think this is reasonable for now.


~~ Limitations ~~

Profiled AOT is not implemented yet:

  * #6053

`$(EnableLLVM)` fails when locating `opt`:

  * dotnet/runtime#56386

`$(AndroidClientHandler)` usage causes runtime crash:

  * dotnet/runtime#56315

Use of folder names like `Build AndÜmläüts` fails:

  * dotnet/runtime#56163


~~ Results ~~

All tests:

 1. Were running on a [Google Pixel 5][2], and
 2. Enabled two architectures, arm64 and x86, and
 3. **JIT time** was average of 10 runs with `-c Release`, with the
    `Activity: Displayed` time, and
 4. **AOT time** was average of 10 runs with
    `-c Release -p:RunAOTCompilation=true` with the
    `Activity: Displayed` time.
 5. Δ values are (AOT / JIT)*100.

| Test                |      JIT time |      AOT time |  Δ time |  JIT apk size |  AOT apk size | Δ size |
| ------------------- | ------------: | ------------: | ------: | ------------: | ------------: | -----: |
| [HelloAndroid][3]   |  00:00:00.308 |  00:00:00.209 |     68% |     8,367,969 |    12,082,123 | 144.4% |
| [HelloMaui][4]      |  00:00:01.117 |  00:00:00.568 |     51% |    16,272,964 |    42,869,016 | 263.4% |

[0]: https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-4/#blazor-webassembly-ahead-of-time-aot-compilation
[1]: https://github.com/dotnet/runtime/blob/69711860262e44458bbe276393ea3eb9f7a2192a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in#L20-L25
[2]: https://store.google.com/us/product/pixel_5_specs?hl=en-US
[3]: https://github.com/dotnet/maui-samples/tree/714460431541f40570e91225e8ba4bc1fe08025f/HelloAndroid
[4]: https://github.com/dotnet/maui-samples/tree/714460431541f40570e91225e8ba4bc1fe08025f/HelloMaui
@github-actions github-actions bot locked and limited conversation to collaborators Jan 24, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[One .NET] implement AOT
5 participants