-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Vectorize some Guid APIs (ctor, TryWriteBytes) #21336
Changes from 7 commits
a4aec27
be3ac7f
0feddf2
90a7eb3
e4d6a6a
fbd651d
e3a44a9
0080ec8
448130d
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 |
---|---|---|
|
@@ -51,6 +51,7 @@ public Guid(ReadOnlySpan<byte> b) | |
if ((uint)b.Length != 16) | ||
throw new ArgumentException(SR.Format(SR.Arg_GuidArrayCtor, "16"), nameof(b)); | ||
|
||
_k = b[15]; | ||
_a = b[3] << 24 | b[2] << 16 | b[1] << 8 | b[0]; | ||
_b = (short)(b[5] << 8 | b[4]); | ||
_c = (short)(b[7] << 8 | b[6]); | ||
|
@@ -61,7 +62,6 @@ public Guid(ReadOnlySpan<byte> b) | |
_h = b[12]; | ||
_i = b[13]; | ||
_j = b[14]; | ||
_k = b[15]; | ||
} | ||
|
||
[CLSCompliant(false)] | ||
|
@@ -93,14 +93,14 @@ public Guid(int a, short b, short c, byte[] d) | |
_a = a; | ||
_b = b; | ||
_c = c; | ||
_k = d[7]; | ||
_d = d[0]; | ||
_e = d[1]; | ||
_f = d[2]; | ||
_g = d[3]; | ||
_h = d[4]; | ||
_i = d[5]; | ||
_j = d[6]; | ||
_k = d[7]; | ||
} | ||
|
||
// Creates a new GUID initialized to the value represented by the | ||
|
@@ -768,43 +768,51 @@ private static bool IsHexPrefix(ReadOnlySpan<char> str, int i) => | |
str[i] == '0' && | ||
(str[i + 1] | 0x20) == 'x'; | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private void WriteByteHelper(Span<byte> destination) | ||
{ | ||
destination[0] = (byte)(_a); | ||
destination[1] = (byte)(_a >> 8); | ||
destination[2] = (byte)(_a >> 16); | ||
destination[3] = (byte)(_a >> 24); | ||
destination[4] = (byte)(_b); | ||
destination[5] = (byte)(_b >> 8); | ||
destination[6] = (byte)(_c); | ||
destination[7] = (byte)(_c >> 8); | ||
destination[8] = _d; | ||
destination[9] = _e; | ||
destination[10] = _f; | ||
destination[11] = _g; | ||
destination[12] = _h; | ||
destination[13] = _i; | ||
destination[14] = _j; | ||
destination[15] = _k; | ||
} | ||
|
||
// Returns an unsigned byte array containing the GUID. | ||
public byte[] ToByteArray() | ||
{ | ||
var g = new byte[16]; | ||
WriteByteHelper(g); | ||
if (BitConverter.IsLittleEndian) | ||
{ | ||
MemoryMarshal.TryWrite<Guid>(g, ref this); | ||
} | ||
else | ||
{ | ||
TryWriteBytes(g); | ||
} | ||
return g; | ||
} | ||
|
||
// Returns whether bytes are sucessfully written to given span. | ||
public bool TryWriteBytes(Span<byte> destination) | ||
{ | ||
if (destination.Length < 16) | ||
return false; | ||
|
||
WriteByteHelper(destination); | ||
return true; | ||
if (BitConverter.IsLittleEndian) | ||
{ | ||
return MemoryMarshal.TryWrite(destination, ref this); | ||
} | ||
|
||
// slower path for BigEndian | ||
if ((uint)destination.Length >= 16) | ||
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. Nit: Does writing it this way help anything? It is generally more readable to use early returns, like:
|
||
{ | ||
destination[0] = (byte)(_a); | ||
destination[1] = (byte)(_a >> 8); | ||
destination[2] = (byte)(_a >> 16); | ||
destination[3] = (byte)(_a >> 24); | ||
destination[4] = (byte)(_b); | ||
destination[5] = (byte)(_b >> 8); | ||
destination[6] = (byte)(_c); | ||
destination[7] = (byte)(_c >> 8); | ||
destination[8] = _d; | ||
destination[9] = _e; | ||
destination[10] = _f; | ||
destination[11] = _g; | ||
destination[12] = _h; | ||
destination[13] = _i; | ||
destination[14] = _j; | ||
destination[15] = _k; | ||
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. Why not to write this one first like you are doing in other places? 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 thought since it's surrounded with 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. Right, this optimization is very sensitive to exact pattern today. 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. Does it still write bounds checks if you specify 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. @grant-d unfortunately yes it ignores (uint) > 16 (and 16u) |
||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
// Returns the guid in "registry" format. | ||
|
@@ -839,10 +847,15 @@ public override bool Equals(object o) | |
public bool Equals(Guid g) | ||
{ | ||
// Now compare each of the elements | ||
#if BIT64 | ||
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. Why not to do this for the first check as well? 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. @jkotas in case if two guids are different (both random) - it becomes slower (see 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.
|
||
return Unsafe.As<int, ulong>(ref g._a) == Unsafe.As<int, ulong>(ref _a) && | ||
Unsafe.Add(ref Unsafe.As<int, ulong>(ref g._a), 1) == Unsafe.Add(ref Unsafe.As<int, ulong>(ref _a), 1); | ||
#else | ||
return g._a == _a && | ||
Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) && | ||
Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) && | ||
Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3); | ||
#endif | ||
} | ||
|
||
private int GetResult(uint me, uint them) | ||
|
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.
The constructor that takes Span can be optimized the same way.
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.
but will it compile? C# requires stucts' constructors to init all fields by hands (however
Decimal
compiles fine hm...).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.
ok it works