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 CacheAlgoDataAdapter::new_mut_most_recently_accessed(
168 cache_store_util,
169 cache_index,
170 )
171 .set_most_recently_accessed();
172 self.recent.push(DoubleLinkListNode {
173 next: old_head,
174 cache_index,
175 });
176
177 if self.size == PosT::from(0) {
179 self.rear = PosT::from(0);
180 } else {
181 unsafe {
183 CacheAlgoDataAdapter::new_mut(
184 cache_store_util,
185 self.get_unchecked_mut(old_head).cache_index,
186 )
187 .placement_new_handle(new_head);
188 }
189 }
190
191 self.size += PosT::from(1);
192
193 CacheAccessResult::MissInsert
194 } else {
195 let new_head = self.rear;
196 let old_head = self.head;
197
198 CacheAlgoDataAdapter::get_mut(cache_store_util, unsafe {
200 self.get_unchecked_mut(old_head).cache_index
201 })
202 .set_handle(new_head);
203
204 let evicted_cache_index;
205 {
206 let mut rear_handle;
207 {
208 let rear_cache_index_mut = unsafe {
209 &mut self.get_unchecked_mut(new_head).cache_index
210 };
211 rear_handle = CacheAlgoDataAdapter::get_mut(
212 cache_store_util,
213 *rear_cache_index_mut,
214 );
215
216 evicted_cache_index =
218 replace(rear_cache_index_mut, cache_index);
219 }
220
221 self.rear = rear_handle.get_prev_pos();
223 rear_handle.set_evicted();
227 }
228
229 self.head = new_head;
231 unsafe {
232 self.get_unchecked_mut(new_head).next = old_head;
233 CacheAlgoDataAdapter::new_mut_most_recently_accessed(
234 cache_store_util,
235 cache_index,
236 )
237 .placement_new_most_recently_accessed();
238 }
239
240 CacheAccessResult::MissReplaced {
241 evicted: vec![evicted_cache_index],
242 evicted_keep_cache_algo_data: vec![],
243 }
244 }
245 }
246
247 fn delete<
248 CacheStoreUtilT: CacheStoreUtil<
249 CacheAlgoData = LRUHandle<PosT>,
250 ElementIndex = CacheIndexT,
251 >,
252 >(
253 &mut self, cache_index: CacheIndexT,
254 cache_store_util: &mut CacheStoreUtilT,
255 ) {
256 let lru_handle = cache_store_util.get(cache_index);
257
258 if lru_handle.is_hit() {
259 let pos_to_delete = self.get_lru_pos_for_handle(&lru_handle);
261 CacheAlgoDataAdapter::get_mut(cache_store_util, cache_index)
262 .set_evicted();
263 if pos_to_delete == self.rear {
264 self.rear = lru_handle.get_prev_pos();
265 if pos_to_delete == self.head {
266 self.head = PosT::from(LRUHandle::<PosT>::NULL_POS);
267 }
268 } else {
269 let next_pos;
270 unsafe {
271 next_pos = self.get_unchecked_mut(pos_to_delete).next;
272 if pos_to_delete != self.head {
273 let prev_pos = lru_handle.get_prev_pos();
274 self.get_unchecked_mut(prev_pos).next = next_pos;
275 } else {
276 self.head = next_pos;
277 }
278
279 CacheAlgoDataAdapter::get_mut(
280 cache_store_util,
281 self.get_unchecked_mut(next_pos).cache_index,
282 )
283 .set_handle(lru_handle.get_prev_pos());
284 }
285 }
286 self.size -= PosT::from(1);
288 let pos_to_move = self.size;
289 if pos_to_delete != pos_to_move {
290 let lru_handle = CacheAlgoDataAdapter::get(
291 cache_store_util,
292 unsafe { self.get_unchecked_mut(pos_to_move) }.cache_index,
293 );
294 if lru_handle.is_most_recently_accessed() {
295 self.head = pos_to_delete;
296 } else {
297 unsafe {
298 self.get_unchecked_mut(lru_handle.get_prev_pos())
299 .next = pos_to_delete;
300 }
301 }
302
303 if pos_to_move != self.rear {
304 unsafe {
305 let next = self.get_unchecked_mut(pos_to_move).next;
306 CacheAlgoDataAdapter::new_mut(
307 cache_store_util,
308 self.get_unchecked_mut(next).cache_index,
309 )
310 .placement_new_handle(pos_to_delete);
311 }
312 } else {
313 self.rear = pos_to_delete;
314 }
315
316 self.recent.swap_remove(pos_to_delete.into());
317 } else {
318 self.recent.remove(pos_to_delete.into());
319 }
320 }
321 }
322
323 fn log_usage(&self, prefix: &str) {
324 debug!(
325 "{}lru: capacity {}, size {}",
326 prefix, self.capacity, self.size
327 );
328 }
329}
330
331impl<PosT: PrimitiveNum, CacheIndexT: CacheIndexTrait> LRU<PosT, CacheIndexT> {
332 fn get_lru_pos_for_handle(&mut self, handle: &LRUHandle<PosT>) -> PosT {
333 let element_pos;
334 if handle.is_most_recently_accessed() {
335 element_pos = self.head;
336 } else {
337 element_pos =
338 unsafe { self.get_unchecked_mut(handle.get_prev_pos()).next };
339 }
340
341 element_pos
342 }
343
344 unsafe fn get_unchecked_mut(
345 &mut self, pos: PosT,
346 ) -> &mut DoubleLinkListNode<PosT, CacheIndexT> {
347 self.recent.get_unchecked_mut(MyInto::<usize>::into(pos))
348 }
349
350 pub unsafe fn get_cache_index_mut(
353 &mut self, handle: LRUHandle<PosT>,
354 ) -> &mut CacheIndexT {
355 let pos = self.get_lru_pos_for_handle(&handle);
356 return &mut self.get_unchecked_mut(pos).cache_index;
357 }
358
359 pub fn has_space(&self) -> bool { self.capacity != self.size }
360
361 pub fn is_full(&self) -> bool { self.capacity == self.size }
362
363 pub fn is_empty(&self) -> bool { PosT::from(0) == self.size }
364}