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

Pooled span dicitionary direct pointer #455

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Draft
11 changes: 7 additions & 4 deletions src/Paprika.Benchmarks/PooledSpanDictionaryBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class PooledSpanDictionaryBenchmarks
};

private readonly PooledSpanDictionary _varLengthKeys;
private readonly PooledSpanDictionary _readWrite;
private const int VarLengthKeyCollisions = 8;
private const ulong VarLengthKeyCollisionHash = 2348598349058394;

Expand Down Expand Up @@ -51,6 +52,8 @@ public PooledSpanDictionaryBenchmarks()
{
_bigDict.Set(VarLengthKey[..VarLengthKeyCollisions], VarLengthKeyCollisionHash, Value32Bytes, 1);
}

_readWrite = new PooledSpanDictionary(new BufferPool(128, BufferPool.PageTracking.None, null));
}

[Benchmark]
Expand Down Expand Up @@ -124,16 +127,16 @@ public int Read_missing_with_no_hash_collisions()
[Benchmark]
public int Read_write_small()
{
using var dict = new PooledSpanDictionary(_pool, false);

Span<byte> key = stackalloc byte[2];

var count = 0;
for (byte i = 0; i < 255; i++)
{
key[0] = i;
dict.Set(key, i, key, 1);
dict.TryGet(key, i, out var result);

_readWrite.Set(key, i, key, 1);
_readWrite.TryGet(key, i, out var result);

count += result[0];
}

Expand Down
12 changes: 3 additions & 9 deletions src/Paprika.Tests/Merkle/Commit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,23 +183,17 @@ private static byte[] Concat(in ReadOnlySpan<byte> payload0, in ReadOnlySpan<byt
return bytes;
}

class ChildCommit : IChildCommit
class ChildCommit(ICommit commit) : IChildCommit
{
private readonly ICommit _commit;
private readonly Dictionary<byte[], byte[]> _data = new(Comparer);

public ChildCommit(ICommit commit)
{
_commit = commit;
}

public void Dispose() => _data.Clear();

public ReadOnlySpanOwnerWithMetadata<byte> Get(scoped in Key key)
{
return _data.TryGetValue(GetKey(key), out var value)
? new ReadOnlySpanOwner<byte>(value, null).WithDepth(0)
: _commit.Get(key);
: commit.Get(key);
}

public void Set(in Key key, in ReadOnlySpan<byte> payload, EntryType type)
Expand All @@ -217,7 +211,7 @@ public void Commit()
foreach (var kvp in _data)
{
Key.ReadFrom(kvp.Key, out var key);
_commit.Set(key, kvp.Value);
commit.Set(key, kvp.Value);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Paprika.Tests/Merkle/RootHashFuzzyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public void Over_one_mock_commit(string test)

[TestCase(nameof(Accounts_100_Storage_1), int.MaxValue, 4)]
[TestCase(nameof(Accounts_1_Storage_100), 11, 8)]
[TestCase(nameof(Accounts_1000_Storage_1000), int.MaxValue, 1016, Category = Categories.LongRunning)]
[TestCase(nameof(Accounts_1000_Storage_1000), int.MaxValue, 2500, Category = Categories.LongRunning)]
public async Task In_memory_run(string test, int commitEvery, int blockchainPoolSizeMB)
{
var generator = Build(test);
Expand Down
62 changes: 12 additions & 50 deletions src/Paprika/Chain/Blockchain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
private readonly Action? _beforeMetricsDisposed;
private bool _verify;

public Blockchain(IDb db, IPreCommitBehavior preCommit, TimeSpan? minFlushDelay = null,

Check warning on line 67 in src/Paprika/Chain/Blockchain.cs

View workflow job for this annotation

GitHub Actions / Run Paprika tests

Non-nullable event 'Flushed' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the event as nullable.

Check warning on line 67 in src/Paprika/Chain/Blockchain.cs

View workflow job for this annotation

GitHub Actions / Run Paprika tests

Non-nullable event 'FlusherFailure' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the event as nullable.
CacheBudget.Options cacheBudgetStateAndStorage = default,
CacheBudget.Options cacheBudgetPreCommit = default,
int? finalizationQueueLimit = null, Action? beforeMetricsDisposed = null)
Expand Down Expand Up @@ -1176,73 +1176,35 @@
}
}

IChildCommit ICommit.GetChild() => new ChildCommit(Pool, this);
IChildCommit ICommit.GetChild() => new ChildCommit(this, this);

public IReadOnlySet<Keccak> TouchedAccounts => _touchedAccounts;

public IReadOnlyDictionary<Keccak, IStorageStats> TouchedStorageSlots => _storageSlots;

class ChildCommit(BufferPool pool, ICommit parent) : RefCountingDisposable, IChildCommit
sealed class ChildCommit(ICommit parent, BlockState owner) : IChildCommit
{
private readonly PooledSpanDictionary _dict = new(pool, true);
public ReadOnlySpanOwnerWithMetadata<byte> Get(scoped in Key key) => parent.Get(in key);

[SkipLocalsInit]
public ReadOnlySpanOwnerWithMetadata<byte> Get(scoped in Key key)
{
var hash = GetHash(key);
var keyWritten = key.WriteTo(stackalloc byte[key.MaxByteLength]);
public void Set(in Key key, in ReadOnlySpan<byte> payload, EntryType type = EntryType.Persistent) =>
parent.Set(in key, in payload, type);

if (_dict.TryGet(keyWritten, hash, out var result))
{
AcquireLease();
return new ReadOnlySpanOwnerWithMetadata<byte>(new ReadOnlySpanOwner<byte>(result, this), 0);
}
public void Set(in Key key, in ReadOnlySpan<byte> payload0, in ReadOnlySpan<byte> payload1,
EntryType type = EntryType.Persistent) => parent.Set(in key, in payload0, in payload1, type);

// Don't nest, as reaching to parent should be easy.
return parent.Get(key);
}
public IChildCommit GetChild() => parent.GetChild();

[SkipLocalsInit]
public void Set(in Key key, in ReadOnlySpan<byte> payload, EntryType type)
{
var hash = GetHash(key);
var keyWritten = key.WriteTo(stackalloc byte[key.MaxByteLength]);
public bool Owns(object? actualSpanOwner) => ReferenceEquals(actualSpanOwner, owner);

_dict.Set(keyWritten, hash, payload, (byte)type);
}

[SkipLocalsInit]
public void Set(in Key key, in ReadOnlySpan<byte> payload0, in ReadOnlySpan<byte> payload1, EntryType type)
public void Dispose()
{
var hash = GetHash(key);
var keyWritten = key.WriteTo(stackalloc byte[key.MaxByteLength]);

_dict.Set(keyWritten, hash, payload0, payload1, (byte)type);
// NOOP, nothing to dispose
}

public void Commit()
{
foreach (var kvp in _dict)
{
Key.ReadFrom(kvp.Key, out var key);
var type = (EntryType)kvp.Metadata;

// flush down only volatiles
if (type != EntryType.UseOnce)
{
parent.Set(key, kvp.Value, type);
}
}
}

public IChildCommit GetChild() => new ChildCommit(pool, this);

protected override void CleanUp()
{
_dict.Dispose();
// NOOP, nothing to commit
}

public override string ToString() => _dict.ToString();
}

[SkipLocalsInit]
Expand Down Expand Up @@ -2323,7 +2285,7 @@
_lock.EnterReadLock();
try
{
if (_readers.TryGetValue(rootHash, out state))

Check warning on line 2288 in src/Paprika/Chain/Blockchain.cs

View workflow job for this annotation

GitHub Actions / Run Paprika tests

Possible null reference assignment.
{
state.AcquireLease();
return true;
Expand Down Expand Up @@ -2380,7 +2342,7 @@
{
var removed = _readers.Remove(b.Hash, out ReadOnlyState? state);
Debug.Assert(removed);
toDispose.Add(state);

Check warning on line 2345 in src/Paprika/Chain/Blockchain.cs

View workflow job for this annotation

GitHub Actions / Run Paprika tests

Possible null reference argument for parameter 'item' in 'void List<ReadOnlyState>.Add(ReadOnlyState item)'.
}
}
}
Expand Down
Loading
Loading