-
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
Switch attribute equality to the valuetype equality plan #84095
Conversation
Attribute.Equals/GetHashCode is currently implemented using reflection. The implementation iterates over all instance fields and calls Equals/GetHashCode as needed. This has problems because the code is not actually trim safe (requires special casing in the compiler to work around an invalid suppression in CoreLib) and has extra problems for reflection blocking (we need to make sure the fields are never subject to blocking). In this PR I'm switching Attribute to a similar plan that .NET Native had. In .NET Native we injected a pair of virtual methods to read and access fields on System.Attribute descendants. We can actually get away with one method - I'm reusing all the infrastructure we have for ValueType.Equals/GetHashCode since the problem is the same. Saves 5.8% on a Hello World. For BasicMinimalApi, this is a wash (almost to the byte). It will become an improvement once RyuJIT starts generating better code for the field offset computations we're doing in the injected method. Contributes to dotnet#83069.
Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas Issue Details
This has problems because the code is not actually trim safe (requires special casing in the compiler to work around an invalid suppression in CoreLib) and has extra problems for reflection blocking (we need to make sure the fields are never subject to blocking). In this PR I'm switching Attribute to a similar plan that .NET Native had. In .NET Native we injected a pair of virtual methods to read and access fields on System.Attribute descendants. We can actually get away with one method - I'm reusing all the infrastructure we have for Saves 5.8% on a Hello World. For BasicMinimalApi, this is a wash (almost to the byte). It will become an improvement once RyuJIT starts generating better code for the field offset computations we're doing in the injected method. Contributes to #83069. Cc @dotnet/ilc-contrib
|
/azp run runtime-extra-platforms |
Azure Pipelines successfully started running 1 pipeline(s). |
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Attribute.NativeAot.cs
Outdated
Show resolved
Hide resolved
src/coreclr/tools/Common/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.cs
Outdated
Show resolved
Hide resolved
src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
Both during local morph and during VN. Fix #40021 Saves 3 KB on BasicMinimalApi after #84095 (there's 1111 __GetFieldHelper functions). About 0.04%. There's still a null check kept for each offset computation, which we cannot really get rid of, but NAOT could maybe emit the IL such that there is a dominating null check so that only one is emitted. Example: Base: ``` .managed:0000000140347CC0 loc_140347CC0: ; CODE XREF: S_P_CoreLib_System_Collections_Generic_KeyValuePair_2_System_Net_Security_System_Net_Security_SslSessionsCache_SslCredKey__System___Canon_____GetFieldHelper+23↑j .managed:0000000140347CC0 ; DATA XREF: .rdata:__readonlydata_S_P_CoreLib_System_Collections_Generic_KeyValuePair_2_System_Net_Security_System_Net_Security_SslSessionsCache_SslCredKey__System___Canon_____GetFieldHelper↓o .managed:0000000140347CC0 lea rax, ??_7Boxed_System_Net_Security_System_Net_Security_SslSessionsCache_SslCredKey@@6b@ ; jumptable 0000000140347CB3 case 0 .managed:0000000140347CC7 mov [r9], rax .managed:0000000140347CCA cmp [rcx], cl .managed:0000000140347CCC lea rax, [rcx+8] .managed:0000000140347CD0 sub rax, rcx .managed:0000000140347CD3 add rsp, 8 .managed:0000000140347CD7 retn ``` Diff: ``` .managed:0000000140347AA0 loc_140347AA0: ; CODE XREF: S_P_CoreLib_System_Collections_Generic_KeyValuePair_2_System_Net_Security_System_Net_Security_SslSessionsCache_SslCredKey__System___Canon_____GetFieldHelper+23↑j .managed:0000000140347AA0 ; DATA XREF: .rdata:__readonlydata_S_P_CoreLib_System_Collections_Generic_KeyValuePair_2_System_Net_Security_System_Net_Security_SslSessionsCache_SslCredKey__System___Canon_____GetFieldHelper↓o .managed:0000000140347AA0 lea rax, ??_7Boxed_System_Net_Security_System_Net_Security_SslSessionsCache_SslCredKey@@6b@ ; jumptable 0000000140347A93 case 0 .managed:0000000140347AA7 mov [r9], rax .managed:0000000140347AAA cmp [rcx], cl .managed:0000000140347AAC mov eax, 8 .managed:0000000140347AB1 add rsp, 8 .managed:0000000140347AB5 retn ``` Local morph changes handle the pattern for local structs -- VN changes handle the pattern for classes (and more complicated struct cases, like storing them in locals, which there are a few examples of in #40021).
Attribute.Equals
/GetHashCode
is currently implemented using reflection. The implementation iterates over all instance fields and callsEquals
/GetHashCode
as needed.This has problems because the code is not actually trim safe (requires special casing in the compiler to work around an invalid suppression in CoreLib) and has extra problems for reflection blocking (we need to make sure the fields are never subject to blocking).
In this PR I'm switching Attribute to a similar plan that .NET Native had. In .NET Native we injected a pair of virtual methods to read and access fields on System.Attribute descendants. We can actually get away with one method - I'm reusing all the infrastructure we have for
ValueType.Equals
/GetHashCode
since the problem is the same.Saves 5.8% on a Hello World. For BasicMinimalApi, this is a wash (almost to the byte). It will become an improvement once RyuJIT starts generating better code for the field offset computations we're doing in the injected method.
Contributes to #83069.
Cc @dotnet/ilc-contrib