use std::cell::RefCell;
use std::sync::LazyLock;
use std::time::Instant;
use vyre_foundation::ir::Program;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum Phase {
Prepare,
Ptx,
ModuleKey,
Stage,
Resolve,
Lease,
LaunchLoop,
Release,
Readback,
}
impl Phase {
const COUNT: usize = 9;
const fn index(self) -> usize {
match self {
Self::Prepare => 0,
Self::Ptx => 1,
Self::ModuleKey => 2,
Self::Stage => 3,
Self::Resolve => 4,
Self::Lease => 5,
Self::LaunchLoop => 6,
Self::Release => 7,
Self::Readback => 8,
}
}
const fn label(self) -> &'static str {
match self {
Self::Prepare => "prepare_ns",
Self::Ptx => "ptx_ns",
Self::ModuleKey => "modkey_ns",
Self::Stage => "stage_ns",
Self::Resolve => "resolve_ns",
Self::Lease => "lease_ns",
Self::LaunchLoop => "launch_ns",
Self::Release => "release_ns",
Self::Readback => "readback_ns",
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum Nested {
PtxDigest,
PtxVsa,
}
impl Nested {
const COUNT: usize = 2;
const fn index(self) -> usize {
match self {
Self::PtxDigest => 0,
Self::PtxVsa => 1,
}
}
const fn label(self) -> &'static str {
match self {
Self::PtxDigest => "sub_ptx_digest_ns",
Self::PtxVsa => "sub_ptx_vsa_ns",
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct DispatchPhases {
host_ns: [u64; Phase::COUNT],
nested_ns: [u64; Nested::COUNT],
pub(crate) nodes: u64,
pub(crate) ptx_bytes: u64,
pub(crate) buffers: u64,
pub(crate) bindings: u64,
pub(crate) fixpoint_iterations: u64,
pub(crate) grid_blocks: u64,
pub(crate) kernel_ns: u64,
}
impl DispatchPhases {
fn named_host_ns(&self) -> u64 {
self.host_ns.iter().copied().sum()
}
}
thread_local! {
static CURRENT: RefCell<DispatchPhases> = const { RefCell::new(DispatchPhases::new_zeroed()) };
}
impl DispatchPhases {
const fn new_zeroed() -> Self {
Self {
host_ns: [0; Phase::COUNT],
nested_ns: [0; Nested::COUNT],
nodes: 0,
ptx_bytes: 0,
buffers: 0,
bindings: 0,
fixpoint_iterations: 0,
grid_blocks: 0,
kernel_ns: 0,
}
}
}
static PROBE_ENABLED: LazyLock<bool> =
LazyLock::new(|| std::env::var_os("VYRE_CUDA_DISPATCH_PHASE_PROBE").is_some());
#[inline]
pub(crate) fn enabled() -> bool {
*PROBE_ENABLED
}
#[inline]
pub(crate) fn measure<T>(phase: Phase, work: impl FnOnce() -> T) -> T {
if !enabled() {
return work();
}
let started = Instant::now();
let out = work();
add_host_ns(phase, saturating_elapsed_ns(started));
out
}
pub(crate) fn add_host_ns(phase: Phase, ns: u64) {
if !enabled() {
return;
}
let _ = CURRENT.try_with(|current| {
if let Ok(mut current) = current.try_borrow_mut() {
let slot = &mut current.host_ns[phase.index()];
*slot = slot.saturating_add(ns);
}
});
}
#[inline]
pub(crate) fn measure_nested<T>(nested: Nested, work: impl FnOnce() -> T) -> T {
if !enabled() {
return work();
}
let started = Instant::now();
let out = work();
let ns = saturating_elapsed_ns(started);
let _ = CURRENT.try_with(|current| {
if let Ok(mut current) = current.try_borrow_mut() {
let slot = &mut current.nested_ns[nested.index()];
*slot = slot.saturating_add(ns);
}
});
out
}
#[inline]
pub(crate) fn mark() -> Option<Instant> {
enabled().then(Instant::now)
}
#[inline]
pub(crate) fn charge(phase: Phase, started: Option<Instant>) {
if let Some(started) = started {
add_host_ns(phase, saturating_elapsed_ns(started));
}
}
#[inline]
pub(crate) fn charge_since(phase: Phase, started: Instant) {
if !enabled() {
return;
}
add_host_ns(phase, saturating_elapsed_ns(started));
}
#[inline]
pub(crate) fn charge_remainder(outer: Phase, started: Option<Instant>, inner: Phase) {
let Some(started) = started else {
return;
};
let total = saturating_elapsed_ns(started);
add_host_ns(outer, total.saturating_sub(phase_ns(inner)));
}
fn phase_ns(phase: Phase) -> u64 {
CURRENT
.try_with(|current| {
current
.try_borrow()
.map(|current| current.host_ns[phase.index()])
.unwrap_or(0)
})
.unwrap_or(0)
}
pub(crate) fn record_counts(
program: &Program,
ptx_bytes: usize,
bindings: usize,
fixpoint_iterations: usize,
grid: [u32; 3],
) {
if !enabled() {
return;
}
let mut nodes = 0u64;
vyre_foundation::transform::visit::walk_nodes(program, |_| {
nodes = nodes.saturating_add(1);
});
let blocks = u64::from(grid[0])
.saturating_mul(u64::from(grid[1]))
.saturating_mul(u64::from(grid[2]));
let _ = CURRENT.try_with(|current| {
if let Ok(mut current) = current.try_borrow_mut() {
current.nodes = nodes;
current.ptx_bytes = ptx_bytes as u64;
current.buffers = program.buffers().len() as u64;
current.bindings = bindings as u64;
current.fixpoint_iterations = fixpoint_iterations as u64;
current.grid_blocks = blocks;
}
});
}
pub(crate) fn record_kernel_ns(kernel_ns: u64) {
if !enabled() {
return;
}
let _ = CURRENT.try_with(|current| {
if let Ok(mut current) = current.try_borrow_mut() {
current.kernel_ns = kernel_ns;
}
});
}
fn saturating_elapsed_ns(started: Instant) -> u64 {
u64::try_from(started.elapsed().as_nanos()).unwrap_or(u64::MAX)
}
pub(crate) fn emit(
sequence: u64,
wall_ns: u64,
enqueue_ns: u64,
wait_ns: u64,
device_window_ns: Option<u64>,
ptx_cache_hits: u64,
ptx_cache_misses: u64,
) {
if !enabled() {
return;
}
let Ok(phases) = CURRENT.try_with(|current| {
let taken = current.try_borrow_mut().map(|mut current| {
let taken = *current;
*current = DispatchPhases::new_zeroed();
taken
});
taken.unwrap_or_default()
}) else {
return;
};
let mut line = String::with_capacity(512);
line.push_str("vyre-cuda-dispatch-phase");
push_field(&mut line, "seq", sequence);
push_field(&mut line, "nodes", phases.nodes);
push_field(&mut line, "ptx_bytes", phases.ptx_bytes);
push_field(&mut line, "buffers", phases.buffers);
push_field(&mut line, "bindings", phases.bindings);
push_field(&mut line, "fixpoint", phases.fixpoint_iterations);
push_field(&mut line, "grid_blocks", phases.grid_blocks);
push_field(&mut line, "wall_ns", wall_ns);
push_field(&mut line, "enqueue_ns", enqueue_ns);
push_field(&mut line, "wait_ns", wait_ns);
push_field(&mut line, "device_window_ns", device_window_ns.unwrap_or(0));
push_field(&mut line, "kernel_ns", phases.kernel_ns);
for phase in [
Phase::Prepare,
Phase::Ptx,
Phase::ModuleKey,
Phase::Stage,
Phase::Resolve,
Phase::Lease,
Phase::LaunchLoop,
Phase::Release,
Phase::Readback,
] {
push_field(&mut line, phase.label(), phases.host_ns[phase.index()]);
}
for nested in [Nested::PtxDigest, Nested::PtxVsa] {
push_field(&mut line, nested.label(), phases.nested_ns[nested.index()]);
}
push_field(&mut line, "named_host_ns", phases.named_host_ns());
push_field(
&mut line,
"unattributed_ns",
wall_ns.saturating_sub(phases.named_host_ns()),
);
push_field(&mut line, "ptx_cache_hits", ptx_cache_hits);
push_field(&mut line, "ptx_cache_misses", ptx_cache_misses);
eprintln!("{line}");
}
fn push_field(line: &mut String, name: &str, value: u64) {
use std::fmt::Write;
let _ = write!(line, " {name}={value}");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_phase_index_is_inside_the_accumulator() {
let phases = [
Phase::Prepare,
Phase::Ptx,
Phase::ModuleKey,
Phase::Stage,
Phase::Resolve,
Phase::Lease,
Phase::LaunchLoop,
Phase::Release,
Phase::Readback,
];
assert_eq!(phases.len(), Phase::COUNT);
let mut seen = [false; Phase::COUNT];
for phase in phases {
let index = phase.index();
assert!(index < Phase::COUNT, "{phase:?} indexes {index}");
assert!(!seen[index], "{phase:?} reuses index {index}");
seen[index] = true;
}
assert!(seen.iter().all(|slot| *slot));
}
#[test]
fn phase_labels_are_distinct() {
let labels = [
Phase::Prepare.label(),
Phase::Ptx.label(),
Phase::ModuleKey.label(),
Phase::Stage.label(),
Phase::Resolve.label(),
Phase::Lease.label(),
Phase::LaunchLoop.label(),
Phase::Release.label(),
Phase::Readback.label(),
];
let mut sorted = labels;
sorted.sort_unstable();
let mut deduped = sorted.to_vec();
deduped.dedup();
assert_eq!(deduped.len(), labels.len(), "duplicate phase label");
}
#[test]
fn disabled_measure_records_nothing() {
if enabled() {
return;
}
let value = measure(Phase::Prepare, || 7u32);
assert_eq!(value, 7);
add_host_ns(Phase::Prepare, 1_000_000);
let observed = CURRENT.with(|current| current.borrow().named_host_ns());
assert_eq!(observed, 0);
}
#[test]
fn named_total_is_the_plain_sum_of_phases() {
let mut phases = DispatchPhases::new_zeroed();
phases.host_ns[Phase::Prepare.index()] = 11;
phases.host_ns[Phase::Ptx.index()] = 22;
phases.host_ns[Phase::Readback.index()] = 33;
assert_eq!(phases.named_host_ns(), 66);
}
}