#[cfg(feature = "timing")]
#[derive(Debug, Clone, Copy)]
pub struct Phase {
pub name: &'static str,
pub wall_seconds: f64,
pub gpu_seconds: f64,
}
#[cfg(feature = "timing")]
#[derive(Debug, Clone, Default)]
pub struct ParseTimings {
pub phases: Vec<Phase>,
}
#[cfg(feature = "timing")]
mod imp {
use super::{ParseTimings, Phase};
use std::cell::RefCell;
use std::time::Instant;
thread_local! {
static CURRENT: RefCell<Option<ParseTimings>> = const { RefCell::new(None) };
static KERNELS: RefCell<Vec<(String, f64)>> = const { RefCell::new(Vec::new()) };
}
pub(crate) fn record_kernel(name: &str, gpu_seconds: f64) {
KERNELS.with(|k| k.borrow_mut().push((name.to_owned(), gpu_seconds)));
}
#[must_use]
pub fn take_kernel_timings() -> Vec<(String, f64)> {
KERNELS.with(|k| std::mem::take(&mut *k.borrow_mut()))
}
#[derive(Debug)]
pub struct PhaseTimer(Instant);
pub(crate) fn begin_parse() {
CURRENT.with(|c| *c.borrow_mut() = Some(ParseTimings::default()));
}
pub(crate) fn start() -> PhaseTimer {
PhaseTimer(Instant::now())
}
pub(crate) fn record(name: &'static str, timer: PhaseTimer, gpu_seconds: f64) {
let wall_seconds = timer.0.elapsed().as_secs_f64();
CURRENT.with(|c| {
if let Some(t) = c.borrow_mut().as_mut() {
t.phases.push(Phase {
name,
wall_seconds,
gpu_seconds,
});
}
});
}
#[must_use]
pub fn take_parse_timings() -> Option<ParseTimings> {
CURRENT.with(|c| c.borrow_mut().take())
}
}
#[cfg(not(feature = "timing"))]
mod imp {
#[derive(Debug)]
pub struct PhaseTimer;
#[inline(always)]
pub(crate) fn begin_parse() {}
#[inline(always)]
pub(crate) fn start() -> PhaseTimer {
PhaseTimer
}
#[inline(always)]
pub(crate) fn record(name: &'static str, timer: PhaseTimer, gpu_seconds: f64) {
let _ = (name, timer, gpu_seconds);
}
}
pub(crate) use imp::{begin_parse, record, start};
#[cfg(feature = "timing")]
pub(crate) use imp::record_kernel;
#[cfg(feature = "timing")]
pub use imp::{take_kernel_timings, take_parse_timings};
#[cfg(all(test, feature = "timing"))]
mod tests {
use super::*;
#[test]
fn record_take_roundtrip_and_reset() {
begin_parse();
record("a", start(), 0.25);
record("b", start(), 0.0);
let t = take_parse_timings().expect("recording was begun");
assert_eq!(t.phases.len(), 2);
assert_eq!(t.phases[0].name, "a");
assert!((t.phases[0].gpu_seconds - 0.25).abs() < 1e-12);
assert_eq!(t.phases[1].name, "b");
assert!(t.phases[1].wall_seconds >= 0.0);
assert!(take_parse_timings().is_none());
record("stray", start(), 0.0);
assert!(take_parse_timings().is_none());
begin_parse();
record("c", start(), 0.0);
assert_eq!(take_parse_timings().unwrap().phases.len(), 1);
}
}