ffai_argus/clock.rs
1//! `Instant` on native, a zero clock on wasm.
2//!
3//! `std::time::Instant::now()` **panics** on `wasm32-unknown-unknown`: the
4//! target has no clock behind it. Argus calls it 51 times across `decode`,
5//! `engine`, `siglip` and `text`, and they sit inside the forward pass rather
6//! than behind a profiling flag — so in a browser they are a crash on the
7//! first tile, not a slow path.
8//!
9//! This is a drop-in for `std::time::Instant`: same `now()` / `elapsed()`
10//! surface, so a call site changes its IMPORT and nothing else. On native it
11//! is a newtype that inlines away and behaviour is byte-identical; on wasm
12//! `elapsed()` is always `Duration::ZERO`.
13//!
14//! A `cfg` rather than a feature: this is a property of the target, not a
15//! choice a consumer should be able to get wrong.
16//!
17//! **Zero is a safe reading for every consumer here, and that was checked
18//! rather than assumed.** Every Argus site feeds a REPORT — per-stage and
19//! per-token millisecond tables printed behind `FFAI_ARGUS_PROFILE` — so on
20//! wasm the tables read all-zero rather than lying about a number the target
21//! cannot take. No control flow branches on these durations.
22
23use core::time::Duration;
24
25/// A monotonic instant, or nothing at all on a target without a clock.
26#[derive(Clone, Copy, Debug)]
27pub struct Instant {
28 #[cfg(not(target_arch = "wasm32"))]
29 t0: std::time::Instant,
30}
31
32impl Instant {
33 #[must_use]
34 #[inline]
35 pub fn now() -> Self {
36 Self {
37 #[cfg(not(target_arch = "wasm32"))]
38 t0: std::time::Instant::now(),
39 }
40 }
41
42 /// Time since [`Self::now`] — always `ZERO` on wasm.
43 #[must_use]
44 #[inline]
45 pub fn elapsed(&self) -> Duration {
46 #[cfg(not(target_arch = "wasm32"))]
47 {
48 self.t0.elapsed()
49 }
50 #[cfg(target_arch = "wasm32")]
51 {
52 Duration::ZERO
53 }
54 }
55}