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

iOS application size increase when migrating from legacy xamarin-ios to .net #19381

Open
rotanov opened this issue Nov 2, 2023 · 8 comments
Open
Labels
app-size enhancement The issue or pull request is an enhancement
Milestone

Comments

@rotanov
Copy link

rotanov commented Nov 2, 2023

Description

Context: Migrating game engine written in c# from legacy xamarin ios to .NET7.

After the migration, when comparing minimal engine project for ios ("EmptyProject") I noticed non neglectable increase in size of application (.NET7: 13.3MB , .NET8 10.8MB)

I built the project with legacy xamarin ios, net7 and net8 and tried to make some analysis and comparison. See Details section. Attachment contains built applications, debug symbols, MachO symbol dumps, some stats, binlogs, see Attachment section.

I understand there are a lot of issues on the matter and some growth is to be expected.
But 10Mb (for net8) seems like too much, may be I made some mistakes, or there are methods to reduce application size I'm not aware of? (apart from TrimSafe on engine assembly)

Details

Main object of interest is predicted download size, since that is by what appstore limits availability of download over metered connections. .app/.ipa size is not helpful in that regard, because afaik executable gets encrypted by appstore, making it compression-resistant. We predict download size as uncompressed executable size + everything else in ipa/app compressed size.

SDK legacy-xamarin-ios net7.0-ios net8.0-ios
executable size 23083888 35026384 32599520
executable growth factor x1 x1.51 x1.41
executable growth diff 0 11942496 9515632
rest of the app 8265323 12979960 12168123
rest of the app compressed 4257091 5617476 5588735
predicted app download size 27340979 40643860 38188255
download size growth factor x1 x1.48 x1.39
download size growth diff 0 13302881 10847276

bloaty was used to generate symbols:

bloaty -d segments,sections,symbols,compileunits --debug-file=./bin/Release/EmptyProject.app.dSYM/Contents/Resources/DWARF/EmptyProject ./bin/Release/EmptyProject.app/EmptyProject -n 0 --tsv > symbols.txt

I used slightly modified (included in attachment) script by @ivanpovazan to generate stats. (mentioned here) Generated stats are also in attachment.

Script modifications are:

  1. introduced category, where I tried to couple together everything I think engine and game project are responsible for.
  2. categorized more symbols as CORLIB
  3. 2. broke the diff, so diff is disabled

Even so, the "OTHERS" section grew a lot.

Stats:

legacy xamarin ios
--------------------------------------------------------------------------------
Stats for symbols from __TEXT,__text: 
--------------------------------------------------------------------------------
Total number of _infos:                                    0
Total number of plts:                                  35625
Total number of wrappers:                               3155
Total number of icus:                                      0
Total number of Globalizations:                         1496
Total number of generics:                              10383
--------------------------------------------------------------------------------
Total size of _infos:                                      0
Total size of plts:                                   712672
Total size of wrappers:                               606384
Total size of icus:                                        0
Total size of Globalizations:                         290856
Total size of generics:                              2400912
--------------------------------------------------------------------------------
Total symbols:                                        102682
        Total corelib:                                         16840    16.40%
        Total sysios:                                           2504     2.44%
        Total mono:                                             7023     6.84%
        Total others:                                          26769    26.07%
        Total game and engine:                                 49546    48.25%
Total size:                                         19778536
        Total corelib:                                       3716896    18.79%
        Total sysios:                                         337796     1.71%
        Total mono:                                          3117024    15.76%
        Total others:                                        2240916    11.33%
        Total game and engine:                              10365904    52.41%
--------------------------------------------------------------------------------

net7
--------------------------------------------------------------------------------
Stats for symbols from __TEXT,__text: 
--------------------------------------------------------------------------------
Total number of _infos:                                    0
Total number of plts:                                  34430
Total number of wrappers:                               4171
Total number of icus:                                   5648
Total number of Globalizations:                         1904
Total number of generics:                              19493
--------------------------------------------------------------------------------
Total size of _infos:                                      0
Total size of plts:                                   688912
Total size of wrappers:                              1049952
Total size of icus:                                   872852
Total size of Globalizations:                         382336
Total size of generics:                              5410680
--------------------------------------------------------------------------------
Total symbols:                                        142057
        Total corelib:                                         37509    26.40%
        Total sysios:                                           3032     2.13%
        Total mono:                                             6696     4.71%
        Total others:                                          43358    30.52%
        Total game and engine:                                 51462    36.23%
