metrale_model_arch/
deepseek_v41_layer.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// provenance-id: 526f6e616c6420522e205374657369616b
3
4//! 2026-09-25: DeepSeek-V4.1 Flash: one transformer block on the generic model.
5//!
6//! The layer runs on the f32 mHC highway the generic model provides
7//! (`ctx.buffers.hc_streams()`, `[T, hc, H]`), in the order of the CPU
8//! reference `deepseek_v41_ref::model::forward`:
9//!
10//! ```text
11//! layer 0 only:        hc_expand(embedding -> streams)
12//! engram layers only:  streams += engram(token n-grams)          (EngramV41)
13//! attention:           (pre_a, post_a, comb_a) = hc_mixes(streams, attn site)
14//!                      x = rmsnorm(collapse(streams, pre_prev))   <- delayed pre
15//!                      streams = hc_post(attn(x), streams, post_a, comb_a)
16//! ffn:                 (pre_f, post_f, comb_f) = hc_mixes(streams, ffn site)
17//!                      x = rmsnorm(collapse(streams, pre_a))
18//!                      streams = hc_post(moe(x), streams, post_f, comb_f)
19//!                      pre_prev = pre_f
20//! last layer only:     hidden = collapse(streams, pre_prev)
21//! ```
22//!
23//! `pre_prev` is set by layer 0 to the one-hot on stream 0 (the reference's
24//! initial pre-mix) and lives in [`V41Runtime`], with the shared attention
25//! slots, the engram hasher and the expert cache: attention state is shared
26//! across layers, so one runtime is shared by every layer through an `Arc`,
27//! its mutable parts behind mutexes. One sequence at a time: multi-sequence
28//! decode and the model-level CUDA graph are declined through the layer
29//! hooks. The layer can capture its own single-token step instead
30//! (`METRALE_DS41_GRAPH=1`, see [`GraphMode`], or the multi-layer segments of
31//! `METRALE_DS41_STEP_GRAPH=1`): the host work in the middle of every layer
32//! (the routing download and the expert fetch; on kv and index source layers
33//! also the compressor group and the index top-k) splits the step into graph
34//! segments with host spans between them.
35//!
36//! Routed experts are read on demand into an expert cache (`ExpertLru` over
37//! the `ExpertArena` slots, `ExpertSliceMap` locating each expert in the GGUF
38//! shards) with positional file reads. The arena is device memory behind a
39//! page-locked staging ring, or with `METRALE_DS41_ARENA_DEVICE=0` a
40//! page-locked arena the GPU reads in place. The engram tables are read by
41//! row (`EngramRowReader`).
42//!
43//! Owner: model-arch, DeepSeek-V4.1.
44//! Invariants:
45//! - Layer 0's `step` at position 0 resets the shared attention slots, the
46//!   engram hasher and the cached step hashes after its argument checks and
47//!   before any engram, attention or MoE work.
48
49use std::sync::atomic::AtomicBool;
50use std::sync::{Arc, Mutex, OnceLock};
51
52use anyhow::Result;
53
54use metrale_gpu_runtime::gpu::{DevicePtr, GpuBackend, GraphHandle, KernelHandle};
55use metrale_model_weights::weights::expert_stream::{
56    EngramRowReader, ExpertArena, ExpertLru, ExpertSliceMap,
57};
58
59use crate::attn_v41::{
60    AttnV41, AttnV41Cfg, AttnV41LayerState, AttnV41LayerWeights, LayerRole, SharedV41,
61};
62use crate::engram_v41::{EngramHashTables, EngramHasher, EngramV41};
63use crate::moe_v41::{MoeV41, MoeV41Cfg, MoeV41LayerWeights, RouterWeights};
64use metrale_model_layers::layer::LayerState;
65use metrale_model_layers::layers::qwen3_attention::HcSiteWeights;
66use metrale_model_layers::weight_map::DenseWeight;
67
68mod graph;
69mod hc_launch;
70mod step;
71mod step_ffn;
72mod step_seg;
73mod step_seg_run;
74mod trait_impl;
75
76pub use step_seg::{SegState, step_graph_on};
77
78/// 2026-09-25: Everything the layers share for the one sequence in flight.
79pub struct V41Runtime {
80    pub attn_cfg: AttnV41Cfg,
81    pub moe_cfg: MoeV41Cfg,
82    pub attn: Mutex<AttnV41>,
83    pub moe: Mutex<MoeV41>,
84    pub engram: Mutex<EngramV41>,
85    pub lru: Mutex<ExpertLru>,
86    pub arena: ExpertArena,
87    pub slices: Arc<ExpertSliceMap>,
88    pub rows: EngramRowReader,
89    pub tables: Arc<EngramHashTables>,
90    pub hasher: Mutex<EngramHasher>,
91    pub shared: Mutex<SharedV41>,
92    /// 2026-09-25: The step's engram hashes, computed by the first engram layer
93    /// (hash index 0) or the segment-graph preload, and reused by the others.
94    pub step_hashes: Mutex<Option<Vec<i64>>>,
95    /// 2026-09-25: The delayed `pre` mix, f32 `[max_tokens, hc]`.
96    pub pre_prev: DevicePtr,
97    /// 2026-09-25: f32 `[max_tokens, (2 + hc) * hc]` scratch between
98    /// `hc_v41_mixes_dot` and `hc_v41_finish_collapse`.
99    pub mixes_s: DevicePtr,
100    pub reader_threads: usize,
101    pub n_layers: usize,
102    pub hc_mult: usize,
103    pub hidden: usize,
104    pub sinkhorn_iters: usize,
105    pub hc_eps: f32,
106    pub norm_eps: f32,
107    pub pre_a: DevicePtr,
108    pub pre_f: DevicePtr,
109    pub post_s: DevicePtr,
110    pub comb_s: DevicePtr,
111    pub attn_in: DevicePtr,
112    pub max_tokens: usize,
113    /// 2026-09-25: Per-step totals across layers, logged by the last layer when
114    /// `METRALE_DS41_DIAG=1`.
115    pub step_moe: Mutex<crate::moe_v41::MoeV41Timing>,
116    pub step_attn_ms: Mutex<f64>,
117    pub step_engram_ms: Mutex<f64>,
118    pub step_start: Mutex<Option<std::time::Instant>>,
119    /// 2026-09-25: Set when a `METRALE_DS41_GRAPH` capture failed: segments
120    /// without a graph run eagerly from then on; captured graphs keep replaying.
121    pub graph_disabled: AtomicBool,
122    /// 2026-09-25: The multi-layer segment graphs (`METRALE_DS41_STEP_GRAPH=1`,
123    /// `step_seg.rs`).
124    pub seg: Mutex<SegState>,
125    /// 2026-09-25: Every layer's role, filled as the layers are built; a segment
126    /// owner reads the roles of the layers its graph spans.
127    pub roles: Mutex<Vec<Option<LayerRole>>>,
128    /// 2026-09-25: `(layer, hash index)` of the engram layers.
129    pub engram_layers: Mutex<Vec<(usize, usize)>>,
130    /// 2026-09-25: `METRALE_DS41_PREDICT_TRACE`: the last three layers' MoE
131    /// inputs (bf16 `[3, hidden]`, slot `layer % 3`) and the trace file.
132    pub pred_x: DevicePtr,
133    pub pred_trace: Mutex<Option<std::io::BufWriter<std::fs::File>>>,
134}
135
136/// 2026-09-25: How the single-token step runs, read once.
137/// `METRALE_DS41_GRAPH_ORACLE=1` runs every layer both ways and logs whether
138/// the highway, the delayed mix and the hidden match bit for bit;
139/// `METRALE_DS41_GRAPH=1` replays the captured segments; anything else is the
140/// eager step.
141#[derive(Clone, Copy, Debug, PartialEq, Eq)]
142pub enum GraphMode {
143    Off,
144    On,
145    Oracle,
146}
147
148pub fn graph_mode() -> GraphMode {
149    static MODE: OnceLock<GraphMode> = OnceLock::new();
150    *MODE.get_or_init(|| {
151        let is_one = |k: &str| std::env::var(k).is_ok_and(|v| v == "1");
152        if is_one("METRALE_DS41_GRAPH_ORACLE") {
153            GraphMode::Oracle
154        } else if is_one("METRALE_DS41_GRAPH") {
155            GraphMode::On
156        } else {
157            GraphMode::Off
158        }
159    })
160}
161
162/// 2026-09-25: The pointers a layer's captured segments bake that are not the
163/// runtime's own. Compared on every step: a different set destroys the
164/// graphs, which are then captured again.
165#[derive(Clone, Copy, Debug, PartialEq, Eq)]
166struct Baked {
167    hidden: DevicePtr,
168    streams: DevicePtr,
169    normed: DevicePtr,
170    window: DevicePtr,
171    rows_b: Option<DevicePtr>,
172    attn_out: DevicePtr,
173    moe_out: DevicePtr,
174}
175
176/// 2026-09-25: One layer's captured single-token step, held in the sequence's
177/// layer state (it bakes the sequence's window ring and the kv source's cache).
178///
179/// Segment A = the attention site's mixes, collapse and norm, the attention
180/// (capturable layers only), `hc_post`, the ffn site's mixes, collapse and
181/// norm, the router GEMV. On kv and index source layers the attention runs
182/// eagerly between `a[0]` (through the norm) and `a[1]` (from `hc_post`).
183/// Host span: `MoeV41::stage_m1`. Segment B = the expert compute, `hc_post`,
184/// the delayed-mix copy, and on the last layer the final collapse.
185struct LayerGraphs {
186    a: [Option<GraphHandle>; 2],
187    b: Option<GraphHandle>,
188    baked: Baked,
189}
190
191impl LayerGraphs {
192    fn destroy(self, gpu: &dyn GpuBackend) -> Result<()> {
193        for g in self.a.into_iter().chain([self.b]).flatten() {
194            gpu.destroy_graph(g)?;
195        }
196        Ok(())
197    }
198}
199
200// 2026-09-25: SAFETY: the raw device pointers are allocated at load and not
201// reassigned behind the `Arc`; the mutable state is behind the mutexes, and
202// one sequence runs at a time.
203unsafe impl Send for V41Runtime {}
204unsafe impl Sync for V41Runtime {}
205
206pub struct V41LayerState {
207    pub attn: AttnV41LayerState,
208    graphs: Option<LayerGraphs>,
209}
210
211impl LayerState for V41LayerState {
212    fn as_any(&self) -> &dyn std::any::Any {
213        self
214    }
215    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
216        self
217    }
218}
219
220pub struct DeepSeekV41Layer {
221    pub idx: usize,
222    pub role: LayerRole,
223    pub rt: Arc<V41Runtime>,
224    pub attn_w: AttnV41LayerWeights,
225    pub moe_w: MoeV41LayerWeights,
226    /// 2026-09-25: The next layer's router. With expert prefetch armed
227    /// (`prefetch_k() > 0` and the reader pool), `MoeV41::forward` /
228    /// `stage_m1` run it on this layer's MoE input to start reading the
229    /// experts the next layer is predicted to ask for.
230    pub next_router: Option<RouterWeights>,
231    /// 2026-09-25: `Some(hash index)` on engram layers (`EngramHashTables::hash_index`).
232    pub engram_index: Option<usize>,
233    pub hc_attn: HcSiteWeights,
234    pub hc_ffn: HcSiteWeights,
235    pub attn_norm: DenseWeight,
236    pub ffn_norm: DenseWeight,
237    pub k_hc_expand: KernelHandle,
238    pub k_hc_post: KernelHandle,
239    pub k_mixes_dot: KernelHandle,
240    pub k_mixes_finish: KernelHandle,
241    pub k_collapse: KernelHandle,
242    /// 2026-09-25: `hc_v41_finish_collapse`: a site's mix finish and the
243    /// block's collapse in one launch over ceil(H / 256) blocks a token.
244    pub k_finish_collapse: KernelHandle,
245    /// 2026-09-25: `hc_v41_collapse_wide` and `hc_v41_post_wide`: the collapse
246    /// and `hc_post` over ceil(H / 256) blocks a token.
247    pub k_collapse_wide: KernelHandle,
248    pub k_post_wide: KernelHandle,
249    pub k_rms_norm: KernelHandle,
250}