-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Bringing down the size of Hello World under 2 MB #80165
Comments
Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas Issue DetailsHello World with Native AOT is currently at around 2.7 MB. I spent a couple hours to see what would it take to bring it down to the same size as Golang (2 MB). It might actually be achievable with our default trimming settings and feature switches with a couple days of BCL/compiler work. The benefits would be threefold:
The 6686dbc commit has a couple hacks in it around areas of interest. With those hacks, the size of a hello world goes down to 2.16 MB. With Should we choose to accept this challenge, we'll need to fix all of the issues in the commit to really reap the benefit. The benefit is elimination of the entire reflection stack that deals with type members (in fact my goal was to get Here's the problems:
Cc @dotnet/ilc-contrib
|
I did a quick hack and indeed if we don't generate any custom attributes, there will be no constructed System.Attribute descendant in the closure. It also produces another 20 kB of savings. |
After #80607 we should no longer need the hacks around Type.IsValueType. |
Contributes to dotnet#80165. Unfortunately, the `NativeLibrary` APIs contain a pattern where one can skip providing a parameter to the API and then something expensive (custom attribute reading) will happen to compute the value. We have a `TryLoad` call in a hello world that does provide the value (in GlobalizationMode.cs). Make it possible to avoid the expensive thing internally.
Contributes to dotnet#80165. Resolving a `MethodBase` is a pretty expensive operation and currently even a hello world needs it because of `Exception.ToString()`. This pull request is trying to get `MethodBase` out of the picture when constructing the exception string. We currently only need `_method` for two things - the public `GetMethod` API, and `StackFrame.ToString`. The `StackFrame.ToString` can already deal with `_method` being null (and that codepath exists solely for NativeAOT). This is the minimal change. We could potentially explore other approaches - leave `MethodBase` always at null, or unshare the `StackFrame` class.
Contributes to #80165. Unfortunately, the `NativeLibrary` APIs contain a pattern where one can skip providing a parameter to the API and then something expensive (custom attribute reading) will happen to compute the value. We have a `TryLoad` call in a hello world that does provide the value (in GlobalizationMode.cs). Make it possible to avoid the expensive thing internally.
Contributes to #80165. Resolving a `MethodBase` is a pretty expensive operation and currently even a hello world needs it because of `Exception.ToString()`. This pull request is trying to get `MethodBase` out of the picture when constructing the exception string. We currently only need `_method` for two things - the public `GetMethod` API, and `StackFrame.ToString`. The `StackFrame.ToString` can already deal with `_method` being null (and that codepath exists solely for NativeAOT). This is the minimal change. We could potentially explore other approaches - leave `MethodBase` always at null, or unshare the `StackFrame` class.
Contributes to dotnet#80165. The compiler is not able to get rid of the call to `PerNameQueryCache<M>.Factory` virtual method due to generic code sharing. Making this not shareable allows trimming it because now we can see `ConcurrentUnifier<StringKey, __Canon>.Factory` is never called (whereas `ConcurrentUnifier<__Canon, __Canon>.Factory` is called). This is a ~5 kB regression for apps that do use reflection. We could explore different strategies to make this trimmable and not be a size regression, but I'm not sure if it's really worth it. We'd like to get rid of this reflection stack implementation anyway and replace it with something more lightweight.
Contributes to #80165. Dispensing of reflection member infos is done through a member policies class. This is a generic class that has specialized implementations for each kind of member info. It used a clever trick to get to the specific implementations. Just access `MemberPolicies<EventInfo>.Default` to get one for events or `MemberPolicies<PropertyInfo>.Default` to get one for properties. It was also absolutely not trimming friendly. This change removes the clever trick and replaces it with good old fashioned parameter passing.
Contributes to #80165. Not aware of a good reason to keep using it. Removes `TypeDelegator` from apps that don't use it. Saves 7 kB (but also stepping stone for the larger savings later).
Contributes to dotnet#80165. Dispensing of reflection member infos is done through a member policies class. This is a generic class that has specialized implementations for each kind of member info. It used a clever trick to get to the specific implementations. Just access `MemberPolicies<EventInfo>.Default` to get one for events or `MemberPolicies<PropertyInfo>.Default` to get one for properties. It was also absolutely not trimming friendly. This change removes the clever trick and replaces it with good old fashioned parameter passing.
Contributes to dotnet#80165. Not aware of a good reason to keep using it. Removes `TypeDelegator` from apps that don't use it. Saves 7 kB (but also stepping stone for the larger savings later).
Please don't delete reflection-disabled mode.
|
For a hello world, the difference is 1.77 MB by default, and 1.47 MB with reflection disabled mode (coming to a .NET 8 Preview 1 near you soon). It can be squashed down to less with the options to optimize codegen for size, disable stack trace data, globalization invariant mode, etc, but those options equally apply to both modes. It really is not that much. Those 300 kB fix issues people have been running into like There's not much incentive to enable it. I don't think it's going to pressure any libraries into supporting it - it breaks too much stuff. For the argument of perf - there's no law against slow software. We don't offer compiler switches to break other slow code either. There can be a lot of non-reflection slow code in the NuGets. Not planning to fully delete it, but I think it's time for people to think about letting that mode go. I've made my peace even though it's my baby. |
Hello World with Native AOT is currently at around 2.7 MB. I spent a couple hours to see what would it take to bring it down to the same size as Golang (2 MB). It might actually be achievable with our default trimming settings and feature switches with a couple days of BCL/compiler work.
The benefits would be threefold:
The 6686dbc commit has a couple hacks in it around areas of interest. With those hacks, the size of a hello world goes down to 2.16 MB. With
InvariantGlobalization
andUseSystemResourceKeys
it goes further down to 1.53 MB. This is awfully close to what is achievable with reflection disabled mode. We actually regressed the reflection disabled mode because a Hello World now has a generic virtual method call around enums and we need the entirety of the type loader to support that. But I digress.Should we choose to accept this challenge, we'll need to fix all of the issues in the commit to really reap the benefit. The benefit is elimination of the entire reflection stack that deals with type members (in fact my goal was to get
MemberPolicies
class out of the hello world).Here's the problems:
MethodTable
s #79594 might already help with this to some extent but I didn't check. Would be nice if it did.)Attribute.Equals and GetHashCode. This is a tough one. .NET Native didn't use a reflection-based implementation. We could restore that. Other alternative is to see if we can eliminate all custom attribute descendants from a hello world. The theory is that if we make generation of custom attributes conditional on the presence of the code to read them, we might be able to get rid of them all. But not sure.Chose not to address this and live with a small increase in size due to FieldInfo reflection. That one is less impactful than Method/ConstructorInfo.Cc @dotnet/ilc-contrib
The text was updated successfully, but these errors were encountered: