OXYGENHUB
# 4f264d6f20b6 oxygen / crates / oxy-core / src / timing.rs
2.5 KiB · Rust · last changed A Git import commit costs what it changes 1 month ago source blame history raw
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.
4
5
use std::sync::OnceLock;
6
use std::time::Instant;
7
8
pub fn enabled() -> bool {
9
    static ON: OnceLock<bool> = OnceLock::new();
10
    *ON.get_or_init(|| std::env::var_os("OXY_TIMINGS").is_some_and(|v| !v.is_empty() && v != "0"))
11
}
12
13
/// Report a duration measured by hand (several intervals summed), if timings are on.
14
pub fn record(name: &str, d: std::time::Duration) {
15
    if enabled() {
16
        eprintln!("timing {:<28} {:>9.2} ms", name, d.as_secs_f64() * 1000.0);
17
    }
18
}
19
20
/// Prints `timing <name> <ms>` when dropped, if timings are on.
21
pub struct Phase {
22
    name: &'static str,
23
    started: Option<Instant>,
24
}
25
26
/// Start timing a phase; the guard reports when it goes out of scope.
27
pub fn phase(name: &'static str) -> Phase {
28
    Phase {
29
        name,
30
        started: enabled().then(Instant::now),
31
    }
32
}
33
34
impl Drop for Phase {
35
    fn drop(&mut self) {
36
        if let Some(t) = self.started {
37
            eprintln!(
38
                "timing {:<28} {:>9.2} ms",
39
                self.name,
40
                t.elapsed().as_secs_f64() * 1000.0
41
            );
42
        }
43
    }
44
}
45
46
static TOTALS: std::sync::Mutex<Vec<(&'static str, std::time::Duration)>> =
47
    std::sync::Mutex::new(Vec::new());
48
49
/// Add `d` to the running total for `name`, for a phase repeated many
50
/// times (once per imported commit, say); `report_totals` prints them.
51
pub fn add(name: &'static str, d: std::time::Duration) {
52
    if !enabled() {
53
        return;
54
    }
55
    let mut t = TOTALS.lock().unwrap_or_else(|e| e.into_inner());
56
    match t.iter_mut().find(|(n, _)| *n == name) {
57
        Some((_, total)) => *total += d,
58
        None => t.push((name, d)),
59
    }
60
}
61
62
/// Print and clear the totals `add` gathered.
63
pub fn report_totals() {
64
    if !enabled() {
65
        return;
66
    }
67
    let t = std::mem::take(&mut *TOTALS.lock().unwrap_or_else(|e| e.into_inner()));
68
    for (name, d) in t {
69
        record(name, d);
70
    }
71
}
72
73
/// Times from one point to the next, adding each interval to a total.
74
pub struct Laps(Option<Instant>);
75
76
impl Laps {
77
    pub fn start() -> Laps {
78
        Laps(enabled().then(Instant::now))
79
    }
80
    /// Add the time since the last lap to `name`'s total.
81
    pub fn lap(&mut self, name: &'static str) {
82
        if let Some(t) = &mut self.0 {
83
            let now = Instant::now();
84
            add(name, now - *t);
85
            *t = now;
86
        }
87
    }
88
}