Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Mono.Android] avoid System.Reflection.Emit usage for common calls #6657

Merged
merged 3 commits into from
Jan 28, 2022

Commits on Jan 28, 2022

  1. [Mono.Android] avoid System.Reflection.Emit usage for common calls

    `dotnet new android` calls `JNINativeWrapper.CreateDelegate()` twice,
    while `dotnet new maui` ends up calling it 58 times during startup.
    
    This code path calls System.Reflection.Emit to generate a
    `System.Delegate` at runtime to be invoked. After some thought, it is
    possible to "hard code" common delegate types and avoid SRE:
    
        private static Delegate CreateBuiltInDelegate (Delegate dlg, Type delegateType)
        {
            switch (delegateType.Name) {
                case nameof (_JniMarshal_PP_V): {
                        _JniMarshal_PP_V callback = (_JniMarshal_PP_V) Delegate.CreateDelegate (typeof (_JniMarshal_PP_V), dlg.Target, dlg.Method);
                        _JniMarshal_PP_V result = (jnienv, klazz) => {
                            JNIEnv.WaitForBridgeProcessing ();
                            try {
                                callback (jnienv, klazz);
                            } catch (Exception e) {
                                bool filter = Debugger.IsAttached || !JNIEnv.PropagateExceptions;
                                if (filter && JNIEnv.mono_unhandled_exception != null) {
                                    JNIEnv.mono_unhandled_exception (e);
                                }
                                AndroidEnvironment.UnhandledException (e);
                                if (filter)
                                    throw;
    
                            }
                        };
                        return result;
                    }
            // etc.
    
    `dotnet new maui`'s startup can be covered if we hardcoded:
    
        _JniMarshal_PP_V
        _JniMarshal_PPI_V
        _JniMarshal_PPL_L
        _JniMarshal_PPL_V
        _JniMarshal_PPL_Z
        _JniMarshal_PPII_V
        _JniMarshal_PPLI_V
        _JniMarshal_PPLL_V
        _JniMarshal_PPLL_Z
        _JniMarshal_PPIIL_V
        _JniMarshal_PPILL_V
        _JniMarshal_PPLIL_Z
        _JniMarshal_PPLLL_L
        _JniMarshal_PPLLL_Z
        _JniMarshal_PPIIII_V
        _JniMarshal_PPLLLL_V
        _JniMarshal_PPLIIII_V
        _JniMarshal_PPZIIII_V
        _JniMarshal_PPLIIIIIIII_V
    
    Then System.Reflection.Emit isn't used at all.
    
    Other changes:
    
    * `TypeManager.GetActivateHandler()` needs to use
      `_JniMarshal_PPLLLL_V` instead of `Action<IntPtr, IntPtr, IntPtr,
      IntPtr, IntPtr, IntPtr>`, so the fast path can be used.
    
    * Added a log message for `debug.mono.log assembly`:
    
        Falling back to System.Reflection.Emit for delegate type '{delegateType}': {dlg.Method}
    
    * I was also able to remove `mono_unhandled_exception_method` from
      `JNINativeWrapper` as we already has this value in `JNIEnv`.
    
    ~~ Results ~~
    
    Testing `dotnet new maui` with version:
    
        msbuild Xamarin.Android.sln -t:InstallMaui -bl -p:MauiVersion=6.0.200-preview.13.2536
    
    A `Release` build on a Pixel 5 device, total startup time:
    
        Before:
        01-21 11:58:39.030  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s83ms
        01-21 11:58:41.297  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s127ms
        01-21 11:58:43.429  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s68ms
        01-21 11:58:45.702  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s113ms
        01-21 11:58:47.906  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s125ms
        01-21 11:58:50.082  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s92ms
        01-21 11:58:52.297  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s133ms
        01-21 11:58:54.465  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s102ms
        01-21 11:58:56.673  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s126ms
        01-21 11:58:58.848  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s94ms
        Average(ms): 1106.3
        Std Err(ms): 6.91865433287267
        Std Dev(ms): 21.8787060352704
    
        After:
        01-21 12:00:33.312  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s61ms
        01-21 12:00:35.513  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s79ms
        01-21 12:00:37.724  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s85ms
        01-21 12:00:39.928  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s83ms
        01-21 12:00:42.117  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s60ms
        01-21 12:00:44.337  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s59ms
        01-21 12:00:46.612  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s106ms
        01-21 12:00:48.782  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s47ms
        01-21 12:00:51.018  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s75ms
        01-21 12:00:53.200  1867  2226 I ActivityTaskManager: Displayed com.companyname.bar/crc6474063b540de74d1e.MainActivity: +1s53ms
        Average(ms): 1070.8
        Std Err(ms): 5.68584988272544
        Std Dev(ms): 17.9802360632137
    
    This might save ~35ms on average?
    
    If I time the message for one call, such as:
    
        01-21 12:14:01.132 29819 29819 I monodroid-timing: Runtime.register: registering type `Microsoft.Maui.MauiApplication, Microsoft.Maui, Version=1.0.0.0,
        Culture=neutral, PublicKeyToken=null`
        01-21 12:14:01.150 29819 29819 I monodroid-timing: Runtime.register: end time; elapsed: 0s:17::794845
    
    https://github.com/dotnet/maui/blob/bfba62ed796d3416c4fcaa7cfbea86dc8d5e04c2/src/Compatibility/ControlGallery/src/Android/MainApplication.cs
    
    The result is:
    
        Before:
        01-21 13:21:22.261  9359  9359 I monodroid-timing: Runtime.register: end time; elapsed: 0s:23::868440
        01-21 13:21:24.446  9424  9424 I monodroid-timing: Runtime.register: end time; elapsed: 0s:23::875159
        01-21 13:21:26.639  9497  9497 I monodroid-timing: Runtime.register: end time; elapsed: 0s:23::829742
        01-21 13:21:28.842  9570  9570 I monodroid-timing: Runtime.register: end time; elapsed: 0s:24::165211
        01-21 13:21:31.021  9631  9631 I monodroid-timing: Runtime.register: end time; elapsed: 0s:23::976721
        01-21 13:21:33.221  9697  9697 I monodroid-timing: Runtime.register: end time; elapsed: 0s:24::237034
        01-21 13:21:35.418  9759  9759 I monodroid-timing: Runtime.register: end time; elapsed: 0s:23::857346
        01-21 13:21:37.585  9821  9821 I monodroid-timing: Runtime.register: end time; elapsed: 0s:23::872138
        01-21 13:21:39.805  9884  9884 I monodroid-timing: Runtime.register: end time; elapsed: 0s:23::840107
        01-21 13:21:42.014  9952  9952 I monodroid-timing: Runtime.register: end time; elapsed: 0s:23::723805
        Average(ms): 23.9245703
        Std Err(ms): 0.0502830551065224
        Std Dev(ms): 0.159008981848371
    
        After:
        01-21 13:22:28.004 10226 10226 I monodroid-timing: Runtime.register: end time; elapsed: 0s:18::12345
        01-21 13:22:30.261 10298 10298 I monodroid-timing: Runtime.register: end time; elapsed: 0s:18::338022
        01-21 13:22:32.443 10362 10362 I monodroid-timing: Runtime.register: end time; elapsed: 0s:18::56251
        01-21 13:22:34.664 10427 10427 I monodroid-timing: Runtime.register: end time; elapsed: 0s:17::997397
        01-21 13:22:36.902 10497 10497 I monodroid-timing: Runtime.register: end time; elapsed: 0s:17::787554
        01-21 13:22:39.117 10563 10563 I monodroid-timing: Runtime.register: end time; elapsed: 0s:17::801772
        01-21 13:22:41.306 10630 10630 I monodroid-timing: Runtime.register: end time; elapsed: 0s:18::28752
        01-21 13:22:43.552 10695 10695 I monodroid-timing: Runtime.register: end time; elapsed: 0s:18::75522
        01-21 13:22:45.761 10759 10759 I monodroid-timing: Runtime.register: end time; elapsed: 0s:17::958075
        01-21 13:22:47.978 10823 10823 I monodroid-timing: Runtime.register: end time; elapsed: 0s:18::961773
        Average(ms): 18.1017463
        Std Err(ms): 0.107071213504746
        Std Dev(ms): 0.338588906513177
    
    Saving ~5.8ms for this one call.
    
    `.apk` size difference for `dotnet new android`:
    
        > apkdiff -f before.apk after.apk
        Size difference in bytes ([*1] apk1 only, [*2] apk2 only):
        +       3,390 assemblies/assemblies.blob
        +          54 assemblies/assemblies.x86_64.blob
        -           4 assemblies/assemblies.arm64_v8a.blob
        -          15 assemblies/assemblies.x86.blob
        -          65 assemblies/assemblies.armeabi_v7a.blob
        Summary:
        +       3,360 Other entries 0.03% (of 10,526,432)
        +           0 Dalvik executables 0.00% (of 7,816,392)
        +           0 Shared libraries 0.00% (of 18,414,404)
        +       4,096 Package size difference 0.02% (of 21,006,128)
    jonathanpeppers committed Jan 28, 2022
    Configuration menu
    Copy the full SHA
    5faa35a View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    a76034b View commit details
    Browse the repository at this point in the history
  3. Configuration menu
    Copy the full SHA
    decce29 View commit details
    Browse the repository at this point in the history