Total size:                                         30762632
        Total corelib:                                       9797056    31.85%
        Total sysios:                                         528448     1.72%
        Total mono:                                          4327068    14.07%
        Total others:                                        4056944    13.19%
        Total game and engine:                              12053116    39.18%
--------------------------------------------------------------------------------
net8
--------------------------------------------------------------------------------
Stats for symbols from __TEXT,__text: 
--------------------------------------------------------------------------------
Total number of _infos:                                    0
Total number of plts:                                      1
Total number of wrappers:                               3604
Total number of icus:                                   5621
Total number of Globalizations:                         1323
Total number of generics:                              10026
--------------------------------------------------------------------------------
Total size of _infos:                                      0
Total size of plts:                                      140
Total size of wrappers:                               861976
Total size of icus:                                   872424
Total size of Globalizations:                         533484
Total size of generics:                              3182648
--------------------------------------------------------------------------------
Total symbols:                                        103924
        Total corelib:                                         34695    33.38%
        Total sysios:                                           2618     2.52%
        Total mono:                                             6379     6.14%
        Total others:                                          30602    29.45%
        Total game and engine:                                 29630    28.51%
Total size:                                         27785504
        Total corelib:                                       9430576    33.94%
        Total sysios:                                         414288     1.49%
        Total mono:                                          3943104    14.19%
        Total others:                                        4024752    14.49%
        Total game and engine:                               9972784    35.89%
--------------------------------------------------------------------------------

MachO sections comparison:

macho-sections-comparison

Environment

  • macOS Sonoma 14.0, intel CPU
  • XCode 15.0
Version information
Visual Studio Community 2022 for Mac
Version 17.6.6 (build 408)
Installation UUID: 7571e8bb-d341-4541-a456-2451824e8830

Runtime
.NET 7.0.3 (64-bit)
Architecture: X64
Microsoft.macOS.Sdk 13.1.1007; git-rev-head:8afca776a0a96613dfb7200e0917bb57f9ed5583; git-branch:release/7.0.1xx-xcode14.2

Roslyn (Language Service)
5.6.0-3.23180.6+99e956e42697a6dd886d1e12478ea2b27cceacfa

NuGet
Version: 6.4.0.117

Xamarin Designer
Version: 17.6.3.9
Hash: 2648399ae8
Branch: remotes/origin/d17-6
Build date: 2023-10-23 17:40:07 UTC

.NET SDK (x64)
SDK: /usr/local/share/dotnet/sdk/7.0.403/Sdks
SDK Version: 7.0.403
MSBuild SDKs: /Applications/Visual Studio.app/Contents/MonoBundle/MSBuild/Current/bin/Sdks

.NET Runtime (x64)
Runtime: /usr/local/share/dotnet/dotnet
Runtime Version: 7.0.13

Xamarin.Profiler
Version: 1.8.0.49
Location: /Applications/Xamarin Profiler.app/Contents/MacOS/Xamarin Profiler

Updater
Version: 11

Xamarin.Android
Not Installed

Microsoft Build of OpenJDK
Java SDK: Not Found

Eclipse Temurin JDK
Java SDK: Not Found

Android SDK Manager
Version: 17.6.0.50
Hash: a715dca
Branch: HEAD
Build date: 2023-10-23 17:40:12 UTC

Android Device Manager
Version: 0.0.0.1309
Hash: 06e3e77
Branch: HEAD
Build date: 2023-10-23 17:40:12 UTC

Apple Developer Tools
Xcode: 15.0 22265
Build: 15A240d

Xamarin.Mac
Version: 9.3.0.18 Visual Studio Community
Hash: 9d266025e
Branch: xcode14.3
Build date: 2023-09-06 19:52:26-0400

Xamarin.iOS
Version: 16.4.0.18 Visual Studio Community
Hash: 9d266025e
Branch: xcode14.3
Build date: 2023-09-06 19:52:27-0400

