Skip to content

Commit

Permalink
Changed: Unroll threshold for lower string hash
Browse files Browse the repository at this point in the history
  • Loading branch information
Sewer56 committed Mar 12, 2024
1 parent c7f9f88 commit d490827
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 121 deletions.
14 changes: 8 additions & 6 deletions src/Reloaded.Memory/Internals/Algorithms/UnstableStringHash.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ internal static unsafe nuint GetHashCodeUnstable(this ReadOnlySpan<char> text)
// 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.

// Over 4 Vec256 regs (32 * 4 = 128 bytes)
if (Vector256.IsHardwareAccelerated && length >= (sizeof(Vector256<ulong>) / sizeof(char)) * 4)
return text.UnstableHashVec256();
// 64 byte threshold.
if (length >= 32)
{
if (Vector256.IsHardwareAccelerated)
return text.UnstableHashVec256();

// Over 4 Vec128 regs (16 * 4 = 64 bytes)
if (Vector128.IsHardwareAccelerated && length >= (sizeof(Vector128<ulong>) / sizeof(char)) * 4)
return text.UnstableHashVec128();
if (Vector128.IsHardwareAccelerated)
return text.UnstableHashVec128();
}
#endif

return text.UnstableHashNonVector();
Expand Down
133 changes: 18 additions & 115 deletions src/Reloaded.Memory/Internals/Algorithms/UnstableStringHashLower.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@ internal static unsafe nuint GetHashCodeUnstableLower(this ReadOnlySpan<char> te
// 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.

// Over 4 Vec256 regs (32 * 4 = 128 bytes)
if (Vector256.IsHardwareAccelerated && length >= (sizeof(Vector256<ulong>) / sizeof(char)) * 4)
return text.UnstableHashVec256Lower();
// 64 byte threshold.
if (length >= 32)
{
if (Vector256.IsHardwareAccelerated)
return text.UnstableHashVec256Lower();

// Over 4 Vec128 regs (16 * 4 = 64 bytes)
if (Vector128.IsHardwareAccelerated && length >= (sizeof(Vector128<ulong>) / sizeof(char)) * 4)
return text.UnstableHashVec128Lower();
if (Vector128.IsHardwareAccelerated)
return text.UnstableHashVec128Lower();
}
#endif

return text.UnstableHashNonVectorLower();
Expand Down Expand Up @@ -126,9 +128,9 @@ internal static unsafe UIntPtr UnstableHashVec128Lower(this ReadOnlySpan<char> t
// be ok because we expect this to be very rare in practice.
var toLower = Vector128.Create<short>(0x0020).AsUInt64();

while (length >= sizeof(Vector128<ulong>) / sizeof(char) * 4) // 64 byte chunks.
while (length >= sizeof(Vector128<ulong>) / sizeof(char) * 2) // 32 byte chunks.
{
length -= (sizeof(Vector128<ulong>) / sizeof(char)) * 4;
length -= (sizeof(Vector128<ulong>) / sizeof(char)) * 2;

var v0 = Vector128.Load((ulong*)ptr);
if (!AllCharsInVector128AreAscii(v0.AsUInt16()))
Expand All @@ -143,24 +145,10 @@ internal static unsafe UIntPtr UnstableHashVec128Lower(this ReadOnlySpan<char> t

hash2_128 = Vector128.Xor(hash2_128, Vector128.BitwiseOr(v0, toLower));
hash2_128 = HashMultiply128(hash2_128, prime);

v0 = Vector128.Load((ulong*)ptr + 4);
if (!AllCharsInVector128AreAscii(v0.AsUInt16()))
goto NotAscii;

hash1_128 = Vector128.Xor(hash1_128, Vector128.BitwiseOr(v0, toLower));
hash1_128 = HashMultiply128(hash1_128, prime);

v0 = Vector128.Load((ulong*)ptr + 6);
if (!AllCharsInVector128AreAscii(v0.AsUInt16()))
goto NotAscii;

hash2_128 = Vector128.Xor(hash2_128, Vector128.BitwiseOr(v0, toLower));
hash2_128 = HashMultiply128(hash2_128, prime);
ptr += (sizeof(Vector128<ulong>) / sizeof(nuint)) * 4;
ptr += (sizeof(Vector128<ulong>) / sizeof(nuint)) * 2;
}

while (length >= sizeof(Vector128<ulong>) / sizeof(char)) // 16 byte chunks.
if (length >= sizeof(Vector128<ulong>) / sizeof(char)) // 16 byte chunk.
{
length -= sizeof(Vector128<ulong>) / sizeof(char);

Expand Down Expand Up @@ -249,10 +237,9 @@ internal static unsafe UIntPtr UnstableHashVec256Lower(this ReadOnlySpan<char> t
// be ok because we expect this to be very rare in practice.
var toLower = Vector256.Create<short>(0x0020).AsUInt64();

while (length >= sizeof(Vector256<ulong>) / sizeof(char) * 4) // 128 byte chunks.
while (length >= sizeof(Vector256<ulong>) / sizeof(char) * 2) // 64 byte chunks.
{
length -= (sizeof(Vector256<ulong>) / sizeof(char)) * 4;

length -= (sizeof(Vector256<ulong>) / sizeof(char)) * 2;
var v0 = Vector256.Load((ulong*)ptr);
if (!AllCharsInVector256AreAscii(v0.AsUInt16()))
goto NotAscii;
Expand All @@ -266,24 +253,10 @@ internal static unsafe UIntPtr UnstableHashVec256Lower(this ReadOnlySpan<char> t

hash2_256 = Vector256.Xor(hash2_256, Vector256.BitwiseOr(v0, toLower));
hash2_256 = HashMultiply256(hash2_256, prime);

v0 = Vector256.Load((ulong*)ptr + 8);
if (!AllCharsInVector256AreAscii(v0.AsUInt16()))
goto NotAscii;

hash1_256 = Vector256.Xor(hash1_256, Vector256.BitwiseOr(v0, toLower));
hash1_256 = HashMultiply256(hash1_256, prime);

v0 = Vector256.Load((ulong*)ptr + 12);
if (!AllCharsInVector256AreAscii(v0.AsUInt16()))
goto NotAscii;

hash2_256 = Vector256.Xor(hash2_256, Vector256.BitwiseOr(v0, toLower));
hash2_256 = HashMultiply256(hash2_256, prime);
ptr += (sizeof(Vector256<ulong>) / sizeof(nuint)) * 4;
ptr += (sizeof(Vector256<ulong>) / sizeof(nuint)) * 2;
}

while (length >= sizeof(Vector256<ulong>) / sizeof(char)) // 32 byte chunks.
if (length >= sizeof(Vector256<ulong>) / sizeof(char)) // 32 byte chunk.
{
length -= sizeof(Vector256<ulong>) / sizeof(char);

Expand Down Expand Up @@ -386,47 +359,8 @@ internal static unsafe UIntPtr UnstableHashNonVectorLower32(this ReadOnlySpan<ch
// be ok because we expect this to be very rare in practice.
const uint normalizeToLowercase = 0x0020_0020; // valid both for big-endian and for little-endian

// 32 byte
while (length >= (sizeof(uint) / sizeof(char)) * 8)
{
length -= (sizeof(uint) / sizeof(char)) * 8;

var p0 = ptr[0];
var p1 = ptr[1];
if (!AllCharsInUIntAreAscii(p0 | p1))
goto NotAscii;

hash1 = (hash1 ^ (p0 | normalizeToLowercase)) * prime;
hash2 = (hash2 ^ (p1 | normalizeToLowercase)) * prime;

p0 = ptr[2];
p1 = ptr[3];
if (!AllCharsInUIntAreAscii(p0 | p1))
goto NotAscii;

hash1 = (hash1 ^ (p0 | normalizeToLowercase)) * prime;
hash2 = (hash2 ^ (p1 | normalizeToLowercase)) * prime;

p0 = ptr[4];
p1 = ptr[5];
if (!AllCharsInUIntAreAscii(p0 | p1))
goto NotAscii;

hash1 = (hash1 ^ (p0 | normalizeToLowercase)) * prime;
hash2 = (hash2 ^ (p1 | normalizeToLowercase)) * prime;

p0 = ptr[6];
p1 = ptr[7];
if (!AllCharsInUIntAreAscii(p0 | p1))
goto NotAscii;

hash1 = (hash1 ^ (p0 | normalizeToLowercase)) * prime;
hash2 = (hash2 ^ (p1 | normalizeToLowercase)) * prime;
ptr += 8;
}

// 16 byte
if (length >= (sizeof(uint) / sizeof(char)) * 4)
while (length >= (sizeof(uint) / sizeof(char)) * 4)
{
length -= (sizeof(uint) / sizeof(char)) * 4;

Expand Down Expand Up @@ -506,39 +440,8 @@ internal static unsafe UIntPtr UnstableHashNonVectorLower64(this ReadOnlySpan<ch
// be ok because we expect this to be very rare in practice.
const ulong normalizeToLowercase = 0x0020_0020_0020_0020; // valid both for big-endian and for little-endian

// 64 byte
while (length >= (sizeof(ulong) / sizeof(char)) * 8)
{
length -= (sizeof(ulong) / sizeof(char)) * 8;

var p0 = ptr[0];
var p1 = ptr[1];
var p2 = ptr[2];
var p3 = ptr[3];
if (!AllCharsInULongAreAscii(p0 | p1 | p2 | p3))
goto NotAscii;

hash1 = (hash1 ^ (p0 | normalizeToLowercase)) * prime;
hash2 = (hash2 ^ (p1 | normalizeToLowercase)) * prime;
hash1 = (hash1 ^ (p2 | normalizeToLowercase)) * prime;
hash2 = (hash2 ^ (p3 | normalizeToLowercase)) * prime;

p0 = ptr[4];
p1 = ptr[5];
p2 = ptr[6];
p3 = ptr[7];
if (!AllCharsInULongAreAscii(p0 | p1 | p2 | p3))
goto NotAscii;

hash1 = (hash1 ^ (p0 | normalizeToLowercase)) * prime;
hash2 = (hash2 ^ (p1 | normalizeToLowercase)) * prime;
hash1 = (hash1 ^ (p2 | normalizeToLowercase)) * prime;
hash2 = (hash2 ^ (p3 | normalizeToLowercase)) * prime;
ptr += 8;
}

// 32 byte
if (length >= (sizeof(ulong) / sizeof(char)) * 4)
while (length >= (sizeof(ulong) / sizeof(char)) * 4)
{
length -= (sizeof(ulong) / sizeof(char)) * 4;

Expand Down

0 comments on commit d490827

Please sign in to comment.