-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Trimming System.SR #46940
Comments
Tagging subscribers to 'size-reduction': @eerhardt, @SamMonoRT, @marek-safar Issue DetailsIssue
I think there are two feasible fixes we could have here... Proposal 1We specially annotate the properties and have the IL Linker inline these at the usage site. Given that every one of these methods is auto-generated today and that they always follow the same format, this should be generally "safe" (and I believe similar to the Proposal 2We rework how resources are resolved so that, rather than going through a property ( A local prototype of this (proposal 2) for
|
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
I'm not sure I understand what's being inlined here. Is it the property body into the caller? |
Yes. Today, if you decompile public InvalidOperationException()
: base(SR.Arg_InvalidOperationException)
{
} With proposal 1, you would instead see (the linker having inlined public InvalidOperationException()
: base(SR.GetResourceString("Arg_InvalidOperationException"))
{
} With proposal 2, we manually change all the code and it becomes something like: public InvalidOperationException()
: base(SR.GetResourceString(nameof(SRID.Arg_InvalidOperationException)))
{
} The last is what I manually did locally for a subset of I would suggest that it stay internal only and have similar semantics to |
Most of our libraries don't call |
A very related problem to this one is outlining of the exception throwing into throw helpers. Ideally, we would have all exception throwing goo outlined into a helper methods that enables better code quality and code sharing. We do that manually today in CoreLib (https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs) and to lesser degree in other libraries too. The manually written ThrowHelpers are bug prone and not linker friendly (the linker is not able to trim unused strings and paths from the manually written ThrowHelpers). Can we solve both of these problems together? |
I think we're blurring the lines between the linker and crossgen here. It's potentially a good reason to merge the two tools in the future |
This wouldn't have to be restricted to the SR class - if illink can prove the method/property is not a target of reflection, there's no side effects, and inlining would be profitable, illink could inline methods in general (and slap an IgnoreAccessChecks attribute on the assembly to shut up warnings about violating visibility/accessibility checks). With the annotations we added in .NET 5 illink has a pretty good picture of what is visible from reflection. As a cheaper alternative, we could consider illink stripping the properties on the SR class and leaving just the methods. We'll probably get close to half of the expected benefit of inlining with that and such change is pretty cheap (we get rid of the entry in the Property table, the extra name without the Properties are only interesting for reflection and the runtime doesn't care about them. Illink could strip properties on all types where it knows the type is not a target of a |
I'm wondering if we could use source generators for this during compilation time and do less or no special work in IL linker. We need to get rid of code like this https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs#L524-L716 anyway and why to rewrite the code if we could generate it as size optimal from the start. There is also the aspect of and resources and the resources accessors duplication between all runtime assemblies which has a size impact too. |
Related, note that with dotnet/csharplang#2145 / https://github.com/dotnet/csharplang/blob/master/proposals/null-arg-checking.md, which is on track for C# 10, for at least argument null validation the C# compiler will generate code like that used for ThrowHelper. |
How would you make it linker friendly without losing compactness of the current manually written ThrowHelper? |
I was thinking about something like this (not sure if it's possible with source generators). class SomeType
{
public void Method(int startIndex, int length)
{
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), "Length cannot be less than zero.");
if (startIndex < 0)
throw new ArgumentOutOfRangeException(nameof(startIndex), "StartIndex cannot be less than zero.");
}
// Compiler rewritten/generated version of same method
public void Method(int startIndex, int length)
{
if (length < 0)
Global.Throw1 ();
if (startIndex < 0)
Global.Throw2 ();
}
}
static class Global
{
// It will be reused when the data match
public static void Throw1 ()
{
throw new ArgumentOutOfRangeException("length", "Length cannot be less than zero.");
}
public static void Throw2 ()
{
throw new ArgumentOutOfRangeException("startIndex", "StartIndex cannot be less than zero.");
}
} |
Source generators don't currently allow you to replace the contents of a method, only to define new code. |
@vitek-karas @marek-safar - is this something we should do for 6.0? Or can it be moved to 'Future'? |
Moving to 7.0.0 |
Issue
System.SR
is currently one of the largest types when looking at the trimmer dependency dump. As of6.0.100-alpha.1.21057.4
it is reported at23470
bytes.System.SR
exists of over a thousand small properties that are effectively justinternal static string Name => GetResourceString("Name");
. Given that each of these methods actually uses a different string constant, they each take up their own space in metadata and each user ends up having a call to the relevant method.I think there are two feasible fixes we could have here...
Proposal 1
We specially annotate the properties and have the IL Linker inline these at the usage site. Given that every one of these methods is auto-generated today and that they always follow the same format, this should be generally "safe" (and I believe similar to the
NonVersionable
attribute).Proposal 2
We rework how resources are resolved so that, rather than going through a property (
SR.Name
) we do something likeSR.GetResourceString(nameof(SRID.Name))
instead.A local prototype of this (proposal 2) for
S.P.Corelib
in the defaultblazor-wasm
project template shows a size reduction inSystem.Private.CoreLib.dll.br
of 6.4kb (and a reduction of 26.5kb for the uncompressedS.P.Corelib
).The text was updated successfully, but these errors were encountered: