cfx_storage/impls/delta_mpt/cache/algorithm/
lru.rs1use super::{
6 CacheAccessResult, CacheAlgoDataAdapter, CacheAlgoDataTrait,
7 CacheAlgorithm, CacheIndexTrait, CacheStoreUtil, MyInto, PrimitiveNum,
8};
9use malloc_size_of_derive::MallocSizeOf as MallocSizeOfDerive;
10use std::{mem::replace, vec::Vec};
11
12#[derive(Clone, Copy, MallocSizeOfDerive)]
13pub struct LRUHandle<PosT: PrimitiveNum> {
14 prev_pos: PosT,
15}
16
17impl<PosT: PrimitiveNum> CacheAlgoDataTrait for LRUHandle<PosT> {}
18
19impl<PosT: PrimitiveNum> LRUHandle<PosT> {
20 pub const HEAD_POS: i32 = -2;
21 pub const NULL_POS: i32 = -1;
22
23 fn placement_new_most_recently_accessed(&mut self) {
27 self.set_most_recently_accessed();
28 }
29
30 fn placement_new_handle(&mut self, prev_pos: PosT) {
31 self.set_handle(prev_pos);
32 }
33
34 pub fn is_hit(&self) -> bool { self.prev_pos != PosT::from(Self::NULL_POS) }
35
36 fn set_evicted(&mut self) { self.prev_pos = PosT::from(Self::NULL_POS); }
37
38 pub fn is_most_recently_accessed(&self) -> bool {
39 self.prev_pos == PosT::from(Self::HEAD_POS)
40 }
41
42 pub fn set_most_recently_accessed(&mut self) {
43 self.prev_pos = PosT::from(Self::HEAD_POS);
44 }
45
46 fn get_prev_pos(&self) -> PosT { self.prev_pos }
47
48 fn set_handle(&mut self, prev_pos: PosT) { self.prev_pos = prev_pos; }
49}
50
51impl<PosT: PrimitiveNum> Default for LRUHandle<PosT> {
52 fn default() -> Self {
53 Self {
54 prev_pos: PosT::from(Self::NULL_POS),
55 }
56 }
57}
58
59#[derive(MallocSizeOfDerive)]
60struct DoubleLinkListNode<PosT: PrimitiveNum, CacheIndexT: CacheIndexTrait> {
61 next: PosT,
62 cache_index: CacheIndexT,
64}
65
66#[derive(MallocSizeOfDerive)]
67pub struct LRU<PosT: PrimitiveNum, CacheIndexT: CacheIndexTrait> {
68 size: PosT,
69 capacity: PosT,
70 head: PosT,
71 rear: PosT,
72 recent: Vec<DoubleLinkListNode<PosT, CacheIndexT>>,
73}
74
75impl<PosT: PrimitiveNum, CacheIndexT: CacheIndexTrait> LRU<PosT, CacheIndexT> {
76 pub fn new(capacity: PosT) -> Self {
77 if capacity == PosT::from(LRUHandle::<PosT>::NULL_POS) {
78 panic!("LRU: capacity {:?} is too large!", capacity)
79 }
80
81 Self {
82 size: PosT::from(0),
83 capacity,
84 head: PosT::from(LRUHandle::<PosT>::NULL_POS),
85 rear: PosT::from(LRUHandle::<PosT>::HEAD_POS),
86 recent: Vec::with_capacity(capacity.into()),
87 }
88 }
89}
90
91impl<PosT: PrimitiveNum, CacheIndexT: CacheIndexTrait> CacheAlgorithm
92 for LRU<PosT, CacheIndexT>
93{
94 type CacheAlgoData = LRUHandle<PosT>;
95 type CacheIndex = CacheIndexT;
96
97 fn access<
98 CacheStoreUtilT: CacheStoreUtil<
99 CacheAlgoData = LRUHandle<PosT>,
100 ElementIndex = CacheIndexT,
101 >,
102 >(
103 &mut self, cache_index: CacheIndexT,
104 cache_store_util: &mut CacheStoreUtilT,
105 ) -> CacheAccessResult<CacheIndexT> {
106 let lru_handle =
109 cache_store_util.get_most_recently_accessed(cache_index);
110 let is_hit = lru_handle.is_hit();
111
112 if is_hit {
113 if lru_handle.is_most_recently_accessed() {
114 } else {
116 let prev_pos = lru_handle.get_prev_pos();
117 let element_pos =
118 unsafe { self.get_unchecked_mut(prev_pos).next };
119 let old_head = self.head;
121
122 if element_pos == self.rear {
124 self.rear = prev_pos;
125 }
126 else {
128 unsafe {
129 let next = self.get_unchecked_mut(element_pos).next;
130 self.get_unchecked_mut(prev_pos).next = next;
131 CacheAlgoDataAdapter::new_mut(
132 cache_store_util,
133 self.get_unchecked_mut(next).cache_index,
134 )
135 .placement_new_handle(prev_pos);
136 }
137 }
138
139 self.head = element_pos;
141 unsafe {
142 self.get_unchecked_mut(element_pos).next = old_head;
143 CacheAlgoDataAdapter::new_mut_most_recently_accessed(
144 cache_store_util,
145 cache_index,
146 )
147 .placement_new_most_recently_accessed();
148 }
149
150 unsafe {
152 CacheAlgoDataAdapter::new_mut(
153 cache_store_util,
154 self.get_unchecked_mut(old_head).cache_index,
155 )
156 .placement_new_handle(element_pos);
157 }
158 }
159
160 CacheAccessResult::Hit
161 } else if self.size < self.capacity {
162 let old_head = self.head;
163
164 let new_head = self.size;
166 self.head = new_head;
167 unsafe {
168 CacheAlgoDataAdapter::new_mut_most_recently_accessed(
169 cache_store_util,
170 cache_index,
171 )
172 .set_most_recently_accessed();
173 }
174 self.recent.push(DoubleLinkListNode {
175 next: old_head,
176 cache_index,
177 });
178
179 if self.size == PosT::from(0) {
181 self.rear = PosT::from(0);
182 } else {
183 unsafe {
185 CacheAlgoDataAdapter::new_mut(
186 cache_store_util,
187 self.get_unchecked_mut(old_head).cache_index,
188 )
189 .placement_new_handle(new_head);
190 }
191 }
192
193 self.size += PosT::from(1);
194
195 CacheAccessResult::MissInsert
196 } else {
197 let new_head = self.rear;
198 let old_head = self.head;
199
200 CacheAlgoDataAdapter::get_mut(cache_store_util, unsafe {
202 self.get_unchecked_mut(old_head).cache_index
203 })
204 .set_handle(new_head);
205
206 let evicted_cache_index;
207 {
208 let mut rear_handle;
209 {
210 let rear_cache_index_mut = unsafe {
211 &mut self.get_unchecked_mut(new_head).cache_index
212 };
213 rear_handle = CacheAlgoDataAdapter::get_mut(
214 cache_store_util,
215 *rear_cache_index_mut,
216 );
217
218 evicted_cache_index =
220 replace(rear_cache_index_mut, cache_index);
221 }
222
223 self.rear = rear_handle.get_prev_pos();
225 rear_handle.set_evicted();
229 }
230
231 self.head = new_head;
233 unsafe {
234 self.get_unchecked_mut(new_head).next = old_head;
235 CacheAlgoDataAdapter::new_mut_most_recently_accessed(
236 cache_store_util,
237 cache_index,
238 )
239 .placement_new_most_recently_accessed();
240 }
241
242 CacheAccessResult::MissReplaced {
243 evicted: vec![evicted_cache_index],
244 evicted_keep_cache_algo_data: vec![],
245 }
246 }
247 }
248
249 fn delete<
250 CacheStoreUtilT: CacheStoreUtil<
251 CacheAlgoData = LRUHandle<PosT>,
252 ElementIndex = CacheIndexT,
253 >,
254 >(
255 &mut self, cache_index: CacheIndexT,
256 cache_store_util: &mut CacheStoreUtilT,
257 ) {
258 let lru_handle = cache_store_util.get(cache_index);
259
260 if lru_handle.is_hit() {
261 let pos_to_delete = self.get_lru_pos_for_handle(&lru_handle);
263 CacheAlgoDataAdapter::get_mut(cache_store_util, cache_index)
264 .set_evicted();
265 if pos_to_delete == self.rear {
266 self.rear = lru_handle.get_prev_pos();
267 if pos_to_delete == self.head {
268 self.head = PosT::from(LRUHandle::<PosT>::NULL_POS);
269 }
270 } else {
271 let next_pos;
272 unsafe {
273 next_pos = self.get_unchecked_mut(pos_to_delete).next;
274 if pos_to_delete != self.head {
275 let prev_pos = lru_handle.get_prev_pos();
276 self.get_unchecked_mut(prev_pos).next = next_pos;
277 } else {
278 self.head = next_pos;
279 }
280
281 CacheAlgoDataAdapter::get_mut(
282 cache_store_util,
283 self.get_unchecked_mut(next_pos).cache_index,
284 )
285 .set_handle(lru_handle.get_prev_pos());
286 }
287 }
288 self.size -= PosT::from(1);
290 let pos_to_move = self.size;
291 if pos_to_delete != pos_to_move {
292 let lru_handle = CacheAlgoDataAdapter::get(
293 cache_store_util,
294 unsafe { self.get_unchecked_mut(pos_to_move) }.cache_index,
295 );
296 if lru_handle.is_most_recently_accessed() {
297 self.head = pos_to_delete;
298 } else {
299 unsafe {
300 self.get_unchecked_mut(lru_handle.get_prev_pos())
301 .next = pos_to_delete;
302 }
303 }
304
305 if pos_to_move != self.rear {
306 unsafe {
307 let next = self.get_unchecked_mut(pos_to_move).next;
308 CacheAlgoDataAdapter::new_mut(
309 cache_store_util,
310 self.get_unchecked_mut(next).cache_index,
311 )
312 .placement_new_handle(pos_to_delete);
313 }
314 } else {
315 self.rear = pos_to_delete;
316 }
317
318 self.recent.swap_remove(pos_to_delete.into());
319 } else {
320 self.recent.remove(pos_to_delete.into());
321 }
322 }
323 }
324
325 fn log_usage(&self, prefix: &str) {
326 debug!(
327 "{}lru: capacity {}, size {}",
328 prefix, self.capacity, self.size
329 );
330 }
331}
332
333impl<PosT: PrimitiveNum, CacheIndexT: CacheIndexTrait> LRU<PosT, CacheIndexT> {
334 fn get_lru_pos_for_handle(&mut self, handle: &LRUHandle<PosT>) -> PosT {
335 let element_pos;
336 if handle.is_most_recently_accessed() {
337 element_pos = self.head;
338 } else {
339 element_pos =
340 unsafe { self.get_unchecked_mut(handle.get_prev_pos()).next };
341 }
342
343 element_pos
344 }
345
346 unsafe fn get_unchecked_mut(
347 &mut self, pos: PosT,
348 ) -> &mut DoubleLinkListNode<PosT, CacheIndexT> {
349 self.recent.get_unchecked_mut(MyInto::<usize>::into(pos))
350 }
351
352 pub unsafe fn get_cache_index_mut(
355 &mut self, handle: LRUHandle<PosT>,
356 ) -> &mut CacheIndexT {
357 let pos = self.get_lru_pos_for_handle(&handle);
358 return &mut self.get_unchecked_mut(pos).cache_index;
359 }
360
361 pub fn has_space(&self) -> bool { self.capacity != self.size }
362
363 pub fn is_full(&self) -> bool { self.capacity == self.size }
364
365 pub fn is_empty(&self) -> bool { PosT::from(0) == self.size }
366}