From f9287b52a81e3696d2b9b80b2ebd657b379e25f0 Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Thu, 12 Jun 2025 01:40:41 +0100 Subject: [PATCH 1/5] Optimise `Guid.DecodeByte` --- .../System.Private.CoreLib/src/System/Guid.cs | 99 ++++++++++--------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 2c24b8fdd1fe16..0075b5886ccdd9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -516,25 +516,25 @@ private static bool TryParseExactD(ReadOnlySpan guidString, ref Gu } Span bytes = MemoryMarshal.AsBytes(new Span(ref result)); - int invalidIfNegative = 0; - bytes[0] = DecodeByte(guidString[6], guidString[7], ref invalidIfNegative); - bytes[1] = DecodeByte(guidString[4], guidString[5], ref invalidIfNegative); - bytes[2] = DecodeByte(guidString[2], guidString[3], ref invalidIfNegative); - bytes[3] = DecodeByte(guidString[0], guidString[1], ref invalidIfNegative); - bytes[4] = DecodeByte(guidString[11], guidString[12], ref invalidIfNegative); - bytes[5] = DecodeByte(guidString[9], guidString[10], ref invalidIfNegative); - bytes[6] = DecodeByte(guidString[16], guidString[17], ref invalidIfNegative); - bytes[7] = DecodeByte(guidString[14], guidString[15], ref invalidIfNegative); - bytes[8] = DecodeByte(guidString[19], guidString[20], ref invalidIfNegative); - bytes[9] = DecodeByte(guidString[21], guidString[22], ref invalidIfNegative); - bytes[10] = DecodeByte(guidString[24], guidString[25], ref invalidIfNegative); - bytes[11] = DecodeByte(guidString[26], guidString[27], ref invalidIfNegative); - bytes[12] = DecodeByte(guidString[28], guidString[29], ref invalidIfNegative); - bytes[13] = DecodeByte(guidString[30], guidString[31], ref invalidIfNegative); - bytes[14] = DecodeByte(guidString[32], guidString[33], ref invalidIfNegative); - bytes[15] = DecodeByte(guidString[34], guidString[35], ref invalidIfNegative); - - if (invalidIfNegative >= 0) + uint invalidIfGreaterThan7F = 0; + bytes[0] = DecodeByte(guidString[6], guidString[7], ref invalidIfGreaterThan7F); + bytes[1] = DecodeByte(guidString[4], guidString[5], ref invalidIfGreaterThan7F); + bytes[2] = DecodeByte(guidString[2], guidString[3], ref invalidIfGreaterThan7F); + bytes[3] = DecodeByte(guidString[0], guidString[1], ref invalidIfGreaterThan7F); + bytes[4] = DecodeByte(guidString[11], guidString[12], ref invalidIfGreaterThan7F); + bytes[5] = DecodeByte(guidString[9], guidString[10], ref invalidIfGreaterThan7F); + bytes[6] = DecodeByte(guidString[16], guidString[17], ref invalidIfGreaterThan7F); + bytes[7] = DecodeByte(guidString[14], guidString[15], ref invalidIfGreaterThan7F); + bytes[8] = DecodeByte(guidString[19], guidString[20], ref invalidIfGreaterThan7F); + bytes[9] = DecodeByte(guidString[21], guidString[22], ref invalidIfGreaterThan7F); + bytes[10] = DecodeByte(guidString[24], guidString[25], ref invalidIfGreaterThan7F); + bytes[11] = DecodeByte(guidString[26], guidString[27], ref invalidIfGreaterThan7F); + bytes[12] = DecodeByte(guidString[28], guidString[29], ref invalidIfGreaterThan7F); + bytes[13] = DecodeByte(guidString[30], guidString[31], ref invalidIfGreaterThan7F); + bytes[14] = DecodeByte(guidString[32], guidString[33], ref invalidIfGreaterThan7F); + bytes[15] = DecodeByte(guidString[34], guidString[35], ref invalidIfGreaterThan7F); + + if (invalidIfGreaterThan7F <= 0x7F) { if (!BitConverter.IsLittleEndian) { @@ -602,25 +602,25 @@ private static bool TryParseExactN(ReadOnlySpan guidString, ref Gu } Span bytes = MemoryMarshal.AsBytes(new Span(ref result)); - int invalidIfNegative = 0; - bytes[0] = DecodeByte(guidString[6], guidString[7], ref invalidIfNegative); - bytes[1] = DecodeByte(guidString[4], guidString[5], ref invalidIfNegative); - bytes[2] = DecodeByte(guidString[2], guidString[3], ref invalidIfNegative); - bytes[3] = DecodeByte(guidString[0], guidString[1], ref invalidIfNegative); - bytes[4] = DecodeByte(guidString[10], guidString[11], ref invalidIfNegative); - bytes[5] = DecodeByte(guidString[8], guidString[9], ref invalidIfNegative); - bytes[6] = DecodeByte(guidString[14], guidString[15], ref invalidIfNegative); - bytes[7] = DecodeByte(guidString[12], guidString[13], ref invalidIfNegative); - bytes[8] = DecodeByte(guidString[16], guidString[17], ref invalidIfNegative); - bytes[9] = DecodeByte(guidString[18], guidString[19], ref invalidIfNegative); - bytes[10] = DecodeByte(guidString[20], guidString[21], ref invalidIfNegative); - bytes[11] = DecodeByte(guidString[22], guidString[23], ref invalidIfNegative); - bytes[12] = DecodeByte(guidString[24], guidString[25], ref invalidIfNegative); - bytes[13] = DecodeByte(guidString[26], guidString[27], ref invalidIfNegative); - bytes[14] = DecodeByte(guidString[28], guidString[29], ref invalidIfNegative); - bytes[15] = DecodeByte(guidString[30], guidString[31], ref invalidIfNegative); - - if (invalidIfNegative >= 0) + uint invalidIfGreaterThan7F = 0; + bytes[0] = DecodeByte(guidString[6], guidString[7], ref invalidIfGreaterThan7F); + bytes[1] = DecodeByte(guidString[4], guidString[5], ref invalidIfGreaterThan7F); + bytes[2] = DecodeByte(guidString[2], guidString[3], ref invalidIfGreaterThan7F); + bytes[3] = DecodeByte(guidString[0], guidString[1], ref invalidIfGreaterThan7F); + bytes[4] = DecodeByte(guidString[10], guidString[11], ref invalidIfGreaterThan7F); + bytes[5] = DecodeByte(guidString[8], guidString[9], ref invalidIfGreaterThan7F); + bytes[6] = DecodeByte(guidString[14], guidString[15], ref invalidIfGreaterThan7F); + bytes[7] = DecodeByte(guidString[12], guidString[13], ref invalidIfGreaterThan7F); + bytes[8] = DecodeByte(guidString[16], guidString[17], ref invalidIfGreaterThan7F); + bytes[9] = DecodeByte(guidString[18], guidString[19], ref invalidIfGreaterThan7F); + bytes[10] = DecodeByte(guidString[20], guidString[21], ref invalidIfGreaterThan7F); + bytes[11] = DecodeByte(guidString[22], guidString[23], ref invalidIfGreaterThan7F); + bytes[12] = DecodeByte(guidString[24], guidString[25], ref invalidIfGreaterThan7F); + bytes[13] = DecodeByte(guidString[26], guidString[27], ref invalidIfGreaterThan7F); + bytes[14] = DecodeByte(guidString[28], guidString[29], ref invalidIfGreaterThan7F); + bytes[15] = DecodeByte(guidString[30], guidString[31], ref invalidIfGreaterThan7F); + + if (invalidIfGreaterThan7F <= 0x7F) { if (!BitConverter.IsLittleEndian) { @@ -813,20 +813,21 @@ private static bool TryParseExactX(ReadOnlySpan guidString, ref Gu } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte DecodeByte(TChar ch1, TChar ch2, ref int invalidIfNegative) where TChar : unmanaged, IUtfChar + private static byte DecodeByte(TChar ch1, TChar ch2, ref uint invalidIfGreaterThan7F) + where TChar : unmanaged, IUtfChar { ReadOnlySpan lookup = HexConverter.CharToHexLookup; Debug.Assert(lookup.Length == 256); - int upper = (sbyte)lookup[byte.CreateTruncating(ch1)]; - int lower = (sbyte)lookup[byte.CreateTruncating(ch2)]; - int result = (upper << 4) | lower; - - uint c1 = TChar.CastToUInt32(ch1); - uint c2 = TChar.CastToUInt32(ch2); - // Result will be negative if ch1 or/and ch2 are greater than 0xFF - result = (c1 | c2) >> 8 == 0 ? result : -1; - invalidIfNegative |= result; - return (byte)result; + + int c1 = typeof(TChar) == typeof(byte) ? byte.CreateTruncating(ch1) : (int)Math.Min(TChar.CastToUInt32(ch1), 0x7F); + uint upper = lookup[c1]; + invalidIfGreaterThan7F |= upper; + + int c2 = typeof(TChar) == typeof(byte) ? byte.CreateTruncating(ch2) : (int)Math.Min(TChar.CastToUInt32(ch2), 0x7F); + uint lower = lookup[c2]; + invalidIfGreaterThan7F |= lower; + + return (byte)((upper << 4) | lower); } private static bool TryParseHex(ReadOnlySpan guidString, out ushort result, ref bool overflow) where TChar : unmanaged, IUtfChar From 7d58c446a835e79fee58873660f5d7e586ce1d9c Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Thu, 12 Jun 2025 17:05:26 +0100 Subject: [PATCH 2/5] Use `TChar..CastToUInt32` --- src/libraries/System.Private.CoreLib/src/System/Guid.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 0075b5886ccdd9..27d89285f6b6f6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -819,12 +819,12 @@ private static byte DecodeByte(TChar ch1, TChar ch2, ref uint invalidIfGr ReadOnlySpan lookup = HexConverter.CharToHexLookup; Debug.Assert(lookup.Length == 256); - int c1 = typeof(TChar) == typeof(byte) ? byte.CreateTruncating(ch1) : (int)Math.Min(TChar.CastToUInt32(ch1), 0x7F); - uint upper = lookup[c1]; + uint c1 = typeof(TChar) == typeof(byte) ? TChar.CastToUInt32(ch1) : Math.Min(TChar.CastToUInt32(ch1), 0x7F); + uint upper = lookup[(int)c1]; invalidIfGreaterThan7F |= upper; - int c2 = typeof(TChar) == typeof(byte) ? byte.CreateTruncating(ch2) : (int)Math.Min(TChar.CastToUInt32(ch2), 0x7F); - uint lower = lookup[c2]; + uint c2 = typeof(TChar) == typeof(byte) ? TChar.CastToUInt32(ch2) : Math.Min(TChar.CastToUInt32(ch2), 0x7F); + uint lower = lookup[(int)c2]; invalidIfGreaterThan7F |= lower; return (byte)((upper << 4) | lower); From 012d58e84efc1f131f38a80abc1191d11b36c72b Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Thu, 12 Jun 2025 18:13:43 +0100 Subject: [PATCH 3/5] Use sign-extension --- .../System.Private.CoreLib/src/System/Guid.cs | 90 +++++++++---------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 27d89285f6b6f6..1059da23ac498c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -516,25 +516,25 @@ private static bool TryParseExactD(ReadOnlySpan guidString, ref Gu } Span bytes = MemoryMarshal.AsBytes(new Span(ref result)); - uint invalidIfGreaterThan7F = 0; - bytes[0] = DecodeByte(guidString[6], guidString[7], ref invalidIfGreaterThan7F); - bytes[1] = DecodeByte(guidString[4], guidString[5], ref invalidIfGreaterThan7F); - bytes[2] = DecodeByte(guidString[2], guidString[3], ref invalidIfGreaterThan7F); - bytes[3] = DecodeByte(guidString[0], guidString[1], ref invalidIfGreaterThan7F); - bytes[4] = DecodeByte(guidString[11], guidString[12], ref invalidIfGreaterThan7F); - bytes[5] = DecodeByte(guidString[9], guidString[10], ref invalidIfGreaterThan7F); - bytes[6] = DecodeByte(guidString[16], guidString[17], ref invalidIfGreaterThan7F); - bytes[7] = DecodeByte(guidString[14], guidString[15], ref invalidIfGreaterThan7F); - bytes[8] = DecodeByte(guidString[19], guidString[20], ref invalidIfGreaterThan7F); - bytes[9] = DecodeByte(guidString[21], guidString[22], ref invalidIfGreaterThan7F); - bytes[10] = DecodeByte(guidString[24], guidString[25], ref invalidIfGreaterThan7F); - bytes[11] = DecodeByte(guidString[26], guidString[27], ref invalidIfGreaterThan7F); - bytes[12] = DecodeByte(guidString[28], guidString[29], ref invalidIfGreaterThan7F); - bytes[13] = DecodeByte(guidString[30], guidString[31], ref invalidIfGreaterThan7F); - bytes[14] = DecodeByte(guidString[32], guidString[33], ref invalidIfGreaterThan7F); - bytes[15] = DecodeByte(guidString[34], guidString[35], ref invalidIfGreaterThan7F); - - if (invalidIfGreaterThan7F <= 0x7F) + int invalidIfNegative = 0; + bytes[0] = DecodeByte(guidString[6], guidString[7], ref invalidIfNegative); + bytes[1] = DecodeByte(guidString[4], guidString[5], ref invalidIfNegative); + bytes[2] = DecodeByte(guidString[2], guidString[3], ref invalidIfNegative); + bytes[3] = DecodeByte(guidString[0], guidString[1], ref invalidIfNegative); + bytes[4] = DecodeByte(guidString[11], guidString[12], ref invalidIfNegative); + bytes[5] = DecodeByte(guidString[9], guidString[10], ref invalidIfNegative); + bytes[6] = DecodeByte(guidString[16], guidString[17], ref invalidIfNegative); + bytes[7] = DecodeByte(guidString[14], guidString[15], ref invalidIfNegative); + bytes[8] = DecodeByte(guidString[19], guidString[20], ref invalidIfNegative); + bytes[9] = DecodeByte(guidString[21], guidString[22], ref invalidIfNegative); + bytes[10] = DecodeByte(guidString[24], guidString[25], ref invalidIfNegative); + bytes[11] = DecodeByte(guidString[26], guidString[27], ref invalidIfNegative); + bytes[12] = DecodeByte(guidString[28], guidString[29], ref invalidIfNegative); + bytes[13] = DecodeByte(guidString[30], guidString[31], ref invalidIfNegative); + bytes[14] = DecodeByte(guidString[32], guidString[33], ref invalidIfNegative); + bytes[15] = DecodeByte(guidString[34], guidString[35], ref invalidIfNegative); + + if (invalidIfNegative >= 0) { if (!BitConverter.IsLittleEndian) { @@ -602,25 +602,25 @@ private static bool TryParseExactN(ReadOnlySpan guidString, ref Gu } Span bytes = MemoryMarshal.AsBytes(new Span(ref result)); - uint invalidIfGreaterThan7F = 0; - bytes[0] = DecodeByte(guidString[6], guidString[7], ref invalidIfGreaterThan7F); - bytes[1] = DecodeByte(guidString[4], guidString[5], ref invalidIfGreaterThan7F); - bytes[2] = DecodeByte(guidString[2], guidString[3], ref invalidIfGreaterThan7F); - bytes[3] = DecodeByte(guidString[0], guidString[1], ref invalidIfGreaterThan7F); - bytes[4] = DecodeByte(guidString[10], guidString[11], ref invalidIfGreaterThan7F); - bytes[5] = DecodeByte(guidString[8], guidString[9], ref invalidIfGreaterThan7F); - bytes[6] = DecodeByte(guidString[14], guidString[15], ref invalidIfGreaterThan7F); - bytes[7] = DecodeByte(guidString[12], guidString[13], ref invalidIfGreaterThan7F); - bytes[8] = DecodeByte(guidString[16], guidString[17], ref invalidIfGreaterThan7F); - bytes[9] = DecodeByte(guidString[18], guidString[19], ref invalidIfGreaterThan7F); - bytes[10] = DecodeByte(guidString[20], guidString[21], ref invalidIfGreaterThan7F); - bytes[11] = DecodeByte(guidString[22], guidString[23], ref invalidIfGreaterThan7F); - bytes[12] = DecodeByte(guidString[24], guidString[25], ref invalidIfGreaterThan7F); - bytes[13] = DecodeByte(guidString[26], guidString[27], ref invalidIfGreaterThan7F); - bytes[14] = DecodeByte(guidString[28], guidString[29], ref invalidIfGreaterThan7F); - bytes[15] = DecodeByte(guidString[30], guidString[31], ref invalidIfGreaterThan7F); - - if (invalidIfGreaterThan7F <= 0x7F) + int invalidIfNegative = 0; + bytes[0] = DecodeByte(guidString[6], guidString[7], ref invalidIfNegative); + bytes[1] = DecodeByte(guidString[4], guidString[5], ref invalidIfNegative); + bytes[2] = DecodeByte(guidString[2], guidString[3], ref invalidIfNegative); + bytes[3] = DecodeByte(guidString[0], guidString[1], ref invalidIfNegative); + bytes[4] = DecodeByte(guidString[10], guidString[11], ref invalidIfNegative); + bytes[5] = DecodeByte(guidString[8], guidString[9], ref invalidIfNegative); + bytes[6] = DecodeByte(guidString[14], guidString[15], ref invalidIfNegative); + bytes[7] = DecodeByte(guidString[12], guidString[13], ref invalidIfNegative); + bytes[8] = DecodeByte(guidString[16], guidString[17], ref invalidIfNegative); + bytes[9] = DecodeByte(guidString[18], guidString[19], ref invalidIfNegative); + bytes[10] = DecodeByte(guidString[20], guidString[21], ref invalidIfNegative); + bytes[11] = DecodeByte(guidString[22], guidString[23], ref invalidIfNegative); + bytes[12] = DecodeByte(guidString[24], guidString[25], ref invalidIfNegative); + bytes[13] = DecodeByte(guidString[26], guidString[27], ref invalidIfNegative); + bytes[14] = DecodeByte(guidString[28], guidString[29], ref invalidIfNegative); + bytes[15] = DecodeByte(guidString[30], guidString[31], ref invalidIfNegative); + + if (invalidIfNegative >= 0) { if (!BitConverter.IsLittleEndian) { @@ -813,21 +813,19 @@ private static bool TryParseExactX(ReadOnlySpan guidString, ref Gu } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte DecodeByte(TChar ch1, TChar ch2, ref uint invalidIfGreaterThan7F) - where TChar : unmanaged, IUtfChar + private static byte DecodeByte(TChar ch1, TChar ch2, ref int invalidIfNegative) where TChar : unmanaged, IUtfChar { ReadOnlySpan lookup = HexConverter.CharToHexLookup; Debug.Assert(lookup.Length == 256); uint c1 = typeof(TChar) == typeof(byte) ? TChar.CastToUInt32(ch1) : Math.Min(TChar.CastToUInt32(ch1), 0x7F); - uint upper = lookup[(int)c1]; - invalidIfGreaterThan7F |= upper; - uint c2 = typeof(TChar) == typeof(byte) ? TChar.CastToUInt32(ch2) : Math.Min(TChar.CastToUInt32(ch2), 0x7F); - uint lower = lookup[(int)c2]; - invalidIfGreaterThan7F |= lower; + int upper = (sbyte)Unsafe.Add(ref MemoryMarshal.GetReference(lookup), c1); + int lower = (sbyte)Unsafe.Add(ref MemoryMarshal.GetReference(lookup), c2); - return (byte)((upper << 4) | lower); + int result = (upper << 4) | lower; + invalidIfNegative |= result; + return (byte)result; } private static bool TryParseHex(ReadOnlySpan guidString, out ushort result, ref bool overflow) where TChar : unmanaged, IUtfChar From ba3c7a07a47d343cf8c238ca321a3957b74cb54e Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Fri, 13 Jun 2025 02:16:03 +0100 Subject: [PATCH 4/5] add TODO comment --- src/libraries/System.Private.CoreLib/src/System/Guid.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 1059da23ac498c..847865dde7e827 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -815,6 +815,9 @@ private static bool TryParseExactX(ReadOnlySpan guidString, ref Gu [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte DecodeByte(TChar ch1, TChar ch2, ref int invalidIfNegative) where TChar : unmanaged, IUtfChar { + // TODO: https://github.com/dotnet/runtime/issues/116593 + // Restore to (sbyte)lookup[c1] and (sbyte)lookup[c2] once issue is resolved. + ReadOnlySpan lookup = HexConverter.CharToHexLookup; Debug.Assert(lookup.Length == 256); From db859fdaf64cafef1a1731a9f6c2b4cc97401781 Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Fri, 13 Jun 2025 12:26:54 +0100 Subject: [PATCH 5/5] Use indexing instead of unsafe --- src/libraries/System.Private.CoreLib/src/System/Guid.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 847865dde7e827..c8d5797add712d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -815,16 +815,13 @@ private static bool TryParseExactX(ReadOnlySpan guidString, ref Gu [MethodImpl(MethodImplOptions.AggressiveInlining)] private static byte DecodeByte(TChar ch1, TChar ch2, ref int invalidIfNegative) where TChar : unmanaged, IUtfChar { - // TODO: https://github.com/dotnet/runtime/issues/116593 - // Restore to (sbyte)lookup[c1] and (sbyte)lookup[c2] once issue is resolved. - ReadOnlySpan lookup = HexConverter.CharToHexLookup; Debug.Assert(lookup.Length == 256); uint c1 = typeof(TChar) == typeof(byte) ? TChar.CastToUInt32(ch1) : Math.Min(TChar.CastToUInt32(ch1), 0x7F); uint c2 = typeof(TChar) == typeof(byte) ? TChar.CastToUInt32(ch2) : Math.Min(TChar.CastToUInt32(ch2), 0x7F); - int upper = (sbyte)Unsafe.Add(ref MemoryMarshal.GetReference(lookup), c1); - int lower = (sbyte)Unsafe.Add(ref MemoryMarshal.GetReference(lookup), c2); + int upper = (sbyte)lookup[(int)c1]; + int lower = (sbyte)lookup[(int)c2]; int result = (upper << 4) | lower; invalidIfNegative |= result;