From b06e41cb74a4f342136bb965c58fc12a5067c2d3 Mon Sep 17 00:00:00 2001 From: Pent Ploompuu Date: Thu, 12 Mar 2026 22:36:43 +0800 Subject: [PATCH 1/3] Optimize Guid formatting --- .../Text/Utf8Formatter/Utf8Formatter.Guid.cs | 41 +- .../System.Private.CoreLib/src/System/Guid.cs | 531 +++++++----------- 2 files changed, 214 insertions(+), 358 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs index 31b60eba9ea61c..33bece9919ebaa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs @@ -28,29 +28,32 @@ public static partial class Utf8Formatter /// public static bool TryFormat(Guid value, Span destination, out int bytesWritten, StandardFormat format = default) { - int flags; - - switch (FormattingHelpers.GetSymbolOrDefault(format, 'D')) + int flags = format.Symbol; + if (flags is '\0' or 'D') { - case 'D': // nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn - flags = 36 + Guid.TryFormatFlags_UseDashes; - break; - - case 'B': // {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn} - flags = 38 + Guid.TryFormatFlags_UseDashes + Guid.TryFormatFlags_CurlyBraces; - break; + // nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn + flags = 36 + Guid.TryFormatFlags_UseDashes; + } + else + { + switch (flags) + { + case 'N': // nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn + flags = 32; + break; - case 'P': // (nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn) - flags = 38 + Guid.TryFormatFlags_UseDashes + Guid.TryFormatFlags_Parens; - break; + case 'B': // {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn} + flags = 38 + Guid.TryFormatFlags_UseDashes + Guid.TryFormatFlags_CurlyBraces; + break; - case 'N': // nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn - flags = 32; - break; + case 'P': // (nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn) + flags = 38 + Guid.TryFormatFlags_UseDashes + Guid.TryFormatFlags_Parens; + break; - default: - ThrowHelper.ThrowFormatException_BadFormatSpecifier(); - goto case 'D'; // unreachable + default: + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); + break; // unreachable + } } return value.TryFormatCore(destination, out bytesWritten, flags); diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 270da4ab7d0b09..9cc9263576375c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -1104,8 +1104,6 @@ private static bool EqualsCore(in Guid left, in Guid right) && Unsafe.Add(ref rA, 3) == Unsafe.Add(ref rB, 3); } - private static int GetResult(uint me, uint them) => me < them ? -1 : 1; - public int CompareTo(object? value) { if (value == null) @@ -1123,62 +1121,24 @@ public int CompareTo(Guid value) { if (value._a != _a) { - return GetResult((uint)_a, (uint)value._a); - } - - if (value._b != _b) - { - return GetResult((uint)_b, (uint)value._b); - } - - if (value._c != _c) - { - return GetResult((uint)_c, (uint)value._c); + return ((uint)_a).CompareTo((uint)value._a); } - if (value._d != _d) + if ((ushort)value._b != (ushort)_b) { - return GetResult(_d, value._d); + return ((ushort)_b).CompareTo((ushort)value._b); } - if (value._e != _e) + if ((ushort)value._c != (ushort)_c) { - return GetResult(_e, value._e); + return ((ushort)_c).CompareTo((ushort)value._c); } - if (value._f != _f) - { - return GetResult(_f, value._f); - } - - if (value._g != _g) - { - return GetResult(_g, value._g); - } - - if (value._h != _h) - { - return GetResult(_h, value._h); - } - - if (value._i != _i) - { - return GetResult(_i, value._i); - } - - if (value._j != _j) - { - return GetResult(_j, value._j); - } - - if (value._k != _k) - { - return GetResult(_k, value._k); - } - - return 0; + return GetLow64().CompareTo(value.GetLow64()); } + private ulong GetLow64() => BinaryPrimitives.ReadUInt64BigEndian(MemoryMarshal.CreateReadOnlySpan(in _d, 8)); + public static bool operator ==(Guid a, Guid b) => EqualsCore(a, b); public static bool operator !=(Guid a, Guid b) => !EqualsCore(a, b); @@ -1196,21 +1156,26 @@ private static unsafe int HexsToChars(TChar* guidChars, int a, int b) whe } // Returns the guid in "registry" format. - public override string ToString() => ToString("d", null); - - public string ToString([StringSyntax(StringSyntaxAttribute.GuidFormat)] string? format) + public override string ToString() { - return ToString(format, null); + const int flags = 36 + TryFormatFlags_UseDashes; + string guidString = string.FastAllocateString(36); + bool result = TryFormatCore(new Span(ref guidString.GetRawStringData(), 36), out int charsWritten, flags); + Debug.Assert(result && charsWritten == guidString.Length); + return guidString; + } // IFormattable interface // We currently ignore provider - public string ToString([StringSyntax(StringSyntaxAttribute.GuidFormat)] string? format, IFormatProvider? provider) + public string ToString([StringSyntax(StringSyntaxAttribute.GuidFormat)] string? format, IFormatProvider? provider) => ToString(format); + + public string ToString([StringSyntax(StringSyntaxAttribute.GuidFormat)] string? format) { - int guidSize; + int flags; if (string.IsNullOrEmpty(format)) { - guidSize = 36; + flags = 36 + TryFormatFlags_UseDashes; } else { @@ -1223,32 +1188,34 @@ public string ToString([StringSyntax(StringSyntaxAttribute.GuidFormat)] string? switch (format[0] | 0x20) { case 'd': - guidSize = 36; + flags = 36 + TryFormatFlags_UseDashes; break; case 'n': - guidSize = 32; + flags = 32; break; - case 'b' or 'p': - guidSize = 38; + case 'b': + flags = 38 + TryFormatFlags_UseDashes + TryFormatFlags_CurlyBraces; break; - case 'x': - guidSize = 68; + case 'p': + flags = 38 + TryFormatFlags_UseDashes + TryFormatFlags_Parens; break; + case 'x': return ToStringX(); + default: - guidSize = 0; + flags = 0; ThrowBadGuidFormatSpecification(); break; - }; + } } - string guidString = string.FastAllocateString(guidSize); + string guidString = string.FastAllocateString((byte)flags); - bool result = TryFormatCore(new Span(ref guidString.GetRawStringData(), guidString.Length), out int bytesWritten, format); - Debug.Assert(result && bytesWritten == guidString.Length, "Formatting guid should have succeeded."); + bool result = TryFormatCore(new Span(ref guidString.GetRawStringData(), (byte)flags), out int charsWritten, flags); + Debug.Assert(result && charsWritten == guidString.Length, "Formatting guid should have succeeded."); return guidString; } @@ -1322,6 +1289,8 @@ private bool TryFormatCore(Span destination, out int charsWritten, return TryFormatCore(destination, out charsWritten, flags); } + // (Vectorized) implementation for D, N, P and B formats: + // [{|(]dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd[}|)] [MethodImpl(MethodImplOptions.AggressiveInlining)] // only used from two callers internal unsafe bool TryFormatCore(Span destination, out int charsWritten, int flags) where TChar : unmanaged, IUtfChar { @@ -1335,81 +1304,155 @@ internal unsafe bool TryFormatCore(Span destination, out int chars charsWritten = (byte)flags; flags >>= 8; - fixed (TChar* guidChars = &MemoryMarshal.GetReference(destination)) + ref TChar d = ref MemoryMarshal.GetReference(destination); + + // The low byte of flags now contains the opening brace char (if any) + if ((byte)flags != 0) { - TChar* p = guidChars; + d = TChar.CastFrom((byte)flags); + d = ref Unsafe.Add(ref d, 1); + } + flags >>= 8; - // The low byte of flags now contains the opening brace char (if any) - if ((byte)flags != 0) + if (Avx512Vbmi.VL.IsSupported) + { + Vector128 hexMap = Vector128.Create( + (byte)'0', (byte)'1', (byte)'2', (byte)'3', + (byte)'4', (byte)'5', (byte)'6', (byte)'7', + (byte)'8', (byte)'9', (byte)'a', (byte)'b', + (byte)'c', (byte)'d', (byte)'e', (byte)'f'); + + Vector256 srcVec = Avx2.ConvertToVector256Int64(Unsafe.BitCast>(this)); + // because of Guid's layout (int _a, short _b, _c, <8 byte fields>) + // we have to shuffle some bytes for _a, _b and _c + Vector256 control = Vector256.Create( + 28, 24, 20, 16, 12, 8, 4, 0, + 12, 8, 4, 0, 28, 24, 20, 16, + 4, 0, 12, 8, 20, 16, 28, 24, + 4, 0, 12, 8, 20, 16, 28, 24); + Vector256 nibbles = Avx512Vbmi.VL.MultiShift(control, srcVec).AsByte(); + Vector256 hex = Avx512Vbmi.VL.PermuteVar32x8(Vector256.Create(hexMap), nibbles).AsByte(); + + if (flags < 0 /* dash */) { - *p++ = TChar.CastFrom((byte)flags); - } - flags >>= 8; + // Create the middle part that must be merged in this order later: + // + // ________-____-____-____-____________ + // yyyyyyyyyyyyyyyy + // xxxxxxxxxxxxxxxx + // zzzzzzzzzzzzzzzz + // + // "x" is the low part of "hex" and "y" is the high part of "hex". + // "z" - middle part of hex blended with 4 dashes. + Vector256 mid = Avx2.Blend(hex.AsInt32(), Vector256.Create((int)'-'), 1).AsByte(); + Vector128 midMask = Vector128.Create((byte)0, 8, 9, 10, 11, 0, 12, 13, 14, 15, 0, 16, 17, 18, 19, 0); + Vector128 vecZ = Avx512Vbmi.VL.PermuteVar32x8(mid, midMask.ToVector256Unsafe()).GetLower(); - if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian) + if (typeof(TChar) == typeof(byte)) + { + ref byte pChar = ref Unsafe.As(ref d); + hex.StoreUnsafe(ref pChar, 4); + hex.GetLower().StoreUnsafe(ref pChar); + vecZ.StoreUnsafe(ref pChar, 8); + } + else + { + ref ushort pChar = ref Unsafe.As(ref d); + Vector256.WidenUpper(hex).StoreUnsafe(ref pChar, 20); + Vector128.WidenLower(hex.GetLower()).StoreUnsafe(ref pChar); + Vector256.WidenLower(Vector128.ToVector256Unsafe(vecZ)).StoreUnsafe(ref pChar, 8); + } + d = ref Unsafe.Add(ref d, 36); + } + else { - // Vectorized implementation for D, N, P and B formats: - // [{|(]dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd[}|)] - (Vector128 vecX, Vector128 vecY, Vector128 vecZ) = FormatGuidVector128Utf8(this, flags < 0 /* dash */); - if (typeof(TChar) == typeof(byte)) { - byte* pChar = (byte*)p; - if (flags < 0 /* dash */) + hex.StoreUnsafe(ref Unsafe.As(ref d)); + } + else + { + Vector512.WidenLower(Vector256.ToVector512Unsafe(hex)).StoreUnsafe(ref Unsafe.As(ref d)); + } + d = ref Unsafe.Add(ref d, 32); + } + } + else if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian) + { + (Vector128 vecX, Vector128 vecY, Vector128 vecZ) = FormatGuidVector128Utf8(this, flags < 0 /* dash */); + + if (typeof(TChar) == typeof(byte)) + { + ref byte pChar = ref Unsafe.As(ref d); + if (flags < 0 /* dash */) + { + // We need to merge these vectors in this order: + // xxxxxxxxxxxxxxxx + // yyyyyyyyyyyyyyyy + // zzzzzzzzzzzzzzzz + vecX.StoreUnsafe(ref pChar); + vecY.StoreUnsafe(ref pChar, 20); + vecZ.StoreUnsafe(ref pChar, 8); + d = ref Unsafe.Add(ref d, 36); + } + else + { + // xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyy + vecX.StoreUnsafe(ref pChar); + vecY.StoreUnsafe(ref pChar, 16); + d = ref Unsafe.Add(ref d, 32); + } + } + else + { + ref ushort pChar = ref Unsafe.As(ref d); + if (flags < 0 /* dash */) + { + // We need to merge these vectors in this order: + // xxxxxxxxxxxxxxxx + // yyyyyyyyyyyyyyyy + // zzzzzzzzzzzzzzzz + Vector128.WidenLower(vecX).StoreUnsafe(ref pChar); + if (Vector256.IsHardwareAccelerated) { - // We need to merge these vectors in this order: - // xxxxxxxxxxxxxxxx - // yyyyyyyyyyyyyyyy - // zzzzzzzzzzzzzzzz - vecX.Store(pChar); - vecY.Store(pChar + 20); - vecZ.Store(pChar + 8); - p += 36; + Vector256.WidenLower(Vector128.ToVector256Unsafe(vecY)).StoreUnsafe(ref pChar, 20); + Vector256.WidenLower(Vector128.ToVector256Unsafe(vecZ)).StoreUnsafe(ref pChar, 8); } else { - // xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyy - vecX.Store(pChar); - vecY.Store(pChar + 16); - p += 32; + Vector128.WidenLower(vecY).StoreUnsafe(ref pChar, 20); + Vector128.WidenUpper(vecY).StoreUnsafe(ref pChar, 28); + Vector128.WidenLower(vecZ).StoreUnsafe(ref pChar, 8); + Vector128.WidenUpper(vecZ).StoreUnsafe(ref pChar, 16); } + d = ref Unsafe.Add(ref d, 36); } else { - // Expand to UTF-16 - (Vector128 x0, Vector128 x1) = Vector128.Widen(vecX); - (Vector128 y0, Vector128 y1) = Vector128.Widen(vecY); - ushort* pChar = (ushort*)p; - if (flags < 0 /* dash */) + // xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyy + if (Vector256.IsHardwareAccelerated) { - (Vector128 z0, Vector128 z1) = Vector128.Widen(vecZ); - - // We need to merge these vectors in this order: - // xxxxxxxxxxxxxxxx - // yyyyyyyyyyyyyyyy - // zzzzzzzzzzzzzzzz - x0.Store(pChar); - y0.Store(pChar + 20); - y1.Store(pChar + 28); - z0.Store(pChar + 8); // overlaps x1 - z1.Store(pChar + 16); - p += 36; + Vector256.WidenLower(Vector128.ToVector256Unsafe(vecX)).StoreUnsafe(ref pChar); + Vector256.WidenLower(Vector128.ToVector256Unsafe(vecY)).StoreUnsafe(ref pChar, 16); } else { - // xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyy - x0.Store(pChar); - x1.Store(pChar + 8); - y0.Store(pChar + 16); - y1.Store(pChar + 24); - p += 32; + Vector128.WidenLower(vecX).StoreUnsafe(ref pChar); + Vector128.WidenUpper(vecX).StoreUnsafe(ref pChar, 8); + Vector128.WidenLower(vecY).StoreUnsafe(ref pChar, 16); + Vector128.WidenUpper(vecY).StoreUnsafe(ref pChar, 24); } + d = ref Unsafe.Add(ref d, 32); } } - else + } + else + { + fixed (TChar* guidChars = &d) { - // Non-vectorized fallback for D, N, P and B formats: - // [{|(]dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd[}|)] + TChar* p = guidChars; + // Non-vectorized fallback for: + // dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd p += HexsToChars(p, _a >> 24, _a >> 16); p += HexsToChars(p, _a >> 8, _a); if (flags < 0 /* dash */) @@ -1434,20 +1477,28 @@ internal unsafe bool TryFormatCore(Span destination, out int chars p += HexsToChars(p, _f, _g); p += HexsToChars(p, _h, _i); p += HexsToChars(p, _j, _k); + d = ref *p; } + } - // The low byte of flags now contains the closing brace char (if any) - if ((byte)flags != 0) - { - *p = TChar.CastFrom((byte)flags); - } - - Debug.Assert(p == guidChars + charsWritten - ((byte)flags != 0 ? 1 : 0)); + // The low byte of flags now contains the closing brace char (if any) + if ((byte)flags != 0) + { + d = TChar.CastFrom((byte)flags); } + Debug.Assert(Unsafe.AreSame(in d, in Unsafe.Add(ref destination[0], charsWritten - ((byte)flags != 0 ? 1 : 0)))); return true; } + private string ToStringX() + { + string guidString = string.FastAllocateString(68); + bool result = TryFormatX(new Span(ref guidString.GetRawStringData(), guidString.Length), out int charsWritten); + Debug.Assert(result && charsWritten == guidString.Length, "Formatting guid should have succeeded."); + return guidString; + } + private bool TryFormatX(Span dest, out int charsWritten) where TChar : unmanaged, IUtfChar { if (dest.Length < 68) @@ -1543,13 +1594,11 @@ private static (Vector128, Vector128, Vector128) FormatGuidVec // yyyyyyyyyyyyyyyy // zzzzzzzzzzzzzzzz // - // Vector "x" - just one dash, shift all elements after it. - Vector128 vecX = Vector128.Shuffle(hexLow, - Vector128.Create(0x706050403020100, 0xD0CFF0B0A0908FF).AsByte()); + // Vector "x" - the dash and everything after it is going to be overwritten by "z" + Vector128 vecX = hexLow; - // Vector "y" - same here. - Vector128 vecY = Vector128.Shuffle(hexHigh, - Vector128.Create(0x7060504FF030201, 0xF0E0D0C0B0A0908).AsByte()); + // Vector "y" - the dash and everything before it is going to be overwritten by "z" + Vector128 vecY = hexHigh; // Vector "z" - we need to merge some elements of hexLow with hexHigh and add 4 dashes. Vector128 vecZ; @@ -1593,57 +1642,17 @@ private static (Vector128, Vector128, Vector128) FormatGuidVec return (uint)left._a < (uint)right._a; } - if (left._b != right._b) - { - return (uint)left._b < (uint)right._b; - } - - if (left._c != right._c) - { - return (uint)left._c < (uint)right._c; - } - - if (left._d != right._d) - { - return left._d < right._d; - } - - if (left._e != right._e) - { - return left._e < right._e; - } - - if (left._f != right._f) - { - return left._f < right._f; - } - - if (left._g != right._g) - { - return left._g < right._g; - } - - if (left._h != right._h) - { - return left._h < right._h; - } - - if (left._i != right._i) - { - return left._i < right._i; - } - - if (left._j != right._j) + if ((ushort)left._b != (ushort)right._b) { - return left._j < right._j; + return (ushort)left._b < (ushort)right._b; } - if (left._k != right._k) + if ((ushort)left._c != (ushort)right._c) { - return left._k < right._k; + return (ushort)left._c < (ushort)right._c; } - return false; + return left.GetLow64() < right.GetLow64(); } /// @@ -1654,180 +1663,24 @@ private static (Vector128, Vector128, Vector128) FormatGuidVec return (uint)left._a < (uint)right._a; } - if (left._b != right._b) - { - return (uint)left._b < (uint)right._b; - } - - if (left._c != right._c) - { - return (uint)left._c < (uint)right._c; - } - - if (left._d != right._d) - { - return left._d < right._d; - } - - if (left._e != right._e) - { - return left._e < right._e; - } - - if (left._f != right._f) - { - return left._f < right._f; - } - - if (left._g != right._g) - { - return left._g < right._g; - } - - if (left._h != right._h) - { - return left._h < right._h; - } - - if (left._i != right._i) + if ((ushort)left._b != (ushort)right._b) { - return left._i < right._i; + return (ushort)left._b < (ushort)right._b; } - if (left._j != right._j) + if ((ushort)left._c != (ushort)right._c) { - return left._j < right._j; + return (ushort)left._c < (ushort)right._c; } - if (left._k != right._k) - { - return left._k < right._k; - } - - return true; + return left.GetLow64() <= right.GetLow64(); } /// - public static bool operator >(Guid left, Guid right) - { - if (left._a != right._a) - { - return (uint)left._a > (uint)right._a; - } - - if (left._b != right._b) - { - return (uint)left._b > (uint)right._b; - } - - if (left._c != right._c) - { - return (uint)left._c > (uint)right._c; - } - - if (left._d != right._d) - { - return left._d > right._d; - } - - if (left._e != right._e) - { - return left._e > right._e; - } - - if (left._f != right._f) - { - return left._f > right._f; - } - - if (left._g != right._g) - { - return left._g > right._g; - } - - if (left._h != right._h) - { - return left._h > right._h; - } - - if (left._i != right._i) - { - return left._i > right._i; - } - - if (left._j != right._j) - { - return left._j > right._j; - } - - if (left._k != right._k) - { - return left._k > right._k; - } - - return false; - } + public static bool operator >(Guid left, Guid right) => right < left; /// - public static bool operator >=(Guid left, Guid right) - { - if (left._a != right._a) - { - return (uint)left._a > (uint)right._a; - } - - if (left._b != right._b) - { - return (uint)left._b > (uint)right._b; - } - - if (left._c != right._c) - { - return (uint)left._c > (uint)right._c; - } - - if (left._d != right._d) - { - return left._d > right._d; - } - - if (left._e != right._e) - { - return left._e > right._e; - } - - if (left._f != right._f) - { - return left._f > right._f; - } - - if (left._g != right._g) - { - return left._g > right._g; - } - - if (left._h != right._h) - { - return left._h > right._h; - } - - if (left._i != right._i) - { - return left._i > right._i; - } - - if (left._j != right._j) - { - return left._j > right._j; - } - - if (left._k != right._k) - { - return left._k > right._k; - } - - return true; - } + public static bool operator >=(Guid left, Guid right) => right <= left; // // IParsable From 338bcfc1d4b20a617ccd0295843a061283e5bb24 Mon Sep 17 00:00:00 2001 From: Pent Ploompuu Date: Fri, 13 Mar 2026 19:33:49 +0800 Subject: [PATCH 2/3] Drop AVX-512 and comparison changes --- .../System.Private.CoreLib/src/System/Guid.cs | 368 +++++++++++++----- 1 file changed, 263 insertions(+), 105 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 9cc9263576375c..15d5886acd8d8e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -1104,6 +1104,8 @@ private static bool EqualsCore(in Guid left, in Guid right) && Unsafe.Add(ref rA, 3) == Unsafe.Add(ref rB, 3); } + private static int GetResult(uint me, uint them) => me < them ? -1 : 1; + public int CompareTo(object? value) { if (value == null) @@ -1121,23 +1123,61 @@ public int CompareTo(Guid value) { if (value._a != _a) { - return ((uint)_a).CompareTo((uint)value._a); + return GetResult((uint)_a, (uint)value._a); } - if ((ushort)value._b != (ushort)_b) + if (value._b != _b) { - return ((ushort)_b).CompareTo((ushort)value._b); + return GetResult((uint)_b, (uint)value._b); } - if ((ushort)value._c != (ushort)_c) + if (value._c != _c) { - return ((ushort)_c).CompareTo((ushort)value._c); + return GetResult((uint)_c, (uint)value._c); } - return GetLow64().CompareTo(value.GetLow64()); - } + if (value._d != _d) + { + return GetResult(_d, value._d); + } + + if (value._e != _e) + { + return GetResult(_e, value._e); + } + + if (value._f != _f) + { + return GetResult(_f, value._f); + } + + if (value._g != _g) + { + return GetResult(_g, value._g); + } + + if (value._h != _h) + { + return GetResult(_h, value._h); + } + + if (value._i != _i) + { + return GetResult(_i, value._i); + } + + if (value._j != _j) + { + return GetResult(_j, value._j); + } - private ulong GetLow64() => BinaryPrimitives.ReadUInt64BigEndian(MemoryMarshal.CreateReadOnlySpan(in _d, 8)); + if (value._k != _k) + { + return GetResult(_k, value._k); + } + + return 0; + } public static bool operator ==(Guid a, Guid b) => EqualsCore(a, b); @@ -1156,15 +1196,7 @@ private static unsafe int HexsToChars(TChar* guidChars, int a, int b) whe } // Returns the guid in "registry" format. - public override string ToString() - { - const int flags = 36 + TryFormatFlags_UseDashes; - string guidString = string.FastAllocateString(36); - bool result = TryFormatCore(new Span(ref guidString.GetRawStringData(), 36), out int charsWritten, flags); - Debug.Assert(result && charsWritten == guidString.Length); - return guidString; - - } + public override string ToString() => ToString(null); // IFormattable interface // We currently ignore provider @@ -1314,70 +1346,7 @@ internal unsafe bool TryFormatCore(Span destination, out int chars } flags >>= 8; - if (Avx512Vbmi.VL.IsSupported) - { - Vector128 hexMap = Vector128.Create( - (byte)'0', (byte)'1', (byte)'2', (byte)'3', - (byte)'4', (byte)'5', (byte)'6', (byte)'7', - (byte)'8', (byte)'9', (byte)'a', (byte)'b', - (byte)'c', (byte)'d', (byte)'e', (byte)'f'); - - Vector256 srcVec = Avx2.ConvertToVector256Int64(Unsafe.BitCast>(this)); - // because of Guid's layout (int _a, short _b, _c, <8 byte fields>) - // we have to shuffle some bytes for _a, _b and _c - Vector256 control = Vector256.Create( - 28, 24, 20, 16, 12, 8, 4, 0, - 12, 8, 4, 0, 28, 24, 20, 16, - 4, 0, 12, 8, 20, 16, 28, 24, - 4, 0, 12, 8, 20, 16, 28, 24); - Vector256 nibbles = Avx512Vbmi.VL.MultiShift(control, srcVec).AsByte(); - Vector256 hex = Avx512Vbmi.VL.PermuteVar32x8(Vector256.Create(hexMap), nibbles).AsByte(); - - if (flags < 0 /* dash */) - { - // Create the middle part that must be merged in this order later: - // - // ________-____-____-____-____________ - // yyyyyyyyyyyyyyyy - // xxxxxxxxxxxxxxxx - // zzzzzzzzzzzzzzzz - // - // "x" is the low part of "hex" and "y" is the high part of "hex". - // "z" - middle part of hex blended with 4 dashes. - Vector256 mid = Avx2.Blend(hex.AsInt32(), Vector256.Create((int)'-'), 1).AsByte(); - Vector128 midMask = Vector128.Create((byte)0, 8, 9, 10, 11, 0, 12, 13, 14, 15, 0, 16, 17, 18, 19, 0); - Vector128 vecZ = Avx512Vbmi.VL.PermuteVar32x8(mid, midMask.ToVector256Unsafe()).GetLower(); - - if (typeof(TChar) == typeof(byte)) - { - ref byte pChar = ref Unsafe.As(ref d); - hex.StoreUnsafe(ref pChar, 4); - hex.GetLower().StoreUnsafe(ref pChar); - vecZ.StoreUnsafe(ref pChar, 8); - } - else - { - ref ushort pChar = ref Unsafe.As(ref d); - Vector256.WidenUpper(hex).StoreUnsafe(ref pChar, 20); - Vector128.WidenLower(hex.GetLower()).StoreUnsafe(ref pChar); - Vector256.WidenLower(Vector128.ToVector256Unsafe(vecZ)).StoreUnsafe(ref pChar, 8); - } - d = ref Unsafe.Add(ref d, 36); - } - else - { - if (typeof(TChar) == typeof(byte)) - { - hex.StoreUnsafe(ref Unsafe.As(ref d)); - } - else - { - Vector512.WidenLower(Vector256.ToVector512Unsafe(hex)).StoreUnsafe(ref Unsafe.As(ref d)); - } - d = ref Unsafe.Add(ref d, 32); - } - } - else if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian) + if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian) { (Vector128 vecX, Vector128 vecY, Vector128 vecZ) = FormatGuidVector128Utf8(this, flags < 0 /* dash */); @@ -1594,12 +1563,7 @@ private static (Vector128, Vector128, Vector128) FormatGuidVec // yyyyyyyyyyyyyyyy // zzzzzzzzzzzzzzzz // - // Vector "x" - the dash and everything after it is going to be overwritten by "z" - Vector128 vecX = hexLow; - - // Vector "y" - the dash and everything before it is going to be overwritten by "z" - Vector128 vecY = hexHigh; - + // "x" is hexLow and "y" is hexHigh. // Vector "z" - we need to merge some elements of hexLow with hexHigh and add 4 dashes. Vector128 vecZ; Vector128 dashesMask = Vector128.Create(0x00002D000000002D, 0x2D000000002D0000).AsByte(); @@ -1616,14 +1580,12 @@ private static (Vector128, Vector128, Vector128) FormatGuidVec } else { - Vector128 mid1 = Vector128.Shuffle(hexLow, - Vector128.Create(0x0D0CFF0B0A0908FF, 0xFFFFFFFFFFFF0F0E).AsByte()); - Vector128 mid2 = Vector128.Shuffle(hexHigh, - Vector128.Create(0xFFFFFFFFFFFFFFFF, 0xFF03020100FFFFFF).AsByte()); - vecZ = (mid1 | mid2 | dashesMask); + vecZ = Vector128.Shuffle(Ssse3.AlignRight(hexHigh, hexLow, 8), + Vector128.Create(0x0504FF03020100FF, 0xFF0B0A0908FF0706).AsByte()); + vecZ |= dashesMask; } - return (vecX, vecY, vecZ); + return (hexLow, hexHigh, vecZ); } // N format - no dashes. @@ -1642,17 +1604,57 @@ private static (Vector128, Vector128, Vector128) FormatGuidVec return (uint)left._a < (uint)right._a; } - if ((ushort)left._b != (ushort)right._b) + if (left._b != right._b) + { + return (uint)left._b < (uint)right._b; + } + + if (left._c != right._c) + { + return (uint)left._c < (uint)right._c; + } + + if (left._d != right._d) { - return (ushort)left._b < (ushort)right._b; + return left._d < right._d; } - if ((ushort)left._c != (ushort)right._c) + if (left._e != right._e) { - return (ushort)left._c < (ushort)right._c; + return left._e < right._e; } - return left.GetLow64() < right.GetLow64(); + if (left._f != right._f) + { + return left._f < right._f; + } + + if (left._g != right._g) + { + return left._g < right._g; + } + + if (left._h != right._h) + { + return left._h < right._h; + } + + if (left._i != right._i) + { + return left._i < right._i; + } + + if (left._j != right._j) + { + return left._j < right._j; + } + + if (left._k != right._k) + { + return left._k < right._k; + } + + return false; } /// @@ -1663,24 +1665,180 @@ private static (Vector128, Vector128, Vector128) FormatGuidVec return (uint)left._a < (uint)right._a; } - if ((ushort)left._b != (ushort)right._b) + if (left._b != right._b) + { + return (uint)left._b < (uint)right._b; + } + + if (left._c != right._c) { - return (ushort)left._b < (ushort)right._b; + return (uint)left._c < (uint)right._c; } - if ((ushort)left._c != (ushort)right._c) + if (left._d != right._d) { - return (ushort)left._c < (ushort)right._c; + return left._d < right._d; } - return left.GetLow64() <= right.GetLow64(); + if (left._e != right._e) + { + return left._e < right._e; + } + + if (left._f != right._f) + { + return left._f < right._f; + } + + if (left._g != right._g) + { + return left._g < right._g; + } + + if (left._h != right._h) + { + return left._h < right._h; + } + + if (left._i != right._i) + { + return left._i < right._i; + } + + if (left._j != right._j) + { + return left._j < right._j; + } + + if (left._k != right._k) + { + return left._k < right._k; + } + + return true; } /// - public static bool operator >(Guid left, Guid right) => right < left; + public static bool operator >(Guid left, Guid right) + { + if (left._a != right._a) + { + return (uint)left._a > (uint)right._a; + } + + if (left._b != right._b) + { + return (uint)left._b > (uint)right._b; + } + + if (left._c != right._c) + { + return (uint)left._c > (uint)right._c; + } + + if (left._d != right._d) + { + return left._d > right._d; + } + + if (left._e != right._e) + { + return left._e > right._e; + } + + if (left._f != right._f) + { + return left._f > right._f; + } + + if (left._g != right._g) + { + return left._g > right._g; + } + + if (left._h != right._h) + { + return left._h > right._h; + } + + if (left._i != right._i) + { + return left._i > right._i; + } + + if (left._j != right._j) + { + return left._j > right._j; + } + + if (left._k != right._k) + { + return left._k > right._k; + } + + return false; + } /// - public static bool operator >=(Guid left, Guid right) => right <= left; + public static bool operator >=(Guid left, Guid right) + { + if (left._a != right._a) + { + return (uint)left._a > (uint)right._a; + } + + if (left._b != right._b) + { + return (uint)left._b > (uint)right._b; + } + + if (left._c != right._c) + { + return (uint)left._c > (uint)right._c; + } + + if (left._d != right._d) + { + return left._d > right._d; + } + + if (left._e != right._e) + { + return left._e > right._e; + } + + if (left._f != right._f) + { + return left._f > right._f; + } + + if (left._g != right._g) + { + return left._g > right._g; + } + + if (left._h != right._h) + { + return left._h > right._h; + } + + if (left._i != right._i) + { + return left._i > right._i; + } + + if (left._j != right._j) + { + return left._j > right._j; + } + + if (left._k != right._k) + { + return left._k > right._k; + } + + return true; + } // // IParsable From 7104a9ba6ce71e63bbfa21feb29b2efa8a9e7231 Mon Sep 17 00:00:00 2001 From: Pent Ploompuu Date: Fri, 13 Mar 2026 15:24:16 +0200 Subject: [PATCH 3/3] Remove stale comment Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/libraries/System.Private.CoreLib/src/System/Guid.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 15d5886acd8d8e..37f656e03e976c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -1323,7 +1323,7 @@ private bool TryFormatCore(Span destination, out int charsWritten, // (Vectorized) implementation for D, N, P and B formats: // [{|(]dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd[}|)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] // only used from two callers + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal unsafe bool TryFormatCore(Span destination, out int charsWritten, int flags) where TChar : unmanaged, IUtfChar { // The low byte of flags contains the required length.