Skip to content

[ios] Investigate stripping EntryPoint strings from assemblies as a size optimization for Mono iOS applications #21732

Open
@ivanpovazan

Description

@ivanpovazan

Description

In .NET 9 we enabled managed-static registrar to be the default setting when targeting iOS platforms with Mono.
This change regressed MAUI sample application size when targeting iOS by ~2-3% as reported in: #20174
The reason for the regression is generating more managed code which needs to be AOT-compiled.

During some investigation I noticed that after we run ILStrip (Mono's size-optimization task which removes unneeded IL code from assemblies which are preserved mostly for metadata information and reflection support) we keep UnmanagedCallersOnly attributes data on methods which are generated for the new registrar, for example:

.class auto ansi sealed nested assembly __Registrar_Callbacks__
         extends [System.Private.CoreLib]System.Object
  {
    .method public hidebysig static valuetype [Microsoft.iOS]ObjCRuntime.NativeHandle 
            callback_0_Eone_AppDelegate_get_Window(native int pobj,
                                                   native int sel,
                                                   native int* exception_gchandle) cil managed noinlining
    {
      .custom instance void [System.Private.CoreLib]System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( 01 00 01 00 53 0E 0A 45 6E 74 72 79 50 6F 69 6E   // ....S..EntryPoin
                                                                                                                              74 26 63 61 6C 6C 62 61 63 6B 5F 30 5F 45 6F 6E   // t&callback_0_Eon
                                                                                                                              65 5F 41 70 70 44 65 6C 65 67 61 74 65 5F 67 65   // e_AppDelegate_ge
                                                                                                                              74 5F 57 69 6E 64 6F 77 )                         // t_Window
      // Code size       1 (0x1)
      .maxstack  8
      IL_0000:  ret
    } // end of method __Registrar_Callbacks__::callback_0_Eone_AppDelegate_get_Window
...

Here we can see a size-saving opportunity, specifically in regard to EntryPoint values on UCO methods which are not needed or used during runtime.

SIze-saving

By creating a simple Mono.Cecil task which strips out only custom attributes from methods defined on __Registrar_Callbacks__ types from stripped assemblies of the app, it has been estimated that with a dotnet new maui app and .NET 9 GA SDK we could save ~2% of the application size when using Mono with this approach.

NOTE: The save saving is currently applicable only to Mono, as with Native AOT we do not need/keep managed assemblies in the application bundle.

Comparing managed-static registrar without and with UCO EntryPoint stripping with .NET 9 GA

dotnet new maui managed-static managed-static w stripped UCOs diff (%)
SOD (MB) 44,08 43,04 -2,36%
.ipa (MB) 15,45 15,07 -2,46%

Comparing static with managed-static registrar and UCO EntryPoint stripping with .NET 9 GA

Such an improvement would make the app sizes between the new managed-static and the old static registrar almost identical.

dotnet new maui static managed-static w stripped UCOs diff (%)
SOD (MB) 42,87 43,04 0,39%
.ipa (MB) 15,14 15,07 -0,47%

Proposal

Investigate adapting https://github.com/dotnet/runtime/tree/e7d837da5b1aacd9325a8b8f2214cfaf4d3f0ff6/src/tasks/MonoTargetsTasks/ILStrip/AssemblyStripper
so that it strips out EntryPoint values on UCO methods introduced for managed-static registrar and measure the actual size saving.

Metadata

Metadata

Assignees

No one assigned

    Labels

    app-sizeenhancementThe issue or pull request is an enhancement

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions