xamarin-macios
xamarin-macios copied to clipboard
.NET 6: SingleView app size
App sizes are currently significantly bigger with .NET 6 with a very simple hello world app.
Test case
- Checkout
xamarin-macios
- Execute:
cd tests/dotnet
make compare
This will build a very simple hello world app for both .NET 6 and the old-style Xamarin.iOS and generate a report in markdown format. This can be pasted into the issue (e.g. for each preview) so the progress can be tracked.
Known Issues
- [x] The (dynamic) registrar isn't removed when enabling all the optimizations
- The dynamic registrar requires a larger subset of
System.Reflection
API (see below)
- The dynamic registrar requires a larger subset of
- [x] The .NET 6 version does not strip the native executable.
- [x] The mono-cil-strip tool is not run on the final assemblies (it's not available without the mono SDK installed)
- All assemblies still have their IL even when it's not required (FullAOT by default for device builds)
- Tracked, among other tools, in https://github.com/dotnet/runtime/issues/35852 @marek-safar recently mentioned it was being looked at for Blazor (there might be another issue I missed)
Historical
App sizes
Xamarin.iOS
du -hs size-comparison/MySingleView/oldnet/bin/iPhone/Release/MySingleView.app 4.4M size-comparison/MySingleView/oldnet/bin/iPhone/Release/MySingleView.app
.NET 6
du -hs size-comparison/MySingleView/dotnet/bin/iPhone/Release/net6.0-ios/ios-arm64/*.app 27M size-comparison/MySingleView/dotnet/bin/iPhone/Release/net6.0-ios/ios-arm64/MySingleView.app
Assembly sizes
Xamarin.iOS
ls -lahS size-comparison/MySingleView/oldnet/bin/iPhone/Release/MySingleView.app/.dll size-comparison/MySingleView/oldnet/bin/iPhone/Release/MySingleView.app/.exe -rw-r--r-- 1 rolf wheel 397K Dec 9 19:04 size-comparison/MySingleView/oldnet/bin/iPhone/Release/MySingleView.app/mscorlib.dll -rw-r--r-- 1 rolf wheel 54K Dec 9 19:04 size-comparison/MySingleView/oldnet/bin/iPhone/Release/MySingleView.app/Xamarin.iOS.dll -rw-r--r-- 1 rolf wheel 4.5K Dec 9 19:04 size-comparison/MySingleView/oldnet/bin/iPhone/Release/MySingleView.app/MySingleView.exe -rw-r--r-- 1 rolf wheel 4.5K Dec 9 19:05 size-comparison/MySingleView/oldnet/bin/iPhone/Release/MySingleView.app/System.dll
.NET 6
ls -lahS size-comparison/MySingleView/dotnet/bin/iPhone/Release/net6.0-ios/ios-arm64/.app/.dll -rw-r--r-- 1 rolf wheel 1.3M Dec 9 19:05 size-comparison/MySingleView/dotnet/bin/iPhone/Release/net6.0-ios/ios-arm64/MySingleView.app/System.Private.CoreLib.dll -rw-r--r-- 1 rolf wheel 161K Dec 9 19:05 size-comparison/MySingleView/dotnet/bin/iPhone/Release/net6.0-ios/ios-arm64/MySingleView.app/Xamarin.iOS.dll -rw-r--r-- 1 rolf wheel 17K Dec 9 19:05 size-comparison/MySingleView/dotnet/bin/iPhone/Release/net6.0-ios/ios-arm64/MySingleView.app/System.Runtime.dll -rw-r--r-- 1 rolf wheel 16K Dec 9 19:05 size-comparison/MySingleView/dotnet/bin/iPhone/Release/net6.0-ios/ios-arm64/MySingleView.app/System.Runtime.Serialization.Formatters.dll -rw-r--r-- 1 rolf wheel 5.0K Dec 9 19:05 size-comparison/MySingleView/dotnet/bin/iPhone/Release/net6.0-ios/ios-arm64/MySingleView.app/MySingleView.dll
There are multiple issues here:
- [x] ~The .NET 6 version has unnecessary dylibs (because it's linking libraries statically).~ fix
- [x] ~The .NET 6 version does not strip the native executable.~
- [ ] The .NET 6 version's assemblies are much bigger (the linker doesn't remove as much): 1,5 MB vs 460 KB.
Additionally the .NET 6 version takes twice as long to compile (22s vs 11s on my machine), but that's a different issue (filed as https://github.com/xamarin/xamarin-macios/issues/10251).
Shameless plug but you might want to use https://github.com/spouliot/dotnet-tools/tree/master/app-compare to compare the size of the current/dotnet apps
Additionally the .NET 6 version takes twice as long to compile (22s vs 11s on my machine), but that's a different issue.
Considering most of the device build time comes from the AOT compiler (at least in current form) then a 2x build time increase is not so bad as there's more than 3x more managed code (1,5 MB vs 460 KB) to AOT. IOW we can consider this the "same issue", unless the time increase comes from elsewhere.
It looks like you don't have some of the feature switched enabled, see https://github.com/dotnet/runtime/blob/master/docs/workflow/trimming/feature-switches.md. I'm not saying that's all that needs to be done but it's a good start ;-)
Application Comparer
-
A
oldnet/bin/iPhone/Release/MySingleView.app
-
B
dotnet/bin/iPhone/Release/net6.0-ios/ios-arm64/MySingleView.app
Directories / Files | A | B | diff | % |
---|---|---|---|---|
.//_CodeSignature | ||||
.// | ||||
embedded.mobileprovision | 12,218 | 12,218 | 0 | 0.0% |
Info.plist | 1,055 | 1,055 | 0 | 0.0% |
libmonosgen-2.0.dylib | 0 | 3,177,312 | 3,177,312 | - |
libSystem.IO.Compression.Native.dylib | 0 | 827,104 | 827,104 | - |
libSystem.Native.dylib | 0 | 123,040 | 123,040 | - |
libSystem.Net.Security.Native.dylib | 0 | 72,016 | 72,016 | - |
libSystem.Security.Cryptography.Native.Apple.dylib | 0 | 80,656 | 80,656 | - |
libxamarin-debug.dylib | 0 | 623,856 | 623,856 | - |
libxamarin.dylib | 0 | 425,408 | 425,408 | - |
mscorlib.aotdata.arm64 | 296,872 | 0 | -296,872 | -100.0% |
mscorlib.dll | 406,528 | 0 | -406,528 | -100.0% |
MySingleView | 3,748,192 | 18,281,760 | 14,533,568 | 387.7% |
MySingleView.aotdata.arm64 | 816 | 1,136 | 320 | 39.2% |
MySingleView.dll | 0 | 5,120 | 5,120 | - |
MySingleView.exe | 4,608 | 0 | -4,608 | -100.0% |
MySingleView.pdb | 0 | 9,720 | 9,720 | - |
PkgInfo | 8 | 8 | 0 | 0.0% |
System.aotdata.arm64 | 752 | 0 | -752 | -100.0% |
System.dll | 4,608 | 0 | -4,608 | -100.0% |
System.Private.CoreLib.aotdata.arm64 | 0 | 3,082,504 | 3,082,504 | - |
System.Private.CoreLib.dll | 0 | 1,391,616 | 1,391,616 | - |
System.Runtime.aotdata.arm64 | 0 | 504 | 504 | - |
System.Runtime.dll | 0 | 17,408 | 17,408 | - |
System.Runtime.Serialization.Formatters.aotdata.arm64 | 0 | 1,144 | 1,144 | - |
System.Runtime.Serialization.Formatters.dll | 0 | 15,872 | 15,872 | - |
Xamarin.iOS.aotdata.arm64 | 35,048 | 319,112 | 284,064 | 810.5% |
Xamarin.iOS.dll | 55,296 | 163,840 | 108,544 | 196.3% |
Statistics | ||||
Native subtotal | 3,748,192 | 18,281,760 | 14,533,568 | 387.7% |
Executable | 3,748,192 | 18,281,760 | 14,533,568 | 387.7% |
AOT data *.aotdata | 0 | 0 | 0 | - |
Managed *.dll/exe | 471,040 | 1,593,856 | 1,122,816 | 238.4% |
TOTAL | 4,571,262 | 28,640,968 | 24,069,706 | 526.5% |
other issues
- [x] ~debugging symbol files, like
MySingleView.pdb
, should not be copied to release .app~ - [x] ~
otool -L MySingleView
does not showlibicu*
nor is there any data files. AFAIK those are required for globalization of net5+ apps (and the existingoldnet
app has globalization support)~
update: both issues are fixed with P4
@spouliot could you share the binaries?
@marek-safar yes but likely not today. My main internet connection is down :( and I'm waiting for a new modem to be delivered. Trying to limit myself to low bandwidth (cellular) tasks.
It looks like you don't have some of the feature switched enabled, see https://github.com/dotnet/runtime/blob/master/docs/workflow/trimming/feature-switches.md. I'm not saying that's all that needs to be done but it's a good start ;-)
https://github.com/xamarin/xamarin-macios/pull/10250 - that shaves off a good chunk.
@marek-safar
@spouliot could you share the binaries?
That's after https://github.com/xamarin/xamarin-macios/pull/10250, so the runtime switches have been toggled for these apps.
Additionally the .NET 6 version takes twice as long to compile (22s vs 11s on my machine), but that's a different issue.
Considering most of the device build time comes from the AOT compiler (at least in current form) then a 2x build time increase is not so bad as there's more than 3x more managed code (1,5 MB vs 460 KB) to AOT. IOW we can consider this the "same issue", unless the time increase comes from elsewhere.
I filed https://github.com/xamarin/xamarin-macios/issues/10251 for this: just executing the .NET 6 linker takes longer than linking + AOT compilation + native compilation used to.
This update was done with today's dotnet 6.0.100-preview.2.21158.2 build and xamarin-macios main
91ce22c57cadd3519ac4433e8d73cbe4be5be7e5. Not quite the final, to be released, P2 bits but close.
Most of the previous comments (e.g. extra files, no ICU in numbers) still applies.
Noticeable/visible changes are:
- removal of
System.Runtime.dll
- removal of
System.Runtime.Serialization.Formatters.dll
- reduction of
System.Private.CoreLib.dll
It's a great improvement, over the original numbers, but there's still work to be done compared to the current SDK size.
Application Comparer
-
A
/Users/poupou/git/master/xamarin-macios/tests/dotnet/size-comparison/MySingleView/oldnet/bin/iPhone/Release/MySingleView.app
-
B
/Users/poupou/git/master/xamarin-macios/tests/dotnet/size-comparison/MySingleView/dotnet/bin/iPhone/Release/net6.0-ios/ios-arm64/MySingleView.app
Directories / Files | A | B | diff | % |
---|---|---|---|---|
.//_CodeSignature | ||||
.// | ||||
embedded.mobileprovision | 14,271 | 14,271 | 0 | 0.0% |
Info.plist | 1,058 | 1,058 | 0 | 0.0% |
libmonosgen-2.0.dylib | 0 | 3,503,920 | 3,503,920 | - |
libSystem.IO.Compression.Native.dylib | 0 | 827,248 | 827,248 | - |
libSystem.Native.dylib | 0 | 123,792 | 123,792 | - |
libSystem.Net.Security.Native.dylib | 0 | 72,016 | 72,016 | - |
libSystem.Security.Cryptography.Native.Apple.dylib | 0 | 80,656 | 80,656 | - |
libxamarin-debug.dylib | 0 | 625,472 | 625,472 | - |
libxamarin-dotnet-debug.dylib | 0 | 625,600 | 625,600 | - |
libxamarin-dotnet.dylib | 0 | 425,984 | 425,984 | - |
libxamarin.dylib | 0 | 425,856 | 425,856 | - |
mscorlib.aotdata.arm64 | 296,808 | 0 | -296,808 | -100.0% |
mscorlib.dll | 406,528 | 0 | -406,528 | -100.0% |
MySingleView | 3,748,304 | 10,213,120 | 6,464,816 | 172.5% |
MySingleView.aotdata.arm64 | 816 | 792 | -24 | -2.9% |
MySingleView.dll | 0 | 4,608 | 4,608 | - |
MySingleView.exe | 4,608 | 0 | -4,608 | -100.0% |
MySingleView.pdb | 0 | 10,280 | 10,280 | - |
PkgInfo | 8 | 8 | 0 | 0.0% |
System.aotdata.arm64 | 752 | 0 | -752 | -100.0% |
System.dll | 4,608 | 0 | -4,608 | -100.0% |
System.Private.CoreLib.aotdata.arm64 | 0 | 623,496 | 623,496 | - |
System.Private.CoreLib.dll | 0 | 685,056 | 685,056 | - |
Xamarin.iOS.aotdata.arm64 | 34,856 | 78,784 | 43,928 | 126.0% |
Xamarin.iOS.dll | 55,296 | 157,696 | 102,400 | 185.2% |
Statistics | ||||
Native subtotal | 3,748,304 | 10,213,120 | 6,464,816 | 172.5% |
Executable | 3,748,304 | 10,213,120 | 6,464,816 | 172.5% |
AOT data *.aotdata | 0 | 0 | 0 | - |
Managed *.dll/exe | 471,040 | 847,360 | 376,320 | 79.9% |
TOTAL | 4,573,174 | 18,507,576 | 13,934,402 | 304.7% |
@spouliot @rolfbjarne - do we know why Xamarin.iOS.dll
is 185% larger in .NET 6?
That could contribute a lot to why .NET 6's CoreLib is bigger than mono's mscorlib. More code in higher level assemblies means the possibility of more code being rooted lower in corelib.
do we know why Xamarin.iOS.dll is 185% larger in .NET 6?
No, at least not yet. I wanted numbers to go along P2 even if we did not have time to look at the results right away - they will have historical value ;-)
Quick guess some of our optimizations were not yet ported/enabled (last time I checked).
That could contribute a lot to why .NET 6's CoreLib is bigger than mono's mscorlib.
That definitively plays its part. Still even if Xamarin.iOS.dll is huge (lots of bindings) there's not a lot of variety in the API being used. I'll dig up more useful data asap
@eerhardt here's the references diff between the old (current) and new (dotnet) versions of the linked Xamarin.iOS.dll
--- old 2021-03-11 19:54:16.000000000 -0500
+++ new 2021-03-11 19:54:50.000000000 -0500
@@ -1,9 +1,13 @@
A: Xamarin.iOS, Version=0.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065
- AR: mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
+ AR: System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
+ TR: System.AggregateException
+ MR: System.Collections.ObjectModel.ReadOnlyCollection`1<System.Exception> System.AggregateException::get_InnerExceptions()
+ MR: System.Void System.AggregateException::.ctor(System.Collections.Generic.IEnumerable`1<System.Exception>)
TR: System.AppDomain
MR: System.AppDomain System.AppDomain::get_CurrentDomain()
MR: System.Reflection.Assembly[] System.AppDomain::GetAssemblies()
TR: System.ArgumentException
+ MR: System.Void System.ArgumentException::.ctor(System.String,System.String)
MR: System.Void System.ArgumentException::.ctor(System.String)
TR: System.ArgumentNullException
MR: System.Void System.ArgumentNullException::.ctor(System.String)
@@ -11,28 +15,35 @@
MR: System.Void System.ArgumentOutOfRangeException::.ctor(System.String)
TR: System.Array
MR: !!0[] System.Array::Empty()
- TR: System.AsyncCallback
+ MR: System.Int32 System.Array::IndexOf(!!0[],!!0)
+ MR: System.Void System.Array::Resize(!!0[]&,System.Int32)
TR: System.Attribute
+ MR: System.Attribute System.Attribute::GetCustomAttribute(System.Reflection.MemberInfo,System.Type)
MR: System.Void System.Attribute::.ctor()
TR: System.AttributeTargets
TR: System.AttributeUsageAttribute
MR: System.Void System.AttributeUsageAttribute::.ctor(System.AttributeTargets)
TR: System.Boolean
+ TR: System.Char
+ MR: System.Boolean System.Char::IsWhiteSpace(System.Char)
+ MR: System.String System.Char::ToString()
TR: System.Collections.Generic.Dictionary`2
- TR: System.Collections.Generic.EqualityComparer`1
+ TR: System.Collections.Generic.Dictionary`2/ValueCollection
+ TR: System.Collections.Generic.Dictionary`2/ValueCollection/Enumerator
TR: System.Collections.Generic.ICollection`1
- TR: System.Collections.Generic.IDictionary`2
TR: System.Collections.Generic.IEnumerable`1
TR: System.Collections.Generic.IEnumerator`1
TR: System.Collections.Generic.IEqualityComparer`1
+ TR: System.Collections.Generic.IList`1
TR: System.Collections.Generic.KeyValuePair`2
TR: System.Collections.Generic.List`1
TR: System.Collections.Generic.List`1/Enumerator
TR: System.Collections.IDictionary
- TR: System.Collections.IEnumerable
TR: System.Collections.IEnumerator
MR: System.Boolean System.Collections.IEnumerator::MoveNext()
+ TR: System.Collections.ObjectModel.ReadOnlyCollection`1
TR: System.Delegate
+ MR: System.Reflection.MethodInfo System.Delegate::get_Method()
TR: System.Double
MR: System.Boolean System.Double::Equals(System.Double)
MR: System.Boolean System.Double::Equals(System.Object)
@@ -40,14 +51,14 @@
MR: System.Int32 System.Double::CompareTo(System.Object)
MR: System.Int32 System.Double::GetHashCode()
MR: System.String System.Double::ToString()
- MR: System.String System.Double::ToString(System.IFormatProvider)
MR: System.String System.Double::ToString(System.String,System.IFormatProvider)
- MR: System.TypeCode System.Double::GetTypeCode()
TR: System.Enum
- TR: System.EventArgs
- MR: System.EventArgs System.EventArgs::Empty
+ MR: System.Type System.Enum::GetUnderlyingType(System.Type)
+ TR: System.Environment
+ MR: System.Int32 System.Environment::get_CurrentManagedThreadId()
+ MR: System.OperatingSystem System.Environment::get_OSVersion()
+ MR: System.String System.Environment::GetEnvironmentVariable(System.String)
TR: System.EventHandler
- MR: System.Void System.EventHandler::Invoke(System.Object,System.EventArgs)
TR: System.Exception
MR: System.String System.Exception::ToString()
MR: System.Void System.Exception::.ctor()
@@ -55,16 +66,13 @@
MR: System.Void System.Exception::.ctor(System.String)
TR: System.FlagsAttribute
MR: System.Void System.FlagsAttribute::.ctor()
- TR: System.Func`1
- TR: System.Func`2
TR: System.GC
MR: System.Void System.GC::SuppressFinalize(System.Object)
- TR: System.Globalization.CultureInfo
- MR: System.Globalization.CultureInfo System.Globalization.CultureInfo::get_CurrentCulture()
- TR: System.IAsyncResult
TR: System.IComparable
TR: System.IComparable`1
TR: System.IConvertible
+ MR: System.Int16 System.IConvertible::ToInt16(System.IFormatProvider)
+ MR: System.Int32 System.IConvertible::ToInt32(System.IFormatProvider)
MR: System.Int64 System.IConvertible::ToInt64(System.IFormatProvider)
MR: System.UInt64 System.IConvertible::ToUInt64(System.IFormatProvider)
TR: System.IDisposable
@@ -73,6 +81,7 @@
TR: System.IFormatProvider
TR: System.IFormattable
TR: System.Int32
+ MR: System.String System.Int32::ToString(System.String)
TR: System.Int64
MR: System.Boolean System.Int64::Equals(System.Int64)
MR: System.Boolean System.Int64::Equals(System.Object)
@@ -81,7 +90,6 @@
MR: System.Int32 System.Int64::GetHashCode()
MR: System.String System.Int64::ToString()
MR: System.String System.Int64::ToString(System.String,System.IFormatProvider)
- MR: System.TypeCode System.Int64::GetTypeCode()
TR: System.IntPtr
MR: System.Boolean System.IntPtr::op_Equality(System.IntPtr,System.IntPtr)
MR: System.Boolean System.IntPtr::op_Inequality(System.IntPtr,System.IntPtr)
@@ -94,12 +102,22 @@
MR: System.IntPtr System.IntPtr::op_Subtraction(System.IntPtr,System.Int32)
MR: System.IntPtr System.IntPtr::Zero
MR: System.String System.IntPtr::ToString(System.String)
+ MR: System.Void System.IntPtr::.ctor(System.Int32)
MR: System.Void* System.IntPtr::op_Explicit(System.IntPtr)
TR: System.InvalidCastException
MR: System.Void System.InvalidCastException::.ctor(System.String)
+ TR: System.InvalidOperationException
+ MR: System.Void System.InvalidOperationException::.ctor()
+ TR: System.IO.FileNotFoundException
+ MR: System.String System.IO.FileNotFoundException::get_FileName()
+ TR: System.Math
+ MR: System.Int32 System.Math::Max(System.Int32,System.Int32)
TR: System.MulticastDelegate
+ TR: System.NotImplementedException
+ MR: System.Void System.NotImplementedException::.ctor()
TR: System.NotSupportedException
- MR: System.Void System.NotSupportedException::.ctor()
+ MR: System.Void System.NotSupportedException::.ctor(System.String)
+ TR: System.Nullable`1
TR: System.Object
MR: System.Boolean System.Object::Equals(System.Object)
MR: System.Int32 System.Object::GetHashCode()
@@ -109,26 +127,46 @@
MR: System.Void System.Object::Finalize()
TR: System.ObjectDisposedException
MR: System.Void System.ObjectDisposedException::.ctor(System.String)
+ TR: System.OperatingSystem
+ MR: System.PlatformID System.OperatingSystem::get_Platform()
TR: System.ParamArrayAttribute
MR: System.Void System.ParamArrayAttribute::.ctor()
+ TR: System.PlatformID
+ TR: System.Reflection.AmbiguousMatchException
+ MR: System.Void System.Reflection.AmbiguousMatchException::.ctor(System.String)
TR: System.Reflection.Assembly
MR: System.Boolean System.Reflection.Assembly::op_Equality(System.Reflection.Assembly,System.Reflection.Assembly)
MR: System.Boolean System.Reflection.Assembly::op_Inequality(System.Reflection.Assembly,System.Reflection.Assembly)
+ MR: System.Reflection.Assembly System.Reflection.Assembly::Load(System.Reflection.AssemblyName)
MR: System.Reflection.AssemblyName System.Reflection.Assembly::GetName()
+ MR: System.Reflection.AssemblyName[] System.Reflection.Assembly::GetReferencedAssemblies()
MR: System.Reflection.Module[] System.Reflection.Assembly::GetModules()
+ MR: System.String System.Reflection.Assembly::get_FullName()
+ MR: System.Type System.Reflection.Assembly::GetType(System.String,System.Boolean)
+ MR: System.Type[] System.Reflection.Assembly::GetTypes()
+ TR: System.Reflection.AssemblyInformationalVersionAttribute
+ MR: System.Void System.Reflection.AssemblyInformationalVersionAttribute::.ctor(System.String)
TR: System.Reflection.AssemblyName
MR: System.String System.Reflection.AssemblyName::get_Name()
+ TR: System.Reflection.Binder
TR: System.Reflection.BindingFlags
TR: System.Reflection.ConstructorInfo
MR: System.Boolean System.Reflection.ConstructorInfo::op_Equality(System.Reflection.ConstructorInfo,System.Reflection.ConstructorInfo)
+ MR: System.Boolean System.Reflection.ConstructorInfo::op_Inequality(System.Reflection.ConstructorInfo,System.Reflection.ConstructorInfo)
MR: System.Object System.Reflection.ConstructorInfo::Invoke(System.Object[])
TR: System.Reflection.CustomAttributeExtensions
+ MR: !!0 System.Reflection.CustomAttributeExtensions::GetCustomAttribute(System.Reflection.MemberInfo,System.Boolean)
MR: !!0 System.Reflection.CustomAttributeExtensions::GetCustomAttribute(System.Reflection.MemberInfo)
MR: System.Collections.Generic.IEnumerable`1<!!0> System.Reflection.CustomAttributeExtensions::GetCustomAttributes(System.Reflection.MemberInfo)
+ TR: System.Reflection.DefaultMemberAttribute
+ MR: System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)
TR: System.Reflection.FieldInfo
+ MR: System.Boolean System.Reflection.FieldInfo::get_IsStatic()
MR: System.Boolean System.Reflection.FieldInfo::op_Equality(System.Reflection.FieldInfo,System.Reflection.FieldInfo)
MR: System.Object System.Reflection.FieldInfo::GetValue(System.Object)
+ MR: System.Type System.Reflection.FieldInfo::get_FieldType()
TR: System.Reflection.ICustomAttributeProvider
+ MR: System.Boolean System.Reflection.ICustomAttributeProvider::IsDefined(System.Type,System.Boolean)
MR: System.Object[] System.Reflection.ICustomAttributeProvider::GetCustomAttributes(System.Type,System.Boolean)
TR: System.Reflection.InterfaceMapping
MR: System.Reflection.MethodInfo[] System.Reflection.InterfaceMapping::InterfaceMethods
@@ -136,36 +174,61 @@
TR: System.Reflection.MemberInfo
MR: System.Boolean System.Reflection.MemberInfo::IsDefined(System.Type,System.Boolean)
MR: System.Boolean System.Reflection.MemberInfo::op_Equality(System.Reflection.MemberInfo,System.Reflection.MemberInfo)
+ MR: System.Boolean System.Reflection.MemberInfo::op_Inequality(System.Reflection.MemberInfo,System.Reflection.MemberInfo)
MR: System.Int32 System.Reflection.MemberInfo::get_MetadataToken()
+ MR: System.Object[] System.Reflection.MemberInfo::GetCustomAttributes(System.Boolean)
MR: System.Object[] System.Reflection.MemberInfo::GetCustomAttributes(System.Type,System.Boolean)
MR: System.String System.Reflection.MemberInfo::get_Name()
MR: System.Type System.Reflection.MemberInfo::get_DeclaringType()
TR: System.Reflection.MethodBase
MR: System.Boolean System.Reflection.MethodBase::get_ContainsGenericParameters()
+ MR: System.Boolean System.Reflection.MethodBase::get_IsGenericMethod()
+ MR: System.Boolean System.Reflection.MethodBase::get_IsGenericMethodDefinition()
MR: System.Boolean System.Reflection.MethodBase::get_IsSpecialName()
MR: System.Boolean System.Reflection.MethodBase::get_IsStatic()
+ MR: System.Boolean System.Reflection.MethodBase::get_IsVirtual()
MR: System.Boolean System.Reflection.MethodBase::op_Equality(System.Reflection.MethodBase,System.Reflection.MethodBase)
MR: System.Boolean System.Reflection.MethodBase::op_Inequality(System.Reflection.MethodBase,System.Reflection.MethodBase)
MR: System.Object System.Reflection.MethodBase::Invoke(System.Object,System.Object[])
MR: System.Reflection.ParameterInfo[] System.Reflection.MethodBase::GetParameters()
TR: System.Reflection.MethodInfo
MR: System.Boolean System.Reflection.MethodInfo::op_Equality(System.Reflection.MethodInfo,System.Reflection.MethodInfo)
+ MR: System.Boolean System.Reflection.MethodInfo::op_Inequality(System.Reflection.MethodInfo,System.Reflection.MethodInfo)
MR: System.Reflection.ICustomAttributeProvider System.Reflection.MethodInfo::get_ReturnTypeCustomAttributes()
MR: System.Reflection.MethodInfo System.Reflection.MethodInfo::GetBaseDefinition()
+ MR: System.Type System.Reflection.MethodInfo::get_ReturnType()
TR: System.Reflection.Module
MR: System.Int32 System.Reflection.Module::get_MetadataToken()
MR: System.Reflection.Assembly System.Reflection.Module::get_Assembly()
MR: System.Reflection.MethodBase System.Reflection.Module::ResolveMethod(System.Int32)
MR: System.Type System.Reflection.Module::ResolveType(System.Int32)
TR: System.Reflection.ParameterInfo
+ MR: System.Boolean System.Reflection.ParameterInfo::get_IsOut()
+ MR: System.Boolean System.Reflection.ParameterInfo::IsDefined(System.Type,System.Boolean)
+ MR: System.Object[] System.Reflection.ParameterInfo::GetCustomAttributes(System.Type,System.Boolean)
+ MR: System.String System.Reflection.ParameterInfo::get_Name()
MR: System.Type System.Reflection.ParameterInfo::get_ParameterType()
+ TR: System.Reflection.ParameterModifier
TR: System.Reflection.PropertyInfo
+ MR: System.Boolean System.Reflection.PropertyInfo::get_CanRead()
+ MR: System.Boolean System.Reflection.PropertyInfo::get_CanWrite()
+ MR: System.Boolean System.Reflection.PropertyInfo::op_Equality(System.Reflection.PropertyInfo,System.Reflection.PropertyInfo)
MR: System.Boolean System.Reflection.PropertyInfo::op_Inequality(System.Reflection.PropertyInfo,System.Reflection.PropertyInfo)
+ MR: System.Reflection.MethodInfo System.Reflection.PropertyInfo::get_GetMethod()
+ MR: System.Reflection.MethodInfo System.Reflection.PropertyInfo::get_SetMethod()
MR: System.Reflection.MethodInfo System.Reflection.PropertyInfo::GetGetMethod()
+ MR: System.Reflection.MethodInfo System.Reflection.PropertyInfo::GetGetMethod(System.Boolean)
MR: System.Reflection.MethodInfo System.Reflection.PropertyInfo::GetSetMethod()
+ MR: System.Reflection.MethodInfo System.Reflection.PropertyInfo::GetSetMethod(System.Boolean)
+ MR: System.Type System.Reflection.PropertyInfo::get_PropertyType()
+ TR: System.Reflection.TypeFilter
+ MR: System.Void System.Reflection.TypeFilter::.ctor(System.Object,System.IntPtr)
+ TR: System.Runtime.CompilerServices.CompilationRelaxationsAttribute
+ MR: System.Void System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(System.Int32)
+ TR: System.Runtime.CompilerServices.CompilerGeneratedAttribute
+ MR: System.Void System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
TR: System.Runtime.CompilerServices.ConditionalWeakTable`2
TR: System.Runtime.CompilerServices.ExtensionAttribute
- MR: System.Void System.Runtime.CompilerServices.ExtensionAttribute::.ctor()
TR: System.Runtime.CompilerServices.InternalsVisibleToAttribute
MR: System.Void System.Runtime.CompilerServices.InternalsVisibleToAttribute::.ctor(System.String)
TR: System.Runtime.CompilerServices.IsVolatile
@@ -174,10 +237,12 @@
TR: System.Runtime.CompilerServices.RuntimeCompatibilityAttribute
MR: System.Void System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor()
TR: System.Runtime.CompilerServices.RuntimeHelpers
- MR: System.Int32 System.Runtime.CompilerServices.RuntimeHelpers::get_OffsetToStringData()
+ MR: System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)
TR: System.Runtime.ExceptionServices.ExceptionDispatchInfo
MR: System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo::Capture(System.Exception)
MR: System.Void System.Runtime.ExceptionServices.ExceptionDispatchInfo::Throw()
+ TR: System.Runtime.InteropServices.FieldOffsetAttribute
+ MR: System.Int32 System.Runtime.InteropServices.FieldOffsetAttribute::get_Value()
TR: System.Runtime.InteropServices.GCHandle
MR: System.IntPtr System.Runtime.InteropServices.GCHandle::op_Explicit(System.Runtime.InteropServices.GCHandle)
MR: System.IntPtr System.Runtime.InteropServices.GCHandle::ToIntPtr(System.Runtime.InteropServices.GCHandle)
@@ -187,6 +252,7 @@
MR: System.Runtime.InteropServices.GCHandle System.Runtime.InteropServices.GCHandle::FromIntPtr(System.IntPtr)
MR: System.Void System.Runtime.InteropServices.GCHandle::Free()
TR: System.Runtime.InteropServices.GCHandleType
+ TR: System.Runtime.InteropServices.InAttribute
TR: System.Runtime.InteropServices.Marshal
MR: System.Int32 System.Runtime.InteropServices.Marshal::ReadInt32(System.IntPtr)
MR: System.Int32 System.Runtime.InteropServices.Marshal::SizeOf(System.Type)
@@ -200,14 +266,25 @@
MR: System.Void System.Runtime.InteropServices.Marshal::Copy(System.Byte[],System.Int32,System.IntPtr,System.Int32)
MR: System.Void System.Runtime.InteropServices.Marshal::FreeHGlobal(System.IntPtr)
MR: System.Void System.Runtime.InteropServices.Marshal::WriteByte(System.IntPtr,System.Byte)
+ MR: System.Void System.Runtime.InteropServices.Marshal::WriteInt32(System.IntPtr,System.Int32)
+ MR: System.Void System.Runtime.InteropServices.Marshal::WriteIntPtr(System.IntPtr,System.IntPtr)
+ TR: System.Runtime.InteropServices.MarshalAsAttribute
+ MR: System.Int32 System.Runtime.InteropServices.MarshalAsAttribute::SizeConst
+ MR: System.Runtime.InteropServices.UnmanagedType System.Runtime.InteropServices.MarshalAsAttribute::get_Value()
+ TR: System.Runtime.InteropServices.UnmanagedType
+ TR: System.RuntimeFieldHandle
TR: System.RuntimeTypeHandle
TR: System.String
MR: System.Boolean System.String::Equals(System.String,System.String)
MR: System.Boolean System.String::IsNullOrEmpty(System.String)
+ MR: System.Boolean System.String::op_Equality(System.String,System.String)
MR: System.Boolean System.String::op_Inequality(System.String,System.String)
+ MR: System.Boolean System.String::StartsWith(System.String,System.StringComparison)
MR: System.Char System.String::get_Chars(System.Int32)
+ MR: System.Char& modreq(System.Runtime.InteropServices.InAttribute) System.String::GetPinnableReference()
MR: System.Int32 System.String::get_Length()
MR: System.Int32 System.String::IndexOf(System.Char)
+ MR: System.Int32 System.String::IndexOfAny(System.Char[])
MR: System.String System.String::Concat(System.String,System.String,System.String,System.String)
MR: System.String System.String::Concat(System.String,System.String,System.String)
MR: System.String System.String::Concat(System.String,System.String)
@@ -217,16 +294,32 @@
MR: System.String System.String::Format(System.String,System.Object,System.Object)
MR: System.String System.String::Format(System.String,System.Object)
MR: System.String System.String::Format(System.String,System.Object[])
+ MR: System.String System.String::Insert(System.Int32,System.String)
+ MR: System.String System.String::Replace(System.String,System.String)
+ MR: System.String System.String::Substring(System.Int32)
+ MR: System.String System.String::Trim()
+ MR: System.String[] System.String::Split(System.Char,System.StringSplitOptions)
+ TR: System.StringComparison
+ TR: System.StringSplitOptions
TR: System.Text.Encoding
MR: System.Byte[] System.Text.Encoding::GetBytes(System.String)
MR: System.Text.Encoding System.Text.Encoding::get_UTF8()
- TR: System.Threading._ThreadPoolWaitCallback
- MR: System.Void System.Threading._ThreadPoolWaitCallback::SetDispatcher(System.Func`2<System.Func`1<System.Boolean>,System.Boolean>)
+ TR: System.Text.StringBuilder
+ MR: System.Text.StringBuilder System.Text.StringBuilder::Append(System.Char)
+ MR: System.Text.StringBuilder System.Text.StringBuilder::Append(System.Object)
+ MR: System.Text.StringBuilder System.Text.StringBuilder::Append(System.String)
+ MR: System.Text.StringBuilder System.Text.StringBuilder::AppendFormat(System.String,System.Object,System.Object,System.Object)
+ MR: System.Text.StringBuilder System.Text.StringBuilder::AppendFormat(System.String,System.Object,System.Object)
+ MR: System.Text.StringBuilder System.Text.StringBuilder::AppendFormat(System.String,System.Object)
+ MR: System.Void System.Text.StringBuilder::.ctor()
+ MR: System.Void System.Text.StringBuilder::.ctor(System.String,System.Int32,System.Int32,System.Int32)
TR: System.Threading.Interlocked
MR: System.Int32 System.Threading.Interlocked::Decrement(System.Int32&)
TR: System.Threading.Monitor
MR: System.Void System.Threading.Monitor::Enter(System.Object,System.Boolean&)
MR: System.Void System.Threading.Monitor::Exit(System.Object)
+ TR: System.Threading.SendOrPostCallback
+ MR: System.Void System.Threading.SendOrPostCallback::Invoke(System.Object)
TR: System.Threading.SynchronizationContext
MR: System.Void System.Threading.SynchronizationContext::.ctor()
MR: System.Void System.Threading.SynchronizationContext::SetSynchronizationContext(System.Threading.SynchronizationContext)
@@ -234,30 +327,49 @@
MR: System.Threading.Thread System.Threading.Thread::get_CurrentThread()
TR: System.Tuple`2
TR: System.Type
+ MR: System.Boolean System.Type::get_ContainsGenericParameters()
+ MR: System.Boolean System.Type::get_IsAbstract()
MR: System.Boolean System.Type::get_IsArray()
MR: System.Boolean System.Type::get_IsByRef()
+ MR: System.Boolean System.Type::get_IsEnum()
+ MR: System.Boolean System.Type::get_IsExplicitLayout()
+ MR: System.Boolean System.Type::get_IsGenericParameter()
MR: System.Boolean System.Type::get_IsGenericType()
+ MR: System.Boolean System.Type::get_IsGenericTypeDefinition()
MR: System.Boolean System.Type::get_IsInterface()
+ MR: System.Boolean System.Type::get_IsNested()
MR: System.Boolean System.Type::get_IsPointer()
+ MR: System.Boolean System.Type::get_IsValueType()
MR: System.Boolean System.Type::IsAssignableFrom(System.Type)
MR: System.Boolean System.Type::IsSubclassOf(System.Type)
MR: System.Boolean System.Type::op_Equality(System.Type,System.Type)
MR: System.Boolean System.Type::op_Inequality(System.Type,System.Type)
+ MR: System.Int32 System.Type::GetArrayRank()
MR: System.Reflection.Assembly System.Type::get_Assembly()
MR: System.Reflection.ConstructorInfo[] System.Type::GetConstructors(System.Reflection.BindingFlags)
MR: System.Reflection.FieldInfo System.Type::GetField(System.String,System.Reflection.BindingFlags)
+ MR: System.Reflection.FieldInfo[] System.Type::GetFields(System.Reflection.BindingFlags)
MR: System.Reflection.InterfaceMapping System.Type::GetInterfaceMap(System.Type)
+ MR: System.Reflection.MethodInfo System.Type::GetMethod(System.String,System.Reflection.BindingFlags,System.Reflection.Binder,System.Type[],System.Reflection.ParameterModifier[])
+ MR: System.Reflection.MethodInfo System.Type::GetMethod(System.String)
MR: System.Reflection.MethodInfo[] System.Type::GetMethods(System.Reflection.BindingFlags)
MR: System.Reflection.Module System.Type::get_Module()
+ MR: System.Reflection.PropertyInfo System.Type::GetProperty(System.String,System.Reflection.BindingFlags)
MR: System.Reflection.PropertyInfo[] System.Type::GetProperties()
+ MR: System.Reflection.PropertyInfo[] System.Type::GetProperties(System.Reflection.BindingFlags)
MR: System.String System.Type::get_AssemblyQualifiedName()
MR: System.String System.Type::get_FullName()
+ MR: System.String System.Type::get_Namespace()
MR: System.Type System.Type::get_BaseType()
MR: System.Type System.Type::GetElementType()
MR: System.Type System.Type::GetGenericTypeDefinition()
MR: System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)
+ MR: System.Type System.Type::MakeByRefType()
+ MR: System.Type System.Type::MakeGenericType(System.Type[])
+ MR: System.Type[] System.Type::FindInterfaces(System.Reflection.TypeFilter,System.Object)
+ MR: System.Type[] System.Type::GetGenericArguments()
+ MR: System.Type[] System.Type::GetGenericParameterConstraints()
MR: System.Type[] System.Type::GetInterfaces()
- TR: System.TypeCode
TR: System.UInt32
TR: System.UInt64
MR: System.Boolean System.UInt64::Equals(System.Object)
@@ -267,26 +379,14 @@
MR: System.Int32 System.UInt64::GetHashCode()
MR: System.String System.UInt64::ToString()
MR: System.String System.UInt64::ToString(System.String,System.IFormatProvider)
- MR: System.TypeCode System.UInt64::GetTypeCode()
TR: System.ValueType
- AR: System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
- TR: Mono.SystemDependencyProvider
- MR: System.Void Mono.SystemDependencyProvider::Initialize()
-A: mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
+ TR: System.Version
+ MR: System.Boolean System.Version::op_Inequality(System.Version,System.Version)
+ MR: System.Boolean System.Version::op_LessThanOrEqual(System.Version,System.Version)
+ MR: System.Int32 System.Version::get_Build()
+ MR: System.Int32 System.Version::get_Major()
+ MR: System.Int32 System.Version::get_Minor()
+ MR: System.Void System.Version::.ctor(System.Int32,System.Int32)
+ TR: System.Void
+A: System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
-
-A: System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
- AR: mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
- TR: Mono.DependencyInjector
- MR: System.Void Mono.DependencyInjector::Register(Mono.ISystemDependencyProvider)
- TR: Mono.ISystemDependencyProvider
- TR: System.Object
- MR: System.Void System.Object::.ctor()
- TR: System.Runtime.CompilerServices.ExtensionAttribute
- MR: System.Void System.Runtime.CompilerServices.ExtensionAttribute::.ctor()
- TR: System.Runtime.CompilerServices.InternalsVisibleToAttribute
- MR: System.Void System.Runtime.CompilerServices.InternalsVisibleToAttribute::.ctor(System.String)
- TR: System.Runtime.CompilerServices.RuntimeCompatibilityAttribute
- MR: System.Void System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor()
- TR: System.Threading.Monitor
- MR: System.Void System.Threading.Monitor::Enter(System.Object,System.Boolean&)
- MR: System.Void System.Threading.Monitor::Exit(System.Object)
I took a quick look at the assembly last night and what I've seen is largely (still) disabled optimizations.
- The dynamic registrar is not removed (missing optimization) so there's more usage (than normal) of
System.Reflection
API. - Several build-time only attributes are not removed (missing optimization) -> mostly metadata
- UIThread checks are not removed (missing optimization) -> an extra call for some methods
Only one (dynamic registrar) would have a significant impact on what's required inside System.Private.CoreLib.dll
but the recursive effect might be larger.
Update
I missed the most obvious - since I can see the IL of the dotnet version it means the mono-cil-strip
was not run against the assembly (which is normally done as Full AOT is used). That's likely the biggest contributor to the extra size of the assembly.
Numbers from 6.0.1xx-preview3
using 6.0.100-preview.3.21202.5
Notes:
- ICU is not enable (no data file included) but likely part of the binary (executable) size increase.
Application Comparer
-
A
/Users/poupou/git/net6/p3/xamarin-macios/tests/dotnet/size-comparison/MySingleView/oldnet/bin/iPhone/Release/MySingleView.app
-
B
/Users/poupou/git/net6/p3/xamarin-macios/tests/dotnet/size-comparison/MySingleView/dotnet/bin/iPhone/Release/net6.0-ios/ios-arm64/MySingleView.app
Directories / Files | A | B | diff | % |
---|---|---|---|---|
.//_CodeSignature | ||||
.// | ||||
embedded.mobileprovision | 12,391 | 12,391 | 0 | 0.0% |
Info.plist | 1,063 | 1,063 | 0 | 0.0% |
libmonosgen-2.0.dylib | 0 | 6,245,360 | 6,245,360 | - |
libSystem.IO.Compression.Native.dylib | 0 | 827,248 | 827,248 | - |
libSystem.Native.dylib | 0 | 124,480 | 124,480 | - |
libSystem.Net.Security.Native.dylib | 0 | 72,016 | 72,016 | - |
libSystem.Security.Cryptography.Native.Apple.dylib | 0 | 80,992 | 80,992 | - |
libxamarin-dotnet-debug.dylib | 0 | 627,008 | 627,008 | - |
libxamarin-dotnet.dylib | 0 | 428,032 | 428,032 | - |
mscorlib.aotdata.arm64 | 297,304 | 0 | -297,304 | -100.0% |
mscorlib.dll | 407,552 | 0 | -407,552 | -100.0% |
MySingleView | 3,749,104 | 13,298,528 | 9,549,424 | 254.7% |
MySingleView.aotdata.arm64 | 816 | 792 | -24 | -2.9% |
MySingleView.dll | 0 | 4,608 | 4,608 | - |
MySingleView.exe | 4,608 | 0 | -4,608 | -100.0% |
MySingleView.pdb | 0 | 10,296 | 10,296 | - |
PkgInfo | 8 | 8 | 0 | 0.0% |
System.aotdata.arm64 | 752 | 0 | -752 | -100.0% |
System.dll | 4,608 | 0 | -4,608 | -100.0% |
System.Private.CoreLib.aotdata.arm64 | 0 | 653,536 | 653,536 | - |
System.Private.CoreLib.dll | 0 | 721,408 | 721,408 | - |
System.Runtime.CompilerServices.Unsafe.aotdata.arm64 | 0 | 7,232 | 7,232 | - |
System.Runtime.CompilerServices.Unsafe.dll | 0 | 6,656 | 6,656 | - |
Xamarin.iOS.aotdata.arm64 | 35,720 | 80,912 | 45,192 | 126.5% |
Xamarin.iOS.dll | 56,320 | 159,744 | 103,424 | 183.6% |
Statistics | ||||
Native subtotal | 3,749,104 | 13,298,528 | 9,549,424 | 254.7% |
Executable | 3,749,104 | 13,298,528 | 9,549,424 | 254.7% |
AOT data *.aotdata | 0 | 0 | 0 | - |
Managed *.dll/exe | 473,088 | 892,416 | 419,328 | 88.6% |
TOTAL | 4,575,507 | 23,370,285 | 18,794,778 | 410.8% |
Results with the release/6.0.1xx-preview4
branch
Major changes from P3 is that it includes the ICU data file icudt.dat
for globalization while removing the .dylib
and .pdb
.
Application Comparer
Directories / Files | Legacy | 6.0.1xx-preview4 | diff | % |
---|---|---|---|---|
.//_CodeSignature | ||||
.// | ||||
embedded.mobileprovision | 12,391 | 12,391 | 0 | 0.0% |
icudt.dat | 0 | 1,713,152 | 1,713,152 | - |
Info.plist | 1,076 | 1,076 | 0 | 0.0% |
mscorlib.aotdata.arm64 | 297,312 | 0 | -297,312 | -100.0% |
mscorlib.dll | 407,552 | 0 | -407,552 | -100.0% |
MySingleView | 3,749,280 | 13,989,808 | 10,240,528 | 273.1% |
MySingleView.aotdata.arm64 | 816 | 792 | -24 | -2.9% |
MySingleView.dll | 0 | 4,608 | 4,608 | - |
MySingleView.exe | 4,608 | 0 | -4,608 | -100.0% |
PkgInfo | 8 | 8 | 0 | 0.0% |
System.aotdata.arm64 | 752 | 0 | -752 | -100.0% |
System.dll | 4,608 | 0 | -4,608 | -100.0% |
System.Private.CoreLib.aotdata.arm64 | 0 | 718,504 | 718,504 | - |
System.Private.CoreLib.dll | 0 | 802,304 | 802,304 | - |
System.Runtime.aotdata.arm64 | 0 | 504 | 504 | - |
System.Runtime.dll | 0 | 5,120 | 5,120 | - |
Xamarin.iOS.aotdata.arm64 | 35,720 | 80,272 | 44,552 | 124.7% |
Xamarin.iOS.dll | 56,320 | 157,696 | 101,376 | 180.0% |
Statistics | ||||
Native subtotal | 3,749,280 | 13,989,808 | 10,240,528 | 273.1% |
Executable | 3,749,280 | 13,989,808 | 10,240,528 | 273.1% |
AOT data *.aotdata | 0 | 0 | 0 | - |
Managed *.dll/exe | 473,088 | 969,728 | 496,640 | 105.0% |
TOTAL | 4,575,704 | 17,491,860 | 12,916,156 | 282.3% |
@marek-safar here's the binlog for the dotnet version. It comes from main
(not P4) as this includes the latest (feature switches) changes build-dotnet.binlog.zip
@spouliot from the log there is still one optimization disabled (--disable-opt unusedtypechecks) which I think is worth investigating why.
Another reason (beside stripping which I have working locally) for the native code to be larger might be related to the lack of the LLVM AOT backend.
if ((abi & Abi.LLVM) == Abi.LLVM)
throw ErrorHelper.CreateError (99, $"Support for LLVM hasn't been implemented yet.");
not sure what's the status on this... it does not seems to be tracked in https://github.com/xamarin/xamarin-macios/issues/8901 @rolfbjarne @marek-safar ?
@spouliot from the log there is still one optimization disabled (--disable-opt unusedtypechecks) which I think is worth investigating why.
59cdbc6098
The linker will treat type checks as a constant value (false), if the type in question is not instantiated, but we're instantiating types using reflection, which the linker can't see. The result is that the CIKernel_BasicTest/CIKernel_TestFromPrograms tests in monotouch-test fail.
So here we disable this particular linker optimization.
Ref: https://github.com/mono/linker/pull/1595 Ref: https://discord.com/channels/732297728826277939/751137004007456849/776033084431925268
Another reason (beside stripping which I have working locally) for the native code to be larger might be related to the lack of the LLVM AOT backend.
if ((abi & Abi.LLVM) == Abi.LLVM) throw ErrorHelper.CreateError (99, $"Support for LLVM hasn't been implemented yet.");
not sure what's the status on this... it does not seems to be tracked in #8901 @rolfbjarne @marek-safar ?
This is basically just a TODO for our side, I'm not aware of anything the runtime team has to do here.
I've filed https://github.com/xamarin/xamarin-macios/issues/11379 to keep track of it.
Results with the release/6.0.1xx-preview5
branch, commit ea0097c56837c34a484e06d0abdb2e658ea4556f
Highlights
- Enabled stripping the native executable
- Enabled removal of the static registrar
- Removal of additional custom attributes with linker xml files
Directories / Files | Legacy | preview.5 | diff | % |
---|---|---|---|---|
.//_CodeSignature | ||||
.// | ||||
embedded.mobileprovision | 12,391 | 12,391 | 0 | 0.0% |
icudt.dat | 0 | 1,713,152 | 1,713,152 | - |
Info.plist | 1,073 | 1,073 | 0 | 0.0% |
mscorlib.aotdata.arm64 | 297,320 | 0 | -297,320 | -100.0% |
mscorlib.dll | 407,552 | 0 | -407,552 | -100.0% |
MySingleView | 3,749,488 | 7,529,904 | 3,780,416 | 100.8% |
MySingleView.aotdata.arm64 | 816 | 792 | -24 | -2.9% |
MySingleView.dll | 0 | 4,608 | 4,608 | - |
MySingleView.exe | 4,608 | 0 | -4,608 | -100.0% |
PkgInfo | 8 | 8 | 0 | 0.0% |
System.aotdata.arm64 | 752 | 0 | -752 | -100.0% |
System.dll | 4,608 | 0 | -4,608 | -100.0% |
System.Private.CoreLib.aotdata.arm64 | 0 | 674,896 | 674,896 | - |
System.Private.CoreLib.dll | 0 | 713,216 | 713,216 | - |
System.Runtime.aotdata.arm64 | 0 | 504 | 504 | - |
System.Runtime.dll | 0 | 4,608 | 4,608 | - |
Xamarin.iOS.aotdata.arm64 | 38,464 | 44,784 | 6,320 | 16.4% |
Xamarin.iOS.dll | 57,856 | 73,216 | 15,360 | 26.5% |
Statistics | ||||
Native subtotal | 4,086,840 | 8,250,880 | 4,164,040 | 101.9% |
Executable | 3,749,488 | 7,529,904 | 3,780,416 | 100.8% |
AOT data | 337,352 | 720,976 | 383,624 | 113.7% |
Managed *.dll/exe | 474,624 | 795,648 | 321,024 | 67.6% |
TOTAL | 4,580,197 | 10,778,777 | 6,198,580 | 135.3% |
Notes
IL stripping
This is not yet available for net6 builds.
Manually using the mono-cil-strip
tools hints at the expected size reduction.
Directories / Files | Legacy | preview.5 | diff | % |
---|---|---|---|---|
TOTAL | 4,580,197 | 10,459,289 | 5,879,092 | 128.4% |
See more details
Globalization Impact
The above P5 table is not a perfect comparison as legacy globalization support does not, by default, support for all cultures, i.e. that requires some optional I18N.*.dll
assemblies. In contrast the default net6 app supports every cultures (just like other net6 platforms).
The following numbers are when net6 "Invariant Globalization" option is used. However this is, again, not a perfect comparison. The legacy app has support for several cultures included by default.
Directories / Files | Legacy | preview.5 | diff | % |
---|---|---|---|---|
TOTAL | 4,580,197 | 8,802,021 | 4,221,824 | 92.2% |
See more details
AFAICT the closest match for what legacy offered would be using icudt_EFIGS.dat
(English, French, Italian, German and Spanish). This is not yet easy to configure but the following numbers are probably the closest approximation to compare, at least for the same feature set.
Directories / Files | Legacy | preview.5 | diff | % |
---|---|---|---|---|
TOTAL | 4,580,197 | 9,667,733 | 5,087,536 | 111.1% |
See more details
JFYI there's an unexplained difference in the size of the ICU data files (https://github.com/dotnet/designs/pull/225#discussion_r647159143). Fixing that may shave off another 200kb or so.
@filipnavara I noticed a decrease on main
(P6) in that file size. Not sure if related to your comment or not.
Nope, it's still wrong (big) in today's ICU package.
Nope, it's still wrong (big) in today's ICU package.
Maybe, but I do get a smaller size in main
than P5
| icudt.dat | 0 | 1,636,880 | 1,636,880 | -
Still a different size, even larger, does not mean wrong, e.g. there's a issue open wrt missing calendar data.
Anyway I expect the size of those files to vary over times (reduced by optimizations and enlarged to include missing data) and would not assume any future gain (but would gladly accept them).
The sizes should not vary across platforms within the same ICU package. They are generated from the same data using the same filters and tools. Currently they do differ, the Android and Browser ones are identical (and smaller) and the iOS ones are tiny bit larger.
> find . -name icudt.dat | xargs ls -l
-rw-r--r--@ 1 filipnavara staff 1512896 Jun 7 13:03 ./android-arm/native/lib/icudt.dat
-rw-r--r--@ 1 filipnavara staff 1512896 Jun 7 13:03 ./android-arm64/native/lib/icudt.dat
-rw-r--r--@ 1 filipnavara staff 1512896 Jun 7 13:03 ./android-x64/native/lib/icudt.dat
-rw-r--r--@ 1 filipnavara staff 1512896 Jun 7 13:03 ./android-x86/native/lib/icudt.dat
-rw-r--r--@ 1 filipnavara staff 1512896 Jun 7 13:03 ./browser-wasm/native/lib/icudt.dat
-rw-r--r--@ 1 filipnavara staff 1516384 Jun 7 13:03 ./ios-arm/native/lib/icudt.dat
-rw-r--r--@ 1 filipnavara staff 1713152 Jun 7 13:03 ./ios-arm64/native/lib/icudt.dat
-rw-r--r--@ 1 filipnavara staff 1713152 Jun 7 13:03 ./iossimulator-arm64/native/lib/icudt.dat
-rw-r--r--@ 1 filipnavara staff 1713152 Jun 7 13:03 ./iossimulator-x64/native/lib/icudt.dat
-rw-r--r--@ 1 filipnavara staff 1713152 Jun 7 13:03 ./iossimulator-x86/native/lib/icudt.dat
-rw-r--r--@ 1 filipnavara staff 1713152 Jun 7 13:03 ./maccatalyst-arm64/native/lib/icudt.dat
-rw-r--r--@ 1 filipnavara staff 1713152 Jun 7 13:03 ./maccatalyst-x64/native/lib/icudt.dat
-rw-r--r--@ 1 filipnavara staff 1713152 Jun 7 13:03 ./tvos-arm64/native/lib/icudt.dat
-rw-r--r--@ 1 filipnavara staff 1713152 Jun 7 13:03 ./tvossimulator-arm64/native/lib/icudt.dat
-rw-r--r--@ 1 filipnavara staff 1713152 Jun 7 13:03 ./tvossimulator-x64/native/lib/icudt.dat
Another reason (beside stripping which I have working locally) for the native code to be larger might be related to the lack of the LLVM AOT backend.
if ((abi & Abi.LLVM) == Abi.LLVM) throw ErrorHelper.CreateError (99, $"Support for LLVM hasn't been implemented yet.");
I built System.Private.CoreLib.dll by hand using a Mono LLVM AOT cross-compiler but there isn't very much of a size difference compared to mini's native codegen. And I haven't yet replicated by hand exactly what we're doing to generate a final executable.
But we're failing to constant-fold some runtime type checks (ThrowHelper.ThrowForUnsupportedNumericVectorBaseType
for example) in both modes and I think fixing this would recover at least 300kb of executable code in __text
. We're also generating "impossible" function instantiations (like generating T_REF instantiations for functions with type-level parameters that have struct
constraints). We're also compiling a lot more code--post-link System.Private.CoreLib.dll yields about 23366 native Mach-O symbols while mscorlib.dll from old Mono produces ~11246.
Here's the output of "Bloaty McBloatface" run on the netcore non-LLVM SingleView app as of 732c27afb37feb17f1ca161d55d4f92ea115c929:
https://gist.githubusercontent.com/imhameed/f7c1cf3e4061c659413a428a57187b12/raw/772babda28a1bbad644b41dbab76134ea43b1fd1/bloaty-2021-06-30-732c27afb37feb17f1ca161d55d4f92ea115c929.txt
A tsv is also available at https://gist.github.com/imhameed/f7c1cf3e4061c659413a428a57187b12