dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.25k stars 4.73k forks source link

NativeAOT status for Android #106748

Open vyacheslav-volkov opened 2 months ago

vyacheslav-volkov commented 2 months ago

I had previously raised this topic in another issue https://github.com/dotnet/runtime/issues/101135, but I want to create a separate discussion as I couldn't find a place to track the progress on this matter.

The most serious and long-standing issues with Xamarin.Android is the slow startup time for applications. If you search the internet for "Xamarin.Android slow startup," you'll find hundreds of discussions on this topic. Even with all possible optimizations, including MonoAOT compilation, the startup time remains slow, and even MonoAOT works incorrectly on Android https://github.com/dotnet/runtime/issues/101135. This problem is particularly noticeable with UI frameworks such as Avalonia, UNO, and MAUI. Developers simply don't have the ability to solve this problem on their own, as it is rooted in the fundamental aspects of the platform's operation, and a significant amount of time is spent on JIT compilation. In the end, to write a "fast application" for Android that still lags behind native applications in terms of startup speed, you need to perform a whole range of additional operations, which not every developer can manage, just to make their application work somewhat faster. I believe that this expectation is where the main problem lies. A developer expects that the release build will immediately work as it should, but instead, they encounter performance issues where they don't expect them.

When .NET Native was introduced, I thought it would be the solution to the slow startup problem for Android. Starting with version .NET 8.0, it became stable for iOS, and I began actively using it. The results are impressive: a fairly large application on an iPhone X launches as quickly as any native application and even faster than a similar application on a Samsung Galaxy S22 Ultra, despite all possible optimizations for Android. The gap between the release of these devices is five years, and I dread to imagine the startup time on a five-year-old Android device. Yes, there are still limitations on using dynamic code, but they are not that difficult to overcome, resulting in an application that performs as fast as a native one. Isn't that what we want for a cross-platform application? Moreover, I’m almost 100% sure that no one uses Android applications without ProfiledAOT or FullAOT because, in that case, you can forget about startup performance. This also means they are already using trimming, so transitioning to NativeAOT wouldn't require much additional effort. Over time, more libraries and frameworks will become fully compatible with NativeAOT, making integration seamless for developers without any issues.

However, observing the discussions about .NET Native and the activity around this topic, I get the impression that the team does not give this problem enough priority, and no specific timelines have been set for its resolution. For example, in one of the discussions on GitHub, the following is mentioned:

These will likely work under Mono, but will need to be fixed one day in .NET 10 or some future release that supports NativeAOT. https://github.com/dotnet/android/issues/8724

This gives the impression that allocating resources for NativeAOT on Android is not a priority, and instead, new releases include optimizations that only provide marginal improvements (e.g., -10% startup time for test cases). However, in real-world conditions, such improvements do not solve the problem. If an application takes 2000ms to start, even reducing it to 1800ms makes little difference, and at best, such optimizations are noticeable only under ideal conditions.

It seems to me that the team does not fully grasp the depth of this issue. Many of my colleagues have already switched to Flutter specifically because of the slow startup times on Android. When their clients or customers ask why the Android application launches so slowly, developers are forced to reply that it is a limitation of the technology they are using, they may also suggest switching to iOS, where there are no such problems, but this is not an option.

In my opinion, the implementation of NativeAOT support for Android, should be considered critically important. I would like to hear the team's thoughts on this matter: what should we expect? Will NativeAOT support for Android be added in the near future, or should we only hope for small, incremental performance improvements that don't solve anything and are waiting for everyone to switch to Flutter?

juepiezhongren commented 1 month ago

it is better to deprecate monoVm in the future, while monoInterpreter to be a preserved component for clr would be a descent result.

jonpryor commented 3 weeks ago

@josephmoresena wrote:

I have been working on a JNI framework for .NET that is fully compatible with NativeAOT

Out of curiosity, why? dotnet/java-interop has a couple of samples running on NativeAOT; the current JNI underpinnings of .NET for Android can work with NativeAOT. The major problem is the GC, as mentioned elsewhere on this issue.

josephmoresena commented 3 weeks ago

@josephmoresena wrote:

I have been working on a JNI framework for .NET that is fully compatible with NativeAOT

Out of curiosity, why? dotnet/java-interop has a couple of samples running on NativeAOT; the current JNI underpinnings of .NET for Android can work with NativeAOT. The major problem is the GC, as mentioned elsewhere on this issue.

When I thought about this, it was December 2021. I also made several static approaches to handling the API, but due to the .NET version at the time, I didn’t like the final result because it depended 100% on instances rather than types.

It wasn’t until the advent of generic math that I was able to make sense of what I wanted. And that’s all; I believe no one would actually use it because, even though it’s friendly to use, its overloaded—precisely because I tried to make it friendly to use.

I think the most notable aspect of that approach is how I avoided the use of reflection (or even how I used it) through generic math to bring in the definitions (methods, fields, constructors, and functions) and data types (primitives, arrays, classes, and interfaces), always focused on a reflection-free scenario.

It doesn't use source generators, and everything is compiled statically. It has switches to trim down scenarios, and in general, it is usable (perhaps with many errors because only I maintain it) in any scenario that uses JNI or the invocation interface.

winkmichael commented 3 weeks ago

Any update on this? Without AOT performance is pretty horrible starting apps.

charlesroddie commented 3 weeks ago

@jonpryor The major problem is the GC, as mentioned elsewhere on this issue.

@jkotas Yes, the first step would be to extract the required functionality into an API proposal.

How far away is the dotnet Android team from being able to create a proposal, as IOS did in [NativeAOT] Low level API support for Objective-C scenarios - with GC API described in https://github.com/dotnet/runtime/issues/44659) ?