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

Techniques for Reducing Size of .NET Native Binaries #7285

Closed
RehanSaeed opened this Issue Apr 12, 2019 · 3 comments

Comments

Projects
None yet
3 participants
@RehanSaeed
Copy link

RehanSaeed commented Apr 12, 2019

I'm working on a UWP app being built with .NET native. Reducing the app size is very important. Are there any techniques, guidance or documentation available to help reduce the app size? Are there ways we can help the .NET native tree shaking algorithm be more effective?

Some of the simple techniques I can think of are pretty basic:

  1. Code reuse.
  2. Avoid await as this generates a lot of state machine code e.g. Adding six awaits increased the size of my app by 20KB.
  3. Compress static assets like images etc.
@MichalStrehovsky

This comment has been minimized.

Copy link
Member

MichalStrehovsky commented Apr 12, 2019

There are some types that are known to be very heavy and are better avoided. E.g. using XmlReader brings 3.5 MB of baggage with it (entire XML schema validation support comes in, for example). WebRequest weights about 800 kB because it includes FTP support and other stuff (use HttpClient instead).

Then there are some properties you could add in your csproj that control what the compiler does: <DisableStackTraceMetadata>true</DisableStackTraceMetadata> - this disables generation of stack trace metadata (the things that generates nice textual stack traces in Exception.ToString(). <DisableExceptionMessages>true</DisableExceptionMessages> disables generation of exception messages in framework-thrown exceptions.

@4creators

This comment has been minimized.

Copy link
Contributor

4creators commented Apr 12, 2019

@MichalStrehovsky Is it possible to have a documentation entry on this subject. I remember that some recent work allowing to get rid from reflection metadata was reducing executable size significantly.

@MichalStrehovsky

This comment has been minimized.

Copy link
Member

MichalStrehovsky commented Apr 15, 2019

I remember that some recent work allowing to get rid from reflection metadata was reducing executable size significantly.

Here's a chapter from an internal document that I wrote on reflection:

A major problem of compiling .NET ahead of time is predicting what code will be used at runtime. Reflection APIs allow users to access any part of the app. To keep the app small, the compiler would like to compile as little code as possible because native code tends to be bigger than IL. Not having a JIT to lean on at runtime, this puts the compiler in a tough spot.

To help the compiler solve this problem, .NET Native adds RD.XML files. RD.XML controls a couple aspects of compilation, but reflection is the most interesting one from size on disk perspective.
By default (if you create a new UWP app from a template in VS), the project includes an RD.XML file that has a *Application* line in it. This line tells the compiler to assume all the non-framework code is accessible by reflection and it should generate code and data structures for it to make it runnable. Without this line, the compiler would basically start from compiling your Main() and all the code statically referenced from it; if the app tries to access code or types dynamically in a way the compiler hasn't seen/predicted, the reflection operation will fail (it will either throw, or return "null", depending on the API).

We use the *Application* setting for compatibility reasons and it can bloat your app size quite a bit (especially if you’re using various 3rd party libraries, but only use a fraction of their functionality). About 80% of apps will just work even without this line. The remaining 20% can tell the compiler on a more granular level what things they need (what types or methods should be generated in addition to what is used statically). The amount of work needed to make the app work without *Application* ranges from a couple rebuild attempts (each one adding a thing to RD.XML that was missing) to days of work (resulting in dozens of lines added to RD.XML, some of them requiring quite some time to debug).

Removing the *Application* line and recompiling the app will tell you how much you can get (size-wise) in the best possible scenario. It’s possible that to get the app actually running, the size will go up a bit, but it should hopefully stay lower than with *Application*.

Useful links:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.