Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fast Keccak cache (wait-free and lockless) #7336

Merged
merged 53 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
280097e
More comments
Scooletz Aug 16, 2024
7ecd7a5
comments
Scooletz Aug 16, 2024
8dce0c7
updated
Scooletz Aug 16, 2024
dec33ca
EVM uses the cached keccak
Scooletz Aug 16, 2024
f5b756c
state tree
Scooletz Aug 16, 2024
df510fd
more tests
Scooletz Aug 16, 2024
154037e
special cases
Scooletz Aug 16, 2024
24a595b
Release with Volatile.Write
Scooletz Aug 16, 2024
790f86d
StorageTree added
Scooletz Aug 16, 2024
2593734
comments
Scooletz Aug 16, 2024
805e28c
one less CAS
Scooletz Aug 16, 2024
b01d503
more go-tos
Scooletz Aug 16, 2024
83e8489
Merge branch 'master' into keccak-cache
benaadams Aug 16, 2024
0118c1d
one less branch in hash
Scooletz Aug 16, 2024
af55085
Merge branch 'keccak-cache' of https://github.com/NethermindEth/nethe…
Scooletz Aug 16, 2024
21d7714
Merge branch 'master' into keccak-cache
benaadams Aug 16, 2024
2e74b76
Start with per instance random
benaadams Aug 16, 2024
a29dcec
Include length in hash seed
benaadams Aug 16, 2024
e654d13
Improve comment
benaadams Aug 16, 2024
c88d931
Move stackalloc out of common path and inline ComputeKey to it
benaadams Aug 17, 2024
6fcfb9a
Unify hashing
benaadams Aug 17, 2024
381511e
Use full hash
benaadams Aug 18, 2024
9e2ea27
Faster StorageCell equality
benaadams Aug 18, 2024
c3996a2
Less copy
benaadams Aug 18, 2024
669cec4
Merge branch 'master' into keccak-cache
benaadams Aug 18, 2024
d1483ce
Even less copy
benaadams Aug 18, 2024
2070cc2
Doesn't need to return
benaadams Aug 19, 2024
28e0fa4
Update alignment comments
benaadams Aug 19, 2024
89bd655
lol; don't do extra work
benaadams Aug 19, 2024
42ef8f6
Faster compares
benaadams Aug 19, 2024
773ef2a
Move other HasCodes to FastHash
benaadams Aug 19, 2024
226be3f
Special case 32 and 20 bytes hashes
benaadams Aug 19, 2024
7d5fba5
Word align vector compare
benaadams Aug 19, 2024
7184d26
constants
Scooletz Aug 19, 2024
795b4c9
stack reduced by 8 and one less comparison on read
Scooletz Aug 19, 2024
c19edb3
Use full entropy of HashCode for comparision
benaadams Aug 19, 2024
1f8b94f
Revert "Use full entropy of HashCode for comparision"
benaadams Aug 19, 2024
928929e
Tweaks
benaadams Aug 19, 2024
e32f995
comments
Scooletz Aug 19, 2024
6938aab
Align 32
benaadams Aug 19, 2024
545384f
Merge branch 'keccak-cache' of https://github.com/NethermindEth/nethe…
benaadams Aug 19, 2024
472ca26
smaller entry, bigger cache
Scooletz Aug 20, 2024
7d1bba6
Missed one
benaadams Aug 20, 2024
b8a6f7f
Merge branch 'master' into keccak-cache
benaadams Aug 20, 2024
9e8b635
Merge branch 'master' into keccak-cache
benaadams Aug 23, 2024
a536245
Merge branch 'master' into keccak-cache
benaadams Aug 31, 2024
50c1e8f
Faster FastHash
benaadams Aug 31, 2024
fc52fcc
alignment handled with if
Scooletz Sep 2, 2024
c11cc94
Merge branch 'master' into keccak-cache
benaadams Sep 11, 2024
ce44b4c
Merge branch 'master' into keccak-cache
benaadams Sep 11, 2024
a2a0155
Merge branch 'master' into keccak-cache
benaadams Sep 19, 2024
7363f2c
Missed one fastHash
benaadams Sep 19, 2024
70f5995
Add memory pressure
benaadams Sep 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions src/Nethermind/Nethermind.Core.Test/KeccakCacheTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Buffers.Binary;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
using NUnit.Framework;

