github.com/phuslu/lru@v1.0.16-0.20240421170520-46288a2fd47c/lru_shard.go (about) 1 // Copyright 2023-2024 Phus Lu. All rights reserved. 2 3 package lru 4 5 import ( 6 "sync" 7 "unsafe" 8 ) 9 10 // lrunode is a list of lru node, storing key-value pairs and related information 11 type lrunode[K comparable, V any] struct { 12 key K 13 next uint32 14 prev uint32 15 value V 16 } 17 18 type lrubucket struct { 19 hdib uint32 // bitfield { hash:24 dib:8 } 20 index uint32 // node index 21 } 22 23 // lrushard is a LRU partition contains a list and a hash table. 24 type lrushard[K comparable, V any] struct { 25 mu sync.Mutex 26 27 // the hash table, with 20% extra space than the list for fewer conflicts. 28 table_buckets []uint64 // []lrubucket 29 table_mask uint32 30 table_length uint32 31 table_hasher func(key unsafe.Pointer, seed uintptr) uintptr 32 table_seed uintptr 33 34 // the list of nodes 35 list []lrunode[K, V] 36 37 // stats 38 stats_getcalls uint64 39 stats_setcalls uint64 40 stats_misses uint64 41 42 // padding 43 _ [24]byte 44 } 45 46 func (s *lrushard[K, V]) Init(size uint32, hasher func(key unsafe.Pointer, seed uintptr) uintptr, seed uintptr) { 47 s.list_Init(size) 48 s.table_Init(size, hasher, seed) 49 } 50 51 func (s *lrushard[K, V]) Get(hash uint32, key K) (value V, ok bool) { 52 s.mu.Lock() 53 54 s.stats_getcalls++ 55 56 if index, exists := s.table_Get(hash, key); exists { 57 s.list_MoveToFront(index) 58 // value = s.list[index].value 59 value = (*lrunode[K, V])(unsafe.Add(unsafe.Pointer(&s.list[0]), uintptr(index)*unsafe.Sizeof(s.list[0]))).value 60 ok = true 61 } else { 62 s.stats_misses++ 63 } 64 65 s.mu.Unlock() 66 67 return 68 } 69 70 func (s *lrushard[K, V]) Peek(hash uint32, key K) (value V, ok bool) { 71 s.mu.Lock() 72 73 if index, exists := s.table_Get(hash, key); exists { 74 value = s.list[index].value 75 ok = true 76 } 77 78 s.mu.Unlock() 79 80 return 81 } 82 83 func (s *lrushard[K, V]) SetIfAbsent(hash uint32, key K, value V) (prev V, replaced bool) { 84 s.mu.Lock() 85 86 if index, exists := s.table_Get(hash, key); exists { 87 prev = s.list[index].value 88 s.mu.Unlock() 89 return 90 } 91 92 s.stats_setcalls++ 93 94 // index := s.list_Back() 95 // node := &s.list[index] 96 index := s.list[0].prev 97 node := (*lrunode[K, V])(unsafe.Add(unsafe.Pointer(&s.list[0]), uintptr(index)*unsafe.Sizeof(s.list[0]))) 98 evictedValue := node.value 99 s.table_Delete(uint32(s.table_hasher(noescape(unsafe.Pointer(&node.key)), s.table_seed)), node.key) 100 101 node.key = key 102 node.value = value 103 s.table_Set(hash, key, index) 104 s.list_MoveToFront(index) 105 prev = evictedValue 106 107 s.mu.Unlock() 108 return 109 } 110 111 func (s *lrushard[K, V]) Set(hash uint32, key K, value V) (prev V, replaced bool) { 112 s.mu.Lock() 113 114 s.stats_setcalls++ 115 116 if index, exists := s.table_Get(hash, key); exists { 117 // node := &s.list[index] 118 node := (*lrunode[K, V])(unsafe.Add(unsafe.Pointer(&s.list[0]), uintptr(index)*unsafe.Sizeof(s.list[0]))) 119 previousValue := node.value 120 s.list_MoveToFront(index) 121 node.value = value 122 prev = previousValue 123 replaced = true 124 125 s.mu.Unlock() 126 return 127 } 128 129 // index := s.list_Back() 130 // node := &s.list[index] 131 index := s.list[0].prev 132 node := (*lrunode[K, V])(unsafe.Add(unsafe.Pointer(&s.list[0]), uintptr(index)*unsafe.Sizeof(s.list[0]))) 133 evictedValue := node.value 134 if key != node.key { 135 s.table_Delete(uint32(s.table_hasher(noescape(unsafe.Pointer(&node.key)), s.table_seed)), node.key) 136 } 137 138 node.key = key 139 node.value = value 140 s.table_Set(hash, key, index) 141 s.list_MoveToFront(index) 142 prev = evictedValue 143 144 s.mu.Unlock() 145 return 146 } 147 148 func (s *lrushard[K, V]) Delete(hash uint32, key K) (v V) { 149 s.mu.Lock() 150 151 if index, exists := s.table_Get(hash, key); exists { 152 node := &s.list[index] 153 value := node.value 154 s.list_MoveToBack(index) 155 node.value = v 156 s.table_Delete(hash, key) 157 v = value 158 } 159 160 s.mu.Unlock() 161 162 return 163 } 164 165 func (s *lrushard[K, V]) Len() (n uint32) { 166 s.mu.Lock() 167 // inlining s.table_Len() 168 n = s.table_length 169 s.mu.Unlock() 170 171 return 172 } 173 174 func (s *lrushard[K, V]) AppendKeys(dst []K) []K { 175 s.mu.Lock() 176 for _, bucket := range s.table_buckets { 177 b := (*lrubucket)(unsafe.Pointer(&bucket)) 178 if b.index == 0 { 179 continue 180 } 181 dst = append(dst, s.list[b.index].key) 182 } 183 s.mu.Unlock() 184 185 return dst 186 }