Build Information
Release ID: 1706060408
Git revision: 3912eca6712af97335aa3a782abaa75ff86ec74a
Build date: 2023-10-23 17:38:20+00
Build branch: release-17.6
Build lane: release-17.6

Operating System
Mac OS X 14.0.0
Darwin 23.0.0 Darwin Kernel Version 23.0.0
    Fri Sep 15 14:42:42 PDT 2023
    root:xnu-10002.1.13~1/RELEASE_X86_64 x86_64

Attachment

I'm sorry for the attachment format, max upload size is 20MB and there's extension whitelist.

  1. Download all the parts of the archive
  2. Swap .00x and .zip in the name of each file
  3. Extract. (I used 7zip-gui for windows to pack and split)

net-ios-app-size-analysis.001.zip
net-ios-app-size-analysis.002.zip
net-ios-app-size-analysis.003.zip
net-ios-app-size-analysis.004.zip
net-ios-app-size-analysis.005.zip

.
│   calc_stats.bat -- runs stats.py in each of top level directories starting with _
│   stats.py -- modified script
│   summary.md
├───legacy-xamarin-ios
│   │   symbols.txt -- symbols made using bloaty
│   │   symbols_*.list -- symbols filtered by stats.py
│   │   symbols_stats.list -- stats made by stats.py
│   │   msbuild.binlog -- binlog
│   │   
│   ├───EmptyProject.app -- the application
│   └───EmptyProject.app.dSYM -- debug symbols
│                               
├───net7.0-ios -- same structure                         
├───net8.0-ios -- same structure
│                           
├───_legacy-vs-net7 -- this and below are directories for running stats.py in (empty, since I commented out diff calculation in stats.py)
├───_legacy-vs-net8
├───_net7-vs-net8
└───macho-sections-comparison.png

@rolfbjarne
Copy link
Member

This is great diagnostics!

I had a look at the symbols_SYSIOS.list files, and for all the symbols I checked that were in multiple files (around a dozen), almost all were bigger in .NET 7/8 (slightly smaller in .NET 8 vs .NET 7, but both significantly bigger than legacy Xamarin).

Selecting a symbol at random (AVFoundation_AVMediaTypesExtensions_get_AVMediaTypeClosedCaption), it's 124 bytes in legacy Xamarin, 176 bytes in .NET 7 and 172 bytes in .NET 8. While this doesn't seem significant, it'll add up if something similar happens to every symbol (there were a few that had the same size in all versions, but none that I checked were smaller in .NET than legacy Xamarin).

Then I checked the AOT-compiled code for this method, and this is what it shows (note that bulid settings might be different from the original reporter, so the actual assembly is likely different).

Legacy Xamarin

Size: 124 bytes

