Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Runtime startup performance improvements
The goal of this commit is to make Xamarin.Android apps start faster. The changes are focused only around the very first stage of application startup - between Android invoking our Java startup code (MonoPackageManager) and the end of our `Runtime.init` (`Java_mono_android_Runtime_init` in the native runtime) which is when the user application is fully initialized and ready to start is launcher Activity. In order to achieve the goal, the following changes were made: * Java class lookup ("reflection"). We used to call the `FindClass` JNI function as part of the startup at a cost of several milliseconds. We now put the class handles (accessed with the `.class` Java accessor) in the `Runtime` class and initialize them from the static constructor. We then read those fields from within `Runtime.init`, which is passed a reference to the Java instance of the Runtime class. Additonally, a handful of class field/method lookups were moved out of the init code so that the code that doesn't use them doesn't have to pay the tax. * Android API level is passed to `Runtime.init` from Java instead of using JNI from the native code. * Limit logging. Previously whenever any of the `log_{info,debug}` functions were called we'd spend time preparing all the parameters to pass to the function, sometimes involving memory allocation, function calls, etc - only to discard all of that work **inside** the `log_*` call because the logging category used in that call was disabled. Now we check whether the category is enabled before we set out to construct the parameters. * Java/JNI type wrappers for string and array of strings. This both a convenience/correctness as well as a performance change. Introduced are two C++ wrapper classes for the `jstring` and `object array` types (specialized for object == jstring) which take care of efficiently caching the retrieved strings as well as of correctly deleting local references to the obtained objects. Both classes, `jstring_wrapper` and `jstring_array_wrapper` are optimized so that they compile into the equivalent of the current, hand-written, code. They also take care to make the minimum necessary number of calls in order to access the strings, both standalone and from arrays, as well as to release the resources. The string and array wrappers are passed around as references, thus using the minimum amount of memory. * Do not preload managed assemblies. We used to preload all of the application assemblies in order to find and invoke type initializers. This resulted in the list of assemblies being processed twice at the great expense of time. We now don't call the type initializers at all and the assemblies are loaded on demand. * Do not store application environment variables in a file inside the apk. The textual file used to be read from the apk(s) early in the process, requiring iteration over all the application apk files, opening each of them, browsing through the ZIP entries and, finally, reading the file line by line, parsing into the name and value parts to create either a property (`mono.aot`, `mono.llvm`) or any environment variables requested by the application developer (or the XA runtime). To speed the process up, this commit replaces the text file with a Java class generated during application build which contains an array of `"name", "value"` pairs. The class is passed to `Java_mono_android_Runtime_init` and its elements are used to create the requested environment variables. A handful of variables is special-cased in that they are not placed in the environment but rather to set flags in the `AndroidSystem` class. The variables are `mono.aot`, `mono.llvm` and `__XA_DSO_IN_APK`. This allowed to remove calls to create (fake) system properties as well as `getenv` in the init native function. * Don't try load LLVM.so when it won't be there because we're not using llvm * Convert package name to hash using optimized code without calling snprintf * Desktop build is determined on compile time instead of dynamically * xamarin_getifaddrs are initialized on demand, not at the startup. Startup time improvements for the XF integration test app (average): * Debug mode: Old: 1s 440ms New: 1s 100ms * Release mode: Old: 650ms New: 270ms
- Loading branch information