use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Instant;
#[derive(Debug)]
pub struct TieredStats {
pub vram_hits: AtomicU64,
pub ram_hits: AtomicU64,
pub nvme_hits: AtomicU64,
pub layers_loaded: AtomicU64,
pub bytes_uploaded: AtomicU64,
pub bytes_downloaded: AtomicU64,
pub nvme_load_time_ns: AtomicU64,
pub vram_upload_time_ns: AtomicU64,
pub vram_evictions: AtomicU64,
pub ram_evictions: AtomicU64,
pub vram_bytes_evicted: AtomicU64,
pub ram_bytes_evicted: AtomicU64,
pub prefetch_requests: AtomicU64,
pub prefetch_hits: AtomicU64,
start_time: Instant,
}
impl TieredStats {
pub fn new() -> Self {
Self {
vram_hits: AtomicU64::new(0),
ram_hits: AtomicU64::new(0),
nvme_hits: AtomicU64::new(0),
layers_loaded: AtomicU64::new(0),
bytes_uploaded: AtomicU64::new(0),
bytes_downloaded: AtomicU64::new(0),
nvme_load_time_ns: AtomicU64::new(0),
vram_upload_time_ns: AtomicU64::new(0),
vram_evictions: AtomicU64::new(0),
ram_evictions: AtomicU64::new(0),
vram_bytes_evicted: AtomicU64::new(0),
ram_bytes_evicted: AtomicU64::new(0),
prefetch_requests: AtomicU64::new(0),
prefetch_hits: AtomicU64::new(0),
start_time: Instant::now(),
}
}
pub fn record_vram_hit(&self) {
self.vram_hits.fetch_add(1, Ordering::Relaxed);
}
pub fn record_ram_hit(&self) {
self.ram_hits.fetch_add(1, Ordering::Relaxed);
}
pub fn record_nvme_hit(&self) {
self.nvme_hits.fetch_add(1, Ordering::Relaxed);
}
pub fn record_layer_loaded(&self) {
self.layers_loaded.fetch_add(1, Ordering::Relaxed);
}
pub fn record_vram_upload(&self, bytes: u64, duration_ns: u64) {
self.bytes_uploaded.fetch_add(bytes, Ordering::Relaxed);
self.vram_upload_time_ns
.fetch_add(duration_ns, Ordering::Relaxed);
}
pub fn record_vram_download(&self, bytes: u64) {
self.bytes_downloaded.fetch_add(bytes, Ordering::Relaxed);
}
pub fn record_nvme_load(&self, duration_ns: u64) {
self.nvme_load_time_ns
.fetch_add(duration_ns, Ordering::Relaxed);
}
pub fn record_vram_eviction(&self, bytes: u64) {
self.vram_evictions.fetch_add(1, Ordering::Relaxed);
self.vram_bytes_evicted.fetch_add(bytes, Ordering::Relaxed);
}
pub fn record_ram_eviction(&self, bytes: u64) {
self.ram_evictions.fetch_add(1, Ordering::Relaxed);
self.ram_bytes_evicted.fetch_add(bytes, Ordering::Relaxed);
}
pub fn record_prefetch_request(&self) {
self.prefetch_requests.fetch_add(1, Ordering::Relaxed);
}
pub fn record_prefetch_hit(&self) {
self.prefetch_hits.fetch_add(1, Ordering::Relaxed);
}
pub fn total_accesses(&self) -> u64 {
self.vram_hits.load(Ordering::Relaxed)
+ self.ram_hits.load(Ordering::Relaxed)
+ self.nvme_hits.load(Ordering::Relaxed)
}
pub fn vram_hit_rate(&self) -> f64 {
let total = self.total_accesses();
if total == 0 {
return 0.0;
}
self.vram_hits.load(Ordering::Relaxed) as f64 / total as f64
}
pub fn ram_hit_rate(&self) -> f64 {
let non_vram =
self.ram_hits.load(Ordering::Relaxed) + self.nvme_hits.load(Ordering::Relaxed);
if non_vram == 0 {
return 0.0;
}
self.ram_hits.load(Ordering::Relaxed) as f64 / non_vram as f64
}
pub fn prefetch_effectiveness(&self) -> f64 {
let requests = self.prefetch_requests.load(Ordering::Relaxed);
if requests == 0 {
return 0.0;
}
self.prefetch_hits.load(Ordering::Relaxed) as f64 / requests as f64
}
pub fn avg_nvme_load_ms(&self) -> f64 {
let hits = self.nvme_hits.load(Ordering::Relaxed);
if hits == 0 {
return 0.0;
}
let total_ns = self.nvme_load_time_ns.load(Ordering::Relaxed);
(total_ns as f64 / hits as f64) / 1_000_000.0
}
pub fn avg_vram_upload_ms(&self) -> f64 {
let uploads = self.layers_loaded.load(Ordering::Relaxed);
if uploads == 0 {
return 0.0;
}
let total_ns = self.vram_upload_time_ns.load(Ordering::Relaxed);
(total_ns as f64 / uploads as f64) / 1_000_000.0
}
pub fn elapsed(&self) -> std::time::Duration {
self.start_time.elapsed()
}
pub fn upload_bandwidth_gbps(&self) -> f64 {
let total_ns = self.vram_upload_time_ns.load(Ordering::Relaxed);
if total_ns == 0 {
return 0.0;
}
let bytes = self.bytes_uploaded.load(Ordering::Relaxed);
let seconds = total_ns as f64 / 1_000_000_000.0;
(bytes as f64 / seconds) / (1024.0 * 1024.0 * 1024.0)
}
pub fn snapshot(&self) -> StatsSnapshot {
StatsSnapshot {
vram_hits: self.vram_hits.load(Ordering::Relaxed),
ram_hits: self.ram_hits.load(Ordering::Relaxed),
nvme_hits: self.nvme_hits.load(Ordering::Relaxed),
layers_loaded: self.layers_loaded.load(Ordering::Relaxed),
bytes_uploaded: self.bytes_uploaded.load(Ordering::Relaxed),
bytes_downloaded: self.bytes_downloaded.load(Ordering::Relaxed),
vram_evictions: self.vram_evictions.load(Ordering::Relaxed),
ram_evictions: self.ram_evictions.load(Ordering::Relaxed),
prefetch_requests: self.prefetch_requests.load(Ordering::Relaxed),
prefetch_hits: self.prefetch_hits.load(Ordering::Relaxed),
elapsed_secs: self.start_time.elapsed().as_secs_f64(),
}
}
}
impl Default for TieredStats {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct StatsSnapshot {
pub vram_hits: u64,
pub ram_hits: u64,
pub nvme_hits: u64,
pub layers_loaded: u64,
pub bytes_uploaded: u64,
pub bytes_downloaded: u64,
pub vram_evictions: u64,
pub ram_evictions: u64,
pub prefetch_requests: u64,
pub prefetch_hits: u64,
pub elapsed_secs: f64,
}
impl std::fmt::Display for StatsSnapshot {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let total = self.vram_hits + self.ram_hits + self.nvme_hits;
let vram_rate = if total > 0 {
self.vram_hits as f64 / total as f64 * 100.0
} else {
0.0
};
let ram_rate = if total > 0 {
self.ram_hits as f64 / total as f64 * 100.0
} else {
0.0
};
write!(
f,
"TieredStats {{ hits: VRAM={} ({:.1}%), RAM={} ({:.1}%), NVMe={}, \
evictions: VRAM={}, RAM={}, uploaded: {:.2}GB, elapsed: {:.1}s }}",
self.vram_hits,
vram_rate,
self.ram_hits,
ram_rate,
self.nvme_hits,
self.vram_evictions,
self.ram_evictions,
self.bytes_uploaded as f64 / (1024.0 * 1024.0 * 1024.0),
self.elapsed_secs
)
}
}