diff --git a/src/Nethermind/Nethermind.AuRa.Test/Transactions/TxPermissionFilterTest.cs b/src/Nethermind/Nethermind.AuRa.Test/Transactions/TxPermissionFilterTest.cs index 6faa8b9061c..67d9aa92947 100644 --- a/src/Nethermind/Nethermind.AuRa.Test/Transactions/TxPermissionFilterTest.cs +++ b/src/Nethermind/Nethermind.AuRa.Test/Transactions/TxPermissionFilterTest.cs @@ -263,7 +263,7 @@ public class TestTxPermissionsBlockchain : TestContractBlockchain public PermissionBasedTxFilter PermissionBasedTxFilter { get; private set; } public PermissionBasedTxFilter.Cache TxPermissionFilterCache { get; private set; } - public ICache TransactionPermissionContractVersions { get; private set; } + public LruCache TransactionPermissionContractVersions { get; private set; } protected override BlockProcessor CreateBlockProcessor() { diff --git a/src/Nethermind/Nethermind.Blockchain/BlockTree.cs b/src/Nethermind/Nethermind.Blockchain/BlockTree.cs index f5500be41f9..30bf2249da0 100644 --- a/src/Nethermind/Nethermind.Blockchain/BlockTree.cs +++ b/src/Nethermind/Nethermind.Blockchain/BlockTree.cs @@ -38,11 +38,11 @@ public partial class BlockTree : IBlockTree private const int CacheSize = 64; - private readonly ICache - _blockCache = new LruCache(CacheSize, CacheSize, "blocks"); + private readonly LruCache + _blockCache = new(CacheSize, CacheSize, "blocks"); - private readonly ICache _headerCache = - new LruCache(CacheSize, CacheSize, "headers"); + private readonly LruCache _headerCache = + new(CacheSize, CacheSize, "headers"); private const int BestKnownSearchLimit = 256_000_000; @@ -53,7 +53,7 @@ public partial class BlockTree : IBlockTree private readonly IDb _blockInfoDb; private readonly IDb _metadataDb; - private readonly ICache _invalidBlocks = + private readonly LruCache _invalidBlocks = new LruCache(128, 128, "invalid blocks"); private readonly BlockDecoder _blockDecoder = new(); diff --git a/src/Nethermind/Nethermind.Blockchain/Receipts/PersistentReceiptStorage.cs b/src/Nethermind/Nethermind.Blockchain/Receipts/PersistentReceiptStorage.cs index aa372d529a9..d7ba9c511a5 100644 --- a/src/Nethermind/Nethermind.Blockchain/Receipts/PersistentReceiptStorage.cs +++ b/src/Nethermind/Nethermind.Blockchain/Receipts/PersistentReceiptStorage.cs @@ -29,7 +29,7 @@ public class PersistentReceiptStorage : IReceiptStorage private static readonly ReceiptStorageDecoder StorageDecoder = ReceiptStorageDecoder.Instance; private const int CacheSize = 64; - private readonly ICache _receiptsCache = new LruCache(CacheSize, CacheSize, "receipts"); + private readonly LruCache _receiptsCache = new (CacheSize, CacheSize, "receipts"); public PersistentReceiptStorage(IColumnsDb receiptsDb, ISpecProvider specProvider, IReceiptsRecovery receiptsRecovery) { diff --git a/src/Nethermind/Nethermind.Consensus.AuRa/AuRaContractGasLimitOverride.cs b/src/Nethermind/Nethermind.Consensus.AuRa/AuRaContractGasLimitOverride.cs index 72047e4c841..f457b6b3add 100644 --- a/src/Nethermind/Nethermind.Consensus.AuRa/AuRaContractGasLimitOverride.cs +++ b/src/Nethermind/Nethermind.Consensus.AuRa/AuRaContractGasLimitOverride.cs @@ -98,7 +98,7 @@ public class Cache { private const int MaxCacheSize = 10; - internal ICache GasLimitCache { get; } = new LruCache(MaxCacheSize, "BlockGasLimit"); + internal LruCache GasLimitCache { get; } = new LruCache(MaxCacheSize, "BlockGasLimit"); } public bool IsGasLimitValid(BlockHeader parentHeader, in long gasLimit, out long? expectedGasLimit) diff --git a/src/Nethermind/Nethermind.Consensus.AuRa/Contracts/VersionedContract.cs b/src/Nethermind/Nethermind.Consensus.AuRa/Contracts/VersionedContract.cs index 6a245975a7c..e8280261535 100644 --- a/src/Nethermind/Nethermind.Consensus.AuRa/Contracts/VersionedContract.cs +++ b/src/Nethermind/Nethermind.Consensus.AuRa/Contracts/VersionedContract.cs @@ -20,10 +20,10 @@ public abstract class VersionedContract : IActivatedAtBlock where T : IVersio private readonly IDictionary _versions; private readonly IVersionedContract _versionSelectorContract; - private readonly ICache _versionsCache; + private readonly LruCache _versionsCache; private readonly ILogger _logger; - protected VersionedContract(IDictionary versions, ICache cache, long activation, ILogManager logManager) + protected VersionedContract(IDictionary versions, LruCache cache, long activation, ILogManager logManager) { _versions = versions ?? throw new ArgumentNullException(nameof(versions)); _versionSelectorContract = versions.Values.Last(); diff --git a/src/Nethermind/Nethermind.Consensus.AuRa/Contracts/VersionedTransactionPermissionContract.cs b/src/Nethermind/Nethermind.Consensus.AuRa/Contracts/VersionedTransactionPermissionContract.cs index 698ea400ed0..4ccee21f90c 100644 --- a/src/Nethermind/Nethermind.Consensus.AuRa/Contracts/VersionedTransactionPermissionContract.cs +++ b/src/Nethermind/Nethermind.Consensus.AuRa/Contracts/VersionedTransactionPermissionContract.cs @@ -21,7 +21,7 @@ public class VersionedTransactionPermissionContract : VersionedContract cache, + LruCache cache, ILogManager logManager, ISpecProvider specProvider) : base( diff --git a/src/Nethermind/Nethermind.Consensus.AuRa/InitializationSteps/AuRaNethermindApi.cs b/src/Nethermind/Nethermind.Consensus.AuRa/InitializationSteps/AuRaNethermindApi.cs index 123179ec0a1..7c4ec8e909c 100644 --- a/src/Nethermind/Nethermind.Consensus.AuRa/InitializationSteps/AuRaNethermindApi.cs +++ b/src/Nethermind/Nethermind.Consensus.AuRa/InitializationSteps/AuRaNethermindApi.cs @@ -26,7 +26,7 @@ public class AuRaNethermindApi : NethermindApi public IValidatorStore? ValidatorStore { get; set; } - public ICache TransactionPermissionContractVersions { get; } + public LruCache TransactionPermissionContractVersions { get; } = new LruCache( PermissionBasedTxFilter.Cache.MaxCacheSize, nameof(TransactionPermissionContract)); diff --git a/src/Nethermind/Nethermind.Consensus.AuRa/Transactions/TxPermissionFilter.cs b/src/Nethermind/Nethermind.Consensus.AuRa/Transactions/TxPermissionFilter.cs index cf3360ebe67..d0f7daa66a3 100644 --- a/src/Nethermind/Nethermind.Consensus.AuRa/Transactions/TxPermissionFilter.cs +++ b/src/Nethermind/Nethermind.Consensus.AuRa/Transactions/TxPermissionFilter.cs @@ -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"); } } diff --git a/src/Nethermind/Nethermind.Consensus.Clique/SnapshotManager.cs b/src/Nethermind/Nethermind.Consensus.Clique/SnapshotManager.cs index 025e3845c1d..63fcad41260 100644 --- a/src/Nethermind/Nethermind.Consensus.Clique/SnapshotManager.cs +++ b/src/Nethermind/Nethermind.Consensus.Clique/SnapshotManager.cs @@ -25,17 +25,17 @@ public class SnapshotManager : ISnapshotManager private readonly IBlockTree _blockTree; private readonly ICliqueConfig _cliqueConfig; private readonly ILogger _logger; - private readonly ICache _signatures; + private readonly LruCache _signatures; private readonly IEthereumEcdsa _ecdsa; private IDb _blocksDb; private ulong _lastSignersCount = 0; - private ICache _snapshotCache = new LruCache(Clique.InMemorySnapshots, "clique snapshots"); + private LruCache _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(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)); diff --git a/src/Nethermind/Nethermind.Consensus.Ethash/EthashSealValidator.cs b/src/Nethermind/Nethermind.Consensus.Ethash/EthashSealValidator.cs index 9e8750c2dd2..f4f03bd666d 100644 --- a/src/Nethermind/Nethermind.Consensus.Ethash/EthashSealValidator.cs +++ b/src/Nethermind/Nethermind.Consensus.Ethash/EthashSealValidator.cs @@ -23,7 +23,7 @@ internal class EthashSealValidator : ISealValidator private readonly ITimestamper _timestamper; private readonly ILogger _logger; - private readonly ICache _sealCache = new LruCache(2048, 2048, "ethash seals"); + private readonly LruCache _sealCache = new (2048, 2048, "ethash seals"); private const int SealValidationIntervalConstantComponent = 1024; private const long AllowedFutureBlockTimeSeconds = 15; private int _sealValidationInterval = SealValidationIntervalConstantComponent; diff --git a/src/Nethermind/Nethermind.Core/Caching/LinkedListNode.cs b/src/Nethermind/Nethermind.Core/Caching/LinkedListNode.cs new file mode 100644 index 00000000000..4724702e8d4 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Caching/LinkedListNode.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; + +namespace Nethermind.Core.Caching; + +internal sealed class LinkedListNode +{ + internal LinkedListNode? Next; + internal LinkedListNode? Prev; + internal T Value; + + public LinkedListNode(T value) + { + Value = value; + } +} diff --git a/src/Nethermind/Nethermind.Core/Caching/LruCache.cs b/src/Nethermind/Nethermind.Core/Caching/LruCache.cs index b55f2500d20..f6cef9c07a0 100644 --- a/src/Nethermind/Nethermind.Core/Caching/LruCache.cs +++ b/src/Nethermind/Nethermind.Core/Caching/LruCache.cs @@ -3,170 +3,226 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using Nethermind.Core.Extensions; -namespace Nethermind.Core.Caching +namespace Nethermind.Core.Caching; + +public sealed class LruCache : ICache where TKey : notnull { - /// - /// The array based solution is preferred to lower the overall memory management overhead. The based approach is very costly. - /// - /// - /// https://stackoverflow.com/questions/754233/is-it-there-any-lru-implementation-of-idictionary - /// - public class LruCache : ICache where TKey : notnull - { - private readonly int _maxCapacity; - private readonly Dictionary> _cacheMap; - private readonly LinkedList _lruList; + private readonly int _maxCapacity; + private readonly Dictionary> _cacheMap; + private LinkedListNode? _first; - public LruCache(int maxCapacity, int startCapacity, string name) + public LruCache(int maxCapacity, int startCapacity, string name) + { + if (maxCapacity < 1) { - if (maxCapacity < 1) - { - throw new ArgumentOutOfRangeException(); - } - - _maxCapacity = maxCapacity; - _cacheMap = typeof(TKey) == typeof(byte[]) - ? new Dictionary>((IEqualityComparer)Bytes.EqualityComparer) - : new Dictionary>(startCapacity); // do not initialize it at the full capacity - _lruList = new LinkedList(); + throw new ArgumentOutOfRangeException(); } - public LruCache(int maxCapacity, string name) - : this(maxCapacity, 0, name) - { - } + _maxCapacity = maxCapacity; + _cacheMap = typeof(TKey) == typeof(byte[]) + ? new Dictionary>((IEqualityComparer)Bytes.EqualityComparer) + : new Dictionary>(startCapacity); // do not initialize it at the full capacity + } - [MethodImpl(MethodImplOptions.Synchronized)] - public void Clear() - { - _cacheMap.Clear(); - _lruList.Clear(); - } + public LruCache(int maxCapacity, string name) + : this(maxCapacity, 0, name) + { + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void Clear() + { + _first = null; + _cacheMap.Clear(); + } - [MethodImpl(MethodImplOptions.Synchronized)] - public TValue Get(TKey key) + [MethodImpl(MethodImplOptions.Synchronized)] + public TValue Get(TKey key) + { + if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) { - if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) - { - TValue value = node.Value.Value; - _lruList.Remove(node); - _lruList.AddLast(node); - return value; - } + TValue value = node.Value.Value; + MoveToLast(node); + return value; + } #pragma warning disable 8603 - // fixed C# 9 - return default; + // fixed C# 9 + return default; #pragma warning restore 8603 - } + } - [MethodImpl(MethodImplOptions.Synchronized)] - public bool TryGet(TKey key, out TValue value) + [MethodImpl(MethodImplOptions.Synchronized)] + public bool TryGet(TKey key, out TValue value) + { + if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) { - if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) - { - value = node.Value.Value; - _lruList.Remove(node); - _lruList.AddLast(node); - return true; - } + value = node.Value.Value; + MoveToLast(node); + return true; + } #pragma warning disable 8601 - // fixed C# 9 - value = default; + // fixed C# 9 + value = default; #pragma warning restore 8601 - return false; - } + return false; + } - [MethodImpl(MethodImplOptions.Synchronized)] - public bool Set(TKey key, TValue val) + [MethodImpl(MethodImplOptions.Synchronized)] + public bool Set(TKey key, TValue val) + { + if (val is null) { - if (val is null) - { - return Delete(key); - } + return Delete(key); + } - if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) + if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) + { + node.Value.Value = val; + MoveToLast(node); + return false; + } + else + { + if (_cacheMap.Count >= _maxCapacity) { - node.Value.Value = val; - _lruList.Remove(node); - _lruList.AddLast(node); - return false; + Replace(key, val); } else { - if (_cacheMap.Count >= _maxCapacity) - { - Replace(key, val); - } - else - { - LruCacheItem cacheItem = new LruCacheItem(key, val); - LinkedListNode newNode = new LinkedListNode(cacheItem); - _lruList.AddLast(newNode); - _cacheMap.Add(key, newNode); - } - - return true; + LinkedListNode newNode = new (new (key, val)); + AddLast(newNode); + _cacheMap.Add(key, newNode); } + + return true; } + } - [MethodImpl(MethodImplOptions.Synchronized)] - public bool Delete(TKey key) + [MethodImpl(MethodImplOptions.Synchronized)] + public bool Delete(TKey key) + { + if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) { - if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) - { - _lruList.Remove(node); - _cacheMap.Remove(key); - return true; - } + Remove(node); + _cacheMap.Remove(key); + return true; + } - return false; + return false; + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public bool Contains(TKey key) => _cacheMap.ContainsKey(key); + + [MethodImpl(MethodImplOptions.Synchronized)] + public IDictionary Clone() => _cacheMap.ToDictionary(i => i.Key, i => i.Value.Value.Value); + + private void Replace(TKey key, TValue value) + { + LinkedListNode? node = _first; + if (node is null) + { + throw new InvalidOperationException( + $"{nameof(LruCache)} called {nameof(Replace)} when empty."); } - [MethodImpl(MethodImplOptions.Synchronized)] - public bool Contains(TKey key) => _cacheMap.ContainsKey(key); + Remove(node); + _cacheMap.Remove(node!.Value.Key); - [MethodImpl(MethodImplOptions.Synchronized)] - public IDictionary Clone() => _lruList.ToDictionary(i => i.Key, i => i.Value); + node.Value = new (key, value); + AddLast(node); + _cacheMap.Add(key, node); + } + + private void MoveToLast(LinkedListNode node) + { + if (node.Next == node) + { + Debug.Assert(_cacheMap.Count == 1 && _first == node, "this should only be true for a list with only one node"); + // Do nothing only one node + } + else + { + Remove(node); + AddLast(node); + } + } - private void Replace(TKey key, TValue value) + private void AddLast(LinkedListNode node) + { + if (_first is null) + { + SetFirst(node); + } + else { - LinkedListNode? node = _lruList.First; - _lruList.RemoveFirst(); - _cacheMap.Remove(node!.Value.Key); - - node.Value.Value = value; - node.Value.Key = key; - _lruList.AddLast(node); - _cacheMap.Add(key, node); + InsertLast(node); } + } + + private void InsertLast(LinkedListNode newNode) + { + LinkedListNode first = _first!; + newNode.Next = first; + newNode.Prev = first.Prev; + first.Prev!.Next = newNode; + first.Prev = newNode; + } - private class LruCacheItem + private void SetFirst(LinkedListNode newNode) + { + Debug.Assert(_first is null && _cacheMap.Count == 0, "LinkedList must be empty when this method is called!"); + newNode.Next = newNode; + newNode.Prev = newNode; + _first = newNode; + } + + private void Remove(LinkedListNode node) + { + Debug.Assert(_first is not null, "This method shouldn't be called on empty list!"); + if (node.Next == node) + { + Debug.Assert(_cacheMap.Count == 1 && _first == node, "this should only be true for a list with only one node"); + _first = null; + } + else { - public LruCacheItem(TKey k, TValue v) + node.Next!.Prev = node.Prev; + node.Prev!.Next = node.Next; + if (_first == node) { - Key = k; - Value = v; + _first = node.Next; } + } + } - public TKey Key; - public TValue Value; + private struct LruCacheItem + { + public LruCacheItem(TKey k, TValue v) + { + Key = k; + Value = v; } - public long MemorySize => CalculateMemorySize(0, _cacheMap.Count); + public readonly TKey Key; + public TValue Value; + } - public static long CalculateMemorySize(int keyPlusValueSize, int currentItemsCount) - { - // it may actually be different if the initial capacity not equal to max (depending on the dictionary growth path) + public long MemorySize => CalculateMemorySize(0, _cacheMap.Count); - const int preInit = 48 /* LinkedList */ + 80 /* Dictionary */ + 24; - int postInit = 52 /* lazy init of two internal dictionary arrays + dictionary size times (entry size + int) */ + MemorySizes.FindNextPrime(currentItemsCount) * 28 + currentItemsCount * 80 /* LinkedListNode and CacheItem times items count */; - return MemorySizes.Align(preInit + postInit + keyPlusValueSize * currentItemsCount); - } + public static long CalculateMemorySize(int keyPlusValueSize, int currentItemsCount) + { + // it may actually be different if the initial capacity not equal to max (depending on the dictionary growth path) + + const int preInit = 48 /* LinkedList */ + 80 /* Dictionary */ + 24; + int postInit = 52 /* lazy init of two internal dictionary arrays + dictionary size times (entry size + int) */ + MemorySizes.FindNextPrime(currentItemsCount) * 28 + currentItemsCount * 80 /* LinkedListNode and CacheItem times items count */; + return MemorySizes.Align(preInit + postInit + keyPlusValueSize * currentItemsCount); } } diff --git a/src/Nethermind/Nethermind.Core/Caching/LruKeyCache.cs b/src/Nethermind/Nethermind.Core/Caching/LruKeyCache.cs index 1079e619ef1..39c7d9fa2cd 100644 --- a/src/Nethermind/Nethermind.Core/Caching/LruKeyCache.cs +++ b/src/Nethermind/Nethermind.Core/Caching/LruKeyCache.cs @@ -3,121 +3,175 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.CompilerServices; using Nethermind.Core.Extensions; -namespace Nethermind.Core.Caching +namespace Nethermind.Core.Caching; + +public sealed class LruKeyCache where TKey : notnull { - /// - /// https://stackoverflow.com/questions/754233/is-it-there-any-lru-implementation-of-idictionary - /// - public class LruKeyCache where TKey : notnull + private readonly int _maxCapacity; + private readonly string _name; + private readonly Dictionary> _cacheMap; + private LinkedListNode? _first; + + public LruKeyCache(int maxCapacity, int startCapacity, string name) { - private readonly int _maxCapacity; - private readonly string _name; - private readonly Dictionary> _cacheMap; - private readonly LinkedList _lruList; + _maxCapacity = maxCapacity; + _name = name ?? throw new ArgumentNullException(nameof(name)); + _cacheMap = typeof(TKey) == typeof(byte[]) + ? new Dictionary>((IEqualityComparer)Bytes.EqualityComparer) + : new Dictionary>(startCapacity); // do not initialize it at the full capacity + } - public LruKeyCache(int maxCapacity, int startCapacity, string name) - { - _maxCapacity = maxCapacity; - _name = name ?? throw new ArgumentNullException(nameof(name)); - _cacheMap = typeof(TKey) == typeof(byte[]) - ? new Dictionary>((IEqualityComparer)Bytes.EqualityComparer) - : new Dictionary>(startCapacity); // do not initialize it at the full capacity - _lruList = new LinkedList(); - } + public LruKeyCache(int maxCapacity, string name) + : this(maxCapacity, 0, name) + { + } - public LruKeyCache(int maxCapacity, string name) - : this(maxCapacity, 0, name) - { - } + [MethodImpl(MethodImplOptions.Synchronized)] + public void Clear() + { + _first = null; + _cacheMap.Clear(); + } - [MethodImpl(MethodImplOptions.Synchronized)] - public void Clear() + [MethodImpl(MethodImplOptions.Synchronized)] + public bool Get(TKey key) + { + if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) { - _cacheMap.Clear(); - _lruList.Clear(); + MoveToLast(node); + return true; } - [MethodImpl(MethodImplOptions.Synchronized)] - public bool Get(TKey key) - { - if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) - { - _lruList.Remove(node); - _lruList.AddLast(node); - return true; - } + return false; + } + [MethodImpl(MethodImplOptions.Synchronized)] + public bool Set(TKey key) + { + if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) + { + MoveToLast(node); return false; } - - [MethodImpl(MethodImplOptions.Synchronized)] - public bool Set(TKey key) + else { - if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) + if (_cacheMap.Count >= _maxCapacity) { - _lruList.Remove(node); - _lruList.AddLast(node); - return false; + Replace(key); } else { - if (_cacheMap.Count >= _maxCapacity) - { - Replace(key); - } - else - { - LinkedListNode newNode = new(key); - _lruList.AddLast(newNode); - _cacheMap.Add(key, newNode); - } - - return true; + LinkedListNode newNode = new(key); + AddLast(newNode); + _cacheMap.Add(key, newNode); } + + return true; } + } - [MethodImpl(MethodImplOptions.Synchronized)] - public void Delete(TKey key) + [MethodImpl(MethodImplOptions.Synchronized)] + public void Delete(TKey key) + { + if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) { - if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) - { - _lruList.Remove(node); - _cacheMap.Remove(key); - } + Remove(node); + _cacheMap.Remove(key); + } + } + + private void MoveToLast(LinkedListNode node) + { + if (node.Next == node) + { + Debug.Assert(_cacheMap.Count == 1 && _first == node, "this should only be true for a list with only one node"); + // Do nothing only one node + } + else + { + Remove(node); + AddLast(node); } + } - private void Replace(TKey key) + private void AddLast(LinkedListNode node) + { + if (_first is null) + { + SetFirst(node); + } + else { - // TODO: some potential null ref issue here? + InsertLast(node); + } + } + + private void InsertLast(LinkedListNode newNode) + { + LinkedListNode first = _first!; + newNode.Next = first; + newNode.Prev = first.Prev; + first.Prev!.Next = newNode; + first.Prev = newNode; + } + + private void SetFirst(LinkedListNode newNode) + { + Debug.Assert(_first is null && _cacheMap.Count == 0, "LinkedList must be empty when this method is called!"); + newNode.Next = newNode; + newNode.Prev = newNode; + _first = newNode; + } - LinkedListNode? node = _lruList.First; - if (node is null) + private void Remove(LinkedListNode node) + { + Debug.Assert(_first is not null, "This method shouldn't be called on empty list!"); + if (node.Next == node) + { + Debug.Assert(_cacheMap.Count == 1 && _first == node, "this should only be true for a list with only one node"); + _first = null; + } + else + { + node.Next!.Prev = node.Prev; + node.Prev!.Next = node.Next; + if (_first == node) { - throw new InvalidOperationException( - $"{nameof(LruKeyCache)} called {nameof(Replace)} when empty."); + _first = node.Next; } + } + } - _lruList.RemoveFirst(); - _cacheMap.Remove(node.Value); + private void Replace(TKey key) + { + // TODO: some potential null ref issue here? - node.Value = key; - _lruList.AddLast(node); - _cacheMap.Add(node.Value, node); + LinkedListNode? node = _first; + if (node is null) + { + throw new InvalidOperationException( + $"{nameof(LruKeyCache)} called {nameof(Replace)} when empty."); } - public long MemorySize => CalculateMemorySize(0, _cacheMap.Count); + _cacheMap.Remove(node.Value); + node.Value = key; + MoveToLast(node); + _cacheMap.Add(key, node); + } - // TODO: memory size on the KeyCache will be smaller because we do not create LruCacheItems - public static long CalculateMemorySize(int keyPlusValueSize, int currentItemsCount) - { - // it may actually be different if the initial capacity not equal to max (depending on the dictionary growth path) + public long MemorySize => CalculateMemorySize(0, _cacheMap.Count); - const int preInit = 48 /* LinkedList */ + 80 /* Dictionary */ + 24; - int postInit = 52 /* lazy init of two internal dictionary arrays + dictionary size times (entry size + int) */ + MemorySizes.FindNextPrime(currentItemsCount) * 28 + currentItemsCount * 80 /* LinkedListNode and CacheItem times items count */; - return MemorySizes.Align(preInit + postInit + keyPlusValueSize * currentItemsCount); - } + // TODO: memory size on the KeyCache will be smaller because we do not create LruCacheItems + public static long CalculateMemorySize(int keyPlusValueSize, int currentItemsCount) + { + // it may actually be different if the initial capacity not equal to max (depending on the dictionary growth path) + + const int preInit = 48 /* LinkedList */ + 80 /* Dictionary */ + 24; + int postInit = 52 /* lazy init of two internal dictionary arrays + dictionary size times (entry size + int) */ + MemorySizes.FindNextPrime(currentItemsCount) * 28 + currentItemsCount * 80 /* LinkedListNode and CacheItem times items count */; + return MemorySizes.Align(preInit + postInit + keyPlusValueSize * currentItemsCount); } } diff --git a/src/Nethermind/Nethermind.Core/Caching/MemCountingCache.cs b/src/Nethermind/Nethermind.Core/Caching/MemCountingCache.cs index 8a67fee3b32..2106726d01e 100644 --- a/src/Nethermind/Nethermind.Core/Caching/MemCountingCache.cs +++ b/src/Nethermind/Nethermind.Core/Caching/MemCountingCache.cs @@ -3,199 +3,250 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.CompilerServices; using Nethermind.Core.Crypto; -using Nethermind.Core.Extensions; -namespace Nethermind.Core.Caching +namespace Nethermind.Core.Caching; + +public sealed class MemCountingCache : ICache { - /// - /// https://stackoverflow.com/questions/754233/is-it-there-any-lru-implementation-of-idictionary - /// - public class MemCountingCache : ICache - { - private readonly int _maxCapacity; - private readonly Dictionary> _cacheMap; - private readonly LinkedList _lruList; + private readonly int _maxCapacity; + private readonly Dictionary> _cacheMap; + private LinkedListNode? _first; - private const int PreInitMemorySize = - 48 /* LinkedList */ + - 80 /* Dictionary */ + - MemorySizes.SmallObjectOverhead + - 8 /* sizeof(int) aligned */; + private const int PreInitMemorySize = + 48 /* LinkedList */ + + 80 /* Dictionary */ + + MemorySizes.SmallObjectOverhead + + 8 /* sizeof(int) aligned */; - private const int PostInitMemorySize = - 52 /* lazy loaded dictionary.Items */ + PreInitMemorySize; + private const int PostInitMemorySize = + 52 /* lazy loaded dictionary.Items */ + PreInitMemorySize; - private const int DictionaryItemSize = 28; - private int _currentDictionaryCapacity; + private const int DictionaryItemSize = 28; + private int _currentDictionaryCapacity; - public void Clear() - { - _cacheMap?.Clear(); - _lruList?.Clear(); - } + public void Clear() + { + _first = null; + _cacheMap?.Clear(); + } - public MemCountingCache(int maxCapacity, int startCapacity, string name) + public MemCountingCache(int maxCapacity, int startCapacity, string name) + { + _maxCapacity = maxCapacity; + _cacheMap = new (startCapacity); // do not initialize it at the full capacity + } + + public MemCountingCache(int maxCapacity, string name) + : this(maxCapacity, 0, name) + { + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public byte[]? Get(KeccakKey key) + { + if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) { - _maxCapacity = maxCapacity; - _cacheMap = typeof(Keccak) == typeof(byte[]) - ? new Dictionary>((IEqualityComparer)Bytes.EqualityComparer) - : new Dictionary>(startCapacity); // do not initialize it at the full capacity - _lruList = new LinkedList(); + byte[] value = node.Value.Value; + Remove(node); + AddLast(node); + return value; } - public MemCountingCache(int maxCapacity, string name) - : this(maxCapacity, 0, name) + return default; + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public bool TryGet(KeccakKey key, out byte[]? value) + { + if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) { + value = node.Value.Value; + MoveToLast(node); + return true; } - [MethodImpl(MethodImplOptions.Synchronized)] - public byte[]? Get(Keccak key) - { - if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) - { - byte[] value = node.Value.Value; - _lruList.Remove(node); - _lruList.AddLast(node); - return value; - } + value = default; + return false; + } - return default; + [MethodImpl(MethodImplOptions.Synchronized)] + public bool Set(KeccakKey key, byte[]? val) + { + if (val is null) + { + return Delete(key); } - [MethodImpl(MethodImplOptions.Synchronized)] - public bool TryGet(Keccak key, out byte[]? value) + if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) { - if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) - { - value = node.Value.Value; - _lruList.Remove(node); - _lruList.AddLast(node); - return true; - } - - value = default; + node.Value.Value = val; + MoveToLast(node); return false; } - - [MethodImpl(MethodImplOptions.Synchronized)] - public bool Set(Keccak key, byte[]? val) + else { - if (val is null) - { - return Delete(key); - } - - if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) + long cacheItemMemory = LruCacheItem.FindMemorySize(val); + int newCount = _cacheMap.Count + 1; + int capacityRemembered = _currentDictionaryCapacity; + long dictionaryNewMemory = CalculateDictionaryPartMemory(_currentDictionaryCapacity, newCount); + int initialGrowth = newCount == 1 ? PostInitMemorySize - PreInitMemorySize : 0; + long newMemorySize = + MemorySizes.Align( + MemorySize + + initialGrowth + + dictionaryNewMemory + + cacheItemMemory + ); + + if (newMemorySize <= _maxCapacity) { - node.Value.Value = val; - _lruList.Remove(node); - _lruList.AddLast(node); - return false; + MemorySize = newMemorySize; + LinkedListNode newNode = new(new(key, val)); + AddLast(newNode); + _cacheMap.Add(key, newNode); } else { - long cacheItemMemory = LruCacheItem.FindMemorySize(val); - int newCount = _lruList.Count + 1; - int capacityRemembered = _currentDictionaryCapacity; - long dictionaryNewMemory = CalculateDictionaryPartMemory(_currentDictionaryCapacity, newCount); - int initialGrowth = newCount == 1 ? PostInitMemorySize - PreInitMemorySize : 0; - long newMemorySize = - MemorySizes.Align( - MemorySize + - initialGrowth + - dictionaryNewMemory + - cacheItemMemory - ); - - if (newMemorySize <= _maxCapacity) - { - MemorySize = newMemorySize; - LruCacheItem cacheItem = new(key, val); - LinkedListNode newNode = new(cacheItem); - _lruList.AddLast(newNode); - _cacheMap.Add(key, newNode); - } - else - { - _currentDictionaryCapacity = capacityRemembered; - Replace(key, val); - } - - return true; + _currentDictionaryCapacity = capacityRemembered; + Replace(key, val); } + + return true; } + } - [MethodImpl(MethodImplOptions.Synchronized)] - public bool Delete(Keccak key) + [MethodImpl(MethodImplOptions.Synchronized)] + public bool Delete(KeccakKey key) + { + if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) { - if (_cacheMap.TryGetValue(key, out LinkedListNode? node)) - { - MemorySize -= node.Value.MemorySize; - _lruList.Remove(node); - _cacheMap.Remove(key); + MemorySize -= node.Value.MemorySize; + Remove(node); + _cacheMap.Remove(key); - return true; - } - - return false; + return true; } - [MethodImpl(MethodImplOptions.Synchronized)] - public bool Contains(Keccak key) => _cacheMap.ContainsKey(key); + return false; + } - private void Replace(Keccak key, byte[] value) - { - LinkedListNode? node = _lruList.First; + [MethodImpl(MethodImplOptions.Synchronized)] + public bool Contains(KeccakKey key) => _cacheMap.ContainsKey(key); - // ReSharper disable once PossibleNullReferenceException - MemorySize += MemorySizes.Align(value.Length) - MemorySizes.Align(node?.Value.Value.Length ?? 0); + private void Replace(KeccakKey key, byte[] value) + { + LinkedListNode node = _first!; + Debug.Assert(node != null); + + // ReSharper disable once PossibleNullReferenceException + MemorySize += MemorySizes.Align(value.Length) - MemorySizes.Align(node.Value.Value.Length); - _lruList.RemoveFirst(); - _cacheMap.Remove(node!.Value.Key); + _cacheMap.Remove(node!.Value.Key); - node.Value.Value = value; - node.Value.Key = key; - _lruList.AddLast(node); - _cacheMap.Add(key, node); + node.Value = new (key, value); + MoveToLast(node); + _cacheMap.Add(key, node); + } + + private void MoveToLast(LinkedListNode node) + { + if (node.Next == node) + { + Debug.Assert(_cacheMap.Count == 1 && _first == node, "this should only be true for a list with only one node"); + // Do nothing only one node + } + else + { + Remove(node); + AddLast(node); } + } - private class LruCacheItem + private void AddLast(LinkedListNode node) + { + if (_first is null) { - public LruCacheItem(Keccak k, byte[] v) - { - Key = k; - Value = v; - } + SetFirst(node); + } + else + { + InsertLast(node); + } + } - public Keccak Key; - public byte[] Value; + private void InsertLast(LinkedListNode newNode) + { + LinkedListNode first = _first!; + newNode.Next = first; + newNode.Prev = first.Prev; + first.Prev!.Next = newNode; + first.Prev = newNode; + } - public long MemorySize => FindMemorySize(Value); + private void SetFirst(LinkedListNode newNode) + { + Debug.Assert(_first is null && _cacheMap.Count == 0, "LinkedList must be empty when this method is called!"); + newNode.Next = newNode; + newNode.Prev = newNode; + _first = newNode; + } - public static long FindMemorySize(byte[] withValue) + private void Remove(LinkedListNode node) + { + Debug.Assert(_first is not null, "This method shouldn't be called on empty list!"); + if (node.Next == node) + { + Debug.Assert(_cacheMap.Count == 1 && _first == node, "this should only be true for a list with only one node"); + _first = null; + } + else + { + node.Next!.Prev = node.Prev; + node.Prev!.Next = node.Next; + if (_first == node) { - return MemorySizes.Align( - Keccak.MemorySize + - MemorySizes.ArrayOverhead + - withValue.Length); + _first = node.Next; } } + } - private long CalculateDictionaryPartMemory(int currentCapacity, int newCount) + private struct LruCacheItem + { + public LruCacheItem(KeccakKey k, byte[] v) { - int previousSize = _currentDictionaryCapacity * DictionaryItemSize; - int newSize = previousSize; - if (newCount > currentCapacity) - { - _currentDictionaryCapacity = MemorySizes.FindNextPrime(Math.Max(currentCapacity, 1) * 2); - newSize = _currentDictionaryCapacity * DictionaryItemSize; - } + Key = k; + Value = v; + } - return newSize - previousSize; + public readonly KeccakKey Key; + public byte[] Value; + + public long MemorySize => FindMemorySize(Value); + + public static long FindMemorySize(byte[] withValue) + { + return MemorySizes.Align( + Keccak.MemorySize + + MemorySizes.ArrayOverhead + + withValue.Length); } + } - public long MemorySize { get; private set; } = PreInitMemorySize; + private long CalculateDictionaryPartMemory(int currentCapacity, int newCount) + { + int previousSize = _currentDictionaryCapacity * DictionaryItemSize; + int newSize = previousSize; + if (newCount > currentCapacity) + { + _currentDictionaryCapacity = MemorySizes.FindNextPrime(Math.Max(currentCapacity, 1) * 2); + newSize = _currentDictionaryCapacity * DictionaryItemSize; + } + + return newSize - previousSize; } + + public long MemorySize { get; private set; } = PreInitMemorySize; } diff --git a/src/Nethermind/Nethermind.Core/Crypto/Keccak.cs b/src/Nethermind/Nethermind.Core/Crypto/Keccak.cs index fa245dbf57a..a090a215798 100644 --- a/src/Nethermind/Nethermind.Core/Crypto/Keccak.cs +++ b/src/Nethermind/Nethermind.Core/Crypto/Keccak.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Nethermind.Core.Extensions; @@ -46,6 +47,68 @@ private static ValueKeccak InternalCompute(byte[] input) } } + /// + /// Used as dictionary key with implicit conversion to devirtualize comparisions + /// + [DebuggerStepThrough] + public readonly struct KeccakKey : IEquatable, IComparable + { + public byte[] Bytes { get; } + + private KeccakKey(byte[] bytes) + { + Bytes = bytes; + } + + public static implicit operator KeccakKey(Keccak k) => new(k.Bytes); + + public int CompareTo(KeccakKey other) + { + return Extensions.Bytes.Comparer.Compare(Bytes, other.Bytes); + } + + public bool Equals(KeccakKey other) + { + if (ReferenceEquals(Bytes, other.Bytes)) + { + return true; + } + + if (Bytes is null) + { + return other.Bytes is null; + } + + if (other.Bytes is null) + { + return false; + } + + return Extensions.Bytes.AreEqual(Bytes, other.Bytes); + } + + public override bool Equals(object? obj) + { + return obj is KeccakKey && Equals((KeccakKey)obj); + } + + public override int GetHashCode() + { + if (Bytes is null) return 0; + + ref byte data = ref MemoryMarshal.GetArrayDataReference(Bytes); + long v0 = Unsafe.ReadUnaligned(ref data); + long v1 = Unsafe.ReadUnaligned(ref Unsafe.Add(ref data, sizeof(long))); + long v2 = Unsafe.ReadUnaligned(ref Unsafe.Add(ref data, sizeof(long) * 2)); + long v3 = Unsafe.ReadUnaligned(ref Unsafe.Add(ref data, sizeof(long) * 3)); + v0 ^= v1; + v2 ^= v3; + v0 ^= v2; + + return (int)v0 ^ (int)(v0 >> 32); + } + } + [DebuggerStepThrough] public class Keccak : IEquatable, IComparable { @@ -86,7 +149,7 @@ public class Keccak : IEquatable, IComparable public byte[] Bytes { get; } public Keccak(string hexString) - : this(Core.Extensions.Bytes.FromHexString(hexString)) { } + : this(Extensions.Bytes.FromHexString(hexString)) { } public Keccak(byte[] bytes) { @@ -154,12 +217,12 @@ public static Keccak Compute(string input) public bool Equals(Keccak? other) { - if (ReferenceEquals(other, null)) + if (other is null) { return false; } - return Core.Extensions.Bytes.AreEqual(other.Bytes, Bytes); + return Extensions.Bytes.AreEqual(other.Bytes, Bytes); } public int CompareTo(Keccak? other) @@ -174,22 +237,31 @@ public override bool Equals(object? obj) public override int GetHashCode() { - return MemoryMarshal.Read(Bytes); + ref byte data = ref MemoryMarshal.GetArrayDataReference(Bytes); + long v0 = Unsafe.ReadUnaligned(ref data); + long v1 = Unsafe.ReadUnaligned(ref Unsafe.Add(ref data, sizeof(long))); + long v2 = Unsafe.ReadUnaligned(ref Unsafe.Add(ref data, sizeof(long) * 2)); + long v3 = Unsafe.ReadUnaligned(ref Unsafe.Add(ref data, sizeof(long) * 3)); + v0 ^= v1; + v2 ^= v3; + v0 ^= v2; + + return (int)v0 ^ (int)(v0 >> 32); } public static bool operator ==(Keccak? a, Keccak? b) { - if (ReferenceEquals(a, null)) + if (a is null) { - return ReferenceEquals(b, null); + return b is null; } - if (ReferenceEquals(b, null)) + if (b is null) { return false; } - return Core.Extensions.Bytes.AreEqual(a.Bytes, b.Bytes); + return Extensions.Bytes.AreEqual(a.Bytes, b.Bytes); } public static bool operator !=(Keccak? a, Keccak? b) @@ -302,15 +374,15 @@ public static KeccakStructRef Compute(string input) public bool Equals(Keccak? other) { - if (ReferenceEquals(other, null)) + if (other is null) { return false; } - return Core.Extensions.Bytes.AreEqual(other.Bytes, Bytes); + return Extensions.Bytes.AreEqual(other.Bytes, Bytes); } - public bool Equals(KeccakStructRef other) => Core.Extensions.Bytes.AreEqual(other.Bytes, Bytes); + public bool Equals(KeccakStructRef other) => Extensions.Bytes.AreEqual(other.Bytes, Bytes); public override bool Equals(object? obj) { @@ -324,27 +396,27 @@ public override int GetHashCode() public static bool operator ==(KeccakStructRef a, Keccak? b) { - if (ReferenceEquals(b, null)) + if (b is null) { return false; } - return Core.Extensions.Bytes.AreEqual(a.Bytes, b.Bytes); + return Extensions.Bytes.AreEqual(a.Bytes, b.Bytes); } public static bool operator ==(Keccak? a, KeccakStructRef b) { - if (ReferenceEquals(a, null)) + if (a is null) { return false; } - return Core.Extensions.Bytes.AreEqual(a.Bytes, b.Bytes); + return Extensions.Bytes.AreEqual(a.Bytes, b.Bytes); } public static bool operator ==(KeccakStructRef a, KeccakStructRef b) { - return Core.Extensions.Bytes.AreEqual(a.Bytes, b.Bytes); + return Extensions.Bytes.AreEqual(a.Bytes, b.Bytes); } public static bool operator !=(KeccakStructRef a, Keccak b) diff --git a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs index e608a3c5cc0..6f9656c1de1 100644 --- a/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs +++ b/src/Nethermind/Nethermind.Core/Extensions/Bytes.cs @@ -43,6 +43,8 @@ public class BytesComparer : Comparer { public override int Compare(byte[]? x, byte[]? y) { + if (ReferenceEquals(x, y)) return 0; + if (x is null) { return y is null ? 0 : 1; @@ -77,6 +79,12 @@ public override int Compare(byte[]? x, byte[]? y) public int Compare(Span x, Span y) { + if (Unsafe.AreSame(ref MemoryMarshal.GetReference(x), ref MemoryMarshal.GetReference(y)) && + x.Length == y.Length) + { + return 0; + } + if (x.Length == 0) { return y.Length == 0 ? 0 : 1; @@ -131,6 +139,12 @@ public static int GetHighestSetBitIndex(this byte b) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool AreEqual(Span a1, Span a2) { + if (Unsafe.AreSame(ref MemoryMarshal.GetReference(a1), ref MemoryMarshal.GetReference(a2)) && + a1.Length == a2.Length) + { + return true; + } + // this works for nulls return a1.SequenceEqual(a2); } diff --git a/src/Nethermind/Nethermind.Db/Blooms/BloomStorage.cs b/src/Nethermind/Nethermind.Db/Blooms/BloomStorage.cs index 4a76d35a916..1c15914de1d 100644 --- a/src/Nethermind/Nethermind.Db/Blooms/BloomStorage.cs +++ b/src/Nethermind/Nethermind.Db/Blooms/BloomStorage.cs @@ -237,7 +237,7 @@ private class BloomStorageLevel : IDisposable private readonly IFileStore _fileStore; private readonly bool _migrationStatistics; - private readonly ICache _cache; + private readonly LruCache _cache; public BloomStorageLevel(IFileStore fileStore, in byte level, in int levelElementSize, in int levelMultiplier, bool migrationStatistics) { diff --git a/src/Nethermind/Nethermind.Db/DbExtensions.cs b/src/Nethermind/Nethermind.Db/DbExtensions.cs index 5e1d0aeabd3..20ffd1b0488 100644 --- a/src/Nethermind/Nethermind.Db/DbExtensions.cs +++ b/src/Nethermind/Nethermind.Db/DbExtensions.cs @@ -48,7 +48,7 @@ public static void Set(this IDb db, Keccak key, Span value) } } - public static KeyValuePair[] MultiGet(this IDb db, IEnumerable keys) + public static KeyValuePair[] MultiGet(this IDb db, IEnumerable keys) { var k = keys.Select(k => k.Bytes).ToArray(); return db[k]; @@ -123,7 +123,7 @@ public static void Delete(this IDb db, long key) db.Remove(key.ToBigEndianByteArrayWithoutLeadingZeros()); } - public static TItem? Get(this IDb db, Keccak key, IRlpStreamDecoder decoder, ICache cache = null, bool shouldCache = true) where TItem : class + public static TItem? Get(this IDb db, Keccak key, IRlpStreamDecoder decoder, LruCache cache = null, bool shouldCache = true) where TItem : class { TItem item = cache?.Get(key); if (item is null) @@ -166,7 +166,7 @@ public static void Delete(this IDb db, long key) return item; } - public static TItem? Get(this IDb db, long key, IRlpStreamDecoder? decoder, ICache? cache = null, bool shouldCache = true) where TItem : class + public static TItem? Get(this IDb db, long key, IRlpStreamDecoder? decoder, LruCache? cache = null, bool shouldCache = true) where TItem : class { TItem? item = cache?.Get(key); if (item is null) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 7b509e2eb7b..d386a056993 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -56,7 +56,7 @@ public class VirtualMachine : IVirtualMachine private readonly IBlockhashProvider _blockhashProvider; private readonly ISpecProvider _specProvider; - private static readonly ICache _codeCache = new LruCache(MemoryAllowance.CodeCacheSize, MemoryAllowance.CodeCacheSize, "VM bytecodes"); + private static readonly LruCache _codeCache = new (MemoryAllowance.CodeCacheSize, MemoryAllowance.CodeCacheSize, "VM bytecodes"); private readonly ILogger _logger; private IWorldState _worldState; private IStateProvider _state; diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs index 1835356a221..45cc9c575ae 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs @@ -42,7 +42,7 @@ public class NewPayloadHandler : IAsyncHandler? _latestBlocks; + private readonly LruCache? _latestBlocks; private readonly ProcessingOptions _defaultProcessingOptions; private readonly TimeSpan _timeout; @@ -76,7 +76,7 @@ public class NewPayloadHandler : IAsyncHandler 0) - _latestBlocks = new LruCache(cacheSize, 0, "LatestBlocks"); + _latestBlocks = new (cacheSize, 0, "LatestBlocks"); } /// diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V65/PooledTxsRequestor.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V65/PooledTxsRequestor.cs index 558aa3481e8..2356150bd12 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V65/PooledTxsRequestor.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V65/PooledTxsRequestor.cs @@ -15,7 +15,7 @@ namespace Nethermind.Network.P2P.Subprotocols.Eth.V65 public class PooledTxsRequestor : IPooledTxsRequestor { private readonly ITxPool _txPool; - private readonly LruKeyCache _pendingHashes = new(MemoryAllowance.TxHashCacheSize, + private readonly LruKeyCache _pendingHashes = new(MemoryAllowance.TxHashCacheSize, Math.Min(1024 * 16, MemoryAllowance.TxHashCacheSize), "pending tx hashes"); public PooledTxsRequestor(ITxPool txPool) diff --git a/src/Nethermind/Nethermind.State/Repositories/ChainLevelInfoRepository.cs b/src/Nethermind/Nethermind.State/Repositories/ChainLevelInfoRepository.cs index eef0f11c7cd..5cbfd8dd5e2 100644 --- a/src/Nethermind/Nethermind.State/Repositories/ChainLevelInfoRepository.cs +++ b/src/Nethermind/Nethermind.State/Repositories/ChainLevelInfoRepository.cs @@ -14,7 +14,7 @@ public class ChainLevelInfoRepository : IChainLevelInfoRepository private const int CacheSize = 64; private readonly object _writeLock = new(); - private readonly ICache _blockInfoCache = new LruCache(CacheSize, CacheSize, "chain level infos"); + private readonly LruCache _blockInfoCache = new LruCache(CacheSize, CacheSize, "chain level infos"); private readonly IDb _blockInfoDb; diff --git a/src/Nethermind/Nethermind.Synchronization/SyncServer.cs b/src/Nethermind/Nethermind.Synchronization/SyncServer.cs index 874599f6bd9..0f561331d10 100644 --- a/src/Nethermind/Nethermind.Synchronization/SyncServer.cs +++ b/src/Nethermind/Nethermind.Synchronization/SyncServer.cs @@ -49,7 +49,7 @@ public class SyncServer : ISyncServer private bool _gossipStopped = false; private readonly Random _broadcastRandomizer = new(); - private readonly LruCache _recentlySuggested = new(128, 128, "recently suggested blocks"); + private readonly LruCache _recentlySuggested = new(128, 128, "recently suggested blocks"); private readonly long _pivotNumber; private readonly Keccak _pivotHash; diff --git a/src/Nethermind/Nethermind.TxPool/HashCache.cs b/src/Nethermind/Nethermind.TxPool/HashCache.cs index 00ceb886479..a83c27099ba 100644 --- a/src/Nethermind/Nethermind.TxPool/HashCache.cs +++ b/src/Nethermind/Nethermind.TxPool/HashCache.cs @@ -23,12 +23,12 @@ internal class HashCache { private const int SafeCapacity = 1024 * 16; - private readonly LruKeyCache _longTermCache = new( + private readonly LruKeyCache _longTermCache = new( MemoryAllowance.TxHashCacheSize, Math.Min(SafeCapacity, MemoryAllowance.TxHashCacheSize), "long term hash cache"); - private readonly LruKeyCache _currentBlockCache = new( + private readonly LruKeyCache _currentBlockCache = new( SafeCapacity, Math.Min(SafeCapacity, MemoryAllowance.TxHashCacheSize), "current block hash cache");