Skip to content

Commit

Permalink
Feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
benaadams committed Feb 9, 2023
1 parent 3b1ff20 commit 8cf1141
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 205 deletions.
62 changes: 62 additions & 0 deletions src/Nethermind/Nethermind.Core/Caching/LinkedListNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// SPDX-License-Identifier: LGPL-3.0-only

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

namespace Nethermind.Core.Caching;

Expand All @@ -15,4 +17,64 @@ 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;
}
}
79 changes: 12 additions & 67 deletions src/Nethermind/Nethermind.Core/Caching/LruCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using Nethermind.Core.Extensions;
Expand Down Expand Up @@ -47,7 +48,7 @@ public TValue Get(TKey key)
if (_cacheMap.TryGetValue(key, out LinkedListNode<LruCacheItem>? node))
{
TValue value = node.Value.Value;
MoveToMostRecent(node);
LinkedListNode<LruCacheItem>.MoveToMostRecent(ref _leastRecentlyUsed, node);
return value;
}

Expand All @@ -63,7 +64,7 @@ public bool TryGet(TKey key, out TValue value)
if (_cacheMap.TryGetValue(key, out LinkedListNode<LruCacheItem>? node))
{
value = node.Value.Value;
MoveToMostRecent(node);
LinkedListNode<LruCacheItem>.MoveToMostRecent(ref _leastRecentlyUsed, node);
return true;
}

Expand All @@ -85,7 +86,7 @@ public bool Set(TKey key, TValue val)
if (_cacheMap.TryGetValue(key, out LinkedListNode<LruCacheItem>? node))
{
node.Value.Value = val;
MoveToMostRecent(node);
LinkedListNode<LruCacheItem>.MoveToMostRecent(ref _leastRecentlyUsed, node);
return false;
}
else
Expand All @@ -97,7 +98,7 @@ public bool Set(TKey key, TValue val)
else
{
LinkedListNode<LruCacheItem> newNode = new(new(key, val));
AddMostRecent(newNode);
LinkedListNode<LruCacheItem>.AddMostRecent(ref _leastRecentlyUsed, newNode);
_cacheMap.Add(key, newNode);
}

Expand All @@ -110,7 +111,7 @@ public bool Delete(TKey key)
{
if (_cacheMap.TryGetValue(key, out LinkedListNode<LruCacheItem>? node))
{
Remove(node);
LinkedListNode<LruCacheItem>.Remove(ref _leastRecentlyUsed, node);
_cacheMap.Remove(key);
return true;
}
Expand All @@ -129,76 +130,20 @@ private void Replace(TKey key, TValue value)
LinkedListNode<LruCacheItem>? node = _leastRecentlyUsed;
if (node is null)
{
throw new InvalidOperationException(
$"{nameof(LruCache<TKey, TValue>)} called {nameof(Replace)} when empty.");
ThrowInvalidOperationException();
}

_cacheMap.Remove(node!.Value.Key);

node.Value = new(key, value);
MoveToMostRecent(node);
LinkedListNode<LruCacheItem>.MoveToMostRecent(ref _leastRecentlyUsed, node);
_cacheMap.Add(key, node);
}

private void MoveToMostRecent(LinkedListNode<LruCacheItem> node)
{
if (node.Next == node)
[DoesNotReturn]
static void ThrowInvalidOperationException()
{
Debug.Assert(_cacheMap.Count == 1 && _leastRecentlyUsed == node, "this should only be true for a list with only one node");
// Do nothing only one node
}
else
{
Remove(node);
AddMostRecent(node);
}
}

private void AddMostRecent(LinkedListNode<LruCacheItem> node)
{
if (_leastRecentlyUsed is null)
{
SetFirst(node);
}
else
{
InsertMostRecent(node);
}
}

private void InsertMostRecent(LinkedListNode<LruCacheItem> newNode)
{
LinkedListNode<LruCacheItem> first = _leastRecentlyUsed!;
newNode.Next = first;
newNode.Prev = first.Prev;
first.Prev!.Next = newNode;
first.Prev = newNode;
}

private void SetFirst(LinkedListNode<LruCacheItem> newNode)
{
Debug.Assert(_leastRecentlyUsed is null && _cacheMap.Count == 0, "LinkedList must be empty when this method is called!");
newNode.Next = newNode;
newNode.Prev = newNode;
_leastRecentlyUsed = newNode;
}

