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

x1.4 Faster LruCache #5251

Merged
merged 7 commits into from
Feb 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ public bool allows_transactions_before_transitions(long blockNumber)
VersionedTransactionPermissionContract transactionPermissionContract = new(AbiEncoder.Instance,
TestItem.AddressA,
5,
Substitute.For<IReadOnlyTxProcessorSource>(), new LruCache<Keccak, UInt256>(100, "TestCache"),
Substitute.For<IReadOnlyTxProcessorSource>(), new LruCache<KeccakKey, UInt256>(100, "TestCache"),
LimboLogs.Instance,
Substitute.For<ISpecProvider>());

Expand All @@ -263,7 +263,7 @@ public class TestTxPermissionsBlockchain : TestContractBlockchain
public PermissionBasedTxFilter PermissionBasedTxFilter { get; private set; }
public PermissionBasedTxFilter.Cache TxPermissionFilterCache { get; private set; }

public ICache<Keccak, UInt256> TransactionPermissionContractVersions { get; private set; }
public LruCache<KeccakKey, UInt256> TransactionPermissionContractVersions { get; private set; }

protected override BlockProcessor CreateBlockProcessor()
{
Expand All @@ -274,7 +274,7 @@ protected override BlockProcessor CreateBlockProcessor()
};

TransactionPermissionContractVersions =
new LruCache<Keccak, UInt256>(PermissionBasedTxFilter.Cache.MaxCacheSize, nameof(TransactionPermissionContract));
new LruCache<KeccakKey, UInt256>(PermissionBasedTxFilter.Cache.MaxCacheSize, nameof(TransactionPermissionContract));

IReadOnlyTrieStore trieStore = new TrieStore(DbProvider.StateDb, LimboLogs.Instance).AsReadOnly();
IReadOnlyTxProcessorSource txProcessorSource = new ReadOnlyTxProcessingEnv(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@ public void Setup()
public ICache<int, object> WithRecreation()
{
LruCache<int, object> cache = new LruCache<int, object>(Capacity, Capacity, string.Empty);
for (int j = 0; j < 1024 * 64; j++)
{
cache.Set(j, _object);
}
Fill(cache);

return cache;

void Fill(LruCache<int, object> cache)
{
for (int j = 0; j < 1024 * 64; j++)
{
cache.Set(j, _object);
}
}
}

[Benchmark]
Expand Down
13 changes: 9 additions & 4 deletions src/Nethermind/Nethermind.Benchmark/Core/LruCacheBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,17 @@ public class LruCacheBenchmarks
public LruCache<int, object> WithItems()
{
LruCache<int, object> cache = new LruCache<int, object>(16, StartCapacity, string.Empty);
for (int j = 0; j < ItemsCount; j++)
{
cache.Set(j, new object());
}
Fill(cache);

return cache;

void Fill(LruCache<int, object> cache)
{
for (int j = 0; j < ItemsCount; j++)
{
cache.Set(j, new object());
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,31 @@ public void InitKeccaks()
}
}

[Params(0, 2, 4, 8, 16, 32)]
public int StartCapacity { get; set; }
[Params(16, 32, 128)]
public int MaxCapacity { get; set; }

[Params(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28)]
[Params(1, 2, 8, 32, 64)]
public int ItemsCount { get; set; }

public Keccak[] Keys { get; set; } = new Keccak[28];
public Keccak[] Keys { get; set; } = new Keccak[64];

public byte[] Value { get; set; } = new byte[0];

[Benchmark]
public LruCache<Keccak, byte[]> WithItems()
public LruCache<KeccakKey, byte[]> WithItems()
{
LruCache<Keccak, byte[]> cache = new LruCache<Keccak, byte[]>(128, StartCapacity, String.Empty);
for (int j = 0; j < ItemsCount; j++)
{
cache.Set(Keys[j], Value);
}
LruCache<KeccakKey, byte[]> cache = new LruCache<KeccakKey, byte[]>(MaxCapacity, MaxCapacity, String.Empty);
Fill(cache);

return cache;

void Fill(LruCache<KeccakKey, byte[]> cache)
{
for (int j = 0; j < ItemsCount; j++)
{
cache.Set(Keys[j], Value);
}
}
}
}
}
12 changes: 6 additions & 6 deletions src/Nethermind/Nethermind.Blockchain/BlockTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ public partial class BlockTree : IBlockTree

private const int CacheSize = 64;

