Skip to content

Commit ee33f98

Browse files
committed
Add Clock, ClockPro, and NRU Policies to Cache Library
- Introduced Clock, ClockPro, and NRU cache replacement policies, enhancing the library's eviction strategy options. - Updated README to include descriptions and examples for the new policies, improving clarity on their use cases and benefits. - Enhanced the builder module to support the new policies, ensuring consistent API access. - Updated policy comparison table to reflect the addition of Clock, ClockPro, and NRU, aiding users in policy selection. - Improved documentation for each policy, detailing architecture, operations, and performance trade-offs.
1 parent 46e72ee commit ee33f98

2 files changed

Lines changed: 73 additions & 12 deletions

File tree

README.md

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,15 @@ let random = CacheBuilder::new(100).build::<u64, String>(CachePolicy::Random);
135135
let slru = CacheBuilder::new(100).build::<u64, String>(
136136
CachePolicy::Slru { probationary_frac: 0.25 }
137137
);
138+
139+
// Clock - Approximate LRU with reference bits (lower overhead)
140+
let clock = CacheBuilder::new(100).build::<u64, String>(CachePolicy::Clock);
141+
142+
// Clock-PRO - Scan-resistant Clock variant
143+
let clock_pro = CacheBuilder::new(100).build::<u64, String>(CachePolicy::ClockPro);
144+
145+
// NRU - Not Recently Used (simple reference bit tracking)
146+
let nru = CacheBuilder::new(100).build::<u64, String>(CachePolicy::Nru);
138147
```
139148

140149
### Policy Selection Guide
@@ -153,35 +162,30 @@ let slru = CacheBuilder::new(100).build::<u64, String>(
153162
| MRU | Anti-recency patterns | Most recent access |
154163
| Random | Baseline/uniform distribution | Random selection |
155164
| SLRU | Scan resistance | Segmented LRU |
165+
| Clock | Low-overhead LRU approximation | Reference bits + hand |
166+
| ClockPro| Scan-resistant Clock variant | Clock + ghost history |
167+
| NRU | Simple coarse tracking | Reference bits (binary) |
156168

157169
See [Choosing a policy](docs/guides/choosing-a-policy.md) for benchmark-driven guidance.
158170

159171
### Direct Policy Access
160172

161-
For advanced use cases requiring policy-specific operations, or to use policies not yet in the builder (like Clock, Clock-PRO, NRU), use the underlying implementations directly:
173+
For advanced use cases requiring policy-specific operations, use the underlying implementations directly:
162174

163175
```rust
164176
use std::sync::Arc;
165177
use cachekit::policy::lru::LruCore;
166-
use cachekit::policy::nru::NruCache;
167-
use cachekit::policy::clock::ClockCache;
168178
use cachekit::traits::{CoreCache, LruCacheTrait};
169179

