Skip to content
Open
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
37217b6
Storage cache
shargon Jan 13, 2025
f0bfc3b
reduce
shargon Jan 13, 2025
038a7c4
clean
shargon Jan 13, 2025
ccce7a4
Fix discard
shargon Jan 13, 2025
4943659
Merge branch 'master' into storage-cache
shargon Jan 20, 2025
cca66ba
Clean
shargon Jan 20, 2025
54901ed
clean
shargon Jan 20, 2025
75f5a06
Merge branch 'master' into storage-cache
shargon Jan 21, 2025
ecdfe09
Merge branch 'master' into storage-cache
shargon Jan 21, 2025
83d13cc
Clean code
shargon Jan 21, 2025
5230d9c
fix cache
shargon Jan 21, 2025
df7ad8e
remove lock
shargon Jan 21, 2025
be5db33
Merge branch 'master' into storage-cache
shargon Jan 22, 2025
c12767a
Clean and Fix UT
shargon Jan 22, 2025
601c366
Clean
shargon Jan 22, 2025
6bdcc21
Anna's feedback
shargon Jan 23, 2025
9c45d94
Merge branch 'master' into storage-cache
shargon Jan 23, 2025
5fb4b62
Add fee values
shargon Jan 23, 2025
3d25c9f
Merge branch 'storage-cache' of https://github.com/neo-project/neo in…
shargon Jan 23, 2025
413d605
Merge branch 'master' into storage-cache
cschuchardt88 Jan 23, 2025
2dd2301
Merge branch 'master' into storage-cache
shargon Jan 26, 2025
336cbac
Merge branch 'master' into storage-cache
Jan 28, 2025
9decaf0
```
shargon Feb 6, 2025
bb6ca9c
Merge branch 'master' into storage-cache
shargon Feb 6, 2025
a84c163
Fix conflicts
shargon Feb 6, 2025
3c6d292
Optimize
shargon Feb 6, 2025
bf97fab
Fix
shargon Feb 6, 2025
62ae410
Clean changes
shargon Feb 6, 2025
ac22a4d
Merge branch 'master' into storage-cache
shargon Feb 6, 2025
a21526f
add UT
Jim8y Feb 6, 2025
f0a9add
Merge branch 'storage-cache' of github.com:neo-project/neo into stora…
Jim8y Feb 6, 2025
a917235
Merge branch 'master' into storage-cache
shargon Feb 7, 2025
f4b5bcd
Merge branch 'master' into storage-cache
shargon Feb 7, 2025
6d1d8a0
Clean changes
shargon Feb 7, 2025
6bd4f42
Update IReadOnlyStore.cs
shargon Feb 7, 2025
aa3b705
Merge branch 'master' into storage-cache
shargon Feb 9, 2025
e4e8130
Update src/Neo/SmartContract/Native/NeoToken.cs
shargon Feb 9, 2025
b5f03e2
Merge branch 'master' into storage-cache
Feb 11, 2025
d31d182
Merge branch 'master' into storage-cache
shargon Feb 11, 2025
c6a7c00
Clean Store and Snapshot
shargon Feb 11, 2025
e503094
Clean code
shargon Feb 11, 2025
13b3e50
Add ut
shargon Feb 11, 2025
a96bdbc
Merge branch 'master' into storage-cache
shargon Feb 11, 2025
3bf2240
Update src/Neo/Persistence/SerializedCache.cs
shargon Feb 11, 2025
53215b7
Update src/Neo/Persistence/SerializedCache.cs
shargon Feb 11, 2025
64b2796
Update benchmarks/Neo.Benchmarks/Benchmarks.Cache.cs
shargon Feb 11, 2025
898d3e6
Merge remote-tracking branch 'origin/master' into storage-cache
shargon Feb 13, 2025
1d971bf
Rename to Upsert
shargon Feb 13, 2025
894f979
Fix clone
shargon Feb 13, 2025
3a7bf7a
Fix
shargon Feb 13, 2025
1f5af5c
Merge branch 'master' into storage-cache
shargon Feb 13, 2025
5f80417
Merge branch 'master' into storage-cache
shargon Feb 15, 2025
fa7ec14
Update Program.cs
shargon Feb 15, 2025
ce5ec54
Merge branch 'master' into storage-cache
shargon Feb 18, 2025
f7ae9de
Merge branch 'master' into storage-cache
shargon Mar 2, 2025
6f30414
Merge branch 'master' into storage-cache
shargon May 19, 2025
c277a80
Fix constructor
shargon May 19, 2025
6af2e0c
tKey
shargon May 19, 2025
3bc447a
Update tests/Neo.Cryptography.MPTTrie.Tests/Cryptography/MPTTrie/UT_T…
shargon May 19, 2025
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
69 changes: 69 additions & 0 deletions benchmarks/Neo.Benchmarks/Benchmarks.Cache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (C) 2015-2025 The Neo Project.
//
// Benchmarks.Cache.cs file belongs to the neo project and is free
// software distributed under the MIT software license, see the
// accompanying file LICENSE in the main directory of the
// repository or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using BenchmarkDotNet.Attributes;
using Neo.Persistence;
using Neo.SmartContract;
using Neo.SmartContract.Native;
using System.Numerics;

namespace Neo.Benchmark
{
public class Benchmarks_Cache
{
// 256 KiB
readonly MemoryStore _store;
readonly SnapshotCache _snapshot;

public Benchmarks_Cache()
{
_store = new MemoryStore();
_snapshot = new(_store.GetSnapshot());

// Ledger.CurrentIndex

_snapshot.GetAndChange(new KeyBuilder(NativeContract.Ledger.Id, 12), () => new StorageItem(new HashIndexState() { Hash = UInt256.Zero, Index = 2 }));

// Gas Per block

_snapshot.GetAndChange(new KeyBuilder(NativeContract.NEO.Id, 29).AddBigEndian(0), () => new StorageItem(0));
_snapshot.GetAndChange(new KeyBuilder(NativeContract.NEO.Id, 29).AddBigEndian(1), () => new StorageItem(1));
_snapshot.GetAndChange(new KeyBuilder(NativeContract.NEO.Id, 29).AddBigEndian(2), () => new StorageItem(2));
}

[Benchmark]
public void WithCache()
{
for (var x = 0; x < 1_000; x++)
{
var ret = NativeContract.NEO.GetGasPerBlock(_snapshot);
if (ret != 2) throw new Exception("Test error");
}
}

[Benchmark]
public void WithoutCache()
{
for (var x = 0; x < 1_000; x++)
{
var ret = OldCode();
if (ret != 2) throw new Exception("Test error");
}
}

private BigInteger OldCode()
{
var end = NativeContract.Ledger.CurrentIndex(_snapshot) + 1;
var last = NativeContract.NEO.GetSortedGasRecords(_snapshot, end).First();
return last.GasPerBlock;
}
}
}
14 changes: 6 additions & 8 deletions benchmarks/Neo.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@

using BenchmarkDotNet.Running;
using Neo.Benchmark;
using Neo.Benchmarks.Persistence.Benchmarks;
using Neo.SmartContract.Benchmark;

// BenchmarkRunner.Run<Benchmarks_PoCs>();
BenchmarkRunner.Run<Benchmarks_UInt160>();
BenchmarkRunner.Run<Benchmarks_Hash>();
BenchmarkRunner.Run<Benchmarks_StorageKey>();
BenchmarkRunner.Run<Bechmarks_ReadOnlyStoreView>();
BenchmarkRunner.Run<Bechmarks_LevelDB>();
BenchmarkRunner.Run<Benchmarks_Cache>();
//BenchmarkRunner.Run<Benchmarks_UInt160>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why comment out these lines?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually we run only one benchmark, isn't it?

//BenchmarkRunner.Run<Benchmarks_Hash>();
//BenchmarkRunner.Run<Benchmarks_StorageKey>();
//BenchmarkRunner.Run<Bechmarks_ReadOnlyStoreView>();
//BenchmarkRunner.Run<Bechmarks_LevelDB>();
1 change: 1 addition & 0 deletions src/Neo/Neo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="Neo.Benchmarks" />
<InternalsVisibleTo Include="Neo.SmartContract.Testing" />
<InternalsVisibleTo Include="Neo.SmartContract.TestEngine" />
<InternalsVisibleTo Include="Neo.Plugins.RpcServer.Tests" />
Expand Down
2 changes: 1 addition & 1 deletion src/Neo/Persistence/ClonedCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class ClonedCache : DataCache
{
private readonly DataCache _innerCache;

public ClonedCache(DataCache innerCache)
public ClonedCache(DataCache innerCache) : base(innerCache.SerializedCache)
{
_innerCache = innerCache;
}
Expand Down
92 changes: 90 additions & 2 deletions src/Neo/Persistence/DataCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,23 @@ public class Trackable(StorageKey key, StorageItem item, TrackState state)
public TrackState State { get; set; } = state;
}

private readonly Dictionary<StorageKey, Trackable> _dictionary = new();
private readonly HashSet<StorageKey> _changeSet = new();
private readonly Dictionary<StorageKey, Trackable> _dictionary = [];
private readonly HashSet<StorageKey> _changeSet = [];
private readonly SerializedCache _serializedCacheChanges = new();

/// <summary>
/// Serialized cache
/// </summary>
public SerializedCache SerializedCache { get; }

/// <summary>
/// Constructor
/// </summary>
/// <param name="serializedCache">Serialized cache</param>
protected DataCache(SerializedCache serializedCache)
{
SerializedCache = serializedCache;
}

/// <summary>
/// Reads a specified entry from the cache. If the entry is not in the cache, it will be automatically loaded from the underlying storage.
Expand Down Expand Up @@ -106,6 +121,64 @@ public void Add(StorageKey key, StorageItem value)
}
}

/// <summary>
/// Adds a new entry to the cache.
/// </summary>
/// <param name="key">The key of the entry.</param>
/// <param name="value">The data of the entry.</param>
/// <exception cref="ArgumentException">The entry has already been cached.</exception>
/// <remarks>Note: This method does not read the internal storage to check whether the record already exists.</remarks>
public void Add<T>(StorageKey key, T value) where T : IStorageCacheEntry
{
lock (_dictionary)
{
Add(key, value.GetStorageItem());
AddToCache(value);
}
}

/// <summary>
/// Adds a new entry to the cache.
/// </summary>
/// <param name="value">The data of the entry.</param>
/// <exception cref="ArgumentException">The entry has already been cached.</exception>
/// <remarks>Note: This method does not read the internal storage to check whether the record already exists.</remarks>
public void AddToCache<T>(T? value = default) where T : IStorageCacheEntry
{
AddToCache(typeof(T), value);
}

/// <summary>
/// Adds a new entry to the cache.
/// </summary>
/// <param name="type">Cache type</param>
/// <param name="value">The data of the entry.</param>
/// <exception cref="ArgumentException">The entry has already been cached.</exception>
/// <remarks>Note: This method does not read the internal storage to check whether the record already exists.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void AddToCache(Type type, IStorageCacheEntry? value)
{
_serializedCacheChanges.Set(type, value);
SerializedCache.Remove(type);
}

/// <summary>
/// Tries to get the entry from cache.
/// </summary>
/// <typeparam name="T">Cache type</typeparam>
/// <returns>The entry if found, null otherwise.</returns>
public T? GetFromCache<T>() where T : IStorageCacheEntry
{
var value = _serializedCacheChanges.Get<T>();

if (value != null)
{
return value;
}

return SerializedCache.Get<T>();
}

/// <summary>
/// Adds a new entry to the underlying storage.
/// </summary>
Expand Down Expand Up @@ -139,6 +212,8 @@ public virtual void Commit()
break;
}
}
SerializedCache.CopyFrom(_serializedCacheChanges);
_serializedCacheChanges.Clear();
_changeSet.Clear();
}
}
Expand Down Expand Up @@ -370,6 +445,19 @@ public bool Contains(StorageKey key)
}
}

/// <summary>
/// Reads a specified entry from the cache, and mark it as <see cref="TrackState.Changed"/>.
/// If the entry is not in the cache, it will be automatically loaded from the underlying storage.
/// </summary>
/// <param name="key">The key of the entry.</param>
/// <param name="serializedCache">Serialized cache</param>
public void GetAndChange<T>(StorageKey key, T serializedCache) where T : IStorageCacheEntry
{
var ret = GetAndChange(key, serializedCache.GetStorageItem);
ret!.FromReplica(serializedCache.GetStorageItem());
AddToCache(serializedCache);
}

/// <summary>
/// Reads a specified entry from the cache.
/// If the entry is not in the cache, it will be automatically loaded from the underlying storage.
Expand Down
5 changes: 5 additions & 0 deletions src/Neo/Persistence/IReadOnlyStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ namespace Neo.Persistence
/// </summary>
public interface IReadOnlyStore
{
/// <summary>
/// Serialized cache
/// </summary>
SerializedCache SerializedCache { get; }

/// <summary>
/// Seeks to the entry with the specified key.
/// </summary>
Expand Down
20 changes: 20 additions & 0 deletions src/Neo/Persistence/IStorageCacheEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (C) 2015-2025 The Neo Project.
//
// IStorageCacheEntry.cs file belongs to the neo project and is free
// software distributed under the MIT software license, see the
// accompanying file LICENSE in the main directory of the
// repository or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Neo.SmartContract;

namespace Neo.Persistence
{
public interface IStorageCacheEntry
{
public StorageItem GetStorageItem();
}
}
5 changes: 4 additions & 1 deletion src/Neo/Persistence/MemorySnapshot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@ internal class MemorySnapshot : ISnapshot
private readonly ImmutableDictionary<byte[], byte[]> _immutableData;
private readonly ConcurrentDictionary<byte[], byte[]?> _writeBatch;

public MemorySnapshot(ConcurrentDictionary<byte[], byte[]> innerData)
public SerializedCache SerializedCache { get; }

public MemorySnapshot(ConcurrentDictionary<byte[], byte[]> innerData, SerializedCache serializedCache)
{
_innerData = innerData;
SerializedCache = serializedCache;
_immutableData = innerData.ToImmutableDictionary(ByteArrayEqualityComparer.Default);
_writeBatch = new ConcurrentDictionary<byte[], byte[]?>(ByteArrayEqualityComparer.Default);
}
Expand Down
3 changes: 2 additions & 1 deletion src/Neo/Persistence/MemoryStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace Neo.Persistence
public class MemoryStore : IStore
{
private readonly ConcurrentDictionary<byte[], byte[]> _innerData = new(ByteArrayEqualityComparer.Default);
public SerializedCache SerializedCache { get; } = new();

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Delete(byte[] key)
Expand All @@ -38,7 +39,7 @@ public void Dispose() { }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ISnapshot GetSnapshot()
{
return new MemorySnapshot(_innerData);
return new MemorySnapshot(_innerData, SerializedCache);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
107 changes: 107 additions & 0 deletions src/Neo/Persistence/SerializedCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (C) 2015-2025 The Neo Project.
//
// SerializedCache.cs file belongs to the neo project and is free
// software distributed under the MIT software license, see the
// accompanying file LICENSE in the main directory of the
// repository or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

#nullable enable

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

namespace Neo.Persistence
{
public class SerializedCache
{
private readonly Dictionary<Type, IStorageCacheEntry> _cache = [];

/// <summary>
/// Get cached entry
/// </summary>
/// <typeparam name="T">Type</typeparam>
/// <returns>Cache</returns>
public T? Get<T>()
{
if (_cache.TryGetValue(typeof(T), out var ret))
{
return (T)ret;
}

return default;
}

/// <summary>
/// Set entry
/// </summary>
/// <typeparam name="T">Type</typeparam>
/// <param name="value">Value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Set<T>(T? value) where T : IStorageCacheEntry
{
Set(typeof(T), value);
}

/// <summary>
/// Set entry
/// </summary>
/// <param name="type">Type</param>
/// <param name="value">Value</param>
public void Set(Type type, IStorageCacheEntry? value)
{
if (value == null)
{
Remove(type);
}
else
{
lock (_cache)
{
_cache[type] = value;
}
}
}

/// <summary>
/// Remove entry
/// </summary>
/// <param name="type">Type</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Remove(Type type)
{
lock (_cache)
{
_cache.Remove(type, out _);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
lock (_cache)
{
_cache.Clear();
}
}

/// <summary>
/// Copy from
/// </summary>
/// <param name="value">Value</param>
public void CopyFrom(SerializedCache value)
{
lock (_cache) lock (value._cache)
{
foreach (var serialized in value._cache)
{
_cache[serialized.Key] = serialized.Value;
}
}
}
}
}
Loading