From 510c7f3b20a7b49a4326d5718b851a49e8376220 Mon Sep 17 00:00:00 2001 From: sewer56 Date: Fri, 17 Nov 2023 07:44:37 +0000 Subject: [PATCH] Fixed: Hash Trigger Conditions, Improve Collisions & Port AVX2 path to Vector256 --- .../Benchmarks/StringHash.cs | 10 +- .../Benchmarks/StringHashIgnoreCase.cs | 8 +- .../Extensions/StringExtensions_Hashing.cs | 4 +- .../Algorithms/UnstableStringHash.cs | 90 ++++++++-------- .../Algorithms/UnstableStringHashLower.cs | 102 +++++++++--------- .../System/Text/Unicode/Utf16Utility.cs | 4 +- 6 files changed, 105 insertions(+), 113 deletions(-) diff --git a/src/Reloaded.Memory.Benchmarks/Benchmarks/StringHash.cs b/src/Reloaded.Memory.Benchmarks/Benchmarks/StringHash.cs index 0b6e8af..cf87d19 100644 --- a/src/Reloaded.Memory.Benchmarks/Benchmarks/StringHash.cs +++ b/src/Reloaded.Memory.Benchmarks/Benchmarks/StringHash.cs @@ -49,17 +49,17 @@ public nuint Custom_Unstable() } [Benchmark] - public nuint Custom_Avx2() + public nuint Custom_Vec256() { nuint result = 0; var maxLen = Input.Length / 4; // unroll for (var x = 0; x < maxLen; x += 4) { - result = UnstableStringHash.UnstableHashAvx2(Input.DangerousGetReferenceAt(x)); - result = UnstableStringHash.UnstableHashAvx2(Input.DangerousGetReferenceAt(x + 1)); - result = UnstableStringHash.UnstableHashAvx2(Input.DangerousGetReferenceAt(x + 2)); - result = UnstableStringHash.UnstableHashAvx2(Input.DangerousGetReferenceAt(x + 3)); + result = UnstableStringHash.UnstableHashVec256(Input.DangerousGetReferenceAt(x)); + result = UnstableStringHash.UnstableHashVec256(Input.DangerousGetReferenceAt(x + 1)); + result = UnstableStringHash.UnstableHashVec256(Input.DangerousGetReferenceAt(x + 2)); + result = UnstableStringHash.UnstableHashVec256(Input.DangerousGetReferenceAt(x + 3)); } return result; diff --git a/src/Reloaded.Memory.Benchmarks/Benchmarks/StringHashIgnoreCase.cs b/src/Reloaded.Memory.Benchmarks/Benchmarks/StringHashIgnoreCase.cs index 0e5ed93..97b7b45 100644 --- a/src/Reloaded.Memory.Benchmarks/Benchmarks/StringHashIgnoreCase.cs +++ b/src/Reloaded.Memory.Benchmarks/Benchmarks/StringHashIgnoreCase.cs @@ -60,10 +60,10 @@ public nuint Custom_Avx2Lower() // unroll for (var x = 0; x < maxLen; x += 4) { - result = UnstableStringHashLower.UnstableHashAvx2Lower(Input.DangerousGetReferenceAt(x)); - result = UnstableStringHashLower.UnstableHashAvx2Lower(Input.DangerousGetReferenceAt(x + 1)); - result = UnstableStringHashLower.UnstableHashAvx2Lower(Input.DangerousGetReferenceAt(x + 2)); - result = UnstableStringHashLower.UnstableHashAvx2Lower(Input.DangerousGetReferenceAt(x + 3)); + result = UnstableStringHashLower.UnstableHashVec256Lower(Input.DangerousGetReferenceAt(x)); + result = UnstableStringHashLower.UnstableHashVec256Lower(Input.DangerousGetReferenceAt(x + 1)); + result = UnstableStringHashLower.UnstableHashVec256Lower(Input.DangerousGetReferenceAt(x + 2)); + result = UnstableStringHashLower.UnstableHashVec256Lower(Input.DangerousGetReferenceAt(x + 3)); } return result; diff --git a/src/Reloaded.Memory.Tests/Tests/Extensions/StringExtensions_Hashing.cs b/src/Reloaded.Memory.Tests/Tests/Extensions/StringExtensions_Hashing.cs index 676c8a5..35cd46a 100644 --- a/src/Reloaded.Memory.Tests/Tests/Extensions/StringExtensions_Hashing.cs +++ b/src/Reloaded.Memory.Tests/Tests/Extensions/StringExtensions_Hashing.cs @@ -30,7 +30,7 @@ public void HashCode_IsConsistent() #if NET7_0_OR_GREATER if (Avx2.IsSupported) - UnstableStringHash.UnstableHashAvx2(text).Should().Be(UnstableStringHash.UnstableHashAvx2(text)); + UnstableStringHash.UnstableHashVec256(text).Should().Be(UnstableStringHash.UnstableHashVec256(text)); if (Sse2.IsSupported) UnstableStringHash.UnstableHashVec128(text).Should().Be(UnstableStringHash.UnstableHashVec128(text)); @@ -58,7 +58,7 @@ public void HashCodeLower_IsCaseInsensitive_UsingAvx2() { var text = RandomStringAsciiMixedCase(x); var lowerInvariantText = text.ToLowerInvariantFast(); - UnstableStringHashLower.UnstableHashAvx2Lower(text).Should().Be(UnstableStringHashLower.UnstableHashAvx2Lower(lowerInvariantText)); + UnstableStringHashLower.UnstableHashVec256Lower(text).Should().Be(UnstableStringHashLower.UnstableHashVec256Lower(lowerInvariantText)); } } } diff --git a/src/Reloaded.Memory/Internals/Algorithms/UnstableStringHash.cs b/src/Reloaded.Memory/Internals/Algorithms/UnstableStringHash.cs index c7a2358..e0b0674 100644 --- a/src/Reloaded.Memory/Internals/Algorithms/UnstableStringHash.cs +++ b/src/Reloaded.Memory/Internals/Algorithms/UnstableStringHash.cs @@ -35,21 +35,17 @@ internal static unsafe nuint GetHashCodeUnstable(this ReadOnlySpan text) // Note: The `/ sizeof(char)` accounts that length is measured in 2-byte chars, not bytes. - // ReSharper disable once InvertIf - // Over 64 bytes (32 chars) + Vector128. Supported on all x64 and ARM64 processors. - if (length >= sizeof(Vector128) / sizeof(char) * 4) // <= do not invert, hot path. - { - // Note. In these implementations we leave some (< sizeof(nuint)) data from the hash. - // For our use of hashing file paths, this is okay, as files with different names but same extension - // would still hash differently. If I were to PR this to runtime though, this would need fixing. + // Note. In these SIMD implementations we leave some (< sizeof(nuint)) data from the hash. + // For our use of hashing file paths, this is okay, as files with different names but same extension + // would still hash differently. If I were to PR this to runtime though, this would need fixing. - // AVX Version - // Ideally I could rewrite this in full Vector256 but I don't know how to get it to emit VPMULUDQ for the multiply operation. - if (!Avx2.IsSupported || length < sizeof(Vector256) / sizeof(char) * 4) // if under 64 chars (hot case), conditionally use Vector128 - return Vector128.IsHardwareAccelerated ? text.UnstableHashVec128() : text.UnstableHashNonVector(); + // Over 4 Vec256 regs (32 * 4 = 128 bytes) + if (Vector256.IsHardwareAccelerated && length >= (sizeof(Vector256) / sizeof(char)) * 4) + return text.UnstableHashVec256(); - return text.UnstableHashAvx2(); - } + // Over 4 Vec128 regs (16 * 4 = 64 bytes) + if (Vector256.IsHardwareAccelerated && length >= (sizeof(Vector128) / sizeof(char)) * 4) + return text.UnstableHashVec128(); #endif return text.UnstableHashNonVector(); @@ -65,32 +61,32 @@ internal static unsafe UIntPtr UnstableHashVec128(this ReadOnlySpan text) var hash2 = hash1; var ptr = (nuint*)(src); - var prime = Vector128.Create((ulong)0x100000001b3); - var hash1_128 = Vector128.Create(0xcbf29ce484222325); - var hash2_128 = Vector128.Create(0xcbf29ce484222325); + var prime = Vector128.Create((uint)0x01000193); + var hash1_128 = Vector128.Create(0x811c9dc5); + var hash2_128 = Vector128.Create(0x811c9dc5); while (length >= sizeof(Vector128) / sizeof(char) * 4) // 64 byte chunks. { length -= (sizeof(Vector128) / sizeof(char)) * 4; - hash1_128 = Vector128.Xor(hash1_128, Vector128.Load((ulong*)ptr)); - hash1_128 = Vector128.Multiply(hash1_128.AsUInt32(), prime.AsUInt32()).AsUInt64(); + hash1_128 = Vector128.Xor(hash1_128, Vector128.Load((ulong*)ptr).AsUInt32()); + hash1_128 = Vector128.Multiply(hash1_128, prime); - hash2_128 = Vector128.Xor(hash2_128, Vector128.Load((ulong*)ptr + 2)); - hash2_128 = Vector128.Multiply(hash2_128.AsUInt32(), prime.AsUInt32()).AsUInt64(); + hash2_128 = Vector128.Xor(hash2_128, Vector128.Load((ulong*)ptr + 2).AsUInt32()); + hash2_128 = Vector128.Multiply(hash2_128, prime); - hash1_128 = Vector128.Xor(hash1_128, Vector128.Load((ulong*)ptr + 4)); - hash1_128 = Vector128.Multiply(hash1_128.AsUInt32(), prime.AsUInt32()).AsUInt64(); + hash1_128 = Vector128.Xor(hash1_128, Vector128.Load((ulong*)ptr + 4).AsUInt32()); + hash1_128 = Vector128.Multiply(hash1_128, prime); - hash2_128 = Vector128.Xor(hash2_128, Vector128.Load((ulong*)ptr + 6)); - hash2_128 = Vector128.Multiply(hash2_128.AsUInt32(), prime.AsUInt32()).AsUInt64(); + hash2_128 = Vector128.Xor(hash2_128, Vector128.Load((ulong*)ptr + 6).AsUInt32()); + hash2_128 = Vector128.Multiply(hash2_128, prime); ptr += (sizeof(Vector128) / sizeof(nuint)) * 4; } while (length >= sizeof(Vector128) / sizeof(char)) // 16 byte chunks. { length -= sizeof(Vector128) / sizeof(char); - hash1_128 = Vector128.Xor(hash1_128, Vector128.Load((ulong*)ptr)); - hash1_128 = Vector128.Multiply(hash1_128.AsUInt32(), prime.AsUInt32()).AsUInt64(); + hash1_128 = Vector128.Xor(hash1_128, Vector128.Load((ulong*)ptr).AsUInt32()); + hash1_128 = Vector128.Multiply(hash1_128, prime); ptr += (sizeof(Vector128) / sizeof(nuint)); } @@ -116,11 +112,11 @@ internal static unsafe UIntPtr UnstableHashVec128(this ReadOnlySpan text) ptr += 1; } - return hash1 + (hash2 * 1566083941); + return hash1 + (hash2 * 0x5D588B65); } } - internal static unsafe UIntPtr UnstableHashAvx2(this ReadOnlySpan text) + internal static unsafe UIntPtr UnstableHashVec256(this ReadOnlySpan text) { fixed (char* src = &text.GetPinnableReference()) { @@ -129,47 +125,47 @@ internal static unsafe UIntPtr UnstableHashAvx2(this ReadOnlySpan text) var hash2 = hash1; var ptr = (nuint*)(src); - var prime = Vector256.Create((ulong)0x100000001b3); - var hash1Avx = Vector256.Create(0xcbf29ce484222325); - var hash2Avx = Vector256.Create(0xcbf29ce484222325); + var prime = Vector256.Create((uint)0x01000193); + var hash1_256 = Vector256.Create(0x811c9dc5); + var hash2_256 = Vector256.Create(0x811c9dc5); while (length >= sizeof(Vector256) / sizeof(char) * 4) // 128 byte chunks. { length -= (sizeof(Vector256) / sizeof(char)) * 4; - hash1Avx = Avx2.Xor(hash1Avx, Avx.LoadVector256((ulong*)ptr)); - hash1Avx = Avx2.Multiply(hash1Avx.AsUInt32(), prime.AsUInt32()); + hash1_256 = Vector256.Xor(hash1_256, Vector256.Load((ulong*)ptr).AsUInt32()); + hash1_256 = Vector256.Multiply(hash1_256, prime).AsUInt32(); - hash2Avx = Avx2.Xor(hash2Avx, Avx.LoadVector256((ulong*)ptr + 4)); - hash2Avx = Avx2.Multiply(hash2Avx.AsUInt32(), prime.AsUInt32()); + hash2_256 = Vector256.Xor(hash2_256, Vector256.Load((ulong*)ptr + 4).AsUInt32()); + hash2_256 = Vector256.Multiply(hash2_256, prime).AsUInt32(); - hash1Avx = Avx2.Xor(hash1Avx, Avx.LoadVector256((ulong*)ptr + 8)); - hash1Avx = Avx2.Multiply(hash1Avx.AsUInt32(), prime.AsUInt32()); + hash1_256 = Vector256.Xor(hash1_256, Vector256.Load((ulong*)ptr + 8).AsUInt32()); + hash1_256 = Vector256.Multiply(hash1_256, prime).AsUInt32(); - hash2Avx = Avx2.Xor(hash2Avx, Avx.LoadVector256((ulong*)ptr + 12)); - hash2Avx = Avx2.Multiply(hash2Avx.AsUInt32(), prime.AsUInt32()); + hash2_256 = Vector256.Xor(hash2_256, Vector256.Load((ulong*)ptr + 12).AsUInt32()); + hash2_256 = Vector256.Multiply(hash2_256, prime).AsUInt32(); ptr += (sizeof(Vector256) / sizeof(nuint)) * 4; } while (length >= sizeof(Vector256) / sizeof(char)) // 32 byte chunks. { length -= sizeof(Vector256) / sizeof(char); - hash1Avx = Avx2.Xor(hash1Avx, Avx.LoadVector256((ulong*)ptr)); - hash1Avx = Avx2.Multiply(hash1Avx.AsUInt32(), prime.AsUInt32()); + hash1_256 = Vector256.Xor(hash1_256, Vector256.Load((ulong*)ptr).AsUInt32()); + hash1_256 = Vector256.Multiply(hash1_256, prime).AsUInt32(); ptr += (sizeof(Vector256) / sizeof(nuint)); } // Flatten - hash1Avx = Avx2.Xor(hash1Avx, hash2Avx); + hash1_256 = Vector256.Xor(hash1_256, hash2_256); if (sizeof(nuint) == 8) { - hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (nuint)hash1Avx[0]; - hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (nuint)hash1Avx[1]; - hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (nuint)hash1Avx[2]; - hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (nuint)hash1Avx[3]; + hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (nuint)hash1_256[0]; + hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (nuint)hash1_256[1]; + hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (nuint)hash1_256[2]; + hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (nuint)hash1_256[3]; } else { - var hash1Uint = hash1Avx.AsUInt32(); + var hash1Uint = hash1_256.AsUInt32(); hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (hash1Uint[0] * hash1Uint[1]); hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (hash1Uint[2] * hash1Uint[3]); hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (hash1Uint[3] * hash1Uint[4]); diff --git a/src/Reloaded.Memory/Internals/Algorithms/UnstableStringHashLower.cs b/src/Reloaded.Memory/Internals/Algorithms/UnstableStringHashLower.cs index 3960042..1d08169 100644 --- a/src/Reloaded.Memory/Internals/Algorithms/UnstableStringHashLower.cs +++ b/src/Reloaded.Memory/Internals/Algorithms/UnstableStringHashLower.cs @@ -39,21 +39,17 @@ internal static unsafe nuint GetHashCodeUnstableLower(this ReadOnlySpan te // Note: The `/ sizeof(char)` accounts that length is measured in 2-byte chars, not bytes. - // ReSharper disable once InvertIf - // Over 64 bytes (32 chars) + Vector128. Supported on all x64 and ARM64 processors. - if (length >= sizeof(Vector128) / sizeof(char) * 4) // <= do not invert, hot path. - { - // Note. In these implementations we leave some (< sizeof(nuint)) data from the hash. - // For our use of hashing file paths, this is okay, as files with different names but same extension - // would still hash differently. If I were to PR this to runtime though, this would need fixing. + // Note. In these SIMD implementations we leave some (< sizeof(nuint)) data from the hash. + // For our use of hashing file paths, this is okay, as files with different names but same extension + // would still hash differently. If I were to PR this to runtime though, this would need fixing. - // AVX Version - // Ideally I could rewrite this in full Vector256 but I don't know how to get it to emit VPMULUDQ for the multiply operation. - if (!Avx2.IsSupported || length < sizeof(Vector256) / sizeof(char) * 4) // if under 64 chars (hot case), conditionally use Vector128 - return Vector128.IsHardwareAccelerated ? text.UnstableHashVec128Lower() : text.UnstableHashNonVectorLower(); + // Over 4 Vec256 regs (32 * 4 = 128 bytes) + if (Vector256.IsHardwareAccelerated && length >= (sizeof(Vector256) / sizeof(char)) * 4) + return text.UnstableHashVec256Lower(); - return text.UnstableHashAvx2Lower(); - } + // Over 4 Vec128 regs (16 * 4 = 64 bytes) + if (Vector256.IsHardwareAccelerated && length >= (sizeof(Vector128) / sizeof(char)) * 4) + return text.UnstableHashVec128Lower(); #endif return text.UnstableHashNonVectorLower(); @@ -117,46 +113,46 @@ internal static unsafe UIntPtr UnstableHashVec128Lower(this ReadOnlySpan t var hash2 = hash1; var ptr = (nuint*)(src); - var prime = Vector128.Create((ulong)0x100000001b3); - var hash1_128 = Vector128.Create(0xcbf29ce484222325); - var hash2_128 = Vector128.Create(0xcbf29ce484222325); + var prime = Vector128.Create((uint)0x01000193); + var hash1_128 = Vector128.Create(0x811c9dc5); + var hash2_128 = Vector128.Create(0x811c9dc5); // We "normalize to lowercase" every char by ORing with 0x0020. This casts // a very wide net because it will change, e.g., '^' to '~'. But that should // be ok because we expect this to be very rare in practice. - var toLower = Vector128.Create(0x0020).AsUInt64(); + var toLower = Vector128.Create(0x0020_0020); while (length >= sizeof(Vector128) / sizeof(char) * 4) // 64 byte chunks. { length -= (sizeof(Vector128) / sizeof(char)) * 4; - Vector128 v0 = Vector128.Load((ulong*)ptr); + var v0 = Vector128.Load((ulong*)ptr).AsUInt32(); if (!AllCharsInVector128AreAscii(v0)) goto NotAscii; hash1_128 = Vector128.Xor(hash1_128, Vector128.BitwiseOr(v0, toLower)); - hash1_128 = Vector128.Multiply(hash1_128.AsUInt32(), prime.AsUInt32()).AsUInt64(); + hash1_128 = Vector128.Multiply(hash1_128, prime); - v0 = Vector128.Load((ulong*)ptr + 2); + v0 = Vector128.Load((ulong*)ptr + 2).AsUInt32(); if (!AllCharsInVector128AreAscii(v0)) goto NotAscii; hash2_128 = Vector128.Xor(hash2_128, Vector128.BitwiseOr(v0, toLower)); - hash2_128 = Vector128.Multiply(hash2_128.AsUInt32(), prime.AsUInt32()).AsUInt64(); + hash2_128 = Vector128.Multiply(hash2_128, prime); - v0 = Vector128.Load((ulong*)ptr + 4); + v0 = Vector128.Load((ulong*)ptr + 4).AsUInt32(); if (!AllCharsInVector128AreAscii(v0)) goto NotAscii; hash1_128 = Vector128.Xor(hash1_128, Vector128.BitwiseOr(v0, toLower)); - hash1_128 = Vector128.Multiply(hash1_128.AsUInt32(), prime.AsUInt32()).AsUInt64(); + hash1_128 = Vector128.Multiply(hash1_128, prime); - v0 = Vector128.Load((ulong*)ptr + 6); + v0 = Vector128.Load((ulong*)ptr + 6).AsUInt32(); if (!AllCharsInVector128AreAscii(v0)) goto NotAscii; hash2_128 = Vector128.Xor(hash2_128, Vector128.BitwiseOr(v0, toLower)); - hash2_128 = Vector128.Multiply(hash2_128.AsUInt32(), prime.AsUInt32()).AsUInt64(); + hash2_128 = Vector128.Multiply(hash2_128, prime); ptr += (sizeof(Vector128) / sizeof(nuint)) * 4; } @@ -164,12 +160,12 @@ internal static unsafe UIntPtr UnstableHashVec128Lower(this ReadOnlySpan t { length -= sizeof(Vector128) / sizeof(char); - Vector128 v0 = Vector128.Load((ulong*)ptr); + var v0 = Vector128.Load((ulong*)ptr).AsUInt32(); if (!AllCharsInVector128AreAscii(v0)) goto NotAscii; hash1_128 = Vector128.Xor(hash1_128, Vector128.BitwiseOr(v0, toLower)); - hash1_128 = Vector128.Multiply(hash1_128.AsUInt32(), prime.AsUInt32()).AsUInt64(); + hash1_128 = Vector128.Multiply(hash1_128, prime); ptr += (sizeof(Vector128) / sizeof(nuint)); } @@ -228,7 +224,7 @@ internal static unsafe UIntPtr UnstableHashVec128Lower(this ReadOnlySpan t return GetHashCodeUnstableLowerSlow(text); } - internal static unsafe UIntPtr UnstableHashAvx2Lower(this ReadOnlySpan text) + internal static unsafe UIntPtr UnstableHashVec256Lower(this ReadOnlySpan text) { fixed (char* src = &text.GetPinnableReference()) { @@ -237,46 +233,46 @@ internal static unsafe UIntPtr UnstableHashAvx2Lower(this ReadOnlySpan tex var hash2 = hash1; var ptr = (nuint*)(src); - var prime = Vector256.Create((ulong)0x100000001b3); - var hash1Avx = Vector256.Create(0xcbf29ce484222325); - var hash2Avx = Vector256.Create(0xcbf29ce484222325); + var prime = Vector256.Create((uint)0x01000193); + var hash1_256 = Vector256.Create(0x811c9dc5); + var hash2_256 = Vector256.Create(0x811c9dc5); // We "normalize to lowercase" every char by ORing with 0x0020. This casts // a very wide net because it will change, e.g., '^' to '~'. But that should // be ok because we expect this to be very rare in practice. - var toLower = Vector256.Create(0x0020).AsUInt64(); + var toLower = Vector256.Create(0x0020_0020); while (length >= sizeof(Vector256) / sizeof(char) * 4) // 128 byte chunks. { length -= (sizeof(Vector256) / sizeof(char)) * 4; - Vector256 v0 = Vector256.Load((ulong*)ptr); + var v0 = Vector256.Load((ulong*)ptr).AsUInt32(); if (!AllCharsInVector256AreAscii(v0)) goto NotAscii; - hash1Avx = Avx2.Xor(hash1Avx, Avx2.Or(v0, toLower)); - hash1Avx = Avx2.Multiply(hash1Avx.AsUInt32(), prime.AsUInt32()); + hash1_256 = Vector256.Xor(hash1_256, Vector256.BitwiseOr(v0, toLower)); + hash1_256 = Vector256.Multiply(hash1_256.AsUInt32(), prime.AsUInt32()); - v0 = Vector256.Load((ulong*)ptr + 4); + v0 = Vector256.Load((ulong*)ptr + 4).AsUInt32(); if (!AllCharsInVector256AreAscii(v0)) goto NotAscii; - hash2Avx = Avx2.Xor(hash2Avx, Avx2.Or(v0, toLower)); - hash2Avx = Avx2.Multiply(hash2Avx.AsUInt32(), prime.AsUInt32()); + hash2_256 = Vector256.Xor(hash2_256, Vector256.BitwiseOr(v0, toLower)); + hash2_256 = Vector256.Multiply(hash2_256.AsUInt32(), prime.AsUInt32()); - v0 = Vector256.Load((ulong*)ptr + 8); + v0 = Vector256.Load((ulong*)ptr + 8).AsUInt32(); if (!AllCharsInVector256AreAscii(v0)) goto NotAscii; - hash1Avx = Avx2.Xor(hash1Avx, Avx2.Or(v0, toLower)); - hash1Avx = Avx2.Multiply(hash1Avx.AsUInt32(), prime.AsUInt32()); + hash1_256 = Vector256.Xor(hash1_256, Vector256.BitwiseOr(v0, toLower)); + hash1_256 = Vector256.Multiply(hash1_256.AsUInt32(), prime.AsUInt32()); - v0 = Vector256.Load((ulong*)ptr + 12); + v0 = Vector256.Load((ulong*)ptr + 12).AsUInt32(); if (!AllCharsInVector256AreAscii(v0)) goto NotAscii; - hash2Avx = Avx2.Xor(hash2Avx, Avx2.Or(v0, toLower)); - hash2Avx = Avx2.Multiply(hash2Avx.AsUInt32(), prime.AsUInt32()); + hash2_256 = Vector256.Xor(hash2_256, Vector256.BitwiseOr(v0, toLower)); + hash2_256 = Vector256.Multiply(hash2_256.AsUInt32(), prime.AsUInt32()); ptr += (sizeof(Vector256) / sizeof(nuint)) * 4; } @@ -284,27 +280,27 @@ internal static unsafe UIntPtr UnstableHashAvx2Lower(this ReadOnlySpan tex { length -= sizeof(Vector256) / sizeof(char); - Vector256 v0 = Vector256.Load((ulong*)ptr); + var v0 = Vector256.Load((ulong*)ptr).AsUInt32(); if (!AllCharsInVector256AreAscii(v0)) goto NotAscii; - hash1Avx = Avx2.Xor(hash1Avx, Avx2.Or(v0, toLower)); - hash1Avx = Avx2.Multiply(hash1Avx.AsUInt32(), prime.AsUInt32()); + hash1_256 = Vector256.Xor(hash1_256, Vector256.BitwiseOr(v0, toLower)); + hash1_256 = Vector256.Multiply(hash1_256.AsUInt32(), prime.AsUInt32()); ptr += (sizeof(Vector256) / sizeof(nuint)); } // Flatten - hash1Avx = Avx2.Xor(hash1Avx, hash2Avx); + hash1_256 = Vector256.Xor(hash1_256, hash2_256); if (sizeof(nuint) == 8) { - hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (nuint)hash1Avx[0]; - hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (nuint)hash1Avx[1]; - hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (nuint)hash1Avx[2]; - hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (nuint)hash1Avx[3]; + hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (nuint)hash1_256[0]; + hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (nuint)hash1_256[1]; + hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (nuint)hash1_256[2]; + hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (nuint)hash1_256[3]; } else { - Vector256 hash1Uint = hash1Avx.AsUInt32(); + Vector256 hash1Uint = hash1_256.AsUInt32(); hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (hash1Uint[0] * hash1Uint[1]); hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ (hash1Uint[2] * hash1Uint[3]); hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ (hash1Uint[3] * hash1Uint[4]); diff --git a/src/Reloaded.Memory/Internals/Backports/System/Text/Unicode/Utf16Utility.cs b/src/Reloaded.Memory/Internals/Backports/System/Text/Unicode/Utf16Utility.cs index 93d77c8..b335617 100644 --- a/src/Reloaded.Memory/Internals/Backports/System/Text/Unicode/Utf16Utility.cs +++ b/src/Reloaded.Memory/Internals/Backports/System/Text/Unicode/Utf16Utility.cs @@ -55,14 +55,14 @@ internal static unsafe bool AllCharsInNuintAreAscii(nuint value) /// Returns true iff the Vector128 represents 8 ASCII UTF-16 characters in machine endianness. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool AllCharsInVector128AreAscii(Vector128 vec) => + internal static bool AllCharsInVector128AreAscii(Vector128 vec) => AllCharsInVector128AreAscii(vec.AsUInt16()); /// /// Returns true iff the Vector256 represents 16 ASCII UTF-16 characters in machine endianness. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool AllCharsInVector256AreAscii(Vector256 vec) => + internal static bool AllCharsInVector256AreAscii(Vector256 vec) => AllCharsInVector256AreAscii(vec.AsUInt16()); ///