use std::sync::{Arc, atomic::AtomicU64, atomic::Ordering::Relaxed};
use crate::{BytePageSize, size::PAGE_SIZES};
const CLASSES: usize = PAGE_SIZES.len();
#[derive(Debug, Default)]
pub struct PageCacheMetrics {
classes: [Class; CLASSES],
}
#[derive(Debug, Default)]
struct Class {
hits: AtomicU64,
misses: AtomicU64,
cached: AtomicU64,
freed: AtomicU64,
added: AtomicU64,
removed: AtomicU64,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct PageCacheSnapshot {
sizes: [PageCacheStats; CLASSES],
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct PageCacheStats {
pub size: BytePageSize,
pub hits: u64,
pub misses: u64,
pub cached: u64,
pub freed: u64,
pub pages: usize,
pub bytes: usize,
}
impl PageCacheMetrics {
pub fn new() -> Arc<Self> {
Arc::default()
}
pub fn current() -> Arc<Self> {
crate::stvec::cache_metrics().unwrap_or_default()
}
pub fn set_current(metrics: Arc<Self>) {
crate::stvec::set_cache_metrics(metrics);
}
pub fn snapshot(&self) -> PageCacheSnapshot {
let mut snapshot = PageCacheSnapshot::default();
for (stats, class) in snapshot.sizes.iter_mut().zip(&self.classes) {
stats.hits = class.hits.load(Relaxed);
stats.misses = class.misses.load(Relaxed);
stats.cached = class.cached.load(Relaxed);
stats.freed = class.freed.load(Relaxed);
let removed = class.removed.load(Relaxed);
let hits = stats.hits;
stats.pages =
(stats.cached + class.added.load(Relaxed)).saturating_sub(hits + removed) as usize;
stats.bytes = stats.pages * stats.size.alloc_size();
}
snapshot
}
pub(crate) fn hit(&self, class: usize) {
inc(&self.classes[class].hits, 1);
}
pub(crate) fn miss(&self, class: usize) {
inc(&self.classes[class].misses, 1);
}
pub(crate) fn cached(&self, class: usize) {
inc(&self.classes[class].cached, 1);
}
pub(crate) fn freed(&self, class: usize) {
inc(&self.classes[class].freed, 1);
}
pub(crate) fn remove_pages(&self, class: usize, pages: usize) {
if pages != 0 {
inc(&self.classes[class].removed, pages as u64);
}
}
pub(crate) fn add_pages(&self, class: usize, pages: usize) {
if pages != 0 {
inc(&self.classes[class].added, pages as u64);
}
}
}
#[inline]
fn inc(counter: &AtomicU64, n: u64) {
counter.store(counter.load(Relaxed).wrapping_add(n), Relaxed);
}
impl PageCacheSnapshot {
pub fn get(&self, size: BytePageSize) -> Option<&PageCacheStats> {
self.sizes.get(size as usize)
}
pub fn iter(&self) -> impl Iterator<Item = &PageCacheStats> {
self.sizes.iter()
}
pub fn total(&self) -> PageCacheStats {
self.sizes.iter().fold(
PageCacheStats {
size: BytePageSize::Unset,
..PageCacheStats::default()
},
|mut total, s| {
total.hits += s.hits;
total.misses += s.misses;
total.cached += s.cached;
total.freed += s.freed;
total.pages += s.pages;
total.bytes += s.bytes;
total
},
)
}
}
impl Default for PageCacheSnapshot {
fn default() -> Self {
Self {
sizes: PAGE_SIZES.map(|size| PageCacheStats {
size,
..PageCacheStats::default()
}),
}
}
}