cfx_storage/impls/delta_mpt/cache/algorithm/
lru.rs

1// Copyright 2019 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5use 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    // LRU capacity must < max PosT - 1 (i.e. != NULL_POS) in order to reserve
24    // HEAD_POS and NULL_POS.
25
26    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    /// prev link is stored in LRUHandle<PosT>
63    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        // Not using get_mut because it borrows cache_store_util which conflicts
107        // with later CacheAlgoDataAdapter calls.
108        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                // Nothing to do, access the most recently visited element.
115            } else {
116                let prev_pos = lru_handle.get_prev_pos();
117                let element_pos =
118                    unsafe { self.get_unchecked_mut(prev_pos).next };
119                // Move the accessed element to head.
120                let old_head = self.head;
121
122                // Update rear.
123                if element_pos == self.rear {
124                    self.rear = prev_pos;
125                }
126                // Update prev_pos. There is no need to update if it's the rear.
127                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                // Set new head.
140                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                // Update old head.
151                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            // Set new head.
165            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            // Update rear.
180            if self.size == PosT::from(0) {
181                self.rear = PosT::from(0);
182            } else {
183                // Update old head.
184                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            // Update old head.
201            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                    // Set cache_index for new head.
219                    evicted_cache_index =
220                        replace(rear_cache_index_mut, cache_index);
221                }
222
223                // Update rear.
224                self.rear = rear_handle.get_prev_pos();
225                // No need to set the next field of rear.
226
227                // Evict least recent used and
228                rear_handle.set_evicted();
229            }
230
231            // Insert new head.
232            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            // First delete this entry.
262            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            // Move the element at size to pos.
289            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    /// User may update the cache index.
353    /// unsafe because we didn't check for invalid handles.
354    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}