Skip to content

Commit

Permalink
[Mono.Android, monodroid] JNIEnv.Initialize optimization (#3729)
Browse files Browse the repository at this point in the history
`JNIEnv.Initialize()` is part of the Xamarin.Android runtime startup
sequence and it contributes a sizable chunk of time to it.  Some code
executed during startup  wasn't as optimized as it could be, so fix
all the low-hanging fruit in that area:

  * Don't use the `Logger` class:
    This removes one class instantiation which wasn't absolutely
    necessary.  Calls to `Logger` are replaced with calls to a new
    native function `monodroid_log()`.

  * Use native functions for timing [^0]:
    This won't affect the user's code (unless it runs with timing
    enabled, of course) but will allow our timing to be more precise
    and have less impact on code performance.  Implemented with two
    new native functions `monodroid_timing_start()` and
    `monodroid_timing_stop()`.

  * Remove calls to `Environment.GetEnvironmentVariable()`
    The calls took a non-trivial amount of time (around 4ms each) and
    were replaced by putting the environment variables in
    `libxamarin-app.so`, in the application environment block.

  * Do not fully initialize `UncaughtExceptionHandler` during init:
    The handler's initialization is quite heavy while the handler
    itself is going to be used only when there's an unhandled
    exception thrown.  Delay full initialization to the point in time
    when the handler is actually needed.

  * Avoid parsing of the `PackageNamingPolicy` enum values at runtime:
    Instead, the desired value is placed in the application's
    environment configuration block and simply casts `int` -> enum.

  * Initialize `Logger` category lazily.
    The logging category is requested by `Logger` lazily instead of
    being set from `JNIEnv.Initialize()`.  This allows the `Logger`
    class to not be instantiated during startup.

  * Avoid static initialization of the `Java.Interop.Runtime` class.

  * Avoid heavy static initialization of `Java.Interop.TypeManager`:
    This is done by initializing the type lookup dictionaries lazily,
    via another "proxy" class.
    
  * Improve Java class name lookup faster:
    This is done by moving the lookup (and processing) to native code
    and P/Invoking `monodroid_TypeManager_get_java_class_name()`.

Other changes:

  * Remove dead code from `JNIEnv`:
    The `JAVA_INTEROP` symbol is always "defined" now, remove code
    used when it was not defined.

  * Introduce fast performance measurement for managed code:
    The timing code used by the native runtime is now exposed to
    managed land, thus allowing us fast and accurate (to the
    nanosecond level) timing measurement.

  * Update all `[DllImport("__Internal")]` declarations to specify
    `CallingConvention=CallingConvention.Cdecl`.  `libmonodroid.so`
    functions all use the Cdecl calling convention, and for maximum
    sanity we should not rely on the Windows/.NET default of
    [CallingConvention.Winapi][1], which is always wrong on Windows.

~~ Startup Improvements ~~

Start the `tests/Xamarin.Forms-Performance-Integration` app 10 times
on a Pixel 3 XL and average the **Runtime init** and **Displayed**
times vs. commit 438a598:

  * `$(AndroidEnablePreloadAssemblies)`=True, 32-bit app:
    * Previous: Runtime init=218.804ms                      Displayed=852.70ms
    *  Current: Runtime init=187.907ms  [30.90ms faster]    Displayed=828.90ms [23.8ms faster]

  * `$(AndroidEnablePreloadAssemblies)`=False, 32-bit app:
    * Previous: Runtime init=185.240ms                      Displayed=827.40ms
    *  Current: Runtime init=154.718ms  [30.52ms faster]    Displayed=803.50ms [23.9ms faster]

  * `$(AndroidEnablePreloadAssemblies)`=True, 64-bit app:
    * Previous: Runtime init=201.779ms                      Displayed=783.20ms
    *  Current: Runtime init=174.211ms  [27.57ms faster]    Displayed=754.50ms [28.7ms faster]

  * `$(AndroidEnablePreloadAssemblies)`=False, 64-bit app:
    * Previous: Runtime init=173.322ms                      Displayed=759.40ms
    *  Current: Runtime init=144.158ms  [29.16ms faster]    Displayed=741.80ms [17.6ms faster]

This is a ~14% reduction in `Runtime init` times and a
2-3% reduction in `Displayed` times.

[^0]: `adb shell setprop debug.mono.log timing`
[1]: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.callingconvention?view=netframework-4.8
  • Loading branch information
grendello authored and jonpryor committed Oct 10, 2019
1 parent 438a598 commit af02a65
Show file tree
Hide file tree
Showing 28 changed files with 574 additions and 737 deletions.
17 changes: 7 additions & 10 deletions src/Mono.Android/Android.Runtime/AndroidEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ static void GetDisplayDPI (out float x_dpi, out float y_dpi)
x_dpi = metrics.Xdpi;
y_dpi = metrics.Ydpi;
}

