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

Fix perf regression in IntPtr operators on 32-bit platforms #41198

Merged
merged 1 commit into from
Aug 24, 2020

Conversation

jkotas
Copy link
Member

@jkotas jkotas commented Aug 22, 2020

Switching to C# built-in uint/nuint types caused these operators to use long/ulong IntPtr/UIntPtr constructors instead of int/uint IntPtr constructors that it were used before.

The fix is to avoid going through the IntPtr/UIntPtr constructors and just depend on the built-in uint/nuint implicit conversions.

Fixes #41167

@Dotnet-GitSync-Bot
Copy link
Collaborator

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

@jkotas jkotas requested a review from danmoseley August 22, 2020 01:47
@jkotas jkotas added area-System.Runtime tenet-performance Performance related issue labels Aug 22, 2020
@danmoseley
Copy link
Member

I don't think I can review this immediately -- my brain is tired.

I'm impressed that this affected perf but not correctness -- how did it regress perf?

@jkotas
Copy link
Member Author

jkotas commented Aug 22, 2020

The code before this change is equivalent to:

public static unsafe IntPtr operator +(IntPtr pointer, int offset) =>
            new IntPtr((long)((nint)pointer._value + offset));

IntPtr(long) constructor on 32-bit platforms does checked long->int conversion that is expensive, and the JIT is not smart enough to optimize it out.

@danmoseley
Copy link
Member

Where was IntPtr involved in these simple collection lookups?

@danmoseley
Copy link
Member

Ah I see the other PR

Switching to C# built-in uint/nuint types caused these operators to use long/ulong IntPtr/UIntPtr constructors instead of int/uint IntPtr constructors that it were used before.

The fix is to avoid going through the IntPtr/UIntPtr constructors and just depend on the built-in uint/nuint implicit conversions.

Fixes dotnet#41167
Copy link
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

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

I've built @jkotas branch and verified that the fix is solving the regression:

Method Size 3.1 5.0 #41198
Array 512 36.196 us 115.129 us 35.911 us
Span 512 33.661 us 124.819 us 32.897 us
List 512 35.549 us 113.872 us 35.540 us
ICollection 512 34.857 us 116.611 us 34.810 us
LinkedList 512 212.843 us 213.416 us 212.328 us
HashSet 512 8.303 us 4.670 us 4.750 us
Queue 512 36.406 us 115.413 us 35.455 us
Stack 512 31.660 us 27.987 us 28.409 us
SortedSet 512 31.978 us 29.323 us 30.133 us
ImmutableArray 512 37.382 us 116.364 us 37.401 us
ImmutableHashSet 512 28.201 us 27.993 us 27.877 us
ImmutableList 512 3,457.214 us 693.476 us 707.291 us
ImmutableSortedSet 512 34.652 us 37.129 us 35.202 us

It's amazing that such a small change could have caused such regression. @jkotas big thanks for a very quick fix!

@adamsitnik adamsitnik added this to the 5.0.0 milestone Aug 24, 2020
@adamsitnik
Copy link
Member

/backport to release/5.0

@github-actions
Copy link
Contributor

Started backporting to release/5.0: https://github.com/dotnet/runtime/actions/runs/221717538

@@ -148,15 +148,15 @@ public unsafe int ToInt32()

[NonVersionable]
public static unsafe IntPtr operator +(IntPtr pointer, int offset) =>
new IntPtr((nint)pointer._value + offset);
Copy link
Member

Choose a reason for hiding this comment

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

Isn't this still implicitly invoking the IntPtr ctor since it returns one? Your change somehow changes it to use the int one on x86?

Copy link
Member Author

Choose a reason for hiding this comment

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

The C# compiler has implicit nint -> IntPtr conversion. This conversion is no-op. It does not generate any code.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, that was what I was missing. OK thanks LGTM

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Perf -196%] System.Collections.ContainsTrue<Int32> (6)
4 participants