use std::cell::Cell;
use std::collections::VecDeque;
use std::sync::{Mutex, OnceLock};
const MAX_STATS: usize = 1024;
#[derive(Clone, Debug, Default)]
pub struct KernelStat {
pub name: &'static str,
pub n: usize,
pub p: usize,
pub k: usize,
pub nnz: usize,
pub flops_est: usize,
pub bytes_est: usize,
pub cpu_ms: f64,
pub gpu_ms: Option<f64>,
}
#[derive(Clone, Debug, Default)]
pub struct KernelStatsSnapshot {
pub stats: Vec<KernelStat>,
}
type DispatchRing = Mutex<VecDeque<KernelStat>>;
const RING_BLOCK: usize = 64;
struct RingArena {
slots: [OnceLock<DispatchRing>; RING_BLOCK],
next: OnceLock<Box<RingArena>>,
}
impl RingArena {
const fn new() -> Self {
Self {
slots: [const { OnceLock::new() }; RING_BLOCK],
next: OnceLock::new(),
}
}
fn claim(&'static self) -> &'static DispatchRing {
let mut block: &'static RingArena = self;
loop {
for slot in block.slots.iter() {
if slot.set(Mutex::new(VecDeque::new())).is_ok() {
return slot.get().expect("the slot was filled just above");
}
}
block = block.next.get_or_init(|| Box::new(RingArena::new())).as_ref();
}
}
fn claimed(&'static self) -> Vec<&'static DispatchRing> {
let mut rings = Vec::new();
let mut block: &'static RingArena = self;
loop {
for slot in block.slots.iter() {
match slot.get() {
Some(ring) => rings.push(ring),
None => return rings,
}
}
match block.next.get() {
Some(next) => block = next.as_ref(),
None => return rings,
}
}
}
}
static RING_ARENA: RingArena = RingArena::new();
thread_local! {
static RING: Cell<Option<&'static DispatchRing>> = const { Cell::new(None) };
}
pub fn record(stat: KernelStat) {
RING.with(|cell| {
let ring = match cell.get() {
Some(ring) => ring,
None => {
let ring = RING_ARENA.claim();
cell.set(Some(ring));
ring
}
};
if let Ok(mut guard) = ring.lock() {
if guard.len() == MAX_STATS {
guard.pop_front();
}
guard.push_back(stat);
}
});
}
pub fn snapshot() -> KernelStatsSnapshot {
let mut stats = Vec::new();
for ring in RING_ARENA.claimed() {
if let Ok(guard) = ring.lock() {
stats.extend(guard.iter().cloned());
}
}
KernelStatsSnapshot { stats }
}
pub fn clear() {
for ring in RING_ARENA.claimed() {
if let Ok(mut guard) = ring.lock() {
guard.clear();
}
}
}
use std::cell::RefCell;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct GpuExecutionTelemetry {
pub h2d_bytes: usize,
pub d2h_bytes: usize,
pub factorization_count: usize,
pub handle_creation_count: usize,
pub kernel_launch_count: usize,
pub cpu_fallback_count: usize,
pub cpu_fallback_reasons: Vec<String>,
pub context_id: usize,
}
thread_local! {
static EXECUTION_TELEMETRY: RefCell<GpuExecutionTelemetry> =
RefCell::new(GpuExecutionTelemetry::default());
}
#[inline]
pub fn telemetry_with<R>(f: impl FnOnce(&mut GpuExecutionTelemetry) -> R) -> R {
EXECUTION_TELEMETRY.with(|cell| f(&mut cell.borrow_mut()))
}
#[inline]
pub fn telemetry_record_h2d(bytes: usize) {
telemetry_with(|t| t.h2d_bytes += bytes);
}
#[inline]
pub fn telemetry_record_d2h(bytes: usize) {
telemetry_with(|t| t.d2h_bytes += bytes);
}
#[inline]
pub fn telemetry_record_factorization() {
telemetry_with(|t| t.factorization_count += 1);
}
#[inline]
pub fn telemetry_record_handle_creation(context_id: usize) {
telemetry_with(|t| {
t.handle_creation_count += 1;
t.context_id = context_id;
});
}
#[inline]
pub fn telemetry_record_kernel_launch() {
telemetry_with(|t| t.kernel_launch_count += 1);
}
#[inline]
pub fn telemetry_record_cpu_fallback(reason: impl Into<String>) {
telemetry_with(|t| {
t.cpu_fallback_count += 1;
t.cpu_fallback_reasons.push(reason.into());
});
}
#[must_use]
pub fn telemetry_snapshot() -> GpuExecutionTelemetry {
telemetry_with(|t| t.clone())
}
pub fn telemetry_reset() {
telemetry_with(|t| *t = GpuExecutionTelemetry::default());
}
#[cfg(test)]
mod dispatch_ring_979_tests {
use super::*;
static EXCLUSIVE: Mutex<()> = Mutex::new(());
fn exclusive() -> std::sync::MutexGuard<'static, ()> {
EXCLUSIVE
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
fn stat(name: &'static str, n: usize) -> KernelStat {
KernelStat {
name,
n,
..Default::default()
}
}
#[test]
fn every_thread_dispatch_reaches_one_snapshot() {
const THREADS: usize = 8;
const PER_THREAD: usize = 32;
let exclusive = exclusive();
clear();
let recorded = std::sync::Barrier::new(THREADS);
std::thread::scope(|scope| {
for thread in 0..THREADS {
let recorded = &recorded;
scope.spawn(move || {
for index in 0..PER_THREAD {
record(stat("ring_test", thread * PER_THREAD + index));
}
recorded.wait();
});
}
});
let recorded = snapshot().stats;
assert_eq!(
recorded.len(),
THREADS * PER_THREAD,
"every thread's dispatches must reach the snapshot"
);
let mut seen: Vec<usize> = recorded.iter().map(|entry| entry.n).collect();
seen.sort_unstable();
let expected: Vec<usize> = (0..THREADS * PER_THREAD).collect();
assert_eq!(
seen, expected,
"no thread's dispatches may be lost or duplicated"
);
clear();
drop(exclusive);
}
#[test]
fn clear_empties_every_ring_and_recording_resumes() {
let exclusive = exclusive();
clear();
std::thread::scope(|scope| {
scope.spawn(|| record(stat("before_clear", 1)));
});
record(stat("before_clear", 2));
assert_eq!(
snapshot().stats.len(),
2,
"a departed thread's attempt and the caller's must both be visible"
);
clear();
assert!(
snapshot().stats.is_empty(),
"clear must empty other threads' rings too, not only the caller's"
);
record(stat("after_clear", 3));
let after = snapshot().stats;
assert_eq!(after.len(), 1, "recording must resume after a clear");
assert_eq!(after[0].n, 3);
clear();
drop(exclusive);
}
#[test]
fn a_rings_capacity_drops_the_oldest_attempt() {
let exclusive = exclusive();
clear();
std::thread::scope(|scope| {
scope.spawn(|| {
for index in 0..(MAX_STATS + 16) {
record(stat("bounded", index));
}
});
});
let recorded = snapshot().stats;
assert_eq!(
recorded.len(),
MAX_STATS,
"the ring is capped at its capacity"
);
assert_eq!(recorded[0].n, 16, "the oldest attempts are the ones dropped");
assert_eq!(
recorded[MAX_STATS - 1].n,
MAX_STATS + 15,
"the newest attempt is kept"
);
clear();
drop(exclusive);
}
}