#![allow(dead_code)]
use std::collections::HashMap;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CacheEntry {
pub key: String,
pub weights: Vec<f32>,
pub access_count: u64,
pub last_access: u64,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct MorphCacheLru {
capacity: usize,
entries: HashMap<String, CacheEntry>,
counter: u64,
hits: u64,
misses: u64,
}
#[allow(dead_code)]
pub fn new_morph_cache_lru(capacity: usize) -> MorphCacheLru {
MorphCacheLru {
capacity: capacity.max(1),
entries: HashMap::new(),
counter: 0,
hits: 0,
misses: 0,
}
}
#[allow(dead_code)]
pub fn cache_put(cache: &mut MorphCacheLru, key: &str, weights: Vec<f32>) {
cache.counter += 1;
if cache.entries.len() >= cache.capacity && !cache.entries.contains_key(key) {
if let Some(lru_key) = cache
.entries
.iter()
.min_by_key(|(_, e)| e.last_access)
.map(|(k, _)| k.clone())
{
cache.entries.remove(&lru_key);
}
}
cache.entries.insert(
key.to_string(),
CacheEntry {
key: key.to_string(),
weights,
access_count: 1,
last_access: cache.counter,
},
);
}
#[allow(dead_code)]
pub fn cache_get(cache: &mut MorphCacheLru, key: &str) -> Option<Vec<f32>> {
cache.counter += 1;
if let Some(entry) = cache.entries.get_mut(key) {
entry.access_count += 1;
entry.last_access = cache.counter;
cache.hits += 1;
Some(entry.weights.clone())
} else {
cache.misses += 1;
None
}
}
#[allow(dead_code)]
pub fn cache_contains(cache: &MorphCacheLru, key: &str) -> bool {
cache.entries.contains_key(key)
}
#[allow(dead_code)]
pub fn cache_evict(cache: &mut MorphCacheLru, key: &str) -> bool {
cache.entries.remove(key).is_some()
}
#[allow(dead_code)]
pub fn cache_size(cache: &MorphCacheLru) -> usize {
cache.entries.len()
}
#[allow(dead_code)]
pub fn cache_capacity(cache: &MorphCacheLru) -> usize {
cache.capacity
}
#[allow(dead_code)]
pub fn cache_hit_rate(cache: &MorphCacheLru) -> f32 {
let total = cache.hits + cache.misses;
if total == 0 {
return 0.0;
}
cache.hits as f32 / total as f32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_morph_cache_lru() {
let c = new_morph_cache_lru(10);
assert_eq!(cache_capacity(&c), 10);
assert_eq!(cache_size(&c), 0);
}
#[test]
fn test_cache_put_get() {
let mut c = new_morph_cache_lru(5);
cache_put(&mut c, "smile", vec![0.5, 0.8]);
let result = cache_get(&mut c, "smile");
assert!(result.is_some());
assert!((result.expect("should succeed")[0] - 0.5).abs() < f32::EPSILON);
}
#[test]
fn test_cache_miss() {
let mut c = new_morph_cache_lru(5);
assert!(cache_get(&mut c, "nope").is_none());
}
#[test]
fn test_cache_contains() {
let mut c = new_morph_cache_lru(5);
cache_put(&mut c, "a", vec![1.0]);
assert!(cache_contains(&c, "a"));
assert!(!cache_contains(&c, "b"));
}
#[test]
fn test_cache_evict() {
let mut c = new_morph_cache_lru(5);
cache_put(&mut c, "a", vec![1.0]);
assert!(cache_evict(&mut c, "a"));
assert!(!cache_contains(&c, "a"));
}
#[test]
fn test_cache_evict_missing() {
let mut c = new_morph_cache_lru(5);
assert!(!cache_evict(&mut c, "nope"));
}
#[test]
fn test_cache_lru_eviction() {
let mut c = new_morph_cache_lru(2);
cache_put(&mut c, "a", vec![1.0]);
cache_put(&mut c, "b", vec![2.0]);
cache_put(&mut c, "c", vec![3.0]); assert!(!cache_contains(&c, "a"));
assert!(cache_contains(&c, "b"));
assert!(cache_contains(&c, "c"));
}
#[test]
fn test_cache_hit_rate_initial() {
let c = new_morph_cache_lru(5);
assert!((cache_hit_rate(&c) - 0.0).abs() < f32::EPSILON);
}
#[test]
fn test_cache_hit_rate_after_ops() {
let mut c = new_morph_cache_lru(5);
cache_put(&mut c, "a", vec![1.0]);
let _ = cache_get(&mut c, "a"); let _ = cache_get(&mut c, "b"); assert!((cache_hit_rate(&c) - 0.5).abs() < f32::EPSILON);
}
#[test]
fn test_cache_size() {
let mut c = new_morph_cache_lru(10);
cache_put(&mut c, "a", vec![1.0]);
cache_put(&mut c, "b", vec![2.0]);
assert_eq!(cache_size(&c), 2);
}
}