1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// RLX — versatile ML compiler + runtime.
// Copyright (C) 2026 Eugene Hauptmann, Nataliya Kosmyna.
//
// SPDX-License-Identifier: GPL-3.0-only
//! True GPU-busy time from `MTLCommandBuffer` `GPUStartTime`/`GPUEndTime`.
//!
//! The per-thunk profiler (`thunk_profile`) wraps each `encode_commit` in a
//! wall-clock `Instant`, which folds CPU encode + `commit` + `wait` latency
//! (~150–500 µs/commit) into every sample — so with hundreds of thunks the
//! reported time is dominated by sync, not GPU work, and over-attributes to
//! high-count thunks. `GPUStartTime`/`GPUEndTime` are the GPU scheduler's own
//! timestamps for when the command buffer actually ran on the device,
//! independent of how it was submitted. Reading the delta after
//! `wait_until_completed` gives the real GPU-busy time — the number that
//! decides whether a decode step is GPU-compute-bound or CPU-orchestration-
//! bound.
//!
//! Enable via `RLX_METAL_GPU_TIME=1` (per-step whole-buffer busy print) or
//! implicitly whenever `RLX_METAL_THUNK_PROFILE=1` (per-thunk busy column).
//!
//! Limitation: a graph that splits into multiple command buffers mid-run
//! (deferred host ops — `GatedDeltaNet`/`SelectiveScan`/`Sample` sync points)
//! only reports the final segment's busy time. The GGUF-Llama/Orpheus decode
//! graph has no in-graph host ops (sampling is host-side, post-readback), so
//! its single command buffer is captured in full.
use ;
use Cell;
use Duration;
thread_local!
/// True GPU-busy seconds for a finished command buffer
/// (`GPUEndTime - GPUStartTime`). Call only after `wait_until_completed`;
/// before completion the timestamps are 0. Clamped to ≥0.
/// Stash the last command buffer's GPU-busy time so a caller that no longer
/// holds the buffer (the thunk profiler / the normal `encode_and_run` path,
/// which let `encode_commit` consume and drop it) can retrieve it via
/// [`take_last`].
/// Take (and clear) the last stashed GPU-busy duration.
/// Whether GPU-busy timestamps should be captured this run.