private void Remove(LinkedListNode<LruCacheItem> node)
{
Debug.Assert(_leastRecentlyUsed is not null, "This method shouldn't be called on empty list!");
if (node.Next == node)
{
Debug.Assert(_cacheMap.Count == 1 && _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 (_leastRecentlyUsed == node)
{
_leastRecentlyUsed = node.Next;
}
throw new InvalidOperationException(
$"{nameof(LruCache<TKey, TValue>)} called {nameof(Replace)} when empty.");
}
}

Expand Down
85 changes: 14 additions & 71 deletions src/Nethermind/Nethermind.Core/Caching/LruKeyCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Nethermind.Core.Extensions;

Expand Down Expand Up @@ -42,7 +43,7 @@ public bool Get(TKey key)
{
if (_cacheMap.TryGetValue(key, out LinkedListNode<TKey>? node))
{
MoveToMostRecent(node);
LinkedListNode<TKey>.MoveToMostRecent(ref _leastRecentlyUsed, node);
return true;
}

Expand All @@ -54,7 +55,7 @@ public bool Set(TKey key)
{
if (_cacheMap.TryGetValue(key, out LinkedListNode<TKey>? node))
{
MoveToMostRecent(node);
LinkedListNode<TKey>.MoveToMostRecent(ref _leastRecentlyUsed, node);
return false;
}
else
Expand All @@ -66,7 +67,7 @@ public bool Set(TKey key)
else
{
LinkedListNode<TKey> newNode = new(key);
AddMostRecent(newNode);
LinkedListNode<TKey>.AddMostRecent(ref _leastRecentlyUsed, newNode);
_cacheMap.Add(key, newNode);
}

Expand All @@ -79,88 +80,30 @@ public void Delete(TKey key)
{
if (_cacheMap.TryGetValue(key, out LinkedListNode<TKey>? node))
{
Remove(node);
LinkedListNode<TKey>.Remove(ref _leastRecentlyUsed, node);
_cacheMap.Remove(key);
}
}

private void MoveToMostRecent(LinkedListNode<TKey> node)
{
if (node.Next == node)
{
Debug.Assert(_cacheMap.Count == 1 && _leastRecentlyUsed == node, "this should only be true for a list with only one node");
// Do nothing only one node
}
else
{
Remove(node);
AddMostRecent(node);
}
}

private void AddMostRecent(LinkedListNode<TKey> node)
{
if (_leastRecentlyUsed is null)
{
SetFirst(node);
}
else
{
InsertMostRecent(node);
}
}

private void InsertMostRecent(LinkedListNode<TKey> newNode)
{
LinkedListNode<TKey> first = _leastRecentlyUsed!;
newNode.Next = first;
newNode.Prev = first.Prev;
first.Prev!.Next = newNode;
first.Prev = newNode;
}

private void SetFirst(LinkedListNode<TKey> newNode)
{
Debug.Assert(_leastRecentlyUsed is null && _cacheMap.Count == 0, "LinkedList must be empty when this method is called!");
newNode.Next = newNode;
newNode.Prev = newNode;
_leastRecentlyUsed = newNode;
}

private void Remove(LinkedListNode<TKey> node)
{
Debug.Assert(_leastRecentlyUsed is not null, "This method shouldn't be called on empty list!");
if (node.Next == node)
{
Debug.Assert(_cacheMap.Count == 1 && _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 (_leastRecentlyUsed == node)
{
_leastRecentlyUsed = node.Next;
}
}
}

private void Replace(TKey key)
{
// TODO: some potential null ref issue here?

LinkedListNode<TKey>? node = _leastRecentlyUsed;
if (node is null)
{
throw new InvalidOperationException(
$"{nameof(LruKeyCache<TKey>)} called {nameof(Replace)} when empty.");
ThrowInvalidOperation();
}

_cacheMap.Remove(node.Value);
node.Value = key;
MoveToMostRecent(node);
LinkedListNode<TKey>.MoveToMostRecent(ref _leastRecentlyUsed, node);
_cacheMap.Add(key, node);

[DoesNotReturn]
static void ThrowInvalidOperation()
{
throw new InvalidOperationException(
$"{nameof(LruKeyCache<TKey>)} called {nameof(Replace)} when empty.");
}
}

public long MemorySize => CalculateMemorySize(0, _cacheMap.Count);
Expand Down
Loading

0 comments on commit 8cf1141

Please sign in to comment.