Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Speed up java-to-managed typemap lookups
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 costed more time than necessary. To improve comparison speed, this commit implements lookups based on hash values (using the `xxHash` algorithm) calculated for all the 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`) to a separate 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 * a simpler binary search function was implemented * 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 Startup performance gains vary depending on where we look. The `Displayed` time sees changes that are negligible, however the most affected area of the startup sequence (the call to `JNIEnv.Initialize`) which registers types and involves the biggest number of lookups sees improvements of up to 12%. The changes will also positively affect application performance after startup: On Pixel 3 XL running Android 12: | Before | After | Δ | Notes | | ------ | ------ | -------- | ---------------------------------------------- | | 14.967 | 13.586 | -9.23% ✓ | preload enabled; 32-bit build | | 15.312 | 14.343 | -6.33% ✓ | preload enabled; 32-bit build; no compression | | 13.577 | 12.792 | -5.78% ✓ | preload enabled; 64-bit build | | 13.677 | 12.894 | -5.73% ✓ | preload disabled; 64-bit build; no compression | | 13.601 | 12.838 | -5.61% ✓ | preload disabled; 64-bit build | | 13.656 | 12.953 | -5.15% ✓ | preload enabled; 64-bit build; no compression | | 14.638 | 14.070 | -3.88% ✓ | preload disabled; 32-bit build | | 15.053 | 14.526 | -3.50% ✓ | preload disabled; 32-bit build; no compression | On Pixel 6 XL running Android 12: | Before | After | Δ | Notes | | ------ | ----- | --------- | ---------------------------------------------- | | 8.972 | 7.826 | -12.78% ✓ | preload enabled; 32-bit build | | 8.833 | 7.823 | -11.43% ✓ | preload enabled; 32-bit build; no compression | | 8.611 | 8.031 | -6.74% ✓ | preload disabled; 32-bit build; no compression | | 6.533 | 6.104 | -6.57% ✓ | preload disabled; 64-bit build; no compression | | 6.504 | 6.119 | -5.92% ✓ | preload enabled; 64-bit build; no compression | | 6.426 | 6.052 | -5.83% ✓ | preload disabled; 64-bit build | | 6.493 | 6.125 | -5.67% ✓ | preload enabled; 64-bit build | | 8.446 | 8.088 | -4.23% ✓ | preload disabled; 32-bit build |
- Loading branch information