use crate::sync::HybridMutex;
use std::collections::HashMap;
use std::hash::Hash;
use std::sync::atomic::{AtomicUsize, Ordering};
const BATCH_STRIPES: usize = 16;
pub(crate) struct AccessBatcher<K> {
active_idx: AtomicUsize,
instances: [Box<[HybridMutex<HashMap<K, u64>>]>; 2],
}
impl<K: Hash + Eq> AccessBatcher<K> {
pub(crate) fn new() -> Self {
let create_instance = || -> Box<[HybridMutex<HashMap<K, u64>>]> {
(0..BATCH_STRIPES)
.map(|_| HybridMutex::new(HashMap::new()))
.collect()
};
Self {
active_idx: AtomicUsize::new(0),
instances: [create_instance(), create_instance()],
}
}
#[inline]
pub(crate) fn record_access(&self, key: &K, hash: u64, cost: u64)
where
K: Clone,
{
let idx = self.active_idx.load(Ordering::Relaxed);
let stripes = &self.instances[idx];
let stripe_idx = hash as usize & (BATCH_STRIPES - 1);
let mut guard = stripes[stripe_idx].lock();
if !guard.contains_key(key) {
guard.insert(key.clone(), cost);
}
}
pub(crate) fn drain(&self) -> HashMap<K, u64> {
let inactive_idx = self.active_idx.swap(1, Ordering::AcqRel);
let active_idx = 1 - inactive_idx;
self.active_idx.store(active_idx, Ordering::Release);
let inactive_stripes = &self.instances[inactive_idx];
let mut final_batch = HashMap::new();
for stripe_mutex in inactive_stripes.iter() {
let drained = {
let mut guard = stripe_mutex.lock();
guard.drain().collect::<Vec<_>>()
};
final_batch.extend(drained);
}
final_batch
}
}