extern crate cache_rs;
use cache_rs::config::{
ConcurrentCacheConfig, ConcurrentGdsfCacheConfig, ConcurrentLfuCacheConfig,
ConcurrentLfudaCacheConfig, ConcurrentLruCacheConfig, ConcurrentSlruCacheConfig,
GdsfCacheConfig, LfuCacheConfig, LfudaCacheConfig, LruCacheConfig, SlruCacheConfig,
};
use cache_rs::{
ConcurrentGdsfCache, ConcurrentLfuCache, ConcurrentLfudaCache, ConcurrentLruCache,
ConcurrentSlruCache,
};
use std::num::NonZeroUsize;
use std::sync::Arc;
use std::thread;
use std::time::Instant;
fn lru_config(capacity: usize, segments: usize) -> ConcurrentLruCacheConfig {
ConcurrentCacheConfig {
base: LruCacheConfig {
capacity: NonZeroUsize::new(capacity).unwrap(),
max_size: u64::MAX,
},
segments,
}
}
fn slru_config(capacity: usize, protected: usize, segments: usize) -> ConcurrentSlruCacheConfig {
ConcurrentCacheConfig {
base: SlruCacheConfig {
capacity: NonZeroUsize::new(capacity).unwrap(),
protected_capacity: NonZeroUsize::new(protected).unwrap(),
max_size: u64::MAX,
},
segments,
}
}
fn lfu_config(capacity: usize, segments: usize) -> ConcurrentLfuCacheConfig {
ConcurrentCacheConfig {
base: LfuCacheConfig {
capacity: NonZeroUsize::new(capacity).unwrap(),
max_size: u64::MAX,
},
segments,
}
}
fn lfuda_config(capacity: usize, segments: usize) -> ConcurrentLfudaCacheConfig {
ConcurrentCacheConfig {
base: LfudaCacheConfig {
capacity: NonZeroUsize::new(capacity).unwrap(),
initial_age: 0,
max_size: u64::MAX,
},
segments,
}
}
fn gdsf_config(capacity: usize, segments: usize) -> ConcurrentGdsfCacheConfig {
ConcurrentCacheConfig {
base: GdsfCacheConfig {
capacity: NonZeroUsize::new(capacity).unwrap(),
initial_age: 0.0,
max_size: u64::MAX,
},
segments,
}
}
fn main() {
println!("Concurrent Cache Usage Examples");
println!("================================\n");
basic_concurrent_usage();
println!();
zero_copy_get_with();
println!();
segment_tuning();
println!();
all_concurrent_cache_types();
println!();
throughput_comparison();
}
fn basic_concurrent_usage() {
println!("1. Basic Concurrent Usage");
println!(" -----------------------");
let cache = Arc::new(ConcurrentLruCache::init(lru_config(1000, 16), None));
let num_threads = 4;
let ops_per_thread = 1000;
let handles: Vec<_> = (0..num_threads)
.map(|thread_id| {
let cache = Arc::clone(&cache);
thread::spawn(move || {
for i in 0..ops_per_thread {
let key = format!("thread{}-key{}", thread_id, i);
let value = thread_id * 10000 + i;
cache.put(key.clone(), value, 1);
if let Some(v) = cache.get(&key) {
assert_eq!(v, value);
}
}
})
})
.collect();
for handle in handles {
handle.join().expect("Thread panicked");
}
println!(
" Completed {} operations across {} threads",
num_threads * ops_per_thread * 2, num_threads
);
println!(" Final cache size: {} items", cache.len());
}
fn zero_copy_get_with() {
println!("2. Zero-Copy Access with get_with()");
println!(" ----------------------------------");
let cache: ConcurrentLruCache<String, Vec<u8>> =
ConcurrentLruCache::init(lru_config(100, 16), None);
let large_data = vec![1u8; 1024]; cache.put("large_key".to_string(), large_data, 1);
let sum: Option<u64> = cache.get_with(&"large_key".to_string(), |data| {
data.iter().map(|&x| x as u64).sum()
});
println!(" Stored 1KB of data in cache");
println!(
" Computed sum without cloning: {}",
sum.unwrap_or_default()
);
let _cloned_data = cache.get(&"large_key".to_string());
println!(" get() returns a clone - use get_with() to avoid cloning");
let has_zeros: Option<bool> =
cache.get_with(&"large_key".to_string(), |data| data.contains(&0));
println!(" Data contains zeros: {}", has_zeros.unwrap_or(false));
}
fn segment_tuning() {
println!("3. Segment Count Tuning");
println!(" ---------------------");
let default_cache: ConcurrentLruCache<String, i32> =
ConcurrentLruCache::init(lru_config(10000, 16), None);
println!(
" Default cache: {} segments",
default_cache.segment_count()
);
let high_concurrency: ConcurrentLruCache<String, i32> =
ConcurrentLruCache::init(lru_config(10000, 32), None);
println!(
" High-concurrency cache: {} segments",
high_concurrency.segment_count()
);
let low_contention: ConcurrentLruCache<String, i32> =
ConcurrentLruCache::init(lru_config(10000, 4), None);
println!(
" Low-contention cache: {} segments",
low_contention.segment_count()
);
println!();
println!(" Segment tuning guidelines:");
println!(" - More segments = better parallelism, higher memory");
println!(" - Use powers of 2 for best hash distribution");
println!(" - Start with default (16), increase if high contention");
}
fn all_concurrent_cache_types() {
println!("4. All Concurrent Cache Types");
println!(" ----------------------------");
let lru: ConcurrentLruCache<String, i32> = ConcurrentLruCache::init(lru_config(100, 16), None);
lru.put("key".to_string(), 1, 1);
println!(" ConcurrentLruCache: General purpose, recency-based");
let slru: ConcurrentSlruCache<String, i32> =
ConcurrentSlruCache::init(slru_config(100, 20, 16), None);
slru.put("key".to_string(), 1, 1);
println!(" ConcurrentSlruCache: Scan resistant, two-segment design");
let lfu: ConcurrentLfuCache<String, i32> = ConcurrentLfuCache::init(lfu_config(100, 16), None);
lfu.put("key".to_string(), 1, 1);
println!(" ConcurrentLfuCache: Frequency-based eviction");
let lfuda: ConcurrentLfudaCache<String, i32> =
ConcurrentLfudaCache::init(lfuda_config(100, 16), None);
lfuda.put("key".to_string(), 1, 1);
println!(" ConcurrentLfudaCache: Frequency + aging for changing patterns");
let gdsf: ConcurrentGdsfCache<String, Vec<u8>> =
ConcurrentGdsfCache::init(gdsf_config(10000, 16), None);
gdsf.put("small.txt".to_string(), vec![0u8; 100], 100);
gdsf.put("large.jpg".to_string(), vec![0u8; 5000], 5000);
println!(" ConcurrentGdsfCache: Size-aware, for variable-size objects");
}
fn throughput_comparison() {
println!("5. Throughput Comparison (8 threads, 10K ops each)");
println!(" -------------------------------------------------");
let ops_per_thread = 10_000;
let num_threads = 8;
for segments in [1, 4, 8, 16, 32] {
let cache: Arc<ConcurrentLruCache<i32, i32>> =
Arc::new(ConcurrentLruCache::init(lru_config(10000, segments), None));
let start = Instant::now();
let handles: Vec<_> = (0..num_threads)
.map(|t| {
let cache = Arc::clone(&cache);
thread::spawn(move || {
let offset = t * ops_per_thread;
for i in 0..ops_per_thread {
let key = offset + i;
cache.put(key, key, 1);
cache.get(&key);
}
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
let elapsed = start.elapsed();
let total_ops = num_threads * ops_per_thread * 2;
let ops_per_sec = (total_ops as f64 / elapsed.as_secs_f64()) as u64;
println!(
" {:2} segments: {:>7.2?} ({:>10} ops/sec)",
segments, elapsed, ops_per_sec
);
}
println!();
println!(" More segments generally improve throughput up to a point.");
println!(" Optimal segment count depends on workload and hardware.");
}