dotnet / linker

388 stars 127 forks source link

Support trimming of framework-dependent applications #2139

Open vitek-karas opened 3 years ago

vitek-karas commented 3 years ago

Currently we only support self-contained applications to be trimmed. Allowing framework-dependent applications could be possible but would require some new functionality:

This would obviously also require non-trivial changes on the SDK side.

MisinformedDNA commented 2 years ago

From https://github.com/dotnet/runtime/issues/44869:

Currently we don't have plans to add a way to trim framework-dependent apps. There are two main reasons:

  • The size saving is typically not that interesting
  • It's much more risky. For self-contained apps we only trim the framework, not the app or NuGets - because it's much less risky to do so. Trimming the app or NuGets is currently risky and may very well break the application.

The second bullet conflicts with the existence of Prepare .NET libraries for trimming and its text:

The .NET SDK makes it possible to reduce the size of self-contained apps by trimming, which removes unused code from the app and its dependencies.

So which is it?

vitek-karas commented 2 years ago

Sorry - my wording was unfortunate. And it's basically stale at this point.

The default configuration is currently such that only assemblies which opt-in to trimming are trimmed (in IL this is marked by having an assembly level attribute [assembly: AssemblyMetadata("IsTrimmable", "True")]). It's not very common for NuGets to opt-in to trimming... yet. And application assemblies are also not opted-in by default.

Given that, trimming apps without framework will not provide much of a benefit.

With the current trimmer in theory there's no "risk" - if the trimmer doesn't report any warnings, it is perfectly OK to trim the app and it should not break.

There's also the fact that in such mode the trimmer would not be as effective as it is now when it trims everything. As noted above, some optimization require global view of the app, and without it they need to be disabled. It's unknown currently what is the impact of that (nobody tried to measure this).

MisinformedDNA commented 2 years ago

You have a chicken and egg problem.

FACT: Framework-dependent apps are the more popular option:

The framework-dependent deployment model has been the most successful deployment model since the inception of .NET... This deployment model continues to be the dominant one in the latest .NET release

So, if framework-dependent apps are dominant, then developers and library owners are less likely to pursue trimming. If fewer libraries use trimming, then the .NET team is less likely to invest in framework-dependent trimming.

This is a con for either deployment model.

Unless the goal is to encourage developers to use the self-contained model instead?

vitek-karas commented 2 years ago

I absolutely agree there's a "chicken and egg" kind of problem. But that's sort of typical for these types of changes. On the other hand there's a prioritization problem - there's only limited amount of resources available. Right now we choose to focus our efforts into two main areas:

It's also true that we haven't seen many requests for trimming framework dependent apps.

There's also the fact that there are other verticals where trimming is sort of needed (or really good to have) like pretty much all Android and iOS apps as well as client side Blazor - all of these scenarios are 100% self-contained.

MisinformedDNA commented 2 years ago

OK, I'm considering switching to self-contained, just to get trimming and NativeAOT.

vitek-karas commented 2 years ago

I can understand the NativeAOT part - as that requires self-contained and so on. But if you're switching to self-contained just to get trimming that feels weird - the app will be very likely bigger due to the framework, even with trimming turned on.

MisinformedDNA commented 2 years ago

Yeah, I probably wouldn't do self-contained just for trimming, but I do have a large library in the form of WinForm controls, so I'll have to do some measurements.

MichalStrehovsky commented 1 year ago

I think it's going to be hard to enumerate all the possible ways an updated framework that was trimmed based on a previous version of the framework can break.

Consider:

V1:

class FrameworkClass
{
    // ...
}

V2:

[DynamicallyAccessedMembers(PublicProperties)]
class FrameworkClass
{
    public void Something()
    {
        this.GetType().GetProperties();
    }

    // ...
}

Now consider this in user app:

class UserClass : FrameworkClass
{
    public int MyProperty { get; }
}

If we're trimming user app against framework v1, MyProperty is free to be trimmed away. But once you swap the framework for v2, MyProperty should not have been trimmed away because the class is annotated to keep all the properties on its descendants. The class will likely not work as intended because we trimmed a descendant, knowing nothing about the logic in v2.

jkotas commented 1 year ago

I think it's going to be hard to enumerate all the possible ways an updated framework that was trimmed based on a previous version of the framework can break.

That's why we discourage major framework version roll forward without recompile.

hamarb123 commented 3 weeks ago

This would be great to have so that I can use TerraFX.Interop.Windows without making my app >35x bigger (size without self-contained just from adding the 1 dll - with self-contained + trimming, it's >25x bigger still without TerraFX.Interop.Windows), and without having to double-compile my app for Windows and macOS (which I currently don't have to, as I use the exe on windows, and the dll on macOS - this wouldn't work with self-contained or NAOT, which would still almost certainly be multiple times larger than currently).