Skip to content

Commit

Permalink
[monodroid] Speed up java-to-managed typemap lookups (#6905)
Browse files Browse the repository at this point in the history
Context: https://en.algorithmica.org/hpc/

Up until now, Xamarin.Android used string comparison when finding a
Managed type corresponding to a given Java type.  Even though the
strings were pre-sorted at build time, multiple string comparisons
cost more time than necessary.  To improve comparison speed, implement
lookups based on hash values using the `xxHash` algorithm (c927026),
calculated for all bound Java names at build time.  This allows us to
process each Java type once at run time, to generate its hash.  After
that, the hash is used to binary search an array of hashes and the
result (if found) is an index into array with the appropriate
Java-to-Managed mapping.

This change also allows us to move Java type names from the mapping
structure (`TypeMapJava`) and array (`map_java`) to a separate
`java_type_names` array.  We used to keep Java type name in the
structure to make matching slightly faster, but it required
unnecessarily complicated structure size calculation at runtime, so
that binary search can properly work on an array of `TypeMapJava`
structures whose size would differ from application to application
(and sometimes even between builds).  The change also saves space,
because when the Java type name was stored in the structure, all the
structures had to have the same size, and thus all type names shorter
than the longest one had to be padded with NUL characters.

A handful of other optimizations are implemented as well.  Namely:

  * the `JNIEnv.RegisterJniNatives()` method is now called
    directly (thanks to the `[UnmanagedCallersOnly]` attribute) when
    running under .NET6+; see also 1668070.

  * A conceptually simpler binary search function was implemented,
    which doesn't use C++ templates and also appears to generate
    faster code.  There are two versions of the function, one "simple"
    using the standard branching binary search algorithm, and the other
    "branchless". The latter is currently not used, needing a better
    timing infrastructure to verify it's actually faster on Android
    devices.  (Microbenchmarks suggest it's faster, application
    measurements when the branchless version is used suggest it's
    slower than the simple one)

  * the `typemap_managed_to_java()` and `typemap_java_to_managed()`
    internal calls are now registered directly from the
    `EmbeddedAssemblies` class instead of from the `MonodroidRuntime`
    class

  * a number of native functions are now forcibly inlined

  * a number of native functions are now `static` instead of instance.

~~ File Formats ~~

The `TypeMapJava::java_name` string field (ce2bc68) is now an
`TypeMapJava::java_name_index` int32 field, which is an index into
the `java_type_names` global array:

	extern "C" const char* const java_type_names[];

A new `map_java_hashes` global array is also introduced, which contains
the xxHash value of each entry within `java_type_names`.
`map_java_hashes` is sorted for binary search purposes:

	extern "C" const xamarin::android::hash_t map_java_hashes[];

~~ Performance ~~

Startup performance was measured on a .NET6 MAUI application created
with the `dotnet new maui` template.  Gains vary depending on where
we look.

The `Displayed` time sees changes that are negligible, however the
most affected area of the startup sequence (`JNIEnv.Initialize()`)
which registers types and involves the biggest number of lookups sees
improvements of up to 12%.  The measurements have a degree of
uncertainty and instability to them because of our use of Android
`logcat` to report timings as they are taken.  (`adb logcat` calls
need to send messages to a system daemon which involves a lot of
steps and allows for a large variation in time spent processing each
call.)  The `Displayed` time is also not a very stable reporting
system (it depends on CPU and GPU load among other factors).

The changes will also positively affect application performance after
startup.  All times are from devices running Android 12.

| JNIEnv.Initialize() time; Scenario    | Before ms |  After ms |         Δ |
| ------------------------------------- | --------: | --------: | --------: |
| Pixel 3 XL, 32-bit, Preload enabled   |    14.967 |    13.586 |  -9.23% ✓ |
| Pixel 3 XL, 64-bit, Preload disabled  |    13.601 |    12.838 |  -5.61% ✓ |
| Pixel 6 XL, 32-bit, Preload enabled   |     8.972 |     7.826 | -12.78% ✓ |
| Pixel 6 XL, 64-bit, Preload disabled  |     6.426 |     6.052 |  -5.83% ✓ |
  • Loading branch information
grendello authored Apr 13, 2022
1 parent 9f3ee58 commit f48b97c
Show file tree
Hide file tree
Showing 18 changed files with 548 additions and 368 deletions.
3 changes: 3 additions & 0 deletions src/Mono.Android/Android.Runtime/JNIEnv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ internal static bool ShouldWrapJavaException (Java.Lang.Throwable? t, [CallerMem
[DllImport ("libc")]
static extern int gettid ();

#if NETCOREAPP
[UnmanagedCallersOnly]
#endif
static unsafe void RegisterJniNatives (IntPtr typeName_ptr, int typeName_len, IntPtr jniClass, IntPtr methods_ptr, int methods_len)
{
string typeName = new string ((char*) typeName_ptr, 0, typeName_len);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public sealed class ApplicationConfig
"map_modules",
"map_module_count",
"java_type_count",
"java_name_width",
"map_java_hashes",
"map_java",
"mono_aot_mode_name",
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
"Size": 3032
},
"assemblies/Java.Interop.dll": {
"Size": 55111
"Size": 55106
},
"assemblies/Mono.Android.dll": {
"Size": 88334
"Size": 88461
},
"assemblies/rc.bin": {
"Size": 1083
Expand All @@ -26,13 +26,13 @@
"Size": 2374
},
"assemblies/UnnamedProject.dll": {
"Size": 3551
"Size": 3546
},
"classes.dex": {
"Size": 345328
},
"lib/arm64-v8a/libmonodroid.so": {
"Size": 380832
"Size": 382304
},
"lib/arm64-v8a/libmonosgen-2.0.so": {
"Size": 3192432
Expand All @@ -47,7 +47,7 @@
"Size": 150032
},
"lib/arm64-v8a/libxamarin-app.so": {
"Size": 8688
"Size": 9424
},
"META-INF/BNDLTOOL.RSA": {
"Size": 1213
Expand All @@ -59,7 +59,7 @@
"Size": 2467
},
"res/drawable-hdpi-v4/icon.png": {
"Size": 4762
"Size": 4791
},
"res/drawable-mdpi-v4/icon.png": {
"Size": 2200
Expand All @@ -83,5 +83,5 @@
"Size": 1904
}
},
"PackageSize": 2701303
"PackageSize": 2705399
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@
"Size": 2604
},
"assemblies/Java.Interop.dll": {
"Size": 67953
"Size": 67956
},
"assemblies/Mono.Android.dll": {
"Size": 256591
"Size": 256630
},
"assemblies/mscorlib.dll": {
"Size": 769016
"Size": 769015
},
"assemblies/System.Core.dll": {
"Size": 28198
"Size": 28199
},
"assemblies/System.dll": {
"Size": 9180
},
"assemblies/UnnamedProject.dll": {
"Size": 2880
"Size": 2881
},
"classes.dex": {
"Size": 347796
Expand All @@ -32,7 +32,7 @@
"Size": 750976
},
"lib/arm64-v8a/libmonodroid.so": {
"Size": 297544
"Size": 296192
},
"lib/arm64-v8a/libmonosgen-2.0.so": {
"Size": 4030448
Expand All @@ -41,7 +41,7 @@
"Size": 65512
},
"lib/arm64-v8a/libxamarin-app.so": {
"Size": 18272
"Size": 19960
},
"META-INF/ANDROIDD.RSA": {
"Size": 1213
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,124 +8,124 @@
"Size": 7247
},
"assemblies/Java.Interop.dll": {
"Size": 62006
"Size": 62017
},
"assemblies/Mono.Android.dll": {
"Size": 441706
"Size": 442008
},
"assemblies/mscorlib.dll": {
"Size": 3798
"Size": 3803
},
"assemblies/netstandard.dll": {
"Size": 5499
"Size": 5503
},
"assemblies/rc.bin": {
"Size": 1083
},
"assemblies/System.Collections.Concurrent.dll": {
"Size": 11227
"Size": 11230
},
"assemblies/System.Collections.dll": {
"Size": 16736
"Size": 16741
},
"assemblies/System.Collections.NonGeneric.dll": {
"Size": 8439
"Size": 8443
},
"assemblies/System.ComponentModel.dll": {
"Size": 1961
"Size": 1965
},
"assemblies/System.ComponentModel.Primitives.dll": {
"Size": 2566
"Size": 2569
},
"assemblies/System.ComponentModel.TypeConverter.dll": {
"Size": 5968
"Size": 5970
},
"assemblies/System.Console.dll": {
"Size": 6530
"Size": 6532
},
"assemblies/System.Core.dll": {
"Size": 1928
"Size": 1932
},
"assemblies/System.Diagnostics.TraceSource.dll": {
"Size": 6756
"Size": 6761
},
"assemblies/System.dll": {
"Size": 2275
"Size": 2279
},
"assemblies/System.Drawing.dll": {
"Size": 1957
"Size": 1961
},
"assemblies/System.Drawing.Primitives.dll": {
"Size": 12199
"Size": 12206
},
"assemblies/System.IO.Compression.dll": {
"Size": 17218
"Size": 17221
},
"assemblies/System.IO.IsolatedStorage.dll": {
"Size": 10566
"Size": 10569
},
"assemblies/System.Linq.dll": {
"Size": 19474
"Size": 19480
},
"assemblies/System.Linq.Expressions.dll": {
"Size": 182081
"Size": 182084
},
"assemblies/System.Net.Http.dll": {
"Size": 65831
"Size": 65842
},
"assemblies/System.Net.Primitives.dll": {
"Size": 22364
"Size": 22368
},
"assemblies/System.Net.Requests.dll": {
"Size": 3731
"Size": 3734
},
"assemblies/System.ObjectModel.dll": {
"Size": 11970
"Size": 11974
},
"assemblies/System.Private.CoreLib.dll": {
"Size": 757425
"Size": 757472
},
"assemblies/System.Private.DataContractSerialization.dll": {
"Size": 191072
"Size": 191079
},
"assemblies/System.Private.Uri.dll": {
"Size": 43677
"Size": 43502
},
"assemblies/System.Private.Xml.dll": {
"Size": 220171
"Size": 220183
},
"assemblies/System.Private.Xml.Linq.dll": {
"Size": 17101
"Size": 17099
},
"assemblies/System.Runtime.CompilerServices.Unsafe.dll": {
"Size": 1214
"Size": 1216
},
"assemblies/System.Runtime.dll": {
"Size": 2557
"Size": 2561
},
"assemblies/System.Runtime.Serialization.dll": {
"Size": 1889
"Size": 1893
},
"assemblies/System.Runtime.Serialization.Formatters.dll": {
"Size": 2634
"Size": 2637
},
"assemblies/System.Runtime.Serialization.Primitives.dll": {
"Size": 3940
"Size": 3943
},
"assemblies/System.Security.Cryptography.Algorithms.dll": {
"Size": 6809
"Size": 6815
},
"assemblies/System.Security.Cryptography.Primitives.dll": {
"Size": 2968
"Size": 2973
},
"assemblies/System.Text.RegularExpressions.dll": {
"Size": 76698
"Size": 76702
},
"assemblies/System.Xml.dll": {
"Size": 1779
"Size": 1782
},
"assemblies/UnnamedProject.dll": {
"Size": 117239
"Size": 117237
},
"assemblies/Xamarin.AndroidX.Activity.dll": {
"Size": 6069
Expand All @@ -134,40 +134,40 @@
"Size": 6095
},
"assemblies/Xamarin.AndroidX.AppCompat.dll": {
"Size": 112590
"Size": 112591
},
"assemblies/Xamarin.AndroidX.CardView.dll": {
"Size": 6809
"Size": 6810
},
"assemblies/Xamarin.AndroidX.CoordinatorLayout.dll": {
"Size": 16603
},
"assemblies/Xamarin.AndroidX.Core.dll": {
"Size": 96723
"Size": 96722
},
"assemblies/Xamarin.AndroidX.DrawerLayout.dll": {
"Size": 14273
"Size": 14271
},
"assemblies/Xamarin.AndroidX.Fragment.dll": {
"Size": 39924
"Size": 39926
},
"assemblies/Xamarin.AndroidX.Legacy.Support.Core.UI.dll": {
"Size": 6132
"Size": 6133
},
"assemblies/Xamarin.AndroidX.Lifecycle.Common.dll": {
"Size": 6592
},
"assemblies/Xamarin.AndroidX.Lifecycle.LiveData.Core.dll": {
"Size": 6671
"Size": 6672
},
"assemblies/Xamarin.AndroidX.Lifecycle.ViewModel.dll": {
"Size": 3273
"Size": 3272
},
"assemblies/Xamarin.AndroidX.Loader.dll": {
"Size": 12671
"Size": 12670
},
"assemblies/Xamarin.AndroidX.RecyclerView.dll": {
"Size": 84688
"Size": 84687
},
"assemblies/Xamarin.AndroidX.SavedState.dll": {
"Size": 5077
Expand All @@ -176,13 +176,13 @@
"Size": 10382
},
"assemblies/Xamarin.AndroidX.ViewPager.dll": {
"Size": 17986
"Size": 17985
},
"assemblies/Xamarin.Forms.Core.dll": {
"Size": 528450
},
"assemblies/Xamarin.Forms.Platform.Android.dll": {
"Size": 384996
"Size": 384997
},
"assemblies/Xamarin.Forms.Platform.dll": {
"Size": 56878
Expand All @@ -191,13 +191,13 @@
"Size": 60774
},
"assemblies/Xamarin.Google.Android.Material.dll": {
"Size": 40134
"Size": 40135
},
"classes.dex": {
"Size": 3458288
},
"lib/arm64-v8a/libmonodroid.so": {
"Size": 382776
"Size": 382304
},
"lib/arm64-v8a/libmonosgen-2.0.so": {
"Size": 3192432
Expand All @@ -212,7 +212,7 @@
"Size": 150032
},
"lib/arm64-v8a/libxamarin-app.so": {
"Size": 133192
"Size": 98624
},
"META-INF/android.support.design_material.version": {
"Size": 12
Expand Down Expand Up @@ -1967,5 +1967,5 @@
"Size": 341228
}
},
"PackageSize": 7930399
"PackageSize": 7942687
}
Loading

0 comments on commit f48b97c

Please sign in to comment.