Skip to content

Commit 2bc0671

Browse files
committed
feat(cache): a variant of sieve, with lazy op
Signed-off-by: Chojan Shang <psiace@apache.org>
1 parent ed0c3f8 commit 2bc0671

File tree

2 files changed

+64
-14
lines changed

2 files changed

+64
-14
lines changed

src/common/cache/src/cache/lru.rs

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ pub struct LruCache<
7575
M: CountableMeter<K, V> = Count,
7676
> {
7777
map: LinkedHashMap<K, V, S>,
78+
visited: LinkedHashMap<K, bool>,
7879
current_measure: M::Measure,
7980
max_capacity: u64,
8081
meter: M,
@@ -92,6 +93,7 @@ impl<K: Eq + Hash, V> LruCache<K, V> {
9293
pub fn new(capacity: u64) -> Self {
9394
LruCache {
9495
map: LinkedHashMap::new(),
96+
visited: LinkedHashMap::new(),
9597
current_measure: (),
9698
max_capacity: capacity,
9799
meter: Count,
@@ -136,6 +138,7 @@ impl<K: Eq + Hash, V, M: CountableMeter<K, V>> LruCache<K, V, DefaultHashBuilder
136138
pub fn with_meter(capacity: u64, meter: M) -> LruCache<K, V, DefaultHashBuilder, M> {
137139
LruCache {
138140
map: LinkedHashMap::new(),
141+
visited: LinkedHashMap::new(),
139142
current_measure: Default::default(),
140143
max_capacity: capacity,
141144
meter,
@@ -148,13 +151,30 @@ impl<K: Eq + Hash, V, S: BuildHasher> LruCache<K, V, S, Count> {
148151
pub fn with_hasher(capacity: u64, hash_builder: S) -> LruCache<K, V, S, Count> {
149152
LruCache {
150153
map: LinkedHashMap::with_hasher(hash_builder),
154+
visited: LinkedHashMap::new(),
151155
current_measure: (),
152156
max_capacity: capacity,
153157
meter: Count,
154158
}
155159
}
156160
}
157161

162+
impl<K: Eq + Hash, V, S: BuildHasher, M: CountableMeter<K, V>> LruCache<K, V, S, M> {
163+
fn find_evict_candidate(&mut self) -> Option<K> {
164+
let mut iter = self.visited.iter_mut();
165+
let mut p: Option<K> = None;
166+
while let Some((key, value)) = iter.next() {
167+
if *value == false && p.is_none() {
168+
p = Some(unsafe { std::ptr::read(key) })
169+
}
170+
if *value == true {
171+
*value = false;
172+
}
173+
}
174+
p
175+
}
176+
}
177+
158178
impl<K: Eq + Hash, V, S: BuildHasher, M: CountableMeter<K, V>> Cache<K, V, S, M>
159179
for LruCache<K, V, S, M>
160180
{
@@ -163,6 +183,7 @@ impl<K: Eq + Hash, V, S: BuildHasher, M: CountableMeter<K, V>> Cache<K, V, S, M>
163183
fn with_meter_and_hasher(capacity: u64, meter: M, hash_builder: S) -> Self {
164184
LruCache {
165185
map: LinkedHashMap::with_hasher(hash_builder),
186+
visited: LinkedHashMap::new(),
166187
current_measure: Default::default(),
167188
max_capacity: capacity,
168189
meter,
@@ -196,8 +217,14 @@ impl<K: Eq + Hash, V, S: BuildHasher, M: CountableMeter<K, V>> Cache<K, V, S, M>
196217
Q: Hash + Eq + ?Sized,
197218
{
198219
match self.map.raw_entry_mut().from_key(k) {
199-
linked_hash_map::RawEntryMut::Occupied(mut occupied) => {
200-
occupied.to_back();
220+
linked_hash_map::RawEntryMut::Occupied(occupied) => {
221+
match self.visited.raw_entry_mut().from_key(k) {
222+
// Since the element has been accessed, we set a flag.
223+
linked_hash_map::RawEntryMut::Occupied(mut occupied) => {
224+
occupied.replace_value(true);
225+
}
226+
linked_hash_map::RawEntryMut::Vacant(_) => (),
227+
}
201228
Some(occupied.into_mut())
202229
}
203230
linked_hash_map::RawEntryMut::Vacant(_) => None,
@@ -244,7 +271,7 @@ impl<K: Eq + Hash, V, S: BuildHasher, M: CountableMeter<K, V>> Cache<K, V, S, M>
244271
/// assert_eq!(cache.peek_by_policy(), Some((&1, &"a")));
245272
/// ```
246273
fn peek_by_policy(&self) -> Option<(&K, &V)> {
247-
self.map.front()
274+
todo!()
248275
}
249276

250277
/// Checks if the map contains the given key.
@@ -286,11 +313,21 @@ impl<K: Eq + Hash, V, S: BuildHasher, M: CountableMeter<K, V>> Cache<K, V, S, M>
286313
let new_size = self.meter.measure(&k, &v);
287314
self.current_measure = self.meter.add(self.current_measure, new_size);
288315
if let Some(old) = self.map.get(&k) {
316+
match self.visited.raw_entry_mut().from_key(&k) {
317+
// Since the key has been accessed, we set a flag.
318+
linked_hash_map::RawEntryMut::Occupied(mut occupied) => {
319+
occupied.replace_value(true);
320+
}
321+
linked_hash_map::RawEntryMut::Vacant(_) => (),
322+
}
289323
self.current_measure = self
290324
.meter
291325
.sub(self.current_measure, self.meter.measure(&k, old));
326+
} else {
327+
let clone_k = unsafe { std::ptr::read(&k) };
328+
self.visited.replace(clone_k, false);
292329
}
293-
let old_val = self.map.insert(k, v);
330+
let old_val = self.map.replace(k, v);
294331
while self.size() > self.capacity() {
295332
self.pop_by_policy();
296333
}
@@ -319,6 +356,7 @@ impl<K: Eq + Hash, V, S: BuildHasher, M: CountableMeter<K, V>> Cache<K, V, S, M>
319356
Q: Hash + Eq + ?Sized,
320357
{
321358
self.map.remove(k).map(|v| {
359+
self.visited.remove(k);
322360
self.current_measure = self
323361
.meter
324362
.sub(self.current_measure, self.meter.measure(k, &v));
@@ -343,12 +381,23 @@ impl<K: Eq + Hash, V, S: BuildHasher, M: CountableMeter<K, V>> Cache<K, V, S, M>
343381
/// ```
344382
#[inline]
345383
fn pop_by_policy(&mut self) -> Option<(K, V)> {
346-
self.map.pop_front().map(|(k, v)| {
347-
self.current_measure = self
348-
.meter
349-
.sub(self.current_measure, self.meter.measure(&k, &v));
350-
(k, v)
351-
})
384+
if let Some(old_key) = self.find_evict_candidate() {
385+
self.map.remove_entry(&old_key).map(|(k, v)| {
386+
self.visited.remove_entry(&old_key);
387+
self.current_measure = self
388+
.meter
389+
.sub(self.current_measure, self.meter.measure(&k, &v));
390+
(k, v)
391+
})
392+
} else {
393+
self.map.pop_front().map(|(k, v)| {
394+
self.visited.pop_front();
395+
self.current_measure = self
396+
.meter
397+
.sub(self.current_measure, self.meter.measure(&k, &v));
398+
(k, v)
399+
})
400+
}
352401
}
353402

354403
/// Sets the size of the key-value pairs the cache can hold, as measured by the `Meter` used by
@@ -427,6 +476,7 @@ impl<K: Eq + Hash, V, S: BuildHasher, M: CountableMeter<K, V>> Cache<K, V, S, M>
427476
/// Removes all key-value pairs from the cache.
428477
fn clear(&mut self) {
429478
self.map.clear();
479+
self.visited.clear();
430480
self.current_measure = Default::default();
431481
}
432482
}

src/common/cache/tests/it/cache/lru.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,13 @@ fn test_debug() {
8989
cache.put(3, 30);
9090
assert_eq!(format!("{:?}", cache), "{3: 30, 2: 20, 1: 10}");
9191
cache.put(2, 22);
92-
assert_eq!(format!("{:?}", cache), "{2: 22, 3: 30, 1: 10}");
92+
assert_eq!(format!("{:?}", cache), "{3: 30, 2: 22, 1: 10}");
9393
cache.put(6, 60);
94-
assert_eq!(format!("{:?}", cache), "{6: 60, 2: 22, 3: 30}");
94+
assert_eq!(format!("{:?}", cache), "{6: 60, 3: 30, 2: 22}");
9595
cache.get(&3);
96-
assert_eq!(format!("{:?}", cache), "{3: 30, 6: 60, 2: 22}");
96+
assert_eq!(format!("{:?}", cache), "{6: 60, 3: 30, 2: 22}");
9797
cache.set_capacity(2);
98-
assert_eq!(format!("{:?}", cache), "{3: 30, 6: 60}");
98+
assert_eq!(format!("{:?}", cache), "{6: 60, 3: 30}");
9999
}
100100

101101
#[test]

0 commit comments

Comments
 (0)