discv5: migrate to minilru (#741)

As it happens, the two share the exact same interface (even the test
suite removed in this PR passes) - `minilru` has an edge on efficiency
however, avoiding the doubly linked list node allocations etc
This commit is contained in:
Jacek Sieka 2024-10-03 12:34:38 +02:00 committed by GitHub
parent 5ce3c4557f
commit 84664b0fc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 9 additions and 180 deletions

View File

@ -19,7 +19,8 @@ requires "nim >= 1.6.0",
"confutils", "confutils",
"testutils", "testutils",
"unittest2", "unittest2",
"results" "results",
"minilru"
let nimc = getEnv("NIMC", "nim") # Which nim compiler to use let nimc = getEnv("NIMC", "nim") # Which nim compiler to use
let lang = getEnv("NIMLANG", "c") # Which backend (c/cpp/js) let lang = getEnv("NIMLANG", "c") # Which backend (c/cpp/js)

View File

@ -6,46 +6,9 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
# #
{.deprecated: "Use minilru directly".}
{.push raises: [].} {.push raises: [].}
import std/[tables, lists], results import minilru
export minilru
export results
type
LRUCache*[K, V] = object of RootObj
list: DoublyLinkedList[(K, V)] # Head is MRU k:v and tail is LRU k:v
table: Table[K, DoublyLinkedNode[(K, V)]] # DoublyLinkedNode is already ref
capacity: int
func init*[K, V](T: type LRUCache[K, V], capacity: int): LRUCache[K, V] =
LRUCache[K, V](capacity: capacity) # Table and list init is done default
func get*[K, V](lru: var LRUCache[K, V], key: K): Opt[V] =
let node = lru.table.getOrDefault(key, nil)
if node.isNil:
return Opt.none(V)
lru.list.remove(node)
lru.list.prepend(node)
return Opt.some(node.value[1])
func put*[K, V](lru: var LRUCache[K, V], key: K, value: V) =
let node = lru.table.getOrDefault(key, nil)
if not node.isNil:
lru.list.remove(node)
else:
if lru.table.len >= lru.capacity:
lru.table.del(lru.list.tail.value[0])
lru.list.remove(lru.list.tail)
lru.list.prepend((key, value))
lru.table[key] = lru.list.head
func del*[K, V](lru: var LRUCache[K, V], key: K) =
var node: DoublyLinkedNode[(K, V)]
if lru.table.pop(key, node):
lru.list.remove(node)
func len*[K, V](lru: LRUCache[K, V]): int =
lru.table.len

View File

@ -14,9 +14,9 @@
import import
std/net, std/net,
stint, stew/endians2, stint, stew/endians2,
node, lru ./node, minilru
export lru export minilru
const const
aesKeySize* = 128 div 8 aesKeySize* = 128 div 8
@ -28,7 +28,7 @@ type
AesKey* = array[aesKeySize, byte] AesKey* = array[aesKeySize, byte]
SessionKey* = array[keySize, byte] SessionKey* = array[keySize, byte]
SessionValue* = array[sizeof(AesKey) + sizeof(AesKey), byte] SessionValue* = array[sizeof(AesKey) + sizeof(AesKey), byte]
Sessions* = LRUCache[SessionKey, SessionValue] Sessions* = LruCache[SessionKey, SessionValue]
func makeKey(id: NodeId, address: Address): SessionKey = func makeKey(id: NodeId, address: Address): SessionKey =
var pos = 0 var pos = 0

View File

@ -3,7 +3,6 @@
import import
./test_enr, ./test_enr,
./test_hkdf, ./test_hkdf,
./test_lru,
./test_ip_vote, ./test_ip_vote,
./test_routing_table, ./test_routing_table,
./test_discoveryv5_encoding, ./test_discoveryv5_encoding,

View File

@ -1,134 +0,0 @@
{.used.}
import
std/options,
unittest2,
../../eth/p2p/discoveryv5/lru
suite "LRUCache":
const
capacity = 10
target = 4
test "LRU value gets removed":
var lru = LRUCache[int, int].init(capacity = capacity)
# Fully fill the LRU
for i in 0..<capacity:
lru.put(i, i) # new key, so new put
# Get value for each key
for i in 0..<capacity:
let val = lru.get(i)
check:
val.isSome()
val.get() == i
check lru.len() == capacity
# Add one new key
lru.put(capacity, 0)
# Oldest one should be gone
check:
lru.len() == capacity
lru.get(0).isNone()
lru.get(capacity).isSome()
test "LRU renew oldest by get":
var lru = LRUCache[int, int].init(capacity = capacity)
for i in 0..<capacity:
lru.put(i, i)
var val = lru.get(0)
check:
val.isSome
val.get() == 0
lru.put(capacity, 0)
val = lru.get(0)
check:
lru.len() == capacity
val.isSome()
val.get() == 0
test "LRU renew oldest by put":
var lru = LRUCache[int, int].init(capacity = capacity)
for i in 0..<capacity:
lru.put(i, i)
lru.put(0, 1)
check lru.len() == capacity
lru.put(capacity, 0)
let val = lru.get(0)
check:
lru.len() == capacity
val.isSome()
val.get() == 1
test "LRU renew by put":
var lru = LRUCache[int, int].init(capacity = capacity)
for i in 0..<capacity:
lru.put(i, i)
lru.put(target, 1)
check lru.len() == capacity
lru.put(capacity, 0)
let val = lru.get(target)
check:
lru.len() == capacity
val.isSome()
val.get() == 1
test "LRU renew by get":
var lru = LRUCache[int, int].init(capacity = capacity)
for i in 0..<capacity:
lru.put(i, i)
var val = lru.get(target)
check:
val.isSome
val.get() == target
lru.put(capacity, 0)
val = lru.get(target)
check:
lru.len() == capacity
val.isSome()
val.get() == target
test "LRU delete oldest and add":
var lru = LRUCache[int, int].init(capacity = capacity)
for i in 0..<capacity:
lru.put(i, i)
lru.del(0)
check lru.len == capacity - 1
lru.put(0, 1)
check lru.len == capacity
lru.put(capacity, 0)
let val = lru.get(0)
check:
lru.len() == capacity
val.isSome()
val.get() == 1
test "LRU delete not existing":
var lru = LRUCache[int, int].init(capacity = capacity)
for i in 0..<capacity:
lru.put(i, i)
lru.del(capacity)
check lru.len == capacity