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

Large DLL size increases when not setting CsWinRTAotOptimizerEnabled to false (WinForms, crossgen/R2R) #1814

Open
rickbrew opened this issue Oct 9, 2024 · 3 comments
Labels
bug Something isn't working

Comments

@rickbrew
Copy link

rickbrew commented Oct 9, 2024

Describe the bug
After building my WinForms app and running crossgen, many of my assemblies are much larger when CsWinRTAotOptimizerEnabled is not set to false. That source generator is emitting a lot of code that isn't needed or even used. A lot of it are these ClassNameWinRTTypeDetails classes, and then there are these enormous GlobalVtableLookup classes, etc.

Obviously I can set CsWinRTAotOptimizerEnabled to false and this isn't a problem for me. However, it does imply that when/if I do want/need to make use of this functionality that it has a substantial cost associated with it.

If I hadn't run into a compile-time bug that's since been fixed ( dotnet/sdk#43746 ), I may not have even noticed this, or I may have attributed it to something else (crossgen codegen changes, perhaps?), or I may have spent hours and days trying to figure it out.

In addition, the ClassNameWinRTTypeDetails classes only seem to affect my classes that implement IDisposable and that also happen to be partial, which is a strange thing to key off of. And they're all identical except for their names. This seems to be happening because IDisposable is mapped to ICloseable, which is understandable. But can't these be consolidated somehow? This is an awful lot of duplicated code.

I'm not using much WinRT functionality. I use some stuff from Windows.Graphics.Display and some more from Windows.UI.Composition. I subscribe to two events using delegates. This code size bloat seems really excessive to me and it would make much more sense for it to be opt-in.

Here are the sizes of affected DLLs:

DLL Before After
PaintDotNet.Base.dll 1,520 KB 1,800 KB
PaintDotNet.Collections.dll 1,336 KB 1,448 KB
PaintDotNet.Core.dll 9,196 KB 10,312 KB
PaintDotNet.Data.dll 1,832 KB 2,112 KB
paintdotnet.dll 19,908 KB 20,832 KB
PaintDotNet.Effects.Core.dll 1,948 KB 2,092 KB
PaintDotNet.Effects.dll 700 KB 844 KB
PaintDotNet.Effects.Gpu.dll 4,184 KB 4,356 KB
PaintDotNet.Effects.Legacy.dll 1,880 KB 2,020 KB
PaintDotNet.Framework.dll 4,768 KB 5,040 KB
PaintDotNet.Fundamentals.dll 2,376 KB 2,600 KB
PaintDotNet.ObjectModel.dll 1,316 KB 1,464 KB
PaintDotNet.Primitives.dll 2,760 KB 2,908 KB
PaintDotNet.PropertySystem.dll 760 KB 916 KB
PaintDotNet.Resources.dll 1,864 KB 2,012 KB
PaintDotNet.SystemLayer.dll 2,436 KB 3,020 KB
PaintDotNet.Systrace.dll 368 KB 372 KB
PaintDotNet.UI.dll 3,856 KB 4,008 KB
PaintDotNet.Windows.Core.dll 3,724 KB 4,076 KB
PaintDotNet.Windows.dll 6,496 KB 7,892 KB
PaintDotNet.Windows.Framework.dll 20,452 KB 20,892 KB
Total 93,680 KB 101,016 KB

In total this adds 7,336 KB to my app's size if I want to use whatever this functionality is, or if I happened to not notice it. I'm not using AOT, and I'm not using trimming (at least not on these DLLs, for reasons), so it's bizarre that all this unnecessary code is being dumped into my assemblies.

Version Info
.NET 9.0-rc2, TFM of net9.0-windows10.0.26100.0 and whatever Windows SDK, CsWinRT, etc. gets pulled in by default there

@rickbrew rickbrew added the bug Something isn't working label Oct 9, 2024
@manodasanW
Copy link
Member

I agree with your feedback here. The WinRT type mappings to built-in interfaces have indeed caused more code to be generated than initially expected with respect to our generator. There are a couple of things I will be looking at doing to improve the situation here:

  • We were already using CsWinRTAotWarningLevel set to 2 as an opt-in for emitting warnings in scenarios related to built-in types to avoid the warnings being noisy. I think we will need to extend that to checking it for code generation too so that making types that only use built-in types perf / trimming / aot safe across the WinRT ABI is an opt-in rather than a default.
  • Detect scenarios that do have the same entries in the vtable being generated and generate a common typedetails class for it rather than unique ones. This should help with size as you mention. Same goes for lookup table too.
  • Have an attribute that types can be attributed with to opt them out from vtable or lookup table code generation to handle scenarios where someone has opted in to the generation for classes implementing only built-in types but don't actually need it for all of them.

@hamarb123
Copy link

hamarb123 commented Oct 10, 2024

I think we will need to extend that to checking it for code generation too so that making types that only use built-in types perf / trimming / aot safe across the WinRT ABI is an opt-in rather than a default.

Have an attribute that types can be attributed with to opt them out from vtable or lookup table code generation to handle scenarios where someone has opted in to the generation for classes implementing only built-in types but don't actually need it for all of them.

I personally would like to see a mode where it's actually opt-in instead. That is, I have to opt-in for the type to be handled at all. Realistically I will have a very small & controlled quantity of types that I want to marshal via WinRT, so I'd much prefer not having to think every time I add any type that implements IDisposable, write any delegate type, etc. about whether I should have to exclude it or not; I'd rather just add some attribute whenever I do actually want to do that (which will be easy to remember since I'm writing WinRT stuff, and also make it easy to quickly find every type that has / doesn't have the ability to be marshalled).

Thanks for your consideration :)

@paulpv
Copy link

paulpv commented Oct 17, 2024

Posting this to hopefully help others that start seeing this.

I am not certain of the OP issue, but on a relatively simple personal hobby project I am working on I started to see these error when compiling:

Type Code Description File Line
Error CS0116 A namespace cannot directly contain members such as fields, methods or statements obj\Debug\net8.0-windows10.0.26100.0\WinRT.SourceGenerator\Generator.WinRTAotSourceGenerator\WinRTGlobalVtableLookup.g.cs 5
Error CS1022 Type or namespace definition, or end-of-file expected obj\Debug\net8.0-windows10.0.26100.0\WinRT.SourceGenerator\Generator.WinRTAotSourceGenerator\WinRTGlobalVtableLookup.g.cs 6
Error CS1514 { expected obj\Debug\net8.0-windows10.0.26100.0\WinRT.SourceGenerator\Generator.WinRTAotSourceGenerator\WinRTGlobalVtableLookup.g.cs 5

My C# WinForms app and libs do not explicitly use WinRT (they do pinvoke some user32.dll calls).

I found dotnet/sdk#44026 and then this issue.

I edited my .csproj file to add CsWinRTAotOptimizerEnabled as follows:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows10.0.26100.0</TargetFramework>
    ...
    <CsWinRTAotOptimizerEnabled>false</CsWinRTAotOptimizerEnabled>
  </PropertyGroup>
...

The problem went away.

I changed CsWinRTAotOptimizerEnabled to true.

The problem came back.

I will be leaving CsWinRTAotOptimizerEnabled set to false.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants