use std::fmt;
use std::sync::atomic::{AtomicU64, Ordering};
use crate::size_class::NUM_CLASSES;
#[derive(Debug)]
pub(crate) struct Counters {
pub gets: AtomicU64,
pub tls_hits: AtomicU64,
pub shared_hits: AtomicU64,
pub allocations: AtomicU64,
pub puts: AtomicU64,
pub discards: AtomicU64,
pub oversize: AtomicU64,
}
impl Counters {
pub const fn new() -> Self {
Self {
gets: AtomicU64::new(0),
tls_hits: AtomicU64::new(0),
shared_hits: AtomicU64::new(0),
allocations: AtomicU64::new(0),
puts: AtomicU64::new(0),
discards: AtomicU64::new(0),
oversize: AtomicU64::new(0),
}
}
pub fn reset(&self) {
self.gets.store(0, Ordering::Relaxed);
self.tls_hits.store(0, Ordering::Relaxed);
self.shared_hits.store(0, Ordering::Relaxed);
self.allocations.store(0, Ordering::Relaxed);
self.puts.store(0, Ordering::Relaxed);
self.discards.store(0, Ordering::Relaxed);
self.oversize.store(0, Ordering::Relaxed);
}
}
#[derive(Debug, Clone)]
pub struct PoolStats {
pub gets: u64,
pub tls_hits: u64,
pub shared_hits: u64,
pub allocations: u64,
pub puts: u64,
pub discards: u64,
pub oversize: u64,
pub tls_hit_rate: f64,
pub hit_rate: f64,
pub classes: [ClassInfo; NUM_CLASSES],
}
#[derive(Debug, Clone, Copy)]
pub struct ClassInfo {
pub boundary: usize,
pub buffered: usize,
}
impl fmt::Display for PoolStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"gets: {} | puts: {} | hit_rate: {:.1}%",
self.gets,
self.puts,
self.hit_rate * 100.0
)?;
writeln!(
f,
" tls_hits: {} ({:.1}%) | shared_hits: {} | allocations: {} | discards: {} | oversize: {}",
self.tls_hits,
self.tls_hit_rate * 100.0,
self.shared_hits,
self.allocations,
self.discards,
self.oversize
)?;
for ci in &self.classes {
if ci.buffered > 0 {
writeln!(f, " [{:>7}] {} buffered", format_size(ci.boundary), ci.buffered)?;
}
}
Ok(())
}
}
fn format_size(bytes: usize) -> String {
if bytes >= 1024 * 1024 {
format!("{}MB", bytes / (1024 * 1024))
} else if bytes >= 1024 {
format!("{}KB", bytes / 1024)
} else {
format!("{bytes}B")
}
}
pub(crate) fn snapshot(counters: &Counters, classes: &[crate::size_class::SizeClass]) -> PoolStats {
let gets = counters.gets.load(Ordering::Relaxed);
let tls_hits = counters.tls_hits.load(Ordering::Relaxed);
let shared_hits = counters.shared_hits.load(Ordering::Relaxed);
let tls_hit_rate = if gets == 0 {
0.0
} else {
tls_hits as f64 / gets as f64
};
let hit_rate = if gets == 0 {
0.0
} else {
(tls_hits + shared_hits) as f64 / gets as f64
};
let mut class_info: [ClassInfo; NUM_CLASSES] =
[ClassInfo { boundary: 0, buffered: 0 }; NUM_CLASSES];
for (i, class) in classes.iter().enumerate() {
class_info[i] = ClassInfo {
boundary: class.class_size,
buffered: class.len(),
};
}
PoolStats {
gets,
tls_hits,
shared_hits,
allocations: counters.allocations.load(Ordering::Relaxed),
puts: counters.puts.load(Ordering::Relaxed),
discards: counters.discards.load(Ordering::Relaxed),
oversize: counters.oversize.load(Ordering::Relaxed),
tls_hit_rate,
hit_rate,
classes: class_info,
}
}