Skip to content

Commit f48b97c

Browse files
authored
[monodroid] Speed up java-to-managed typemap lookups (#6905)
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% ✓ |
1 parent 9f3ee58 commit f48b97c

File tree

18 files changed

+548
-368
lines changed

18 files changed

+548
-368
lines changed

src/Mono.Android/Android.Runtime/JNIEnv.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ internal static bool ShouldWrapJavaException (Java.Lang.Throwable? t, [CallerMem
119119
[DllImport ("libc")]
120120
static extern int gettid ();
121121

122+
#if NETCOREAPP
123+
[UnmanagedCallersOnly]
124+
#endif
122125
static unsafe void RegisterJniNatives (IntPtr typeName_ptr, int typeName_len, IntPtr jniClass, IntPtr methods_ptr, int methods_len)
123126
{
124127
string typeName = new string ((char*) typeName_ptr, 0, typeName_len);

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public sealed class ApplicationConfig
8989
"map_modules",
9090
"map_module_count",
9191
"java_type_count",
92-
"java_name_width",
92+
"map_java_hashes",
9393
"map_java",
9494
"mono_aot_mode_name",
9595
};

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
"Size": 3032
66
},
77
"assemblies/Java.Interop.dll": {
8-
"Size": 55111
8+
"Size": 55106
99
},
1010
"assemblies/Mono.Android.dll": {
11-
"Size": 88334
11+
"Size": 88461
1212
},
1313
"assemblies/rc.bin": {
1414
"Size": 1083
@@ -26,13 +26,13 @@
2626
"Size": 2374
2727
},
2828
"assemblies/UnnamedProject.dll": {
29-
"Size": 3551
29+
"Size": 3546
3030
},
3131
"classes.dex": {
3232
"Size": 345328
3333
},
3434
"lib/arm64-v8a/libmonodroid.so": {
35-
"Size": 380832
35+
"Size": 382304
3636
},
3737
"lib/arm64-v8a/libmonosgen-2.0.so": {
3838
"Size": 3192432
@@ -47,7 +47,7 @@
4747
"Size": 150032
4848
},
4949
"lib/arm64-v8a/libxamarin-app.so": {
50-
"Size": 8688
50+
"Size": 9424
5151
},
5252
"META-INF/BNDLTOOL.RSA": {
5353
"Size": 1213
@@ -59,7 +59,7 @@
5959
"Size": 2467
6060
},
6161
"res/drawable-hdpi-v4/icon.png": {
62-
"Size": 4762
62+
"Size": 4791
6363
},
6464
"res/drawable-mdpi-v4/icon.png": {
6565
"Size": 2200
@@ -83,5 +83,5 @@
8383
"Size": 1904
8484
}
8585
},
86-
"PackageSize": 2701303
86+
"PackageSize": 2705399
8787
}

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@
55
"Size": 2604
66
},
77
"assemblies/Java.Interop.dll": {
8-
"Size": 67953
8+
"Size": 67956
99
},
1010
"assemblies/Mono.Android.dll": {
11-
"Size": 256591
11+
"Size": 256630
1212
},
1313
"assemblies/mscorlib.dll": {
14-
"Size": 769016
14+
"Size": 769015
1515
},
1616
"assemblies/System.Core.dll": {
17-
"Size": 28198
17+
"Size": 28199
1818
},
1919
"assemblies/System.dll": {
2020
"Size": 9180
2121
},
2222
"assemblies/UnnamedProject.dll": {
23-
"Size": 2880
23+
"Size": 2881
2424
},
2525
"classes.dex": {
2626
"Size": 347796
@@ -32,7 +32,7 @@
3232
"Size": 750976
3333
},
3434
"lib/arm64-v8a/libmonodroid.so": {
35-
"Size": 297544
35+
"Size": 296192
3636
},
3737
"lib/arm64-v8a/libmonosgen-2.0.so": {
3838
"Size": 4030448
@@ -41,7 +41,7 @@
4141
"Size": 65512
4242
},
4343
"lib/arm64-v8a/libxamarin-app.so": {
44-
"Size": 18272
44+
"Size": 19960
4545
},
4646
"META-INF/ANDROIDD.RSA": {
4747
"Size": 1213

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc

Lines changed: 55 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -8,124 +8,124 @@
88
"Size": 7247
99
},
1010
"assemblies/Java.Interop.dll": {
11-
"Size": 62006
11+
"Size": 62017
1212
},
1313
"assemblies/Mono.Android.dll": {
14-
"Size": 441706
14+
"Size": 442008
1515
},
1616
"assemblies/mscorlib.dll": {
17-
"Size": 3798
17+
"Size": 3803
1818
},
1919
"assemblies/netstandard.dll": {
20-
"Size": 5499
20+
"Size": 5503
2121
},
2222
"assemblies/rc.bin": {
2323
"Size": 1083
2424
},
2525
"assemblies/System.Collections.Concurrent.dll": {
26-
"Size": 11227
26+
"Size": 11230
2727
},
2828
"assemblies/System.Collections.dll": {
29-
"Size": 16736
29+
"Size": 16741
3030
},
3131
"assemblies/System.Collections.NonGeneric.dll": {
32-
"Size": 8439
32+
"Size": 8443
3333
},
3434
"assemblies/System.ComponentModel.dll": {
35-
"Size": 1961
35+
"Size": 1965
3636
},
3737
"assemblies/System.ComponentModel.Primitives.dll": {
38-
"Size": 2566
38+
"Size": 2569
3939
},
4040
"assemblies/System.ComponentModel.TypeConverter.dll": {
41-
"Size": 5968
41+
"Size": 5970
4242
},
4343
"assemblies/System.Console.dll": {
44-
"Size": 6530
44+
"Size": 6532
4545
},
4646
"assemblies/System.Core.dll": {
47-
"Size": 1928
47+
"Size": 1932
4848
},
4949
"assemblies/System.Diagnostics.TraceSource.dll": {
50-
"Size": 6756
50+
"Size": 6761
5151
},
5252
"assemblies/System.dll": {
53-
"Size": 2275
53+
"Size": 2279
5454
},
5555
"assemblies/System.Drawing.dll": {
56-
"Size": 1957
56+
"Size": 1961
5757
},
5858
"assemblies/System.Drawing.Primitives.dll": {
59-
"Size": 12199
59+
"Size": 12206
6060
},
6161
"assemblies/System.IO.Compression.dll": {
62-
"Size": 17218
62+
"Size": 17221
6363
},
6464
"assemblies/System.IO.IsolatedStorage.dll": {
65-
"Size": 10566
65+
"Size": 10569
6666
},
6767
"assemblies/System.Linq.dll": {
68-
"Size": 19474
68+
"Size": 19480
6969
},
7070
"assemblies/System.Linq.Expressions.dll": {
71-
"Size": 182081
71+
"Size": 182084
7272
},
7373
"assemblies/System.Net.Http.dll": {
74-
"Size": 65831
74+
"Size": 65842
7575
},
7676
"assemblies/System.Net.Primitives.dll": {
77-
"Size": 22364
77+
"Size": 22368
7878
},
7979
"assemblies/System.Net.Requests.dll": {
80-
"Size": 3731
80+
"Size": 3734
8181
},
8282
"assemblies/System.ObjectModel.dll": {
83-
"Size": 11970
83+
"Size": 11974
8484
},
8585
"assemblies/System.Private.CoreLib.dll": {
86-
"Size": 757425
86+
"Size": 757472
8787
},
8888
"assemblies/System.Private.DataContractSerialization.dll": {
89-
"Size": 191072
89+
"Size": 191079
9090
},
9191
"assemblies/System.Private.Uri.dll": {
92-
"Size": 43677
92+
"Size": 43502
9393
},
9494
"assemblies/System.Private.Xml.dll": {
95-
"Size": 220171
95+
"Size": 220183
9696
},
9797
"assemblies/System.Private.Xml.Linq.dll": {
98-
"Size": 17101
98+
"Size": 17099
9999
},
100100
"assemblies/System.Runtime.CompilerServices.Unsafe.dll": {
101-
"Size": 1214
101+
"Size": 1216
102102
},
103103
"assemblies/System.Runtime.dll": {
104-
"Size": 2557
104+
"Size": 2561
105105
},
106106
"assemblies/System.Runtime.Serialization.dll": {
107-
"Size": 1889
107+
"Size": 1893
108108
},
109109
"assemblies/System.Runtime.Serialization.Formatters.dll": {
110-
"Size": 2634
110+
"Size": 2637
111111
},
112112
"assemblies/System.Runtime.Serialization.Primitives.dll": {
113-
"Size": 3940
113+
"Size": 3943
114114
},
115115
"assemblies/System.Security.Cryptography.Algorithms.dll": {
116-
"Size": 6809
116+
"Size": 6815
117117
},
118118
"assemblies/System.Security.Cryptography.Primitives.dll": {
119-
"Size": 2968
119+
"Size": 2973
120120
},
121121
"assemblies/System.Text.RegularExpressions.dll": {
122-
"Size": 76698
122+
"Size": 76702
123123
},
124124
"assemblies/System.Xml.dll": {
125-
"Size": 1779
125+
"Size": 1782
126126
},
127127
"assemblies/UnnamedProject.dll": {
128-
"Size": 117239
128+
"Size": 117237
129129
},
130130
"assemblies/Xamarin.AndroidX.Activity.dll": {
131131
"Size": 6069
@@ -134,40 +134,40 @@
134134
"Size": 6095
135135
},
136136
"assemblies/Xamarin.AndroidX.AppCompat.dll": {
137-
"Size": 112590
137+
"Size": 112591
138138
},
139139
"assemblies/Xamarin.AndroidX.CardView.dll": {
140-
"Size": 6809
140+
"Size": 6810
141141
},
142142
"assemblies/Xamarin.AndroidX.CoordinatorLayout.dll": {
143143
"Size": 16603
144144
},
145145
"assemblies/Xamarin.AndroidX.Core.dll": {
146-
"Size": 96723
146+
"Size": 96722
147147
},
148148
"assemblies/Xamarin.AndroidX.DrawerLayout.dll": {
149-
"Size": 14273
149+
"Size": 14271
150150
},
151151
"assemblies/Xamarin.AndroidX.Fragment.dll": {
152-
"Size": 39924
152+
"Size": 39926
153153
},
154154
"assemblies/Xamarin.AndroidX.Legacy.Support.Core.UI.dll": {
155-
"Size": 6132
155+
"Size": 6133
156156
},
157157
"assemblies/Xamarin.AndroidX.Lifecycle.Common.dll": {
158158
"Size": 6592
159159
},
160160
"assemblies/Xamarin.AndroidX.Lifecycle.LiveData.Core.dll": {
161-
"Size": 6671
161+
"Size": 6672
162162
},
163163
"assemblies/Xamarin.AndroidX.Lifecycle.ViewModel.dll": {
164-
"Size": 3273
164+
"Size": 3272
165165
},
166166
"assemblies/Xamarin.AndroidX.Loader.dll": {
167-
"Size": 12671
167+
"Size": 12670
168168
},
169169
"assemblies/Xamarin.AndroidX.RecyclerView.dll": {
170-
"Size": 84688
170+
"Size": 84687
171171
},
172172
"assemblies/Xamarin.AndroidX.SavedState.dll": {
173173
"Size": 5077
@@ -176,13 +176,13 @@
176176
"Size": 10382
177177
},
178178
"assemblies/Xamarin.AndroidX.ViewPager.dll": {
179-
"Size": 17986
179+
"Size": 17985
180180
},
181181
"assemblies/Xamarin.Forms.Core.dll": {
182182
"Size": 528450
183183
},
184184
"assemblies/Xamarin.Forms.Platform.Android.dll": {
185-
"Size": 384996
185+
"Size": 384997
186186
},
187187
"assemblies/Xamarin.Forms.Platform.dll": {
188188
"Size": 56878
@@ -191,13 +191,13 @@
191191
"Size": 60774
192192
},
193193
"assemblies/Xamarin.Google.Android.Material.dll": {
194-
"Size": 40134
194+
"Size": 40135
195195
},
196196
"classes.dex": {
197197
"Size": 3458288
198198
},
199199
"lib/arm64-v8a/libmonodroid.so": {
200-
"Size": 382776
200+
"Size": 382304
201201
},
202202
"lib/arm64-v8a/libmonosgen-2.0.so": {
203203
"Size": 3192432
@@ -212,7 +212,7 @@
212212
"Size": 150032
213213
},
214214
"lib/arm64-v8a/libxamarin-app.so": {
215-
"Size": 133192
215+
"Size": 98624
216216
},
217217
"META-INF/android.support.design_material.version": {
218218
"Size": 12
@@ -1967,5 +1967,5 @@
19671967
"Size": 341228
19681968
}
19691969
},
1970-
"PackageSize": 7930399
1970+
"PackageSize": 7942687
19711971
}

0 commit comments

Comments
 (0)