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

Switch attribute equality to the valuetype equality plan #84095

Merged
merged 2 commits into from
Mar 30, 2023

Conversation

MichalStrehovsky
Copy link
Member

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 #83069.

Cc @dotnet/ilc-contrib

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.
@ghost
Copy link

ghost commented Mar 29, 2023

Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas
See info in area-owners.md if you want to be subscribed.

Issue Details

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 #83069.

Cc @dotnet/ilc-contrib

Author: MichalStrehovsky
Assignees: -
Labels:

area-NativeAOT-coreclr

Milestone: -

@MichalStrehovsky
Copy link
Member Author

/azp run runtime-extra-platforms

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@jkotas jkotas merged commit 6797c83 into dotnet:main Mar 30, 2023
@MichalStrehovsky MichalStrehovsky deleted the attrEquals branch March 31, 2023 00:28
jakobbotsch added a commit that referenced this pull request Apr 4, 2023
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).
@ghost ghost locked as resolved and limited conversation to collaborators Apr 30, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants