OXYGENHUB

Coverage / crates/oxy-core/src/timing.rs 68.0%

51 of 75 lines run, 21 of 30 functions · run #412 on main · run missed

1
//! Opt-in phase timings (`OXY_TIMINGS=1`): each named phase prints its
2
//! duration to stderr, for measuring where a command spends its time
3
//! without a profiler. With the counting allocator installed (the `oxy`
4
//! binary's `alloc-stats` feature), each also prints live and peak memory
5
//! and what the phase allocated.
6
7
use std::sync::OnceLock;
8
use std::time::Instant;
9
10162044×
pub fn enabled() -> bool {
11
    static ON: OnceLock<bool> = OnceLock::new();
12162044×
    *ON.get_or_init(|| std::env::var_os("OXY_TIMINGS").is_some_and(|v| !v.is_empty() && v != "0"))
13162044×
}
14
15
/// Report a duration measured by hand (several intervals summed), if timings are on.
1612552×
pub fn record(name: &str, d: std::time::Duration) {
1712552×
    if enabled() {
186×
        eprintln!("timing {:<28} {:>9.2} ms", name, d.as_secs_f64() * 1000.0);
1912546×
    }
2012552×
}
21
22
/// Prints `timing <name> <ms>` when dropped, if timings are on.
23
pub struct Phase {
24
    name: &'static str,
25
    started: Option<Instant>,
26
    /// Allocator counters at the start, when counting.
27
    mem: Option<crate::alloc_stats::Stats>,
28
}
29
30
/// Start timing a phase; the guard reports when it goes out of scope.
31108441×
pub fn phase(name: &'static str) -> Phase {
32108441×
    let on = enabled();
33
    Phase {
34108441×
        name,
35108441×
        started: on.then(Instant::now),
36108441×
        mem: if on {
3782×
            crate::alloc_stats::stats()
38
        } else {
39108359×
            None
40
        },
41
    }
42108441×
}
43
440×
fn mib(n: usize) -> f64 {
450×
    n as f64 / (1024.0 * 1024.0)
460×
}
47
48
/// The allocator's counters as one line, when counting: for the end of a
49
/// command.
50163×
pub fn memory_summary() {
51163×
    if let (true, Some(s)) = (enabled(), crate::alloc_stats::stats()) {
520×
        eprintln!(
530×
            "memory peak {:.1} MiB, live {:.1} MiB, {:.1} MiB in {} allocations",
540×
            mib(s.peak),
550×
            mib(s.live),
560×
            mib(s.total),
570×
            s.count
580×
        );
59163×
    }
60163×
}
61
62
impl Drop for Phase {
63108439×
    fn drop(&mut self) {
64108439×
        if let Some(t) = self.started {
6582×
            let ms = t.elapsed().as_secs_f64() * 1000.0;
6682×
            match (self.mem, crate::alloc_stats::stats()) {
670×
                (Some(a), Some(b)) => eprintln!(
68
                    "timing {:<28} {:>9.2} ms  live {:>8.1} MiB  peak {:>8.1} MiB  +{:.1} MiB in {} allocs",
69
                    self.name,
70
                    ms,
710×
                    mib(b.live),
720×
                    mib(b.peak),
730×
                    mib(b.total - a.total),
740×
                    b.count - a.count
75
                ),
7682×
                _ => eprintln!("timing {:<28} {:>9.2} ms", self.name, ms),
77
            }
78108357×
        }
79108439×
    }
80
}
81
82
static TOTALS: std::sync::Mutex<Vec<(&'static str, std::time::Duration)>> =
83
    std::sync::Mutex::new(Vec::new());
84
85
/// Add `d` to the running total for `name`, for a phase repeated many
86
/// times (once per imported commit, say); `report_totals` prints them.
87494×
pub fn add(name: &'static str, d: std::time::Duration) {
88494×
    if !enabled() {
89482×
        return;
9012×
    }
9112×
    let mut t = TOTALS.lock().unwrap_or_else(|e| e.into_inner());
9230×
    match t.iter_mut().find(|(n, _)| *n == name) {
930×
        Some((_, total)) => *total += d,
9412×
        None => t.push((name, d)),
95
    }
96494×
}
97
98
/// Print and clear the totals `add` gathered.
99606×
pub fn report_totals() {
100606×
    if !enabled() {
101606×
        return;
1020×
    }
1030×
    let t = std::mem::take(&mut *TOTALS.lock().unwrap_or_else(|e| e.into_inner()));
1040×
    for (name, d) in t {
1050×
        record(name, d);
1060×
    }
107606×
}
108
109
/// Times from one point to the next, adding each interval to a total.
110
pub struct Laps(Option<Instant>);
111
112
impl Laps {
1133382×
    pub fn start() -> Laps {
1143382×
        Laps(enabled().then(Instant::now))
1153382×
    }
116
    /// Add the time since the last lap to `name`'s total.
11715847×
    pub fn lap(&mut self, name: &'static str) {
11815847×
        if let Some(t) = &mut self.0 {
11912×
            let now = Instant::now();
12012×
            add(name, now - *t);
12112×
            *t = now;
12215835×
        }
12315847×
    }
124
}