bin/iPhone/Release/testapp.app/testapp:
(__TEXT,__text) section
_Xamarin_iOS_AVFoundation_AVMediaTypesExtensions_get_AVMediaTypeClosedCaption:
00000001001b2944        stp     x29, x30, [sp, #-0x10]!
00000001001b2948        adrp    x8, 526 ; 0x1003c0000
00000001001b294c        add     x8, x8, #0x280
00000001001b2950        ldrb    w8, [x8, #0x1aa]
00000001001b2954        cbz     w8, 0x1001b2994
00000001001b2958        adrp    x8, 551 ; 0x1003d9000
00000001001b295c        add     x8, x8, #0xe30
00000001001b2960        ldr     x9, [x8, #0x610]
00000001001b2964        ldr     x9, [x9]
00000001001b2968        cbz     x9, 0x1001b29a0
00000001001b296c        ldr     w10, [x9, #0x18]
00000001001b2970        cmp     w10, #0x3
00000001001b2974        b.ls    0x1001b29b0
00000001001b2978        ldr     x10, [x8, #0x618]
00000001001b297c        ldr     x1, [x8, #0x638]
00000001001b2980        add     x2, x9, #0x38
00000001001b2984        ldr     x0, [x10]
00000001001b2988        bl      _Xamarin_iOS_ObjCRuntime_Dlfcn_CachePointer_intptr_string_intptr_
00000001001b298c        ldp     x29, x30, [sp], #0x10
00000001001b2990        ret
00000001001b2994        mov     w0, #0x1aa
00000001001b2998        bl      _mono_aot_Xamarin_iOS_init_method
00000001001b299c        b       0x1001b2958
00000001001b29a0        adr     x1, #0x0
00000001001b29a4        nop
00000001001b29a8        orr     w0, wzr, #0x7f
00000001001b29ac        bl      _p_2
00000001001b29b0        adr     x1, #0x0
00000001001b29b4        nop
00000001001b29b8        mov     w0, #0x6c
00000001001b29bc        bl      _p_2

.NET 8

Size: 172 bytes

bin/Release/net8.0-ios/ios-arm64/MySimpleApp.app/MySimpleApp:
(__TEXT,__text) section
_Microsoft_iOS_AVFoundation_AVMediaTypesExtensions_get_AVMediaTypeClosedCaption:
0000000100f34338        stp     x20, x19, [sp, #-0x20]!
0000000100f3433c        stp     x29, x30, [sp, #0x10]
0000000100f34340        adrp    x8, 1471 ; 0x1014f3000
0000000100f34344        adrp    x19, 1450 ; 0x1014de000
0000000100f34348        nop
0000000100f3434c        ldrb    w20, [x19, #0xb1e]
0000000100f34350        ldr     x8, [x8, #0x868]
0000000100f34354        cbnz    x8, 0x100f3439c
0000000100f34358        cbz     w20, 0x100f343a4
0000000100f3435c        adrp    x8, 1449 ; 0x1014dd000
0000000100f34360        ldr     x8, [x8, #0x348]
0000000100f34364        ldr     x8, [x8]
0000000100f34368        ldr     w9, [x8, #0x18]
0000000100f3436c        cmp     w9, #0x3
0000000100f34370        b.ls    0x100f343d0
0000000100f34374        adrp    x9, 1449 ; 0x1014dd000
0000000100f34378        adrp    x10, 1449 ; 0x1014dd000
0000000100f3437c        add     x2, x8, #0x38
0000000100f34380        ldr     x9, [x9, #0x350]
0000000100f34384        ldr     x1, [x10, #0x370]
0000000100f34388        ldr     x0, [x9]
0000000100f3438c        bl      _Microsoft_iOS_ObjCRuntime_Dlfcn_CachePointer_intptr_string_intptr_
0000000100f34390        ldp     x29, x30, [sp, #0x10]
0000000100f34394        ldp     x20, x19, [sp], #0x20
0000000100f34398        ret
0000000100f3439c        bl      _mono_aot_Microsoft_iOS_icall_cold_wrapper_264
0000000100f343a0        cbnz    w20, 0x100f3435c
0000000100f343a4        adrp    x0, 920 ; 0x1012cc000
0000000100f343a8        add     x0, x0, #0x19d
0000000100f343ac        bl      _mono_aot_Microsoft_iOS_init_method
0000000100f343b0        mov     w8, #0x1
0000000100f343b4        strb    w8, [x19, #0xb1e]
0000000100f343b8        b       0x100f3435c
0000000100f343bc        adrp    x1, 1267 ; 0x101427000
0000000100f343c0        mov     w0, #0xeb
0000000100f343c4        ldr     x1, [x1, #0xe40]
0000000100f343c8        bl      plt_Microsoft_iOS__jit_icall_llvm_throw_corlib_exception_abs_trampoline
0000000100f343cc        brk     #0x1
0000000100f343d0        adrp    x1, 1267 ; 0x101427000
0000000100f343d4        mov     w0, #0xc9
0000000100f343d8        ldr     x1, [x1, #0xe48]
0000000100f343dc        bl      plt_Microsoft_iOS__jit_icall_llvm_throw_corlib_exception_abs_trampoline
0000000100f343e0        brk     #0x1

This is the IL (identical between legacy Xamarin and .NET):

System.IntPtr AVFoundation.AVMediaTypesExtensions::get_AVMediaTypeClosedCaption () (internal compilercontrolled hidebysig reuseslot specialname static)
	2 local variables:
		System.IntPtr* V_0
		System.IntPtr& V_1
	IL_0000: ldsfld System.IntPtr[] AVFoundation.AVMediaTypesExtensions::values
	IL_0005: ldc.i4.3
	IL_0006: ldelema System.IntPtr
	IL_000b: stloc.1
	IL_000c: ldloc.1
	IL_000d: conv.u
	IL_000e: stloc.0
	IL_000f: ldsfld System.IntPtr ObjCRuntime.Libraries/AVFoundation::Handle
	IL_0014: ldstr "AVMediaTypeClosedCaption"
	IL_0019: ldloc.0
	IL_001a: call System.IntPtr ObjCRuntime.Dlfcn::CachePointer(System.IntPtr,System.String,System.IntPtr*)
	IL_001f: ret

@ivanpovazan not sure who would be able to explain this difference in the AOT-compiled size?

@rolfbjarne rolfbjarne added this to the .NET 9 milestone Nov 6, 2023
@rolfbjarne rolfbjarne added the enhancement The issue or pull request is an enhancement label Nov 6, 2023
@ivanpovazan
Copy link
Contributor

@vargaz could you please take a look at Rolf's comment above?

@vargaz
Copy link
Contributor

vargaz commented Nov 7, 2023

Some differences:

  • the old version doesn't contain GC safe point checks, the new one does.
  • the new version is using a different version of llvm which seems to generate different code, i.e. uses more callee saved registers etc.

@vargaz
Copy link
Contributor

vargaz commented Nov 7, 2023

Most of the code size increase is probably caused by bigger input assemblies, i.e. corlib is probably bigger in net7/8.

@ivanpovazan
Copy link
Contributor

@rotanov thank you for the detailed analysis!

As you investigated differences in size of your iOS application, I was wondering if you considered trying out NativeAOT?

In .NET8 we released experimental support for targeting iOS platforms with NativeAOT which shows great potential in size savings - reaching 50% smaller apps, but has at the same time much stricter limitations regarding trimming and use of AOT-incompatible code than our MonoAOT offering: https://github.com/xamarin/xamarin-macios/blob/main/docs/nativeaot.md

Additionally, the sample code you reported that crashes with MonoAOT Repro.MakeChecker_Crashing in: dotnet/runtime#94063 works fine with NativeAOT.

@AndreyFromGomel
Copy link

Hi, I'm working on similar project as original poster and want to highlight that AppExtensions are significantly larger for .Net 8.
For Xamarin App extension compiles to 2.7 MB appex, for .Net it is ~12 MB with the same project settings.
I tried to change project parameters to resolve this issue, the best scenario I got is 7.5 MB when I set to to use interpreter for app extension. When #17877 be fixed it reduce appex size on additional 2 MB, but still it 2x size compared to Xamarin version.
Building appex with NativeAOT fails with following exception:

  System.Exception: No entrypoint module
     at ILCompiler.Program.Run() + 0x26bc
     at ILCompiler.ILCompilerRootCommand.<>c__DisplayClass227_0.<.ctor>b__0(ParseResult result) + 0x304

so I was not able to check this option.

For reference on this image the same .Net 8 | Xamarin appex.
image

@rolfbjarne
Copy link
Member

rolfbjarne commented May 27, 2024

Hi, I'm working on similar project as original poster and want to highlight that AppExtensions are significantly larger for .Net 8.

This is known, we have a tracking issue for app extension improvements we'd like to get done at some point: #10051

Building appex with NativeAOT fails with following exception:

We never tried NativeAOT on an app extension, so I filed #20653 to have a look and see if it's possible.

@ivanpovazan
Copy link
Contributor

Building appex with NativeAOT fails with following exception:

  System.Exception: No entrypoint module
     at ILCompiler.Program.Run() + 0x26bc
     at ILCompiler.ILCompilerRootCommand.<>c__DisplayClass227_0.<.ctor>b__0(ParseResult result) + 0x304

Just an info: we fixed building app extensions with NativeAOT in 374e902

@github-project-automation github-project-automation bot moved this to Optimizations in .NET 9 Aug 27, 2024
@rolfbjarne rolfbjarne modified the milestones: .NET 9, .NET 10 Sep 26, 2024
@rolfbjarne rolfbjarne removed this from .NET 9 Sep 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
app-size enhancement The issue or pull request is an enhancement
Projects
None yet
Development

No branches or pull requests

5 participants