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            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            // Update rear.
178            if self.size == PosT::from(0) {
179                self.rear = PosT::from(0);
180            } else {
181                // Update old head.
182                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            // Update old head.
199            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                    // Set cache_index for new head.
217                    evicted_cache_index =
218                        replace(rear_cache_index_mut, cache_index);
219                }
220
221                // Update rear.
222                self.rear = rear_handle.get_prev_pos();
223                // No need to set the next field of rear.
224
225                // Evict least recent used and
226                rear_handle.set_evicted();
227            }
228
229            // Insert new head.
230            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            // First delete this entry.
260            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            // Move the element at size to pos.
287            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    /// User may update the cache index.
351    /// unsafe because we didn't check for invalid handles.
352    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}