use crate::lru::raw::EntryNode;
use crate::lru::raw::{
KeysLRUIter, KeysMRUIter, LRUIter, LRUIterMut, MRUIter, MRUIterMut, RawLRU, ValuesLRUIter,
ValuesLRUIterMut, ValuesMRUIter, ValuesMRUIterMut,
};
use crate::lru::{swap_value, CacheError};
use crate::{Cache, DefaultEvictCallback, DefaultHashBuilder, KeyRef, PutResult};
use core::borrow::Borrow;
use core::hash::{BuildHasher, Hash};
pub struct AdaptiveCacheBuilder<
RH = DefaultHashBuilder,
REH = DefaultHashBuilder,
FH = DefaultHashBuilder,
FEH = DefaultHashBuilder,
> {
size: usize,
recent_hasher: Option<RH>,
recent_evict_hasher: Option<REH>,
freq_hasher: Option<FH>,
freq_evict_hasher: Option<FEH>,
}
impl Default for AdaptiveCacheBuilder {
fn default() -> Self {
Self {
size: 0,
recent_hasher: Some(DefaultHashBuilder::default()),
recent_evict_hasher: Some(DefaultHashBuilder::default()),
freq_hasher: Some(DefaultHashBuilder::default()),
freq_evict_hasher: Some(DefaultHashBuilder::default()),
}
}
}
impl AdaptiveCacheBuilder {
pub fn new(size: usize) -> Self {
Self::default().set_size(size)
}
}
impl<RH: BuildHasher, REH: BuildHasher, FH: BuildHasher, FEH: BuildHasher>
AdaptiveCacheBuilder<RH, REH, FH, FEH>
{
pub fn set_size(self, size: usize) -> Self {
Self {
size,
recent_hasher: self.recent_hasher,
recent_evict_hasher: self.recent_evict_hasher,
freq_hasher: self.freq_hasher,
freq_evict_hasher: self.freq_evict_hasher,
}
}
pub fn set_recent_hasher<NRH: BuildHasher>(
self,
hasher: NRH,
) -> AdaptiveCacheBuilder<NRH, REH, FH, FEH> {
AdaptiveCacheBuilder {
size: self.size,
recent_hasher: Some(hasher),
recent_evict_hasher: self.recent_evict_hasher,
freq_hasher: self.freq_hasher,
freq_evict_hasher: self.freq_evict_hasher,
}
}
pub fn set_frequent_hasher<NFH: BuildHasher>(
self,
hasher: NFH,
) -> AdaptiveCacheBuilder<RH, REH, NFH, FEH> {
AdaptiveCacheBuilder {
size: self.size,
recent_hasher: self.recent_hasher,
recent_evict_hasher: self.recent_evict_hasher,
freq_hasher: Some(hasher),
freq_evict_hasher: self.freq_evict_hasher,
}
}
pub fn set_recent_evict_hasher<NREH: BuildHasher>(
self,
hasher: NREH,
) -> AdaptiveCacheBuilder<RH, NREH, FH, FEH> {
AdaptiveCacheBuilder {
size: self.size,
recent_hasher: self.recent_hasher,
recent_evict_hasher: Some(hasher),
freq_hasher: self.freq_hasher,
freq_evict_hasher: self.freq_evict_hasher,
}
}
pub fn set_frequent_evict_hasher<NFEH: BuildHasher>(
self,
hasher: NFEH,
) -> AdaptiveCacheBuilder<RH, REH, FH, NFEH> {
AdaptiveCacheBuilder {
size: self.size,
recent_hasher: self.recent_hasher,
recent_evict_hasher: self.recent_evict_hasher,
freq_hasher: self.freq_hasher,
freq_evict_hasher: Some(hasher),
}
}
pub fn finalize<K: Hash + Eq, V>(
self,
) -> Result<AdaptiveCache<K, V, RH, REH, FH, FEH>, CacheError> {
let size = self.size;
if size == 0 {
return Err(CacheError::InvalidSize(0));
}
let recent = RawLRU::with_hasher(size, self.recent_hasher.unwrap()).unwrap();
let recent_evict = RawLRU::with_hasher(size, self.recent_evict_hasher.unwrap()).unwrap();
let freq = RawLRU::with_hasher(size, self.freq_hasher.unwrap()).unwrap();
let freq_evict = RawLRU::with_hasher(size, self.freq_evict_hasher.unwrap()).unwrap();
Ok(AdaptiveCache {
size,
p: 0,
recent,
recent_evict,
frequent: freq,
frequent_evict: freq_evict,
})
}
}
pub struct AdaptiveCache<
K,
V,
RH = DefaultHashBuilder,
REH = DefaultHashBuilder,
FH = DefaultHashBuilder,
FEH = DefaultHashBuilder,
> {
size: usize,
p: usize,
recent: RawLRU<K, V, DefaultEvictCallback, RH>,
recent_evict: RawLRU<K, V, DefaultEvictCallback, REH>,
frequent: RawLRU<K, V, DefaultEvictCallback, FH>,
frequent_evict: RawLRU<K, V, DefaultEvictCallback, FEH>,
}
impl<K: Hash + Eq, V> AdaptiveCache<K, V> {
pub fn new(size: usize) -> Result<Self, CacheError> {
AdaptiveCacheBuilder::new(size).finalize()
}
pub fn builder(size: usize) -> AdaptiveCacheBuilder {
AdaptiveCacheBuilder::new(size)
}
}
impl<K: Hash + Eq, V, RH: BuildHasher, REH: BuildHasher, FH: BuildHasher, FEH: BuildHasher>
Cache<K, V> for AdaptiveCache<K, V, RH, REH, FH, FEH>
{
fn put(&mut self, k: K, mut v: V) -> PutResult<K, V> {
let key_ref = KeyRef { k: &k };
if self
.recent
.remove_and_return_ent(&k)
.map(|mut ent| {
unsafe {
swap_value(&mut v, ent.as_mut());
}
self.frequent.put_nonnull(ent);
})
.is_some()
{
return PutResult::Update(v);
}
if let Some(ent_ptr) = self.frequent.map.get_mut(&key_ref).map(|node| {
let node_ptr: *mut EntryNode<K, V> = node.as_ptr();
node_ptr
}) {
self.frequent.update(&mut v, ent_ptr);
return PutResult::Update(v);
}
let recent_len = self.recent.len();
let freq_len = self.frequent.len();
let recent_evict_len = self.recent_evict.len();
let freq_evict_len = self.frequent_evict.len();
if self.recent_evict.contains(&k) {
let mut delta = 1usize;
if freq_evict_len > recent_evict_len {
delta = freq_evict_len / recent_evict_len;
}
if self.p + delta >= self.size {
self.p = self.size;
} else {
self.p += delta;
}
if self.recent.len() + self.frequent.len() >= self.size {
self.replace(false);
}
let mut ent = self.recent_evict.map.remove(&key_ref).unwrap();
unsafe {
let ent_ptr = ent.as_mut();
self.recent_evict.detach(ent_ptr);
swap_value(&mut v, ent_ptr);
}
self.frequent.put_nonnull(ent);
return PutResult::Update(v);
}
if self.frequent_evict.map.contains_key(&key_ref) {
let mut delta = 1usize;
if recent_evict_len > freq_evict_len {
delta = recent_evict_len / freq_evict_len;
}
if delta >= self.p {
self.p = 0;
} else {
self.p -= delta;
}
if recent_len + freq_len >= self.size {
self.replace(true);
}
let mut ent = self.frequent_evict.map.remove(&key_ref).unwrap();
unsafe {
let ent_ptr = ent.as_mut();
self.frequent_evict.detach(ent_ptr);
swap_value(&mut v, ent_ptr);
}
self.frequent.put_nonnull(ent);
return PutResult::Update(v);
}
if recent_len + freq_len >= self.size {
self.replace(false);
}
if recent_evict_len > self.size - self.p {
self.recent_evict.remove_lru();
}
if freq_evict_len > self.p {
self.frequent_evict.remove_lru();
}
self.recent.put(k, v)
}
fn get<Q>(&mut self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.recent
.peek_(k)
.and_then(|v| self.move_to_frequent(k, v))
.or_else(|| self.frequent.get_(k))
.map(|v| unsafe { core::mem::transmute(v) })
}
fn get_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.recent
.peek_mut_(k)
.and_then(|v| self.move_to_frequent(k, v))
.or_else(|| self.frequent.get_mut_(k))
.map(|v| unsafe { core::mem::transmute(v) })
}
fn peek<Q>(&'_ self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.recent.peek(k).or_else(|| self.frequent.peek(k))
}
fn peek_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
match self.recent.peek_mut(k) {
Some(v) => Some(v),
None => self.frequent.peek_mut(k),
}
}
fn contains<Q>(&self, k: &Q) -> bool
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.recent.contains(k) || self.frequent.contains(k)
}
fn remove<Q>(&mut self, k: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.recent
.remove(k)
.or_else(|| self.frequent.remove(k))
.or_else(|| self.recent_evict.remove(k))
.or_else(|| self.frequent_evict.remove(k))
}
fn purge(&mut self) {
self.recent.purge();
self.frequent.purge();
self.recent_evict.purge();
self.frequent_evict.purge();
}
fn len(&self) -> usize {
self.recent.len() + self.frequent.len()
}
fn cap(&self) -> usize {
self.size
}
fn is_empty(&self) -> bool {
self.recent.is_empty()
&& self.recent_evict.is_empty()
&& self.frequent.is_empty()
&& self.frequent_evict.is_empty()
}
}
impl<K: Hash + Eq, V, RH: BuildHasher, REH: BuildHasher, FH: BuildHasher, FEH: BuildHasher>
AdaptiveCache<K, V, RH, REH, FH, FEH>
{
pub fn from_builder(
builder: AdaptiveCacheBuilder<RH, REH, FH, FEH>,
) -> Result<Self, CacheError> {
builder.finalize()
}
pub fn partition(&self) -> usize {
self.p
}
pub fn recent_len(&self) -> usize {
self.recent.len()
}
pub fn frequent_len(&self) -> usize {
self.frequent.len()
}
pub fn recent_evict_len(&self) -> usize {
self.recent_evict.len()
}
pub fn frequent_evict_len(&self) -> usize {
self.frequent_evict.len()
}
pub fn recent_keys(&self) -> KeysMRUIter<'_, K, V> {
self.recent.keys()
}
pub fn recent_keys_lru(&self) -> KeysLRUIter<'_, K, V> {
self.recent.keys_lru()
}
pub fn recent_values(&self) -> ValuesMRUIter<'_, K, V> {
self.recent.values()
}
pub fn recent_values_lru(&self) -> ValuesLRUIter<'_, K, V> {
self.recent.values_lru()
}
pub fn recent_values_mut(&mut self) -> ValuesMRUIterMut<'_, K, V> {
self.recent.values_mut()
}
pub fn recent_values_lru_mut(&mut self) -> ValuesLRUIterMut<'_, K, V> {
self.recent.values_lru_mut()
}
pub fn recent_iter(&self) -> MRUIter<'_, K, V> {
self.recent.iter()
}
pub fn recent_iter_lru(&self) -> LRUIter<'_, K, V> {
self.recent.iter_lru()
}
pub fn recent_iter_mut(&mut self) -> MRUIterMut<'_, K, V> {
self.recent.iter_mut()
}
pub fn recent_iter_lru_mut(&mut self) -> LRUIterMut<'_, K, V> {
self.recent.iter_lru_mut()
}
pub fn recent_evict_keys(&self) -> KeysMRUIter<'_, K, V> {
self.recent_evict.keys()
}
pub fn recent_evict_keys_lru(&self) -> KeysLRUIter<'_, K, V> {
self.recent_evict.keys_lru()
}
pub fn recent_evict_values(&self) -> ValuesMRUIter<'_, K, V> {
self.recent_evict.values()
}
pub fn recent_evict_values_lru(&self) -> ValuesLRUIter<'_, K, V> {
self.recent_evict.values_lru()
}
pub fn recent_evict_values_mut(&mut self) -> ValuesMRUIterMut<'_, K, V> {
self.recent_evict.values_mut()
}
pub fn recent_evict_values_lru_mut(&mut self) -> ValuesLRUIterMut<'_, K, V> {
self.recent_evict.values_lru_mut()
}
pub fn recent_evict_iter(&self) -> MRUIter<'_, K, V> {
self.recent_evict.iter()
}
pub fn recent_evict_iter_lru(&self) -> LRUIter<'_, K, V> {
self.recent_evict.iter_lru()
}
pub fn recent_evict_iter_mut(&mut self) -> MRUIterMut<'_, K, V> {
self.recent_evict.iter_mut()
}
pub fn recent_evict_iter_lru_mut(&mut self) -> LRUIterMut<'_, K, V> {
self.recent_evict.iter_lru_mut()
}
pub fn frequent_keys(&self) -> KeysMRUIter<'_, K, V> {
self.frequent.keys()
}
pub fn frequent_keys_lru(&self) -> KeysLRUIter<'_, K, V> {
self.frequent.keys_lru()
}
pub fn frequent_values(&self) -> ValuesMRUIter<'_, K, V> {
self.frequent.values()
}
pub fn frequent_values_lru(&self) -> ValuesLRUIter<'_, K, V> {
self.frequent.values_lru()
}
pub fn frequent_values_mut(&mut self) -> ValuesMRUIterMut<'_, K, V> {
self.frequent.values_mut()
}
pub fn frequent_values_lru_mut(&mut self) -> ValuesLRUIterMut<'_, K, V> {
self.frequent.values_lru_mut()
}
pub fn frequent_iter(&self) -> MRUIter<'_, K, V> {
self.frequent.iter()
}
pub fn frequent_iter_lru(&self) -> LRUIter<'_, K, V> {
self.frequent.iter_lru()
}
pub fn frequent_iter_mut(&mut self) -> MRUIterMut<'_, K, V> {
self.frequent.iter_mut()
}
pub fn frequent_iter_lru_mut(&mut self) -> LRUIterMut<'_, K, V> {
self.frequent.iter_lru_mut()
}
pub fn frequent_evict_keys(&self) -> KeysMRUIter<'_, K, V> {
self.frequent_evict.keys()
}
pub fn frequent_evict_keys_lru(&self) -> KeysLRUIter<'_, K, V> {
self.frequent_evict.keys_lru()
}
pub fn frequent_evict_values(&self) -> ValuesMRUIter<'_, K, V> {
self.frequent_evict.values()
}
pub fn frequent_evict_values_lru(&self) -> ValuesLRUIter<'_, K, V> {
self.frequent_evict.values_lru()
}
pub fn frequent_evict_values_mut(&mut self) -> ValuesMRUIterMut<'_, K, V> {
self.frequent_evict.values_mut()
}
pub fn frequent_evict_values_lru_mut(&mut self) -> ValuesLRUIterMut<'_, K, V> {
self.frequent_evict.values_lru_mut()
}
pub fn frequent_evict_iter(&self) -> MRUIter<'_, K, V> {
self.frequent_evict.iter()
}
pub fn frequent_evict_iter_lru(&self) -> LRUIter<'_, K, V> {
self.frequent_evict.iter_lru()
}
pub fn frequent_evict_iter_mut(&mut self) -> MRUIterMut<'_, K, V> {
self.frequent_evict.iter_mut()
}
pub fn frequent_evict_iter_lru_mut(&mut self) -> LRUIterMut<'_, K, V> {
self.frequent_evict.iter_lru_mut()
}
fn replace(&mut self, freq_contains_key: bool) {
let recent_evict_len = self.recent.len();
if recent_evict_len > 0
&& (recent_evict_len > self.p || (recent_evict_len == self.p && freq_contains_key))
{
match self.recent.remove_lru_in() {
None => None,
Some(ent) => Some(self.recent_evict.put_nonnull(ent)),
};
} else {
match self.frequent.remove_lru_in() {
None => None,
Some(ent) => Some(self.frequent_evict.put_nonnull(ent)),
};
}
}
fn move_to_frequent<T, Q>(&mut self, k: &Q, v: T) -> Option<T>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
match self.recent.remove_and_return_ent(k) {
None => None,
Some(ent) => {
self.frequent.put_nonnull(ent);
Some(v)
}
}
}
}
#[cfg(test)]
mod test {
use crate::{AdaptiveCache, Cache};
use alloc::vec::Vec;
use rand::seq::SliceRandom;
use rand::{thread_rng, Rng};
#[cfg_attr(miri, ignore)]
#[test]
fn test_arc_cache_random_ops() {
let size = 128;
let mut rng = thread_rng();
let mut cases: Vec<u64> = (0..200_000).collect();
cases.shuffle(&mut rng);
let mut cache = AdaptiveCache::new(size).unwrap();
(0..200_000).for_each(|_i| {
let k = rng.gen::<i64>() % 512;
let r: i64 = rng.gen();
match r % 3 {
0 => {
let _ = cache.put(k, k);
}
1 => {
let _ = cache.get(&k);
}
2 => {
let _ = cache.remove(&k);
}
_ => {}
}
assert!(cache.recent.len() + cache.frequent.len() <= size)
})
}
#[test]
fn test_arc_get_recent_to_frequent() {
let mut cache = AdaptiveCache::new(128).unwrap();
(0..128).for_each(|i| {
cache.put(i, i);
});
assert_eq!(cache.recent.len(), 128);
assert_eq!(cache.frequent.len(), 0);
(0..128).for_each(|i| {
assert_ne!(cache.get(&i), None);
});
assert_eq!(cache.recent.len(), 0);
assert_eq!(cache.frequent.len(), 128);
(0..128).for_each(|i| {
assert_ne!(cache.get(&i), None);
});
assert_eq!(cache.recent.len(), 0);
assert_eq!(cache.frequent.len(), 128);
}
#[test]
fn test_arc_add_recent_to_freq() {
let mut cache = AdaptiveCache::new(128).unwrap();
cache.put(1, 1);
assert_eq!(cache.recent.len(), 1);
assert_eq!(cache.frequent.len(), 0);
cache.put(1, 1);
assert_eq!(cache.recent.len(), 0);
assert_eq!(cache.frequent.len(), 1);
cache.put(1, 1);
assert_eq!(cache.recent.len(), 0);
assert_eq!(cache.frequent.len(), 1);
}
#[test]
fn test_arc_adaptive() {
let mut cache = AdaptiveCache::new(4).unwrap();
(0..4).for_each(|i| {
cache.put(i, i);
});
assert_eq!(cache.recent.len(), 4);
cache.get(&0);
cache.get(&1);
assert_eq!(cache.frequent.len(), 2);
cache.put(4, 4);
assert_eq!(cache.recent_evict.len(), 1);
cache.put(2, 2);
assert_eq!(cache.recent_evict.len(), 1);
assert_eq!(cache.p, 1);
assert_eq!(cache.frequent.len(), 3);
cache.put(4, 4);
assert_eq!(cache.recent.len(), 0);
assert_eq!(cache.frequent.len(), 4);
cache.put(5, 5);
assert_eq!(cache.recent.len(), 1);
assert_eq!(cache.frequent.len(), 3);
assert_eq!(cache.frequent_evict.len(), 1);
cache.put(0, 0);
assert_eq!(cache.recent.len(), 0);
assert_eq!(cache.frequent.len(), 4);
assert_eq!(cache.recent_evict.len(), 2);
assert_eq!(cache.frequent_evict.len(), 0);
assert_eq!(cache.p, 0);
}
#[test]
fn test_arc() {
let mut cache = AdaptiveCache::new(128).unwrap();
(0..256).for_each(|i| {
cache.put(i, i);
});
assert_eq!(cache.len(), 128);
let rst = cache
.frequent
.keys_lru()
.chain(cache.recent.keys_lru())
.enumerate()
.map(|(idx, k)| (idx as u64, *k))
.collect::<Vec<(u64, u64)>>();
rst.into_iter().for_each(|(idx, k)| match cache.get(&k) {
None => panic!("bad: {}", k),
Some(val) => assert_eq!(*val, idx + 128),
});
(0..128).for_each(|k| {
assert_eq!(cache.get(&k), None);
});
(128..256).for_each(|k| {
assert_ne!(cache.get(&k), None);
});
(128..192).for_each(|k| {
cache.remove(&k);
assert_eq!(cache.get(&k), None);
});
cache.purge();
assert_eq!(cache.len(), 0);
assert_eq!(cache.get(&200), None);
}
#[test]
fn test_2q_cache_contains() {
let mut cache = AdaptiveCache::new(2).unwrap();
cache.put(1, 1);
cache.put(2, 2);
assert!(cache.contains(&1));
cache.put(3, 3);
assert!(
!cache.contains(&1),
"should be in ghost LRU, and the elements in the ghost is not counted as in cache"
);
}
#[test]
fn test_arc_peek() {
let mut cache = AdaptiveCache::new(2).unwrap();
cache.put(1, 1);
cache.put(2, 2);
assert_eq!(cache.peek(&1), Some(&1));
cache.put(3, 3);
assert!(!cache.contains(&1));
}
}