170180
fn main() {
171181
// LRU with policy-specific operations
172182
let mut lru_cache: LruCore<u64, &str> = LruCore::new(100);
173183
lru_cache.insert(1, Arc::new("value"));
184+
185+
// Access LRU-specific methods
174186
if let Some((key, _)) = lru_cache.peek_lru() {
175187
println!("LRU key: {}", key);
176188
}
177-
178-
// NRU cache (not yet in builder)
179-
let mut nru_cache = NruCache::new(100);
180-
nru_cache.insert(1, "value");
181-
182-
// Clock cache (not yet in builder)
183-
let mut clock_cache = ClockCache::new(100);
184-
clock_cache.insert(1, "value");
185189
}
186190
```
187191

src/builder.rs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
//! │ ├─── CachePolicy::Mfu ─────► MfuCore<K, V> │
2626
//! │ ├─── CachePolicy::Mru ─────► MruCore<K, V> │
2727
//! │ ├─── CachePolicy::Random ──► RandomCore<K, V> │
28-
//! │ └─── CachePolicy::Slru ────► SlruCore<K, V> │
28+
//! │ ├─── CachePolicy::Slru ────► SlruCore<K, V> │
29+
//! │ ├─── CachePolicy::Clock ───► ClockCache<K, V> │
30+
//! │ ├─── CachePolicy::ClockPro ► ClockProCache<K, V> │
31+
//! │ └─── CachePolicy::Nru ─────► NruCache<K, V> │
2932
//! │ │
3033
//! │ ▼ │
3134
//! │ Cache<K, V> (unified wrapper) │
@@ -98,6 +101,8 @@ use std::hash::Hash;
98101
use std::sync::Arc;
99102

100103
use crate::ds::frequency_buckets::DEFAULT_BUCKET_PREALLOC;
104+
use crate::policy::clock::ClockCache;
105+
use crate::policy::clock_pro::ClockProCache;
101106
use crate::policy::fifo::FifoCache;
102107
use crate::policy::heap_lfu::HeapLfuCache;
103108
use crate::policy::lfu::LfuCache;
@@ -106,6 +111,7 @@ use crate::policy::lru::LruCore;
106111
use crate::policy::lru_k::LrukCache;
107112
use crate::policy::mfu::MfuCore;
108113
use crate::policy::mru::MruCore;
114+
use crate::policy::nru::NruCache;
109115
use crate::policy::random::RandomCore;
110116
use crate::policy::s3_fifo::S3FifoCache;
111117
use crate::policy::slru::SlruCore;
@@ -281,6 +287,30 @@ pub enum CachePolicy {
281287
/// Fraction of capacity for the probationary segment.
282288
probationary_frac: f64,
283289
},
290+
291+
/// Clock (Second-Chance) eviction.
292+
///
293+
/// Approximates LRU using reference bits and a clock hand.
294+
/// Lower overhead than full LRU (no list manipulation on access).
295+
///
296+
/// Good for: Low-latency caching, LRU approximation with lower overhead.
297+
Clock,
298+
299+
/// Clock-PRO eviction.
300+
///
301+
/// Scan-resistant Clock variant with adaptive promotion.
302+
/// Combines Clock mechanics with ghost history tracking.
303+
///
304+
/// Good for: Scan-heavy workloads, adaptive caching needs.
305+
ClockPro,
306+
307+
/// NRU (Not Recently Used) eviction.
308+
///
309+
/// Simple reference bit tracking with O(n) worst-case eviction.
310+
/// Coarser granularity than Clock, simpler implementation.
311+
///
312+
/// Good for: Small-to-medium caches, simple coarse recency tracking.
313+
Nru,
284314
}
285315

286316
/// Unified cache wrapper that provides a consistent API regardless of policy.
@@ -350,6 +380,9 @@ where
350380
Mru(MruCore<K, V>),
351381
Random(RandomCore<K, V>),
352382
Slru(SlruCore<K, V>),
383+
Clock(ClockCache<K, V>),
384+
ClockPro(ClockProCache<K, V>),
385+
Nru(NruCache<K, V>),
353386
}
354387

355388
impl<K, V> Cache<K, V>
@@ -402,6 +435,9 @@ where
402435
CacheInner::Mru(mru) => CoreCache::insert(mru, key, value),
403436
CacheInner::Random(random) => CoreCache::insert(random, key, value),
404437
CacheInner::Slru(slru) => CoreCache::insert(slru, key, value),
438+
CacheInner::Clock(clock) => CoreCache::insert(clock, key, value),
439+
CacheInner::ClockPro(clock_pro) => CoreCache::insert(clock_pro, key, value),
440+
CacheInner::Nru(nru) => CoreCache::insert(nru, key, value),
405441
}
406442
}
407443

@@ -434,6 +470,9 @@ where
434470
CacheInner::Mru(mru) => mru.get(key),
435471
CacheInner::Random(random) => random.get(key),
436472
CacheInner::Slru(slru) => slru.get(key),
473+
CacheInner::Clock(clock) => clock.get(key),
474+
CacheInner::ClockPro(clock_pro) => clock_pro.get(key),
475+
CacheInner::Nru(nru) => nru.get(key),
437476
}
438477
}
439478

@@ -466,6 +505,9 @@ where
466505
CacheInner::Mru(mru) => mru.contains(key),
467506
CacheInner::Random(random) => random.contains(key),
468507
CacheInner::Slru(slru) => slru.contains(key),
508+
CacheInner::Clock(clock) => clock.contains(key),
509+
CacheInner::ClockPro(clock_pro) => clock_pro.contains(key),
510+
CacheInner::Nru(nru) => nru.contains(key),
469511
}
470512
}
471513

@@ -497,6 +539,9 @@ where
497539
CacheInner::Mru(mru) => mru.len(),
498540
CacheInner::Random(random) => CoreCache::len(random),
499541
CacheInner::Slru(slru) => slru.len(),
542+
CacheInner::Clock(clock) => CoreCache::len(clock),
543+
CacheInner::ClockPro(clock_pro) => CoreCache::len(clock_pro),
544+
CacheInner::Nru(nru) => CoreCache::len(nru),
500545
}
501546
}
502547

@@ -541,6 +586,9 @@ where
541586
CacheInner::Mru(mru) => mru.capacity(),
542587
CacheInner::Random(random) => CoreCache::capacity(random),
543588
CacheInner::Slru(slru) => slru.capacity(),
589+
CacheInner::Clock(clock) => CoreCache::capacity(clock),
590+
CacheInner::ClockPro(clock_pro) => CoreCache::capacity(clock_pro),
591+
CacheInner::Nru(nru) => CoreCache::capacity(nru),
544592
}
545593
}
546594

@@ -574,6 +622,9 @@ where
574622
CacheInner::Mru(mru) => mru.clear(),
575623
CacheInner::Random(random) => random.clear(),
576624
CacheInner::Slru(slru) => slru.clear(),
625+
CacheInner::Clock(clock) => clock.clear(),
626+
CacheInner::ClockPro(clock_pro) => clock_pro.clear(),
627+
CacheInner::Nru(nru) => nru.clear(),
577628
}
578629
}
579630
}
@@ -666,6 +717,9 @@ impl CacheBuilder {
666717
CachePolicy::Slru { probationary_frac } => {
667718
CacheInner::Slru(SlruCore::new(self.capacity, probationary_frac))
668719
},
720+
CachePolicy::Clock => CacheInner::Clock(ClockCache::new(self.capacity)),
721+
CachePolicy::ClockPro => CacheInner::ClockPro(ClockProCache::new(self.capacity)),
722+
CachePolicy::Nru => CacheInner::Nru(NruCache::new(self.capacity)),
669723
};
670724

671725
Cache { inner }
@@ -698,6 +752,9 @@ mod tests {
698752
CachePolicy::Slru {
699753
probationary_frac: 0.25,
700754
},
755+
CachePolicy::Clock,
756+
CachePolicy::ClockPro,
757+
CachePolicy::Nru,
701758
];
702759

703760
for policy in policies {

0 commit comments

Comments
 (0)