namespace Nethermind.Core.Test
{
[TestFixture]
public class KeccakCacheTests
{
[Test]
public void Multiple()
{
const int spins = 10;

var random = new Random(13);
var bytes = new byte[31]; // misaligned length
random.NextBytes(bytes);

ValueHash256 expected = ValueKeccak.Compute(bytes);

for (int i = 0; i < spins; i++)
{
ValueHash256 actual = KeccakCache.Compute(bytes);
actual.Equals(expected).Should().BeTrue();
}
}

[Test]
public void Empty()
{
ReadOnlySpan<byte> span = ReadOnlySpan<byte>.Empty;
KeccakCache.Compute(span).Should().Be(ValueKeccak.Compute(span));
}

[Test]
public void Very_long()
{
ReadOnlySpan<byte> span = new byte[192];
KeccakCache.Compute(span).Should().Be(ValueKeccak.Compute(span));
}

private string[] GetBucketCollisions()
{
var random = new Random(13);
Span<byte> span = stackalloc byte[32];
string[] collisions = new string[4];

random.NextBytes(span);
var bucket = KeccakCache.GetBucket(span);

Console.WriteLine(span.ToHexString());

collisions[0] = span.ToHexString();
var found = 1;

ulong iterations = 0;
while (found < 4)
{
random.NextBytes(span);
if (KeccakCache.GetBucket(span) == bucket)
{
collisions[found] = span.ToHexString();
Console.WriteLine(span.ToHexString());
found++;
}
iterations++;
}

Console.WriteLine($"{iterations} iterations to find");
return collisions;
}

[Test]
public void Collision()
{
var colliding = GetBucketCollisions();

var collisions = colliding.Length;
var array = colliding.Select(c => Bytes.FromHexString(c)).ToArray();
var values = array.Select(a => ValueKeccak.Compute(a)).ToArray();

var bucket = KeccakCache.GetBucket(array[0]);

for (int i = 1; i < collisions; i++)
{
var input = array[i];
bucket.Should().Be(KeccakCache.GetBucket(input));
KeccakCache.Compute(input).Should().Be(values[i]);
}

Parallel.ForEach(array, (a, state, index) =>
{
ValueHash256 v = values[index];

for (int i = 0; i < 100_000; i++)
{
KeccakCache.Compute(a).Should().Be(v);
}
});
}

[Test]
public void Spin_through_all()
{
Span<byte> span = stackalloc byte[4];
for (int i = 0; i < KeccakCache.Count; i++)
{
BinaryPrimitives.WriteInt32LittleEndian(span, i);
KeccakCache.Compute(span).Should().Be(ValueKeccak.Compute(span));
}
}
}
}
24 changes: 9 additions & 15 deletions src/Nethermind/Nethermind.Core/Address.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Text.Json.Serialization;

using Nethermind.Core.Crypto;
Expand All @@ -20,11 +21,6 @@ namespace Nethermind.Core
[TypeConverter(typeof(AddressTypeConverter))]
public class Address : IEquatable<Address>, IComparable<Address>
{
// Ensure that hashes are different for every run of the node and every node, so if are any hash collisions on
// one node they will not be the same on another node or across a restart so hash collision cannot be used to degrade
// the performance of the network as a whole.
private static readonly uint s_instanceRandom = (uint)System.Security.Cryptography.RandomNumberGenerator.GetInt32(int.MinValue, int.MaxValue);

public const int Size = 20;
private const int HexCharsCount = 2 * Size; // 5a4eab120fb44eb6684e5e32785702ff45ea344d
private const int PrefixedHexCharsCount = 2 + HexCharsCount; // 0x5a4eab120fb44eb6684e5e32785702ff45ea344d
Expand Down Expand Up @@ -153,7 +149,12 @@ public bool Equals(Address? other)
return true;
}

