euv_core/reactive/profiler/impl.rs
1use super::*;
2
3impl ProfilerHandle {
4 /// Records a fresh measurement around the given closure.
5 ///
6 /// Captures the start timestamp, runs `f`, captures the
7 /// end timestamp, and pushes a `ProfileEntry { label,
8 /// elapsed_ms, timestamp_ms }` into the entries signal.
9 /// The `elapsed_ms` is `>= 0.0` by construction (start is
10 /// always captured before `f` runs).
11 ///
12 /// # Arguments
13 ///
14 /// - `&str` - The label for this measurement.
15 /// - `F: FnOnce() -> R` - The closure to measure. Can
16 /// return any type — the return value is forwarded to
17 /// the caller unchanged.
18 ///
19 /// # Returns
20 ///
21 /// - `R` - Whatever `f` returned.
22 pub fn measure<F, R>(&self, label: &str, f: F) -> R
23 where
24 F: FnOnce() -> R,
25 {
26 let started_ms: f64 = now_ms();
27 let result: R = f();
28 let ended_ms: f64 = now_ms();
29 let entry: ProfileEntry =
30 ProfileEntry::new(label.to_string(), ended_ms - started_ms, ended_ms);
31 // Push the entry onto the existing entries vector.
32 // Read-modify-write via `.get()` is necessary because
33 // `Signal<T>::set` requires `T: Clone` (which
34 // `Vec<ProfileEntry>` is) but does NOT take `&mut T`
35 // — the entire new value is supplied as the argument.
36 let mut current: Vec<ProfileEntry> = self.get_entries().get();
37 current.push(entry);
38 self.get_entries().set(current);
39 result
40 }
41
42 /// Starts a measurement that will end later.
43 ///
44 /// Use this when the measured region is not a single
45 /// closure — e.g. you want to bracket an async operation
46 /// or a callback fired from event handling. The returned
47 /// `ProfilerMark` knows the start timestamp and the
48 /// entries signal; pass it to `end()` when the work
49 /// completes.
50 ///
51 /// # Arguments
52 ///
53 /// - `&str` - The label for this measurement.
54 ///
55 /// # Returns
56 ///
57 /// - `ProfilerMark` - An RAII-ish guard. Call `mark.end()`
58 /// to push the `ProfileEntry`; drop without `end()` to
59 /// discard the measurement.
60 pub fn begin(&self, label: &str) -> ProfilerMark {
61 ProfilerMark::new(label.to_string(), now_ms(), self.get_entries().clone())
62 }
63
64 /// Returns a clone of the entries signal so subscribers
65 /// outside this handle can react to new measurements.
66 ///
67 /// `Signal<T>` is `Copy`-by-pointer (the inner state is
68 /// heap-allocated and the handle stores a raw address),
69 /// so cloning is cheap. Subscribers call `.get()` inside
70 /// their render closure to subscribe to new entries.
71 pub fn entries(&self) -> Signal<Vec<ProfileEntry>> {
72 self.get_entries().clone()
73 }
74
75 /// Empties the entries vector. Useful between benchmarks
76 /// ("measure just this call, not the previous ones too")
77 /// and in tests ("start from a clean slate").
78 pub fn clear(&self) {
79 self.get_entries().set(Vec::new());
80 }
81}
82
83impl ProfilerMark {
84 /// Closes the measurement started by `begin()` and pushes
85 /// the resulting `ProfileEntry` into the entries signal.
86 ///
87 /// After calling `end()`, the marker is consumed and
88 /// cannot be reused. Calling `end()` twice is a no-op on
89 /// the second call — the marker is moved into the first
90 /// call, so the borrow checker prevents a second
91 /// invocation in well-typed code.
92 pub fn end(self) {
93 let ended_ms: f64 = now_ms();
94 let entry: ProfileEntry = ProfileEntry::new(
95 self.get_label().clone(),
96 ended_ms - self.get_started_ms(),
97 ended_ms,
98 );
99 let mut current: Vec<ProfileEntry> = self.get_entries().get();
100 current.push(entry);
101 self.get_entries().set(current);
102 }
103}