metrale_speculative/
dflash_rung.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// provenance-id: 526f6e616c6420522e205374657369616b
3
4//! 2026-09-25: The DFlash gamma resolver: the per-step verify width K of a
5//! block-diffusion drafter, chosen by concurrency and, for a single stream,
6//! by the first-draft accept rate.
7//!
8//! Owner: speculative.
9//! Invariants:
10//! - Unarmed (pinned, never configured, or a cap below 3),
11//!   `DflashRung::drafts_for` returns `num_drafts` unchanged.
12//! - Armed, it returns `K - 1` capped at `num_drafts`, and at least 1.
13//! - The single-stream state changes at most once per `dwell` single-stream
14//!   verify steps.
15//!
16//! K is `Rungs::multi` at C >= 2, and at C = 1 `Rungs::wide` or
17//! `Rungs::narrow` by the single-stream state (`k_for`). That state is decided
18//! on the popcount of a shift register holding one bit per C = 1 verify step
19//! (first draft accepted) over the last [`WINDOW`] steps: it goes wide at
20//! `enter` hits or more and narrow at `leave` hits or fewer (`next_wide`).
21//!
22//! Measured on Qwen3.8-27B NVFP4 + DFlash-2 (block 8), one DGX Spark,
23//! 2026-09-03, tok/s:
24//!
25//! | C     | prose (Volvo)        | code (MinHeap)        |
26//! |-------|----------------------|-----------------------|
27//! | 1     | γ5 27.0 / γ10 24.6   | γ10 66.8 / γ4 ~23     |
28//! | 16    | γ4 180.7 / γ5 119.5  | γ4 279.5 / γ10 214.2  |
29//!
30//! At C = 16, K = 4 won both workloads. K = 4 is the width of the
31//! write-on-accept verify kernel, which is opt-in (`METRALE_GDN_WOA=1`,
32//! `gdn_flags::gdn_woa_enabled`); without it `Rungs::from_env` puts the
33//! C >= 2 rung at the cap. At C = 1 prose preferred a narrow block and code
34//! the full block.
35//!
36//! Measured at C = 1 on 2026-09-04 (MinHeap / Volvo): code accepted the first
37//! draft 0.97 of the time on the narrow rung and 0.875 on the wide rung, prose
38//! 0.56..0.76 on either. Code on the narrow rung lost about 18% (51 vs
39//! 63 tok/s), prose on the wide rung about 8% (24.8 vs 27.0). So `LEAVE` sits
40//! low: at 46, a 64-step window of code at 0.875 has 46 hits or fewer with
41//! probability about 6.5e-4 (binomial), and one of prose at 0.76 about 0.26.
42//!
43//! `DflashRung::configure` leaves the resolver unarmed when the operator
44//! passes `--dflash-gamma` or sets `METRALE_DFLASH_STATIC_GAMMA` (presence);
45//! `METRALE_DFLASH_GAMMA_RESOLVER` (presence) arms it even under an explicit
46//! flag. It also reads the overrides, once, through `Rungs::from_env`:
47//! `METRALE_DFLASH_RUNG_MULTI` (K at C >= 2, default 4),
48//! `METRALE_DFLASH_RUNG_NARROW` (default 5) and `METRALE_DFLASH_RUNG_WIDE`
49//! (default the cap), each clamped to `2..=cap`; `METRALE_DFLASH_RUNG_ENTER`
50//! (default 60), `METRALE_DFLASH_RUNG_LEAVE` (46) and
51//! `METRALE_DFLASH_RUNG_DWELL` (64).
52
53use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, AtomicUsize, Ordering};
54
55/// 2026-09-25: Verify width K at C >= 2: the write-on-accept kernel's width.
56const MULTI_K: usize = 4;
57/// 2026-09-25: Single-stream narrow width (prose). Measured 2026-09-03: 27.0
58/// tok/s at K = 5 vs 24.6 at K = 10 (module doc).
59const NARROW_K: usize = 5;
60/// 2026-09-25: Window length in verify steps: every bit of the `u64` shift
61/// register.
62pub const WINDOW: u32 = 64;
63/// 2026-09-25: Go wide when at least this many of the last `WINDOW` first
64/// drafts were accepted (60/64 = 0.9375; on the narrow rung code measured
65/// 0.97 and prose at most 0.76, module doc).
66const ENTER: u32 = 60;
67/// 2026-09-25: Go narrow when at most this many were (46/64 = 0.72). On the
68/// wide rung code measured 0.875 (56/64) and prose 0.56..0.76 (36..49 of 64),
69/// so 46 is 3.8 binomial sigma under code and inside prose. Between `LEAVE`
70/// and `ENTER` the state holds.
71const LEAVE: u32 = 46;
72/// 2026-09-25: Minimum single-stream verify steps between two switches: one
73/// full window, so every decision sees a register refilled since the last
74/// switch.
75const DWELL: u64 = WINDOW as u64;
76
77fn env_usize(name: &str, default: usize) -> usize {
78    std::env::var(name)
79        .ok()
80        .and_then(|v| v.parse().ok())
81        .unwrap_or(default)
82}
83fn env_u32(name: &str, default: u32) -> u32 {
84    std::env::var(name)
85        .ok()
86        .and_then(|v| v.parse().ok())
87        .unwrap_or(default)
88}
89
90/// 2026-09-25: The resolver's widths and thresholds. `defaults` and
91/// `from_env` clamp each width to `2..=max(cap, 2)`.
92#[derive(Clone, Copy, Debug, PartialEq, Eq)]
93pub struct Rungs {
94    /// 2026-09-25: K at C >= 2.
95    pub multi: usize,
96    /// 2026-09-25: K at C = 1 in the narrow state.
97    pub narrow: usize,
98    /// 2026-09-25: K at C = 1 in the wide state.
99    pub wide: usize,
100    /// 2026-09-25: Window hits at or above which C = 1 goes wide.
101    pub enter: u32,
102    /// 2026-09-25: Window hits at or below which C = 1 goes narrow.
103    pub leave: u32,
104    /// 2026-09-25: Minimum single-stream verify steps between two switches.
105    pub dwell: u64,
106}
107
108impl Rungs {
109    /// 2026-09-25: The compiled defaults for a head whose widest verify width
110    /// is `cap`.
111    pub fn defaults(cap: usize) -> Self {
112        Self {
113            multi: MULTI_K.clamp(2, cap.max(2)),
114            narrow: NARROW_K.clamp(2, cap.max(2)),
115            wide: cap.max(2),
116            enter: ENTER,
117            leave: LEAVE,
118            dwell: DWELL,
119        }
120    }
121
122    /// 2026-09-25: The defaults with the environment overrides applied, and
123    /// the C >= 2 rung at the cap when the write-on-accept kernel is off.
124    pub fn from_env(cap: usize, woa_available: bool) -> Self {
125        let d = Self::defaults(cap);
126        Self {
127            multi: if woa_available {
128                env_usize("METRALE_DFLASH_RUNG_MULTI", d.multi).clamp(2, cap.max(2))
129            } else {
130                cap.max(2)
131            },
132            narrow: env_usize("METRALE_DFLASH_RUNG_NARROW", d.narrow).clamp(2, cap.max(2)),
133            wide: env_usize("METRALE_DFLASH_RUNG_WIDE", d.wide).clamp(2, cap.max(2)),
134            enter: env_u32("METRALE_DFLASH_RUNG_ENTER", d.enter),
135            leave: env_u32("METRALE_DFLASH_RUNG_LEAVE", d.leave),
136            dwell: env_usize("METRALE_DFLASH_RUNG_DWELL", d.dwell as usize) as u64,
137        }
138    }
139}
140
141struct Ctl {
142    /// 2026-09-25: The `cap_k` of the last `configure_with`; 0 before any.
143    cap: AtomicUsize,
144    /// 2026-09-25: `false`: `drafts_for` returns `num_drafts` and
145    /// `observe_step` does nothing.
146    armed: AtomicBool,
147    /// 2026-09-25: The configured [`Rungs`], one atomic per field.
148    multi: AtomicUsize,
149    narrow: AtomicUsize,
150    wide_k: AtomicUsize,
151    enter: AtomicU32,
152    leave: AtomicU32,
153    dwell: AtomicU64,
154    /// 2026-09-25: Single-stream state: `true` = wide.
155    wide: AtomicBool,
156    /// 2026-09-25: Shift register: bit i is set when the first draft was
157    /// accepted i single-stream steps ago.
158    hits: AtomicU64,
159    /// 2026-09-25: Single-stream verify steps observed since the last
160    /// `configure_with` (the dwell clock).
161    tick: AtomicU64,
162    last_switch: AtomicU64,
163    /// 2026-09-25: `n_active` of the last armed `drafts_for` call;
164    /// `observe_step` scores a step only when it is 1.
165    last_n: AtomicUsize,
166    flips: AtomicU64,
167}
168
169/// 2026-09-25: The DFlash gamma resolver. The serve builds and configures one
170/// and the scheduler context holds it (`SchedCtx::dflash_rung`).
171pub struct DflashRung {
172    ctl: Ctl,
173}
174
175impl Default for DflashRung {
176    fn default() -> Self {
177        Self::new()
178    }
179}
180
181impl DflashRung {
182    /// 2026-09-25: Unconfigured and unarmed: `drafts_for` returns its input.
183    pub const fn new() -> Self {
184        Self {
185            ctl: Ctl {
186                cap: AtomicUsize::new(0),
187                armed: AtomicBool::new(false),
188                multi: AtomicUsize::new(MULTI_K),
189                narrow: AtomicUsize::new(NARROW_K),
190                wide_k: AtomicUsize::new(0),
191                enter: AtomicU32::new(ENTER),
192                leave: AtomicU32::new(LEAVE),
193                dwell: AtomicU64::new(DWELL),
194                wide: AtomicBool::new(true),
195                hits: AtomicU64::new(0),
196                tick: AtomicU64::new(0),
197                last_switch: AtomicU64::new(0),
198                last_n: AtomicUsize::new(0),
199                flips: AtomicU64::new(0),
200            },
201        }
202    }
203
204    fn rungs(&self) -> Rungs {
205        let c = &self.ctl;
206        Rungs {
207            multi: c.multi.load(Ordering::Relaxed),
208            narrow: c.narrow.load(Ordering::Relaxed),
209            wide: c.wide_k.load(Ordering::Relaxed),
210            enter: c.enter.load(Ordering::Relaxed),
211            leave: c.leave.load(Ordering::Relaxed),
212            dwell: c.dwell.load(Ordering::Relaxed),
213        }
214    }
215
216    /// 2026-09-25: Serve-time configuration. `cap_k` is the head's widest
217    /// verify width (its gamma); `explicit_flag` says the operator passed
218    /// `--dflash-gamma`; `woa_available` says the K = 4 write-on-accept
219    /// kernel is on (otherwise the C >= 2 rung is the cap). Reads the
220    /// environment here.
221    pub fn configure(&self, cap_k: usize, explicit_flag: bool, woa_available: bool) {
222        let pinned = std::env::var_os("METRALE_DFLASH_STATIC_GAMMA").is_some()
223            || (explicit_flag && std::env::var_os("METRALE_DFLASH_GAMMA_RESOLVER").is_none());
224        self.configure_with(cap_k, pinned, Rungs::from_env(cap_k, woa_available));
225        if !self.armed() {
226            tracing::info!(
227                "DFlash gamma PINNED at K={cap_k} ({})",
228                if explicit_flag {
229                    "--dflash-gamma explicit"
230                } else {
231                    "METRALE_DFLASH_STATIC_GAMMA"
232                }
233            );
234        } else if !woa_available {
235            tracing::info!(
236                "DFlash gamma resolver: write-on-accept off (METRALE_GDN_WOA=1 not set), C>=2 rung falls back to the cap K={cap_k}"
237            );
238        }
239    }
240
241    /// 2026-09-25: Resets the controller to the wide state with explicit
242    /// rungs and no environment reads. It arms only when `!pinned` and
243    /// `cap_k >= 3`.
244    pub fn configure_with(&self, cap_k: usize, pinned: bool, r: Rungs) {
245        let c = &self.ctl;
246        let armed = !pinned && cap_k >= 3;
247        c.cap.store(cap_k, Ordering::Relaxed);
248        c.multi.store(r.multi, Ordering::Relaxed);
249        c.narrow.store(r.narrow, Ordering::Relaxed);
250        c.wide_k.store(r.wide, Ordering::Relaxed);
251        c.enter.store(r.enter, Ordering::Relaxed);
252        c.leave.store(r.leave, Ordering::Relaxed);
253        c.dwell.store(r.dwell, Ordering::Relaxed);
254        // 2026-09-25: Single-stream starts wide: of the two wrong states, prose
255        // on the wide rung measured the smaller loss (module doc).
256        c.wide.store(true, Ordering::Relaxed);
257        c.hits.store(0, Ordering::Relaxed);
258        c.tick.store(0, Ordering::Relaxed);
259        c.last_switch.store(0, Ordering::Relaxed);
260        c.last_n.store(0, Ordering::Relaxed);
261        c.armed.store(armed, Ordering::Relaxed);
262        if armed {
263            tracing::info!(
264                "DFlash GAMMA RESOLVER armed: cap K={cap_k}; C>=2 -> K={}; C=1 -> K={} wide / K={} \
265                 narrow on first-draft hits in the last {WINDOW}: enter>={} leave<={} dwell={}",
266                r.multi,
267                r.wide,
268                r.narrow,
269                r.enter,
270                r.leave,
271                r.dwell,
272            );
273        }
274    }
275
276    pub fn armed(&self) -> bool {
277        self.ctl.armed.load(Ordering::Relaxed)
278    }
279
280    /// 2026-09-25: Single-stream switches (wide <-> narrow) so far.
281    pub fn flips(&self) -> u64 {
282        self.ctl.flips.load(Ordering::Relaxed)
283    }
284
285    /// 2026-09-25: The per-step draft count for `n_active` sequences.
286    /// `num_drafts` is the serve's configured count (cap - 1) and is returned
287    /// unchanged when the resolver is unarmed.
288    pub fn drafts_for(&self, n_active: usize, num_drafts: usize) -> usize {
289        if !self.armed() {
290            return num_drafts;
291        }
292        self.ctl.last_n.store(n_active, Ordering::Relaxed);
293        let k = k_for(
294            n_active,
295            self.ctl.wide.load(Ordering::Relaxed),
296            &self.rungs(),
297        );
298        (k - 1).min(num_drafts).max(1)
299    }
300
301    /// 2026-09-25: Scores one verify step: `d1_match` says the first draft
302    /// was accepted. The DFlash verify step calls it every step; it does
303    /// nothing unless the resolver is armed and the last dispatch was C = 1.
304    /// A switch is logged when it happens.
305    pub fn observe_step(&self, d1_match: bool) {
306        let c = &self.ctl;
307        if !self.armed() || c.last_n.load(Ordering::Relaxed) != 1 {
308            return;
309        }
310        let register = shift_in(c.hits.load(Ordering::Relaxed), d1_match);
311        c.hits.store(register, Ordering::Relaxed);
312        let tick = c.tick.fetch_add(1, Ordering::Relaxed) + 1;
313        // 2026-09-25: No decision within `dwell` steps of the last switch or
314        // of arming.
315        let r = self.rungs();
316        if tick.saturating_sub(c.last_switch.load(Ordering::Relaxed)) < r.dwell {
317            return;
318        }
319        let hits = register.count_ones();
320        let was = c.wide.load(Ordering::Relaxed);
321        let now = next_wide(was, hits, &r);
322        if now != was {
323            c.wide.store(now, Ordering::Relaxed);
324            c.last_switch.store(tick, Ordering::Relaxed);
325            let flips = c.flips.fetch_add(1, Ordering::Relaxed) + 1;
326            tracing::info!(
327                "DFlash gamma resolver C=1 -> K={} ({}; hits={hits}/{WINDOW} tick={tick} flips={flips})",
328                k_for(1, now, &r),
329                if now { "wide/code" } else { "narrow/prose" },
330            );
331        }
332    }
333}
334
335/// 2026-09-25: Verify K for `n_active` sequences, given the rungs and the
336/// single-stream state.
337pub fn k_for(n_active: usize, wide: bool, r: &Rungs) -> usize {
338    if n_active >= 2 {
339        r.multi
340    } else if wide {
341        r.wide
342    } else {
343        r.narrow
344    }
345}
346
347/// 2026-09-25: The next single-stream state from the number of first-draft
348/// hits in the last `WINDOW` steps.
349pub fn next_wide(wide: bool, hits: u32, r: &Rungs) -> bool {
350    if wide {
351        hits > r.leave
352    } else {
353        hits >= r.enter
354    }
355}
356
357/// 2026-09-25: Shifts one step into the register and returns the new
358/// register.
359#[inline]
360pub fn shift_in(register: u64, hit: bool) -> u64 {
361    ((register << 1) | u64::from(hit)) & (u64::MAX >> (64 - WINDOW))
362}
363
364#[cfg(test)]
365#[path = "dflash_rung_tests.rs"]
366mod tests;