return Nethermind.Core.Extensions.Bytes.AreEqual(Bytes, other.Bytes);
ref byte bytes0 = ref MemoryMarshal.GetArrayDataReference(Bytes);
ref byte bytes1 = ref MemoryMarshal.GetArrayDataReference(other.Bytes);
// 20 bytes which is uint+Vector128
return Unsafe.As<byte, uint>(ref bytes0) == Unsafe.As<byte, uint>(ref bytes1) &&
Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref bytes0, sizeof(uint))) ==
Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref bytes1, sizeof(uint)));
}

public static Address FromNumber(in UInt256 number)
Expand Down Expand Up @@ -192,14 +193,7 @@ public override bool Equals(object? obj)
return obj.GetType() == GetType() && Equals((Address)obj);
}

public override int GetHashCode()
{
uint hash = s_instanceRandom;
hash = BitOperations.Crc32C(hash, Unsafe.ReadUnaligned<ulong>(ref MemoryMarshal.GetArrayDataReference(Bytes)));
hash = BitOperations.Crc32C(hash, Unsafe.ReadUnaligned<ulong>(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Bytes), sizeof(ulong))));
hash = BitOperations.Crc32C(hash, Unsafe.ReadUnaligned<uint>(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(Bytes), sizeof(long) * 2)));
return (int)hash;
}
public override int GetHashCode() => new ReadOnlySpan<byte>(Bytes).FastHash();

public static bool operator ==(Address? a, Address? b)
{
Expand Down Expand Up @@ -234,7 +228,7 @@ public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destina
destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
}

public Hash256 ToAccountPath => Keccak.Compute(Bytes);
public Hash256 ToAccountPath => KeccakCache.Compute(Bytes);

[SkipLocalsInit]
public ValueHash256 ToHash()
Expand Down
10 changes: 2 additions & 8 deletions src/Nethermind/Nethermind.Core/Bloom.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,7 @@ public override bool Equals(object? obj)
return Equals((Bloom)obj);
}

public override int GetHashCode()
{
return Bytes.GetSimplifiedHashCode();
}
public override int GetHashCode() => new ReadOnlySpan<byte>(Bytes).FastHash();

public void Add(LogEntry[] logEntries, Bloom? blockBloom = null)
{
Expand Down Expand Up @@ -278,10 +275,7 @@ public override readonly bool Equals(object? obj)
return Equals((Bloom)obj);
}

public override readonly int GetHashCode()
{
return Core.Extensions.Bytes.GetSimplifiedHashCode(Bytes);
}
public override readonly int GetHashCode() => Bytes.FastHash();

public readonly bool Matches(LogEntry logEntry)
{
Expand Down
14 changes: 2 additions & 12 deletions src/Nethermind/Nethermind.Core/Crypto/Hash256.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,9 @@ public ValueHash256(ReadOnlySpan<byte> bytes)

public bool Equals(Hash256? other) => _bytes.Equals(other?.ValueHash256._bytes ?? default);

public override int GetHashCode()
{
return GetChainedHashCode(s_instanceRandom);
}
public override int GetHashCode() => GetChainedHashCode(s_instanceRandom);

public int GetChainedHashCode(uint previousHash)
{
uint hash = BitOperations.Crc32C(previousHash, Unsafe.As<Vector256<byte>, ulong>(ref Unsafe.AsRef(in _bytes)));
hash = BitOperations.Crc32C(hash, Unsafe.Add(ref Unsafe.As<Vector256<byte>, ulong>(ref Unsafe.AsRef(in _bytes)), 1));
hash = BitOperations.Crc32C(hash, Unsafe.Add(ref Unsafe.As<Vector256<byte>, ulong>(ref Unsafe.AsRef(in _bytes)), 2));
hash = BitOperations.Crc32C(hash, Unsafe.Add(ref Unsafe.As<Vector256<byte>, ulong>(ref Unsafe.AsRef(in _bytes)), 3));
return (int)hash;
}
public int GetChainedHashCode(uint previousHash) => Bytes.FastHash() ^ (int)previousHash;

public int CompareTo(ValueHash256 other)
{
Expand Down
Loading