private readonly ICache<Keccak, Block>
_blockCache = new LruCache<Keccak, Block>(CacheSize, CacheSize, "blocks");
private readonly LruCache<KeccakKey, Block>
_blockCache = new(CacheSize, CacheSize, "blocks");

private readonly ICache<Keccak, BlockHeader> _headerCache =
new LruCache<Keccak, BlockHeader>(CacheSize, CacheSize, "headers");
private readonly LruCache<KeccakKey, BlockHeader> _headerCache =
new(CacheSize, CacheSize, "headers");

private const int BestKnownSearchLimit = 256_000_000;

Expand All @@ -53,8 +53,8 @@ private readonly ICache<Keccak, Block>
private readonly IDb _blockInfoDb;
private readonly IDb _metadataDb;

private readonly ICache<Keccak, Block> _invalidBlocks =
new LruCache<Keccak, Block>(128, 128, "invalid blocks");
private readonly LruCache<KeccakKey, Block> _invalidBlocks =
new(128, 128, "invalid blocks");

private readonly BlockDecoder _blockDecoder = new();
private readonly HeaderDecoder _headerDecoder = new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class PersistentReceiptStorage : IReceiptStorage
private static readonly ReceiptStorageDecoder StorageDecoder = ReceiptStorageDecoder.Instance;

private const int CacheSize = 64;
private readonly ICache<Keccak, TxReceipt[]> _receiptsCache = new LruCache<Keccak, TxReceipt[]>(CacheSize, CacheSize, "receipts");
private readonly LruCache<KeccakKey, TxReceipt[]> _receiptsCache = new(CacheSize, CacheSize, "receipts");

public PersistentReceiptStorage(IColumnsDb<ReceiptsColumns> receiptsDb, ISpecProvider specProvider, IReceiptsRecovery receiptsRecovery)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public class Cache
{
private const int MaxCacheSize = 10;

internal ICache<Keccak, long?> GasLimitCache { get; } = new LruCache<Keccak, long?>(MaxCacheSize, "BlockGasLimit");
internal LruCache<KeccakKey, long?> GasLimitCache { get; } = new(MaxCacheSize, "BlockGasLimit");
}

