-
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
Performance improvement for Guid.Equals using SSE #53012
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -8,6 +8,8 @@ | |||||||
using System.Numerics; | ||||||||
using System.Runtime.CompilerServices; | ||||||||
using System.Runtime.InteropServices; | ||||||||
using System.Runtime.Intrinsics; | ||||||||
using System.Runtime.Intrinsics.X86; | ||||||||
using System.Runtime.Versioning; | ||||||||
using Internal.Runtime.CompilerServices; | ||||||||
|
||||||||
|
@@ -801,17 +803,38 @@ public override int GetHashCode() | |||||||
|
||||||||
public bool Equals(Guid g) => EqualsCore(this, g); | ||||||||
|
||||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||||||||
private static bool EqualsCore(in Guid left, in Guid right) | ||||||||
{ | ||||||||
ref int rA = ref Unsafe.AsRef(in left._a); | ||||||||
ref int rB = ref Unsafe.AsRef(in right._a); | ||||||||
if (Sse2.IsSupported) | ||||||||
{ | ||||||||
Vector128<byte> g1 = Unsafe.As<Guid, Vector128<byte>>(ref Unsafe.AsRef(left)); | ||||||||
Vector128<byte> g2 = Unsafe.As<Guid, Vector128<byte>>(ref Unsafe.AsRef(right)); | ||||||||
|
||||||||
if (Sse41.IsSupported) | ||||||||
{ | ||||||||
var xor = Sse2.Xor(g1, g2); | ||||||||
return Sse41.TestZ(xor, xor); | ||||||||
} | ||||||||
|
||||||||
var result = Sse2.CompareEqual(g1, g2); | ||||||||
return Sse2.MoveMask(result) == 0b1111_1111_1111_1111; | ||||||||
Comment on lines
+820
to
+821
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Results in same codegen and is simpler to read. Ideally the JIT should emit code that takes SSE4.1's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
But yes, under the SSE2 code path, it should be equivalent to invoke There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, my question was intented for @tannergooding (?) to have a look at this on the JIT-side 😃 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There are a few improvements that could happen for |
||||||||
} | ||||||||
|
||||||||
return SoftwareFallback(left, right); | ||||||||
|
||||||||
// Compare each element | ||||||||
static bool SoftwareFallback(in Guid left, in Guid right) | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Re There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Happy to take direction on this either way. I have found in my performance tests thus far that passing a |
||||||||
{ | ||||||||
ref int rA = ref Unsafe.AsRef(in left._a); | ||||||||
ref int rB = ref Unsafe.AsRef(in right._a); | ||||||||
|
||||||||
return rA == rB | ||||||||
&& Unsafe.Add(ref rA, 1) == Unsafe.Add(ref rB, 1) | ||||||||
&& Unsafe.Add(ref rA, 2) == Unsafe.Add(ref rB, 2) | ||||||||
&& Unsafe.Add(ref rA, 3) == Unsafe.Add(ref rB, 3); | ||||||||
// Compare each element | ||||||||
|
||||||||
return rA == rB | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we special case 64-bits processes by comparing with if (Environment.Is64BitProcess)
{
// code with long
}
else
{
// code with int
} The "fast out behavior" should be given with longs too, but I'm not sure how about alignment on ARM 64. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm happy to update with a special case for 64-bit software fallback if desired. The current |
||||||||
&& Unsafe.Add(ref rA, 1) == Unsafe.Add(ref rB, 1) | ||||||||
&& Unsafe.Add(ref rA, 2) == Unsafe.Add(ref rB, 2) | ||||||||
&& Unsafe.Add(ref rA, 3) == Unsafe.Add(ref rB, 3); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
private static int GetResult(uint me, uint them) => me < them ? -1 : 1; | ||||||||
|
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.
What about ARM64? We care about ARM64 as much as we care about x64.
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.
Well, if
SSE2
is not supported, we can just use a method withoutSSE2
? Much like what we have been doing so far. But still, in most casesSSE2
is supported.