// This is invoked by
// System.Core!System.AndroidPlatform.GetDefaultTimeZone ()
// DO NOT REMOVE
Expand All @@ -241,16 +241,13 @@ static string GetDefaultTimeZone ()
try {
return Marshal.PtrToStringAnsi (id);
} finally {
monodroid_free (id);
JNIEnv.monodroid_free (id);
}
}

[DllImport ("__Internal")]
[DllImport ("__Internal", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr _monodroid_timezone_get_default_id ();

[DllImport ("__Internal")]
static extern void monodroid_free (IntPtr ptr);

// This is invoked by
// mscorlib.dll!System.AndroidPlatform.GetDefaultSyncContext()
// DO NOT REMOVE
Expand All @@ -265,26 +262,26 @@ static SynchronizationContext GetDefaultSyncContext ()
// These are invoked by
// System.dll!System.AndroidPlatform.getifaddrs
// DO NOT REMOVE
[DllImport ("__Internal")]
[DllImport ("__Internal", CallingConvention = CallingConvention.Cdecl)]
static extern int _monodroid_getifaddrs (out IntPtr ifap);

static int GetInterfaceAddresses (out IntPtr ifap)
{
return _monodroid_getifaddrs (out ifap);
}

// These are invoked by
// System.dll!System.AndroidPlatform.freeifaddrs
// DO NOT REMOVE
[DllImport ("__Internal")]
[DllImport ("__Internal", CallingConvention = CallingConvention.Cdecl)]
static extern void _monodroid_freeifaddrs (IntPtr ifap);

static void FreeInterfaceAddresses (IntPtr ifap)
{
_monodroid_freeifaddrs (ifap);
}

[DllImport ("__Internal")]
[DllImport ("__Internal", CallingConvention = CallingConvention.Cdecl)]
static extern void _monodroid_detect_cpu_and_architecture (ref ushort built_for_cpu, ref ushort running_on_cpu, ref byte is64bit);

static void DetectCPUAndArchitecture (out ushort builtForCPU, out ushort runningOnCPU, out bool is64bit)
Expand Down
6 changes: 3 additions & 3 deletions src/Mono.Android/Android.Runtime/AndroidRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public AndroidRuntimeOptions (IntPtr jnienv, IntPtr vm, bool allocNewObjectSuppo

class AndroidObjectReferenceManager : JniRuntime.JniObjectReferenceManager {

[DllImport ("__Internal")]
[DllImport ("__Internal", CallingConvention = CallingConvention.Cdecl)]
static extern int _monodroid_gref_get ();

public override int GlobalReferenceCount {
Expand Down Expand Up @@ -305,7 +305,7 @@ internal static bool CallRegisterMethod (JniNativeMethodRegistrationArguments ar
{
int idx;

if (typeName == null || !typesMap.TryGetValue (typeName, out idx))
if (typeName == null || !typesMap.TryGetValue (typeName, out idx))
return false;

return CallRegisterMethodByIndex (arguments, idx);
Expand Down Expand Up @@ -344,7 +344,7 @@ public override void RegisterNativeMembers (JniType jniType, Type type, string m
throw new InvalidOperationException (String.Format ("Specified managed method '{0}' was not found. Signature: {1}", mname, toks [1]));
callback = CreateDynamicCallback (minfo);
} else {
GetCallbackHandler connector = (GetCallbackHandler) Delegate.CreateDelegate (typeof (GetCallbackHandler),
GetCallbackHandler connector = (GetCallbackHandler) Delegate.CreateDelegate (typeof (GetCallbackHandler),
toks.Length == 4 ? Type.GetType (toks [3], true) : type, toks [2]);
callback = connector ();
}
Expand Down
Loading

0 comments on commit af02a65

Please sign in to comment.