use std::collections::HashMap;
use std::hash::Hash;
use rand::{thread_rng, Rng};
use crate::{CachePolicy, PrefetchStrategy};
use crate::prefetch::{PrefetchType, NoPrefetch};
use super::{BenchmarkablePolicy, PolicyType};
pub struct RandomCache<K, V>
where
K: Hash + Eq + Clone,
V: Clone,
{
map: HashMap<K, V>,
capacity: usize,
prefetch_strategy: Box<dyn PrefetchStrategy<K>>,
prefetch_buffer: HashMap<K, V>,
prefetch_buffer_size: usize,
prefetch_stats: PrefetchStats,
}
#[derive(Debug, Clone, Default)]
pub struct PrefetchStats {
pub predictions_made: u64,
pub prefetch_hits: u64,
pub prefetch_misses: u64,
pub cache_hits_from_prefetch: u64,
}
impl PrefetchStats {
pub fn hit_rate(&self) -> f64 {
if self.predictions_made == 0 {
0.0
} else {
(self.prefetch_hits as f64 / self.predictions_made as f64) * 100.0
}
}
pub fn effectiveness(&self) -> f64 {
if self.prefetch_hits == 0 {
0.0
} else {
(self.cache_hits_from_prefetch as f64 / self.prefetch_hits as f64) * 100.0
}
}
}
impl<K, V> RandomCache<K, V>
where
K: Hash + Eq + Clone,
V: Clone,
{
pub fn new(capacity: usize) -> Self {
Self::with_custom_prefetch(capacity, Box::new(NoPrefetch))
}
pub fn with_custom_prefetch(
capacity: usize,
prefetch_strategy: Box<dyn PrefetchStrategy<K>>,
) -> Self {
assert!(capacity > 0, "Random cache capacity must be greater than 0");
Self {
map: HashMap::new(),
capacity,
prefetch_strategy,
prefetch_buffer: HashMap::new(),
prefetch_buffer_size: (capacity / 4).max(1),
prefetch_stats: PrefetchStats::default(),
}
}
pub fn with_default_capacity() -> Self {
Self::new(100)
}
pub fn set_prefetch_buffer_size(&mut self, size: usize) {
self.prefetch_buffer_size = size.max(1);
self.trim_prefetch_buffer();
}
pub fn prefetch_stats(&self) -> &PrefetchStats {
&self.prefetch_stats
}
pub fn reset_prefetch_stats(&mut self) {
self.prefetch_stats = PrefetchStats::default();
self.prefetch_strategy.reset();
}
fn trim_prefetch_buffer(&mut self) {
while self.prefetch_buffer.len() > self.prefetch_buffer_size {
if let Some(key) = self.prefetch_buffer.keys().next().cloned() {
self.prefetch_buffer.remove(&key);
} else {
break;
}
}
}
fn perform_prefetch(&mut self, accessed_key: &K) {
self.prefetch_strategy.update_access_pattern(accessed_key);
let predictions = self.prefetch_strategy.predict_next(accessed_key);
for predicted_key in predictions {
self.prefetch_stats.predictions_made += 1;
if !self.map.contains_key(&predicted_key)
&& !self.prefetch_buffer.contains_key(&predicted_key)
{
}
}
self.trim_prefetch_buffer();
}
fn evict_random(&mut self) {
if self.map.is_empty() {
return;
}
let mut rng = thread_rng();
let keys: Vec<K> = self.map.keys().cloned().collect();
if let Some(random_key) = keys.get(rng.gen_range(0..keys.len())) {
self.map.remove(random_key);
}
}
}
impl RandomCache<i32, String> {
pub fn with_prefetch_i32(capacity: usize, prefetch_type: PrefetchType) -> Self {
assert!(capacity > 0, "Random cache capacity must be greater than 0");
let prefetch_strategy = crate::prefetch::create_prefetch_strategy_i32(prefetch_type);
Self::with_custom_prefetch(capacity, prefetch_strategy)
}
}
impl RandomCache<i64, String> {
pub fn with_prefetch_i64(capacity: usize, prefetch_type: PrefetchType) -> Self {
assert!(capacity > 0, "Random cache capacity must be greater than 0");
let prefetch_strategy = crate::prefetch::create_prefetch_strategy_i64(prefetch_type);
Self::with_custom_prefetch(capacity, prefetch_strategy)
}
}
impl RandomCache<usize, String> {
pub fn with_prefetch_usize(capacity: usize, prefetch_type: PrefetchType) -> Self {
assert!(capacity > 0, "Random cache capacity must be greater than 0");
let prefetch_strategy = crate::prefetch::create_prefetch_strategy_usize(prefetch_type);
Self::with_custom_prefetch(capacity, prefetch_strategy)
}
}
impl<K, V> CachePolicy<K, V> for RandomCache<K, V>
where
K: Hash + Eq + Clone,
V: Clone,
{
fn get(&mut self, key: &K) -> Option<&V> {
if let Some(_) = self.prefetch_buffer.get(key) {
if let Some(value) = self.prefetch_buffer.remove(key) {
self.prefetch_stats.cache_hits_from_prefetch += 1;
self.insert(key.clone(), value);
return self.get(key);
}
}
if self.map.contains_key(key) {
self.perform_prefetch(key);
self.map.get(key)
} else {
None
}
}
fn insert(&mut self, key: K, value: V) {
self.prefetch_buffer.remove(&key);
if !self.map.contains_key(&key) && self.map.len() == self.capacity {
self.evict_random();
}
self.map.insert(key, value);
}
fn remove(&mut self, key: &K) -> Option<V> {
if let Some(value) = self.prefetch_buffer.remove(key) {
return Some(value);
}
self.map.remove(key)
}
fn len(&self) -> usize {
self.map.len()
}
fn clear(&mut self) {
self.map.clear();
self.prefetch_buffer.clear();
}
fn capacity(&self) -> usize {
self.capacity
}
}
impl<K, V> BenchmarkablePolicy<K, V> for RandomCache<K, V>
where
K: Hash + Eq + Clone,
V: Clone,
{
fn policy_type(&self) -> PolicyType {
PolicyType::Random
}
fn benchmark_name(&self) -> String {
format!("{}_cap_{}_prefetch", self.policy_type().name(), self.capacity())
}
fn reset_for_benchmark(&mut self) {
self.clear();
self.reset_prefetch_stats();
}
}
impl<K, V> Drop for RandomCache<K, V>
where
K: Hash + Eq + Clone,
V: Clone,
{
fn drop(&mut self) {
self.clear();
}
}
unsafe impl<K, V> Send for RandomCache<K, V>
where
K: Hash + Eq + Clone + Send,
V: Clone + Send,
{
}
unsafe impl<K, V> Sync for RandomCache<K, V>
where
K: Hash + Eq + Clone + Sync,
V: Clone + Sync,
{
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_random_basic_operations() {
let mut cache = RandomCache::new(3);
cache.insert(1, "one");
cache.insert(2, "two");
cache.insert(3, "three");
assert_eq!(cache.len(), 3);
assert_eq!(cache.get(&1), Some(&"one"));
assert_eq!(cache.get(&2), Some(&"two"));
assert_eq!(cache.get(&3), Some(&"three"));
}
#[test]
fn test_random_eviction() {
let mut cache = RandomCache::new(2);
cache.insert(1, "one");
cache.insert(2, "two");
cache.insert(3, "three"); assert_eq!(cache.len(), 2);
let one_present = cache.get(&1).is_some();
let two_present = cache.get(&2).is_some();
let three_present = cache.get(&3).is_some();
assert!(one_present || two_present);
assert!(three_present);
}
#[test]
fn test_random_update_existing() {
let mut cache = RandomCache::new(2);
cache.insert(1, "one");
cache.insert(2, "two");
cache.insert(1, "ONE"); assert_eq!(cache.get(&1), Some(&"ONE"));
assert_eq!(cache.len(), 2);
}
#[test]
fn test_random_remove() {
let mut cache = RandomCache::new(3);
cache.insert(1, "one");
cache.insert(2, "two");
assert_eq!(cache.remove(&1), Some("one"));
assert_eq!(cache.len(), 1);
assert_eq!(cache.get(&1), None);
assert_eq!(cache.get(&2), Some(&"two"));
}
#[test]
fn test_random_clear() {
let mut cache = RandomCache::new(3);
cache.insert(1, "one");
cache.insert(2, "two");
cache.clear();
assert_eq!(cache.len(), 0);
assert!(cache.get(&1).is_none());
}
#[test]
#[should_panic(expected = "Random cache capacity must be greater than 0")]
fn test_random_zero_capacity_panics() {
RandomCache::<i32, String>::new(0);
}
}