forked from dotnet/android
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Mono.Android] Reduce Reflection & reduce startup overhead (dotnet#4302)
Reduce or remove reflection use around JNI method registration, and rework how uncaught exceptions are handled. Java.Interop provides an alternate mechanism to allow registering Java native methods: [the `[JniAddNativeMethodRegistrationAttribute]` custom attribute][0] which can be placed on methods of a type to allow "manual" insertion into the [`JNIEnv::RegisterNatives()`][2] process. The `Java.Interop-Tests.dll` unit test assembly (130905e) uses this alternate native method registration mechanism.. Unfortunately, `[JniAddNativeMethodRegistration]` is looked up via Reflection, which isn't exactly "fast". Given that `[JniAddNativeMethodRegistration]` is only used by one assembly *in the world*, let's optimize the common case: [dotnet/java-interop@b33d183d][3] made the `[JniAddNativeMethodRegistration]` lookup *optional*. Disable the custom attribute lookup *by default*, and allow it to be enabled by setting the `$(_SkipJniAddNativeMethodRegistrationAttributeScan)` MSBuild property to True. Yes, this property is "private". We'll investigate more automatic and optimized ideas in the future. Rework how Java-side unhandled exceptions are processed. Previously, this was handled by the `Android.Runtime.UncaughtExceptionHandler` class, which was constructed during process startup. A side effect of this is that it required going through the (*normal* reflection- based) JNI registration logic of `mono.android.Runtime.register()`, contributing to startup for something that *ideally* would Never Happen™. (Nobody likes unhandled exceptions.) Changes this so that instead of a C#-side `UncaughtExceptionHandler` type we instead have a Java-side `mono.android.XamarinUncaughtExceptionHandler` type which is created and provided to `java.lang.Thread.setDefaultUncaughtExceptionHandler()` during startup. `XamarinUncaughtExceptionHandler` doesn't do anything until Java calls `XamarinUncaughtExceptionHandler.uncaughtException()`, which in turn invokes `Runtime.propagateUncaughtException()`, which in turn does what `UncaughtExceptionHandler` previously did: lookup `AppDomain.DoUnhandledException()` via Reflection and invoke it, thus raising the `AppDomain.UnhandledException` event. In this manner all overheads associated with unhandled exceptions are delayed until the exception happens, with minimal impact on startup. Gains in startup time are quite nice: * Device name: **Pixel 3 XL** * Device architecture: **arm64-v8a** * Number of test runs: **10** * Test application: **Xamarin.Forms integration test** | | **Native to managed** | **Runtime init** | **Displayed** | **Notes** | |-----------------|------------------------|------------------|---------------|--------------------------------| | **master** | 131.278 | 149.074 | 789.10 | preload enabled; 32-bit build | | **this commit** | 49.446 | 66.989 | 764.30 | | | **master** | 132.315 | 147.187 | 795.60 | preload disabled; 32-bit build | | **this commit** | 48.876 | 63.822 | 754.30 | | | **master** | 121.544 | 137.736 | 728.20 | preload enabled; 64-bit build | | **this commit** | 45.350 | 61.464 | 699.50 | | | **master** | 123.448 | 137.052 | 727.40 | preload disabled; 64-bit build | | **this commit** | 44.765 | 58.047 | 689.00 | | * Device name: **Pixel 3 XL** * Device architecture: **arm64-v8a** * Number of test runs: **10** * Test application: Xamarin.Forms "Hello World" app with one label | | **Native to managed** | **Runtime init** | **Displayed** | **Notes** | |-----------------|------------------------|------------------|---------------|--------------------------------| | **master** | 122.090 | 142.004 | 639.00 | preload enabled; 32-bit build | | **this commit** | 44.370 | 63.803 | 586.10 | | | **master** | 121.110 | 134.378 | 634.20 | preload disabled; 32-bit build | | **this commit** | 45.085 | 57.992 | 580.40 | | | **master** | 120.973 | 141.235 | 637.20 | preload enabled; 64-bit build | | **this commit** | 44.767 | 63.846 | 578.50 | | | **master** | 120.785 | 134.588 | 627.00 | preload disabled; 64-bit build | | **this commit** | 44.859 | 57.590 | 575.40 | | [0]: dotnet/java-interop@7d51163 [2]: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#RegisterNatives [3]: dotnet/java-interop@b33d183
- Loading branch information
Showing
23 changed files
with
179 additions
and
110 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
### App startup performance | ||
|
||
* [GitHub PR 4302](https://github.com/xamarin/xamarin-android/pull/4302): | ||
Avoid unneeded calls to `GetCustomAttribute()` during app startup for the | ||
common case where an app has no types decorated with the | ||
`[JniAddNativeMethodRegistration]` attribute. Additionally, instead of | ||
using a managed method to propagate uncaught exceptions from Java, use a | ||
Java method that calls into the unmanaged Xamarin.Android runtime. These | ||
changes reduced the time to display the first screen of a small test | ||
Xamarin.Forms app from about 730 milliseconds to about 700 milliseconds for | ||
a Release configuration build on a Google Pixel 3 XL device. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 0 additions & 69 deletions
69
src/Mono.Android/Android.Runtime/UncaughtExceptionHandler.cs
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.