use crossbeam_queue::SegQueue;
use std::sync::atomic::{AtomicUsize, Ordering};
use tracing::debug;
#[derive(Debug)]
pub(super) struct MemoryPool {
available: SegQueue<Vec<f32>>,
max_size: usize,
current_size: AtomicUsize,
vector_capacity: usize,
pool_hits: AtomicUsize,
pool_misses: AtomicUsize,
}
impl MemoryPool {
pub(super) fn new(size: usize, vector_capacity: usize) -> Self {
let pool = Self {
available: SegQueue::new(),
max_size: size,
current_size: AtomicUsize::new(0),
vector_capacity,
pool_hits: AtomicUsize::new(0),
pool_misses: AtomicUsize::new(0),
};
for _ in 0..size {
pool.available.push(Vec::with_capacity(vector_capacity));
}
pool.current_size.store(size, Ordering::Release);
pool
}
pub(super) fn get_or_allocate(&self) -> Vec<f32> {
match self.available.pop() {
Some(vec) => {
self.pool_hits.fetch_add(1, Ordering::Relaxed);
vec
}
None => {
let misses = self.pool_misses.fetch_add(1, Ordering::Relaxed) + 1;
if misses.is_multiple_of(100) {
debug!(
"Memory pool exhausted: {} misses (pool_size={}, capacity={})",
misses, self.max_size, self.vector_capacity
);
}
Vec::with_capacity(self.vector_capacity)
}
}
}
#[allow(dead_code)] pub(super) fn return_vector(&self, mut vec: Vec<f32>) {
if self.available.len() < self.max_size {
vec.clear();
self.available.push(vec);
}
}
#[allow(dead_code)]
pub(super) fn get_stats(&self) -> PoolStats {
PoolStats {
available: self.available.len(),
total: self.current_size.load(Ordering::Acquire),
hits: self.pool_hits.load(Ordering::Relaxed),
misses: self.pool_misses.load(Ordering::Relaxed),
}
}
#[allow(dead_code)]
pub(super) fn hit_rate(&self) -> f64 {
let hits = self.pool_hits.load(Ordering::Relaxed) as f64;
let misses = self.pool_misses.load(Ordering::Relaxed) as f64;
let total = hits + misses;
if total > 0.0 {
(hits / total) * 100.0
} else {
100.0
}
}
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub(super) struct PoolStats {
pub(super) available: usize,
pub(super) total: usize,
pub(super) hits: usize,
pub(super) misses: usize,
}