public bool IsGasLimitValid(BlockHeader parentHeader, in long gasLimit, out long? expectedGasLimit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ public abstract class VersionedContract<T> : IActivatedAtBlock where T : IVersio
private readonly IDictionary<UInt256, T> _versions;

private readonly IVersionedContract _versionSelectorContract;
private readonly ICache<Keccak, UInt256> _versionsCache;
private readonly LruCache<KeccakKey, UInt256> _versionsCache;
private readonly ILogger _logger;

protected VersionedContract(IDictionary<UInt256, T> versions, ICache<Keccak, UInt256> cache, long activation, ILogManager logManager)
protected VersionedContract(IDictionary<UInt256, T> versions, LruCache<KeccakKey, UInt256> cache, long activation, ILogManager logManager)
{
_versions = versions ?? throw new ArgumentNullException(nameof(versions));
_versionSelectorContract = versions.Values.Last();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public VersionedTransactionPermissionContract(IAbiEncoder abiEncoder,
Address contractAddress,
long activation,
IReadOnlyTxProcessorSource readOnlyTxProcessorSource,
ICache<Keccak, UInt256> cache,
LruCache<KeccakKey, UInt256> cache,
ILogManager logManager,
ISpecProvider specProvider)
: base(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public class AuRaNethermindApi : NethermindApi

public IValidatorStore? ValidatorStore { get; set; }

public ICache<Keccak, UInt256> TransactionPermissionContractVersions { get; }
= new LruCache<Keccak, UInt256>(
public LruCache<KeccakKey, UInt256> TransactionPermissionContractVersions { get; }
= new(
PermissionBasedTxFilter.Cache.MaxCacheSize,
nameof(TransactionPermissionContract));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public class Cache
{
public const int MaxCacheSize = 4096;

internal ICache<(Keccak ParentHash, Address Sender), (ITransactionPermissionContract.TxPermissions Permissions, bool ContractExists)> Permissions { get; } =
internal LruCache<(Keccak ParentHash, Address Sender), (ITransactionPermissionContract.TxPermissions Permissions, bool ContractExists)> Permissions { get; } =
new LruCache<(Keccak ParentHash, Address Sender), (ITransactionPermissionContract.TxPermissions Permissions, bool ContractExists)>(MaxCacheSize, "TxPermissions");
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/Nethermind/Nethermind.Consensus.Clique/SnapshotManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ public class SnapshotManager : ISnapshotManager
private readonly IBlockTree _blockTree;
private readonly ICliqueConfig _cliqueConfig;
private readonly ILogger _logger;
private readonly ICache<Keccak, Address> _signatures;
private readonly LruCache<KeccakKey, Address> _signatures;
private readonly IEthereumEcdsa _ecdsa;
private IDb _blocksDb;
private ulong _lastSignersCount = 0;
private ICache<Keccak, Snapshot> _snapshotCache = new LruCache<Keccak, Snapshot>(Clique.InMemorySnapshots, "clique snapshots");
private LruCache<KeccakKey, Snapshot> _snapshotCache = new(Clique.InMemorySnapshots, "clique snapshots");

public SnapshotManager(ICliqueConfig cliqueConfig, IDb blocksDb, IBlockTree blockTree, IEthereumEcdsa ecdsa, ILogManager logManager)
{
_logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager));
_cliqueConfig = cliqueConfig ?? throw new ArgumentNullException(nameof(cliqueConfig));
_signatures = new LruCache<Keccak, Address>(Clique.InMemorySignatures, Clique.InMemorySignatures, "signatures");
_signatures = new(Clique.InMemorySignatures, Clique.InMemorySignatures, "signatures");
_ecdsa = ecdsa ?? throw new ArgumentNullException(nameof(ecdsa));
_blocksDb = blocksDb ?? throw new ArgumentNullException(nameof(blocksDb));
_blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal class EthashSealValidator : ISealValidator
private readonly ITimestamper _timestamper;
private readonly ILogger _logger;

private readonly ICache<Keccak, bool> _sealCache = new LruCache<Keccak, bool>(2048, 2048, "ethash seals");
private readonly LruCache<KeccakKey, bool> _sealCache = new(2048, 2048, "ethash seals");
private const int SealValidationIntervalConstantComponent = 1024;
private const long AllowedFutureBlockTimeSeconds = 15;
private int _sealValidationInterval = SealValidationIntervalConstantComponent;
Expand Down
80 changes: 80 additions & 0 deletions src/Nethermind/Nethermind.Core/Caching/LinkedListNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace Nethermind.Core.Caching;

internal sealed class LinkedListNode<T>
{
internal LinkedListNode<T>? Next;
internal LinkedListNode<T>? Prev;
internal T Value;

public LinkedListNode(T value)
{
Value = value;
}

public static void MoveToMostRecent([NotNull] ref LinkedListNode<T>? leastRecentlyUsed, LinkedListNode<T> node)
{
if (node.Next == node)
{
Debug.Assert(leastRecentlyUsed == node, "this should only be true for a list with only one node");
// Do nothing only one node
}
else
{
Remove(ref leastRecentlyUsed, node);
AddMostRecent(ref leastRecentlyUsed, node);
}
}

public static void Remove(ref LinkedListNode<T>? leastRecentlyUsed, LinkedListNode<T> node)
{
Debug.Assert(leastRecentlyUsed is not null, "This method shouldn't be called on empty list!");
if (node.Next == node)
{
Debug.Assert(leastRecentlyUsed == node, "this should only be true for a list with only one node");
leastRecentlyUsed = null;
}
else
{
node.Next!.Prev = node.Prev;
node.Prev!.Next = node.Next;
if (ReferenceEquals(leastRecentlyUsed, node))
{
leastRecentlyUsed = node.Next;
}
}
}

public static void AddMostRecent([NotNull] ref LinkedListNode<T>? leastRecentlyUsed, LinkedListNode<T> node)
{
if (leastRecentlyUsed is null)
{
SetFirst(ref leastRecentlyUsed, node);
}
else
{
InsertMostRecent(leastRecentlyUsed, node);
}
}

private static void InsertMostRecent(LinkedListNode<T> leastRecentlyUsed, LinkedListNode<T> newNode)
{
newNode.Next = leastRecentlyUsed;
newNode.Prev = leastRecentlyUsed.Prev;
leastRecentlyUsed.Prev!.Next = newNode;
leastRecentlyUsed.Prev = newNode;
}

private static void SetFirst([NotNull] ref LinkedListNode<T>? leastRecentlyUsed, LinkedListNode<T> newNode)
{
newNode.Next = newNode;
newNode.Prev = newNode;
leastRecentlyUsed = newNode;
}
}
Loading