use {
crate::{AccessType, EvictError, EvictResult, EvictionPolicy, FrameId},
hlc_gen::{HlcGenerator, HlcTimestamp},
parking_lot::RwLock,
std::{
collections::{HashMap, VecDeque},
sync::Arc,
},
};
pub const LRUK_REPLACER_K: usize = 10;
pub const LRUK_REPLACER_REF_PERIOD: i64 = 5_000;
#[derive(Debug)]
pub struct LruKConfig {
pub capacity: usize,
pub k: usize,
pub ref_period: i64,
}
impl Default for LruKConfig {
fn default() -> Self {
Self {
capacity: 4096,
k: 2,
ref_period: 0,
}
}
}
#[derive(Debug)]
struct PageInfo {
refs: VecDeque<HlcTimestamp>,
last_ref: HlcTimestamp,
evictable: bool,
}
impl PageInfo {
fn new(k: usize) -> Self {
Self {
refs: VecDeque::with_capacity(k),
last_ref: HlcTimestamp::default(),
evictable: true,
}
}
fn touch(&mut self, timestamp: HlcTimestamp, ref_period: i64) {
if ref_period == 0 || timestamp - self.last_ref > ref_period {
let shift = self.refs.back().map_or(0, |last_ref| timestamp - last_ref);
if shift > 0 {
for history_el in &mut self.refs {
*history_el += shift as u64;
}
}
if self.refs.len() == self.refs.capacity() {
self.refs.pop_front();
}
self.refs.push_back(timestamp);
}
self.last_ref = timestamp;
}
}
pub struct LruKReplacer<F: FrameId> {
inner: Arc<RwLock<Inner<F>>>,
}
struct Inner<F: FrameId> {
config: LruKConfig,
size: usize,
framed_pages: HashMap<F, PageInfo>,
seq: HlcGenerator,
}
impl<F: FrameId> Default for LruKReplacer<F> {
fn default() -> Self {
Self::with_config(LruKConfig::default())
}
}
impl<F: FrameId> LruKReplacer<F> {
pub fn new(capacity: usize, k: usize) -> Self {
Self::with_config(LruKConfig {
capacity,
k,
..LruKConfig::default()
})
}
pub fn with_config(config: LruKConfig) -> Self {
let capacity = config.capacity;
Self {
inner: Arc::new(RwLock::new(Inner {
config,
size: 0,
framed_pages: HashMap::with_capacity(capacity),
seq: HlcGenerator::default(),
})),
}
}
}
impl<F: FrameId> EvictionPolicy<F> for LruKReplacer<F> {
type Error = EvictError<F>;
fn evict(&self) -> Option<F> {
self.peek().inspect(|id| {
let mut inner = self.inner.write();
inner.framed_pages.remove(id);
inner.size -= 1;
})
}
fn peek(&self) -> Option<F> {
let inner = self.inner.read();
let timestamp = inner.seq.next_timestamp()?;
let mut max_k_dist = 0i64;
let mut result = None;
for (id, page) in &inner.framed_pages {
if !page.evictable {
continue;
}
if inner.config.ref_period > 0 && timestamp - page.last_ref <= inner.config.ref_period {
continue;
}
let last_uncorrelated_ref = page.refs.back().copied().unwrap_or_default();
let k_dist = if page.refs.len() < inner.config.k {
i64::MAX - last_uncorrelated_ref.as_u64() as i64
} else {
timestamp.as_u64() as i64 - last_uncorrelated_ref.as_u64() as i64
};
if k_dist >= max_k_dist {
max_k_dist = k_dist;
result = Some(id.clone());
}
}
result
}
fn touch(&self, id: F) -> EvictResult<(), F> {
let mut inner = self.inner.write();
if inner.size >= inner.config.capacity && !inner.framed_pages.contains_key(&id) {
return Err(EvictError::FrameReplacerFull);
}
let timestamp = inner
.seq
.next_timestamp()
.ok_or(EvictError::SequenceExhausted)?;
let ref_period = inner.config.ref_period;
let k = inner.config.k;
if !inner.framed_pages.contains_key(&id) {
inner.size += 1;
}
let page = inner
.framed_pages
.entry(id)
.or_insert_with(move || PageInfo::new(k));
page.touch(timestamp, ref_period);
Ok(())
}
fn touch_with<T: AccessType>(&self, id: F, _access_type: T) -> EvictResult<(), F> {
self.touch(id)
}
fn pin(&self, id: F) -> EvictResult<(), F> {
let mut inner = self.inner.write();
let page = inner
.framed_pages
.get_mut(&id)
.ok_or(EvictError::InvalidFrameId(id))?;
if !page.evictable {
return Ok(());
}
page.evictable = false;
inner.size -= 1;
Ok(())
}
fn unpin(&self, id: F) -> EvictResult<(), F> {
let mut inner = self.inner.write();
let page = inner
.framed_pages
.get_mut(&id)
.ok_or(EvictError::InvalidFrameId(id))?;
if page.evictable {
return Ok(());
}
page.evictable = true;
inner.size += 1;
Ok(())
}
fn remove(&self, id: F) -> EvictResult<(), F> {
let mut inner = self.inner.write();
if let Some(page) = inner.framed_pages.get(&id) {
if !page.evictable {
return Err(EvictError::PinnedFrameRemoval(id));
}
inner.framed_pages.remove(&id);
inner.size -= 1;
}
Ok(())
}
fn capacity(&self) -> usize {
self.inner.read().config.capacity
}
fn size(&self) -> usize {
self.inner.read().size
}
}