memra_engine/model.rs
1//! Dense transformer model: loads GGUF weights to GPU (Stage-1: dequant→f32), runs the
2//! shared full-attention + SwiGLU forward graph. Arch-agnostic via ModelConfig; this path is
3//! exactly the dense-transformer graph (qwen3) and the full-attention layers of hybrids.
4
5use crate::{
6 Engine, QT_BF16, QT_F8_E4M3, QT_F32, QT_IQ3_S, QT_IQ4_XS, QT_NVFP4, QT_NVFP4_RP, QT_Q2_K,
7 QT_Q3_K, QT_Q4_0, QT_Q4_K, QT_Q5_K, QT_Q6_K, QT_Q8_0,
8};
9use cudarc::driver::CudaSlice;
10use memra_gguf::config::ModelConfig;
11use memra_gguf::source::{DiskExtent, GgufSource, TensorSource};
12use memra_gguf::{GgmlType, GgufFile, dequant};
13use std::collections::HashMap;
14
15/// RESIDENCY CENSUS (lane/fp8-decode-v1, 2026-08-05) — per-qtype tally of the 2D matmul weights
16/// that actually went resident, keyed by `QT_*`. The FP8-ST decode arm's whole claim is about
17/// WHICH container the checkpoint's projections end up in, and the two candidate containers
18/// differ in bytes (e4m3 1.0 B/w vs the Q8_0 re-encode 1.0625 B/w). Before this instrument the
19/// only evidence available was end-to-end tok/s, which cannot distinguish "the arm ran and was
20/// flat" from "the arm never engaged" — the exact ambiguity in this lane's first loadprobe pair.
21/// Slot = qtype index; `.0` = tensor count, `.1` = resident bytes.
22static RESIDENCY_CENSUS: [(std::sync::atomic::AtomicUsize, std::sync::atomic::AtomicU64); 16] = {
23 #[allow(clippy::declare_interior_mutable_const)]
24 const Z: (std::sync::atomic::AtomicUsize, std::sync::atomic::AtomicU64) = (
25 std::sync::atomic::AtomicUsize::new(0),
26 std::sync::atomic::AtomicU64::new(0),
27 );
28 [Z; 16]
29};
30
31fn residency_census_note(qtype: i32, bytes: usize) {
32 use std::sync::atomic::Ordering::Relaxed;
33 if let Some(slot) = RESIDENCY_CENSUS.get(qtype as usize) {
34 slot.0.fetch_add(1, Relaxed);
35 slot.1.fetch_add(bytes as u64, Relaxed);
36 }
37}
38
39/// Human-readable residency census: one line per qtype that took at least one 2D weight, plus a
40/// total. Callers print it right after load — see `run-gen`'s `MEMRA_RESIDENCY_CENSUS=1`.
41pub fn residency_census_report() -> String {
42 use std::sync::atomic::Ordering::Relaxed;
43 let name = |q: usize| -> &'static str {
44 match q as i32 {
45 QT_Q8_0 => "Q8_0",
46 QT_Q4_K => "Q4_K",
47 QT_Q6_K => "Q6_K",
48 QT_Q5_K => "Q5_K",
49 QT_Q3_K => "Q3_K",
50 QT_IQ4_XS => "IQ4_XS",
51 QT_IQ3_S => "IQ3_S",
52 QT_NVFP4 => "NVFP4",
53 QT_F32 => "F32",
54 QT_NVFP4_RP => "NVFP4_RP",
55 QT_F8_E4M3 => "F8_E4M3",
56 QT_BF16 => "BF16",
57 QT_Q4_0 => "Q4_0",
58 QT_Q2_K => "Q2_K",
59 crate::QT_F8_E4M3_BLK => "F8_E4M3_BLK",
60 _ => "?",
61 }
62 };
63 let mut out = String::from("residency census (2D matmul weights, resident container):\n");
64 let (mut tn, mut tb) = (0usize, 0u64);
65 for (q, slot) in RESIDENCY_CENSUS.iter().enumerate() {
66 let (n, b) = (slot.0.load(Relaxed), slot.1.load(Relaxed));
67 if n == 0 {
68 continue;
69 }
70 tn += n;
71 tb += b;
72 out += &format!(
73 " {:>9}: {:>4} tensors {:>9.3} MiB\n",
74 name(q),
75 n,
76 b as f64 / (1024.0 * 1024.0)
77 );
78 }
79 out += &format!(
80 " {:>9}: {:>4} tensors {:>9.3} MiB",
81 "TOTAL",
82 tn,
83 tb as f64 / (1024.0 * 1024.0)
84 );
85 out
86}
87
88/// A weight tensor resident on GPU. Quantized weights stay in GGUF block bytes (`Quant`);
89/// small non-quant tensors (norms, sometimes embed/lm_head) are kept dequantized as f32 (`Float`).
90/// This keeps VRAM ~= on-disk quant size (fixes the f32-on-load OOM).
91pub enum GpuTensor {
92 Quant {
93 bytes: CudaSlice<u8>,
94 qtype: i32,
95 row_bytes: usize,
96 ne: Vec<u64>,
97 scale: f32,
98 /// SPLIT-PLANE walk-order repack (A6, 2026-07-04): NVFP4 matmul weights are repacked at
99 /// load into [quant plane out_f x in_f/64 x 32B][scale plane out_f x in_f/64 x 4B] — same
100 /// bytes, same total size, but a lane's per-group weight read becomes ONE 16B-aligned
101 /// LDG.128 + a dense 4B scale word instead of 5 scattered 4B LDGs at 36B stride (the "18B
102 /// straggle"). Every consumer kernel has an `_rp` twin (bit-identical: pure byte
103 /// permutation, same dot order). `rp=false` = original GGUF block layout (all other
104 /// dtypes, MoE-staged expert bytes, MEMRA_RP=0 escape).
105 rp: bool,
106 /// CUTLASS NVFP4 prefill operand (repacked B + swizzled SFB), built ALONGSIDE `bytes` at load
107 /// when MEMRA_FP4_CUTLASS is set. `bytes` stays raw GGUF so decode (MMVQ/dp4a) is untouched;
108 /// prefill (m>=128) reads this. Only ever Some for NVFP4 weights under cfg(memra_cutlass).
109 #[cfg(memra_cutlass)]
110 cutlass: Option<CutlassWeight>,
111 /// FP8-ACT PREFILL operand (MEMRA_PP_FP8=1, probe verdict 2026-07-08): the checkpoint's RAW
112 /// e4m3 bytes + per-tensor f32 weight_scale, stashed ALONGSIDE the Q8_0 re-encode for the
113 /// F8-E4M3-origin 2D projections (~1 B/w extra on those layers). `bytes` stays Q8_0 so
114 /// decode (dp4a/MMVQ) is untouched; only the m>=16 prefill dispatch (cuBLASLt FP8 TN,
115 /// fp8_ffi.rs) reads this. None unless the env is set at load (zero VRAM cost by default).
116 fp8: Option<Fp8Weight>,
117 /// Q4_0 SPLIT-PLANE MIRROR (2026-07-10, the 18B-straggle cure for decode): qs plane
118 /// [out_f x nblk x 16B] + d plane [out_f x nblk x 2B] built device-side at model load
119 /// (q4_0_split_rp_build) for decode-hot trunk weights. Raw `bytes` stay resident —
120 /// prefill (gemm/MMQ) and Stage-A read those; the m<=8 mmvq/batched/fused dispatch
121 /// reads this when present (`_rp` twins; microprobe m=1 1.34x, m=3 1.17x, bitwise).
122 /// None everywhere except where the arch-load hook opted in (VRAM cost = weight size).
123 rp4: Option<CudaSlice<u8>>,
124 /// BLOCK-128 WEIGHT-SCALE GRID for a NATIVE e4m3 resident weight (lane/fp8-blk128-decode,
125 /// 2026-08-05). `Some` iff `qtype == QT_F8_E4M3_BLK`, and then `bytes` are the checkpoint's
126 /// raw e4m3 codes ([out_f, in_f], row_bytes == in_f), `scale == 1.0`, and THIS is the only
127 /// dequant scale in the tensor — decode reads it in-kernel (`qmatvec_e4m3_blk_mmvq`),
128 /// prefill reads it in the per-block MMQ tile. Distinct from `fp8: Some(Fp8Weight { blk })`,
129 /// which is the MEMRA_PP_FP8 *stash*: a SECOND e4m3 copy carried alongside a Q8_0 slab.
130 /// Here there is one copy and `fp8` stays None.
131 blk: Option<Fp8BlockScales>,
132 /// FP16 DEQUANT MIRROR (MEMRA_PP_F16=1, probe 2026-07-26): row-major fp16 of a 2D Q8_0
133 /// projection, built device-side at load (f16_ffi::build_q8_f16). `bytes` stay Q8_0 so
134 /// decode is untouched; the m>=16 prefill dispatch (cuBLASLt FP16 TN, 611-687 TF vs
135 /// MMQ's ~200 TF class) reads this. None unless the env is set (VRAM = 2 B/w extra).
136 f16: Option<CudaSlice<u8>>,
137 },
138 Float {
139 data: CudaSlice<f32>,
140 ne: Vec<u64>,
141 },
142 /// BF16-RESIDENT full-precision matmul weight (MEMRA_FULL_PREC only). Holds the checkpoint's raw
143 /// bf16 bytes (`u8`, little-endian u16 pairs) — 2 B/w vs the 4 B/w a `Float` f32 materialization
144 /// would cost, so the 9B trunk stays ~18GB in VRAM instead of ~36GB. Consumed via dequant-on-use:
145 /// each matmul expands this to a transient f32 scratch and rides the SAME cuBLASLt f32 GEMV the
146 /// `Float` arm uses (bit-identical to a load-time bf16->f32 dequant, just deferred). Never a norm
147 /// (norms stay `Float` f32); never on a fast/GEMM/MMQ path (uses_q8_1_fast/gemm_supports = false).
148 FloatBf16 {
149 data: CudaSlice<u8>,
150 ne: Vec<u64>,
151 },
152}
153
154/// FP8-native prefill operand: raw checkpoint e4m3 codes `[out_f, in_f]` row-major (EXACT — the
155/// weight side of the FP8 GEMM does no re-quantization) + its weight scale(s). Per-tensor class:
156/// `scale` is the dequant scalar folded into the GEMM's scale pointer together with the per-batch
157/// activation scale, `blk == None`. Block-128 class (Qwen official FP8): `blk == Some` and
158/// `scale == 1.0` — see `Fp8BlockScales` for the resident layout contract.
159pub struct Fp8Weight {
160 pub bytes: CudaSlice<u8>,
161 pub scale: f32,
162 pub blk: Option<Fp8BlockScales>,
163}
164
165/// Device-resident block-128 weight-scale grid for an e4m3 operand (B1b, lane fp8st 2026-08-03).
166///
167/// STORAGE LAYOUT (the canonical device layout every future consumer builds from): a flat f32
168/// buffer in the CHECKPOINT'S on-disk order — row-major `[rows = ceil(out_f/128),
169/// cols = ceil(in_f/128)]`, so `scales[ob * cols + kb]` scales the 128x128 weight tile at
170/// output-block `ob`, input-block `kb` (uploaded verbatim from `memra_gguf::source::F8BlockGrid`,
171/// no permutation — one host decode, one htod). Rationale: (1) the per-block-dequant mmvq twin
172/// (qmatvec_e4m3_mmvq extension, DECISION.md B1) indexes `(o >> 7) * cols + (e >> 7)` — natural
173/// in this order; (2) for cuBLASLt BLK128x128 the weight `[out, in]` row-major is the TN GEMM's
174/// column-major `[k=in, n=out]` A operand, and this same linear order IS that view's column-major
175/// block grid with ld = cols(=kblk) — probe P1 (`probe/fp8_lt_blk_probe.cu`) verifies whether
176/// sm_120 accepts it directly; if Lt wants a different order, the reorder happens at the GEMM
177/// plan build, NOT here. NO KERNEL CONSUMES THIS YET: the loader keeps every block-128 tensor's
178/// decode/prefill on the Q8_0 re-encode until the consuming kernels land (try_fp8_gemm skips
179/// blk operands; the QT_F8_E4M3 one-copy arm rejects them). This struct's job is bytes+scales
180/// resident and correct.
181pub struct Fp8BlockScales {
182 pub scales: CudaSlice<f32>,
183 pub rows: usize, // ceil(out_f/128)
184 pub cols: usize, // ceil(in_f/128)
185}
186
187/// Host-side split-plane repack of NVFP4 GGUF block bytes (A6). Input: out_f rows of in_f/64
188/// 36-byte blocks ([4B UE4M3 scales][32B packed e2m1]). Output (same length): quant plane
189/// (out_f x nsb64 x 32B) followed by scale plane (out_f x nsb64 x 4B). Pure byte permutation.
190pub fn repack_nvfp4_split(bytes: &[u8], out_f: usize) -> Vec<u8> {
191 let row_bytes = bytes.len() / out_f;
192 let nsb64 = row_bytes / 36;
193 debug_assert_eq!(
194 row_bytes % 36,
195 0,
196 "NVFP4 row_bytes must be a multiple of 36"
197 );
198 let qplane = out_f * nsb64 * 32;
199 let mut rp = vec![0u8; bytes.len()];
200 for o in 0..out_f {
201 for s in 0..nsb64 {
202 let src = &bytes[o * row_bytes + s * 36..o * row_bytes + s * 36 + 36];
203 rp[qplane + (o * nsb64 + s) * 4..qplane + (o * nsb64 + s) * 4 + 4]
204 .copy_from_slice(&src[0..4]);
205 rp[(o * nsb64 + s) * 32..(o * nsb64 + s) * 32 + 32].copy_from_slice(&src[4..36]);
206 }
207 }
208 rp
209}
210
211/// Inverse of `repack_nvfp4_split` (the roundtrip gate).
212pub fn unpack_nvfp4_split(rp: &[u8], out_f: usize) -> Vec<u8> {
213 let row_bytes = rp.len() / out_f;
214 let nsb64 = row_bytes / 36;
215 let qplane = out_f * nsb64 * 32;
216 let mut back = vec![0u8; rp.len()];
217 for o in 0..out_f {
218 for s in 0..nsb64 {
219 back[o * row_bytes + s * 36..o * row_bytes + s * 36 + 4].copy_from_slice(
220 &rp[qplane + (o * nsb64 + s) * 4..qplane + (o * nsb64 + s) * 4 + 4],
221 );
222 back[o * row_bytes + s * 36 + 4..o * row_bytes + s * 36 + 36]
223 .copy_from_slice(&rp[(o * nsb64 + s) * 32..(o * nsb64 + s) * 32 + 32]);
224 }
225 }
226 back
227}
228
229/// A6 repack seam: default ON, `MEMRA_RP=0` restores the GGUF block layout everywhere (rollback/A-B).
230pub fn rp_enabled() -> bool {
231 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
232 *ON.get_or_init(|| std::env::var("MEMRA_RP").map(|v| v != "0").unwrap_or(true))
233}
234
235/// FULL-PRECISION LOADER MODE (MEMRA_FULL_PREC=1, default OFF — MTP-heal research platform).
236/// Bypasses the standing loader law (large BF16/F8 -> Q8_0/NVFP4 re-encode, the "Float-poison"
237/// tripwire). Under this flag every weight loads as Float and compute rides the Stage-A f32 oracle
238/// path end to end — SLOW IS FINE, this mode exists for exactness (the MTP acceptance CEILING at
239/// full precision), not speed. Large 2D matmul weights stay bf16-resident (`GpuTensor::FloatBf16`)
240/// with dequant-on-use so the 9B (~18GB bf16) + f32 activations fit 24GB instead of blowing to
241/// ~38GB as an all-f32 materialization. The Float-poison tripwire warnings are CORRECT behavior
242/// here and are suppressed. See docs/FLAGS.md and HANDOVER "MEMRA DUAL-SHAPE".
243pub fn full_prec_enabled() -> bool {
244 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
245 *ON.get_or_init(|| {
246 std::env::var("MEMRA_FULL_PREC")
247 .map(|v| v == "1")
248 .unwrap_or(false)
249 })
250}
251
252/// LOADER-LAW allowlist (loadersweep audit 2026-07-08): 2D Float tensors that are DELIBERATELY
253/// Float despite being matmul-class. Every entry needs an audit rationale — this list silences
254/// the tripwire below, so an unjustified entry re-opens the trap.
255/// * ffn_gate_inp (MoE router, 35B GGUF F32 [2048,256] / M3 ST F32 [6144,64]): the router's
256/// top-k SELECTION is discontinuous — quantizing shifts logits and flips expert choice (a
257/// class change, not an FP-order change). llama.cpp keeps every router F32 (its converter
258/// forces F32) so Float is bench-parity, it sits on NO all-or-nothing predicate, and the
259/// decode-exact contract is already built around its cuBLASLt path
260/// (hybrid_forward.rs moe_ffn_sequential_zq8 router comment).
261fn float_2d_audited(name: &str) -> bool {
262 name.ends_with("ffn_gate_inp.weight")
263}
264
265/// Once-per-name-pattern loader-law warning (`blk.{il}.` collapses to `blk.*.` so a 48-layer
266/// offender prints ONE line, not 48). See the call site in `load_from_source` for the law.
267fn warn_float_2d_once(name: &str, ne: &[u64], src_type: GgmlType) {
268 use std::sync::{Mutex, OnceLock};
269 static SEEN: OnceLock<Mutex<std::collections::HashSet<String>>> = OnceLock::new();
270 let pat = match name.strip_prefix("blk.").and_then(|r| r.split_once('.')) {
271 Some((_, suffix)) => format!("blk.*.{suffix}"),
272 None => name.to_string(),
273 };
274 let mut seen = SEEN
275 .get_or_init(|| Mutex::new(std::collections::HashSet::new()))
276 .lock()
277 .unwrap();
278 if seen.insert(pat.clone()) {
279 eprintln!(
280 "[loader-law] WARNING: {pat} loads as 2D Float ne={ne:?} (src {src_type:?}) — \
281 a Float matmul weight rides cuBLAS f32 GEMV and poisons all-or-nothing q8-fast \
282 predicates (uses_q8_1_fast/mixer_in_q8_1_fast). If matmul-class: Q8_0-encode at \
283 load (model.rs ssm arm / source.rs BF16+F8 gates). If deliberately Float: add \
284 it to float_2d_audited with the audit rationale."
285 );
286 }
287}
288
289/// CUTLASS-layout NVFP4 weight (B operand) for the prefill FP4 GEMM. Built once at load from the raw
290/// GGUF bytes (de-interleave + SFB swizzle). Coexists with the raw `bytes` (decode reads bytes).
291#[cfg(memra_cutlass)]
292pub struct CutlassWeight {
293 /// Plain K-contiguous packed e2m1, [out_f, in_f/2] bytes.
294 pub b_packed: CudaSlice<u8>,
295 /// Swizzled SFB (CUTLASS SfAtom layout), sized via cutlass_sfb_size(out_f, in_f).
296 pub sfb_swizzled: CudaSlice<u8>,
297}
298
299impl GpuTensor {
300 pub fn ne(&self) -> &[u64] {
301 match self {
302 GpuTensor::Quant { ne, .. } => ne,
303 GpuTensor::Float { ne, .. } => ne,
304 GpuTensor::FloatBf16 { ne, .. } => ne,
305 }
306 }
307 pub fn in_features(&self) -> usize {
308 self.ne()[0] as usize
309 }
310 pub fn out_features(&self) -> usize {
311 self.ne()[1] as usize
312 }
313 /// Per-tensor post-matmul macro-scale (NVFP4 carries scale != 1.0; all others -> 1.0, a no-op).
314 /// Used by the fused SwiGLU epilogue to fold the gate/up scale into one kernel.
315 pub fn scale(&self) -> f32 {
316 match self {
317 GpuTensor::Quant { scale, .. } => *scale,
318 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => 1.0,
319 }
320 }
321
322 /// Load a tensor, keeping quant types packed and float types as f32. (GGUF entry point —
323 /// thin wrapper over the source-agnostic `load_from_source`; behavior is unchanged.)
324 pub fn load(e: &Engine, g: &GgufFile, name: &str) -> Result<Self, Box<dyn std::error::Error>> {
325 Self::load_from_source(e, &GgufSource(g), name)
326 }
327
328 /// Source-agnostic load: works from any `TensorSource` (GGUF or safetensors). The engine's
329 /// forward graph only ever asks for ggml-style names; the source maps them to its own layout.
330 ///
331 /// RESIDENCY CENSUS (lane/fp8-decode-v1, 2026-08-05): the wrapper tallies what each 2D
332 /// matmul weight ACTUALLY became — resident qtype + resident bytes — so the FP8-ST decode
333 /// arm's claim ("e4m3 stays native instead of paying the Q8_0-slab tax") is a measured
334 /// per-checkpoint fact rather than an assumption about the checkpoint's dtype mix. Read it
335 /// with `residency_census_report()`; zero cost when never read.
336 pub fn load_from_source(
337 e: &Engine,
338 src: &dyn TensorSource,
339 name: &str,
340 ) -> Result<Self, Box<dyn std::error::Error>> {
341 let t = Self::load_from_source_inner(e, src, name)?;
342 if let GpuTensor::Quant {
343 qtype, bytes, ne, ..
344 } = &t
345 {
346 if ne.len() == 2 {
347 residency_census_note(*qtype, bytes.len());
348 }
349 }
350 Ok(t)
351 }
352
353 fn load_from_source_inner(
354 e: &Engine,
355 src: &dyn TensorSource,
356 name: &str,
357 ) -> Result<Self, Box<dyn std::error::Error>> {
358 // A1 DIRECT NVFP4 IMPORT (2026-07-04): a PLAIN modelopt/Reza NVFP4 weight from a
359 // safetensors source repacks straight into the A6 split-plane resident layout in ONE host
360 // pass (nvfp4_repack::repack_modelopt_to_split — the scale plane is the file's
361 // weight_scale bytes verbatim), never materializing the GGUF 36B-block intermediate.
362 // The GGUF hop remains only for MEMRA_ST_DIRECT=0 (rollback/A-B seam — byte-identical
363 // resident weights either way), MEMRA_RP=0, the hybrid V-reorder transforms, and the
364 // opt-in CUTLASS resident operand (which is built from raw GGUF-layout bytes).
365 let cutlass_wants_raw = cfg!(memra_cutlass) && std::env::var("MEMRA_FP4_CUTLASS").is_ok();
366 let st_direct = std::env::var("MEMRA_ST_DIRECT")
367 .map(|v| v != "0")
368 .unwrap_or(true);
369 if rp_enabled() && st_direct && !cutlass_wants_raw {
370 if let Some(nv) = src.find_nvfp4_native(name) {
371 if nv.in_f % 64 == 0 && nv.out_f > 0 {
372 // Same post-matmul macro-scale sibling lookup as the GGUF-layout arm below.
373 let stem = name.strip_suffix(".weight").unwrap_or(name);
374 let scale = match src.find(&format!("{stem}.scale")) {
375 Some(sv) => f32::from_le_bytes(sv.bytes[..4].try_into().unwrap()),
376 None => 1.0,
377 };
378 let bytes =
379 e.htod_bytes(&memra_gguf::nvfp4_repack::repack_modelopt_to_split(
380 nv.wbytes, nv.wscale, nv.out_f, nv.in_f,
381 ))?;
382 return Ok(GpuTensor::Quant {
383 bytes,
384 qtype: QT_NVFP4,
385 row_bytes: nv.in_f / 64 * 36,
386 ne: vec![nv.in_f as u64, nv.out_f as u64],
387 scale,
388 rp: true,
389 #[cfg(memra_cutlass)]
390 cutlass: None,
391 fp8: None,
392 blk: None,
393 f16: None,
394 rp4: None,
395 });
396 }
397 }
398 }
399 // E4M3-DIRECT (DEFAULT since lane/fp8-decode-v1 2026-08-05; MEMRA_ST_E4M3=0 rolls back to the
400 // Q8_0 slab. Introduced default-off by lane e4m3dec 2026-07-08): F8-E4M3-origin 2D projections keep
401 // the checkpoint's RAW e4m3 device bytes + per-tensor weight_scale as the ONE resident copy
402 // (QT_F8_E4M3) instead of the Q8_0 re-encode — decode dequants e4m3 in-kernel
403 // (qmatvec_e4m3_mmvq, the checkpoint's own precision, no lossy re-quant hop), prefill
404 // (m>=16) rides the cuBLASLt FP8 GEMM on the SAME bytes (try_fp8_gemm). Frees the Q8_0
405 // duplicate the MEMRA_PP_FP8 stash needed (~3.4GB on the NV-27B) — full FP8 prefill coverage
406 // with no VRAM budget. Placed BEFORE `find` so the host-side F8->Q8_0 re-encode is skipped
407 // entirely (faster load). in_f%32 is the q8_1 activation block gate (every F8 projection in
408 // the NV-27B satisfies it; a violator falls through to the Q8_0 arm unchanged).
409 // BLOCK-128 CLASS: served by its OWN qtype since lane/fp8-blk128-decode (2026-08-05) —
410 // see the second arm below. It must not enter the per-tensor arm: the QT_F8_E4M3 kernel
411 // family consumes ONE scalar weight scale, so a block-128 operand through it would
412 // silently dequant every tile at scale 1.0.
413 if crate::fp8_ffi::st_e4m3_enabled() {
414 if let Some(f8) = src.find_fp8_native(name) {
415 if f8.blk.is_none() && f8.in_f % 32 == 0 && f8.out_f > 0 {
416 return Ok(GpuTensor::Quant {
417 bytes: e.htod_bytes(&f8.bytes)?,
418 qtype: crate::QT_F8_E4M3,
419 row_bytes: f8.in_f,
420 ne: vec![f8.in_f as u64, f8.out_f as u64],
421 scale: f8.scale,
422 rp: false,
423 #[cfg(memra_cutlass)]
424 cutlass: None,
425 fp8: None,
426 blk: None,
427 f16: None,
428 rp4: None,
429 });
430 }
431 }
432 }
433 // E4M3-BLK-DIRECT (lane/fp8-blk128-decode, 2026-08-05) — the block-128 twin of the arm
434 // above, and the Qwen-3.8 day-one path. A block-128 FP8 checkpoint (Qwen3.6-FP8's
435 // `weight_block_size [128,128]`, the DeepSeek-V3 lineage) keeps its RAW e4m3 codes plus its
436 // [ceil(out/128), ceil(in/128)] f32 scale grid as the ONE resident copy (QT_F8_E4M3_BLK)
437 // instead of the ARM B' Q8_0 slab: decode dequants per k128 block in-kernel
438 // (qmatvec_e4m3_blk_mmvq — the checkpoint's own precision, no lossy re-quant hop) at
439 // 1.0 B/weight instead of 1.0625, and prefill (m>=16) rides the per-block FP8 MMQ tile on
440 // the SAME bytes+grid (try_fp8_blk_mmq) with NO stash duplicate.
441 //
442 // ORDERING / DISJOINTNESS (the decode-v1 landmine, restated for this arm): the three FP8
443 // arms are mutually exclusive by their scale class, checked in this order —
444 // 1. `blk.is_none()` -> QT_F8_E4M3 (per-tensor scalar; arm above)
445 // 2. `blk.is_some()` + native -> QT_F8_E4M3_BLK (this arm)
446 // 3. `blk.is_some()` -> ARM B' Q8_0 slab (MEMRA_FP8_BLK_GPU) / host re-encode
447 // so ARM B' KEEPS working wherever it is still the path: whenever this arm declines (env
448 // rollback, NaN codes present, ragged in_f, grid-shape mismatch) control falls through to
449 // it unchanged. It is not cross-gated on this arm's flag — a tensor this arm CLAIMS
450 // returns here and never reaches ARM B' at all, and one it declines must reach it.
451 //
452 // NaN PRECONDITION, enforced at LOAD (not asserted): the decode kernel decodes e4m3 with
453 // the HARDWARE intrinsic (magnitude 0x7F -> NaN) while the ARM B'/host reference decodes
454 // it to 0.0 (modelopt). A tensor carrying 0x7F/0xFF therefore cannot ride this kernel, so
455 // the bytes are scanned once on the device (fp8_blk_nan_count, the same precondition the
456 // prefill MMQ arm uses) and a non-zero count declines to the Q8_0 floor for THAT tensor.
457 // Real Qwen FP8 checkpoints carry none (the exporter saturates at +-448), so this is a
458 // guard, not a cost centre: one linear pass over bytes already on the device.
459 if crate::fp8_ffi::st_e4m3_blk_enabled() {
460 if let Some(f8) = src.find_fp8_native(name) {
461 if let Some(grid) = f8.blk.as_ref() {
462 let (in_f, out_f) = (f8.in_f, f8.out_f);
463 // in_f % 32: the q8_1 activation block gate (and the kernel's 2x LDG.128 line).
464 // The grid dims must match the shape — a mismatch means operand and grid came
465 // from different tensors; refuse rather than index a wrong block. scale == 1.0
466 // is the block class's identity (source.rs sets it alongside a grid); anything
467 // else would be a second, unapplied factor.
468 if in_f % 32 == 0
469 && out_f > 0
470 && f8.bytes.len() == out_f * in_f
471 && grid.rows == out_f.div_ceil(128)
472 && grid.cols == in_f.div_ceil(128)
473 && grid.scales.len() == grid.rows * grid.cols
474 && f8.scale == 1.0
475 {
476 let bytes = e.htod_bytes(&f8.bytes)?;
477 if e.fp8_blk_nan_count(&bytes)? == 0 {
478 let scales = e.htod(&grid.scales)?;
479 return Ok(GpuTensor::Quant {
480 bytes,
481 qtype: crate::QT_F8_E4M3_BLK,
482 row_bytes: in_f,
483 ne: vec![in_f as u64, out_f as u64],
484 scale: 1.0,
485 rp: false,
486 #[cfg(memra_cutlass)]
487 cutlass: None,
488 fp8: None,
489 blk: Some(Fp8BlockScales {
490 scales,
491 rows: grid.rows,
492 cols: grid.cols,
493 }),
494 f16: None,
495 rp4: None,
496 });
497 }
498 crate::fp8_ffi::note_blk_native_nan_refused();
499 }
500 }
501 }
502 }
503 // ARM B' — GPU BLOCK-128 DEQUANT (MEMRA_FP8_BLK_GPU=1, default OFF; lane fp8-gemm-arm
504 // 2026-08-03). A block-128 FP8 checkpoint (Qwen official FP8 / DeepSeek-V3 lineage)
505 // currently loads via the host path: full f32 dequant of the tensor (f8_deq_f32) then a
506 // host Q8_0 re-encode (f32_to_q8_0) — correct, but a serial CPU pass over every byte of
507 // every projection. This arm does the same math on the GPU in ONE pass
508 // (cu/fp8_blk_dequant.cu): upload the raw e4m3 codes + the scale grid, write Q8_0
509 // blocks directly. BYTE-IDENTICAL to the host path (kernel-check [fp8-blk-gpu] arm
510 // asserts it on ragged and aligned shapes), so the resident tensor, the MMQ/MMVQ
511 // dispatch, and decode are all bit-for-bit unchanged — this is a LOAD-TIME
512 // optimization only, not a numeric config change.
513 //
514 // Placed BEFORE `find` for exactly the reason the MEMRA_ST_E4M3 arm above is: `find`
515 // would otherwise do the host dequant+re-encode we are replacing. Per-tensor and
516 // per-row scale classes are NOT touched (find_fp8_native returns blk=None / None for
517 // them) and neither are V-reorder Transform targets (find_fp8_native rejects those with
518 // a grid — the permutation invalidates the on-disk grid, so they keep the host path).
519 //
520 // NO st_e4m3 EXCLUSION (lane/fp8-decode-v1 2026-08-05): this arm used to carry
521 // `&& !st_e4m3_enabled()`, written when MEMRA_ST_E4M3 was default OFF and meant only as
522 // "the native arm above already claimed this tensor". Once native residency became the
523 // DEFAULT that condition would have been true on every run and silently disabled ARM B'
524 // for the whole block-128 class — the exact silent-slow-path landmine the flags doctrine
525 // forbids. The two arms are already disjoint by construction and need no cross-gate: the
526 // arm above returns only when `f8.blk.is_none()`, this one runs only when `f8.blk` is
527 // Some, so a tensor that reaches here was never eligible for native residency.
528 if crate::fp8_ffi::fp8_blk_gpu_enabled() {
529 if let Some(f8) = src.find_fp8_native(name) {
530 if let Some(grid) = f8.blk.as_ref() {
531 let (in_f, out_f) = (f8.in_f, f8.out_f);
532 if in_f % 32 == 0 && out_f > 0 && f8.bytes.len() == out_f * in_f {
533 let bytes = e.fp8_blk_dequant_q8_0(&f8.bytes, &grid.scales, out_f, in_f)?;
534 return Ok(GpuTensor::Quant {
535 bytes,
536 qtype: QT_Q8_0,
537 row_bytes: in_f / 32 * 34,
538 ne: vec![in_f as u64, out_f as u64],
539 scale: 1.0,
540 rp: false,
541 #[cfg(memra_cutlass)]
542 cutlass: None,
543 fp8: None,
544 blk: None,
545 f16: None,
546 rp4: None,
547 });
548 }
549 }
550 }
551 }
552 let mut v = src
553 .find(name)
554 .unwrap_or_else(|| panic!("missing tensor {name}"));
555 // MEMRA_KQ_NVFP4=1 (opt-in, 2026-07-08): re-encode Q4_K/Q5_K 2D matmul weights to NVFP4 at
556 // load. The k-quant mmvq family runs at 61-70% of the bandwidth wall on this rig (measured
557 // BOTH engines — the kernels share ancestry) while the in-house NVFP4 path runs at 96%.
558 // The daily GGUF's quant mix was chosen for llama's kernels, not ours: Q4_K -> NVFP4 is
559 // 4-bit -> 4-bit at +26pp kernel efficiency; Q5_K -> NVFP4 also drops bytes (0.69 -> 0.56
560 // B/w) at a small real re-quant cost (5 -> 4 bit; gates + acceptance arbitrate). Q6_K/Q8_0
561 // excluded (6/8-bit -> 4-bit is a real quality cliff — the lm_head stays untouched).
562 // MEMRA_KQ_NVFP4 (opt-in SPEED-OVER-QUALITY mode, measured 2026-07-08 on the 9B):
563 // =2 (Q4_K+Q5_K -> NVFP4): +3.9% plain decode (129.5 -> 134.5, the Q5 bytes win),
564 // acceptance tax ~3pts on hard content (p2 74.0 -> 70.7, p3 66.9 -> 64.9).
565 // =1 (Q4_K only): NO perf gain AND still ~3pts tax — Q4_K is ASYMMETRIC (6-bit
566 // scale+min per 32); NVFP4 is symmetric e2m1: dropping the zero-point is real
567 // error even 4-bit -> 4-bit. The "same bpw = same class" assumption is FALSE
568 // across asymmetric/symmetric formats. Kept only for the record.
569 let kq = std::env::var("MEMRA_KQ_NVFP4")
570 .ok()
571 .and_then(|x| x.parse::<u8>().ok())
572 .unwrap_or(0);
573 if (kq >= 1 && v.ggml_type == GgmlType::Q4_K || kq >= 2 && v.ggml_type == GgmlType::Q5_K)
574 && v.ne.len() == 2
575 && v.ne[0] % 64 == 0
576 && !name.starts_with("output")
577 {
578 let n: u64 = v.ne.iter().product();
579 let f32v = dequant::dequantize(v.ggml_type, &v.bytes, n as usize);
580 let packed = memra_gguf::nvfp4_repack::f32_to_nvfp4(&f32v);
581 v = memra_gguf::source::TensorView {
582 bytes: std::borrow::Cow::Owned(packed),
583 ggml_type: GgmlType::NVFP4,
584 ne: v.ne.clone(),
585 };
586 }
587 let qtype = match v.ggml_type {
588 GgmlType::Q8_0 => Some(QT_Q8_0),
589 GgmlType::Q4_K => Some(QT_Q4_K),
590 GgmlType::Q6_K => Some(QT_Q6_K),
591 GgmlType::Q5_K => Some(QT_Q5_K),
592 GgmlType::Q3_K => Some(QT_Q3_K),
593 GgmlType::IQ4_XS => Some(QT_IQ4_XS),
594 GgmlType::IQ3_S => Some(QT_IQ3_S),
595 GgmlType::NVFP4 => Some(QT_NVFP4),
596 GgmlType::Q4_0 => Some(QT_Q4_0),
597 // F32/F16/BF16 (the dtypes safetensors carries) -> Float path below.
598 _ => None,
599 };
600 match qtype {
601 Some(qt) => {
602 let out_f = v.ne[1] as usize;
603 let row_bytes = v.bytes.len() / out_f;
604 // NVFP4 two-level scale: per-16 ue4m3 micro-scale is in the dequant; the per-tensor
605 // F32 macro-scale lives in a sibling "<stem>.scale" tensor, applied POST-matmul
606 // (llama build_lora_mm: ggml_mul(res, w_s)). ".input_scale" is the W4A4 activation
607 // scale — UNUSED on our W4A16/f32 path. Only NVFP4 carries it; others -> 1.0 (no-op).
608 let scale = if qt == QT_NVFP4 {
609 let stem = name.strip_suffix(".weight").unwrap_or(name);
610 match src.find(&format!("{stem}.scale")) {
611 Some(sv) => f32::from_le_bytes(sv.bytes[..4].try_into().unwrap()),
612 None => 1.0,
613 }
614 } else {
615 1.0
616 };
617 // A6 SPLIT-PLANE repack: NVFP4 2-D matmul weights upload in walk-order layout
618 // (host-side permutation before htod — zero VRAM spike, layer-streamed by
619 // construction). Every consumer kernel dispatches its `_rp` twin off the flag.
620 let rp = qt == QT_NVFP4
621 && v.ne.len() == 2
622 && (v.ne[0] as usize) % 64 == 0
623 && v.bytes.len() % out_f == 0
624 && (v.bytes.len() / out_f) % 36 == 0
625 && rp_enabled();
626 let bytes = if rp {
627 e.htod_bytes(&repack_nvfp4_split(&v.bytes, out_f))?
628 } else {
629 e.htod_bytes(&v.bytes)?
630 };
631 // CUTLASS NVFP4 prefill operand, built from the RAW GGUF bytes (a temp raw upload
632 // when the resident `bytes` are repacked). Gated: only NVFP4 weights, only when
633 // MEMRA_FP4_CUTLASS is set, only under cfg(memra_cutlass). in_f%64==0 is the NVFP4
634 // K-block constraint (same as the dispatch).
635 #[cfg(memra_cutlass)]
636 let cutlass = {
637 let in_f = v.ne[0] as usize;
638 // Skip the resident repack when OTF is requested (per-call repack instead) — the
639 // resident path ~doubles NVFP4 weight VRAM and OOMs larger models (e.g. 27B/24GB).
640 if qt == QT_NVFP4
641 && in_f % 64 == 0
642 && v.ne.len() == 2
643 && std::env::var("MEMRA_FP4_CUTLASS").is_ok()
644 && std::env::var("MEMRA_FP4_CUTLASS_OTF").is_err()
645 {
646 let raw_dev;
647 let src_dev = if rp {
648 raw_dev = e.htod_bytes(&v.bytes)?;
649 &raw_dev
650 } else {
651 &bytes
652 };
653 let (b_packed, sfb_swizzled) =
654 e.build_cutlass_weight(src_dev, out_f, in_f, row_bytes)?;
655 Some(CutlassWeight {
656 b_packed,
657 sfb_swizzled,
658 })
659 } else {
660 None
661 }
662 };
663 // FP8-ACT PREFILL operand (MEMRA_PP_FP8=1): for F8-E4M3-sourced projections (they
664 // surface as Q8_0 from the source's re-encode) ALSO stash the raw e4m3 device
665 // bytes + weight_scale. The source guarantees byte order matches `v` (the
666 // Transform arm's V-reorder is baked into both); the ne check guards a mixup.
667 // VRAM BUDGET (24GB rigs, 2026-07-08): the stash duplicates every F8-origin
668 // projection (~+3.4GB on the 27B) — fine on the 96GB box, OOM here. The stash
669 // spends from MEMRA_PP_FP8_BUDGET_MB (default 1536); once spent, remaining
670 // tensors ride the old path. Load order is layer order, so the budget covers a
671 // PREFIX of layers — coverage (and the prefill win) scales with the budget.
672 // MEMRA_FP8_MMQ=1 (lane/fp8-mmq) admits the SAME stash for the block-128 class:
673 // the per-block MMQ prefill kernel is that class's consumer, and it needs exactly
674 // what this arm makes resident (raw e4m3 bytes + the verbatim f32 grid). It shares
675 // the budget accounting below, so a 24GB rig still caps the duplicate.
676 // NOTE the gate here is `fp8_mmq_enabled` (the STASH gate, still opt-in) and NOT
677 // `fp8_blk_mmq_native_enabled` (default ON since 2026-08-05). That is deliberate:
678 // this arm's whole product is a DUPLICATE weight copy, and the native-resident route
679 // exists precisely to avoid one. A QT_F8_E4M3_BLK tensor already carries its own
680 // e4m3 bytes + grid, so it needs nothing from here; wiring the default-ON gate into
681 // this condition would spend the budget on copies no kernel reads.
682 let fp8 = if qt == QT_Q8_0
683 && (crate::fp8_ffi::pp_fp8_enabled() || crate::fp8_ffi::fp8_mmq_enabled())
684 {
685 match src.find_fp8_native(name) {
686 Some(f8)
687 if v.ne.len() == 2
688 && f8.in_f as u64 == v.ne[0]
689 && f8.out_f as u64 == v.ne[1] =>
690 {
691 use std::sync::atomic::{AtomicUsize, Ordering};
692 static FP8_SPENT: AtomicUsize = AtomicUsize::new(0);
693 static FP8_BUDGET: std::sync::OnceLock<usize> =
694 std::sync::OnceLock::new();
695 let budget = *FP8_BUDGET.get_or_init(|| {
696 std::env::var("MEMRA_PP_FP8_BUDGET_MB")
697 .ok()
698 .and_then(|v| v.parse::<usize>().ok())
699 .unwrap_or(1536)
700 << 20
701 });
702 let sz = f8.bytes.len();
703 if FP8_SPENT.fetch_add(sz, Ordering::Relaxed) + sz <= budget {
704 // Block-128 grid rides along resident (checkpoint order,
705 // Fp8BlockScales layout contract). try_fp8_gemm still skips blk
706 // operands (cuBLASLt takes no block grid on sm_120, P1-VERDICT);
707 // try_fp8_blk_mmq is their consumer under MEMRA_FP8_MMQ=1.
708 let blk = match f8.blk {
709 Some(g) => Some(Fp8BlockScales {
710 scales: e.htod(&g.scales)?,
711 rows: g.rows,
712 cols: g.cols,
713 }),
714 None => None,
715 };
716 Some(Fp8Weight {
717 bytes: e.htod_bytes(&f8.bytes)?,
718 scale: f8.scale,
719 blk,
720 })
721 } else {
722 FP8_SPENT.fetch_sub(sz, Ordering::Relaxed);
723 None
724 }
725 }
726 _ => None,
727 }
728 } else {
729 None
730 };
731 Ok(GpuTensor::Quant {
732 bytes,
733 qtype: qt,
734 row_bytes,
735 ne: v.ne.clone(),
736 scale,
737 rp,
738 #[cfg(memra_cutlass)]
739 cutlass,
740 fp8,
741 blk: None,
742 rp4: None,
743 f16: None,
744 })
745 }
746 None => {
747 let n: u64 = v.ne.iter().product();
748 // FULL-PRECISION MODE (MEMRA_FULL_PREC): NO re-encodes. Large 2D bf16 matmul weights
749 // stay bf16-resident (FloatBf16, dequant-on-use) so the trunk fits VRAM; everything
750 // else (small 2D, 1D norms, F16/F32) rides the exact f32 Float path below. The ssm
751 // Q8_0 re-encode and the Float-poison tripwire are BYPASSED here (both are the loader
752 // law this mode exists to suspend — the warnings would be correct but noise).
753 // MEMRA_BF16_MMV=1 shares the FULL_PREC bf16-resident arm for large 2D BF16
754 // sources: raw checkpoint bytes on device (2 B/w, ~halving both VRAM and the
755 // decode read traffic of every preserved non-expert weight — shexp, lm_head,
756 // owning-stage attention, dense FFN, router) with the one-block-per-row bf16
757 // matvec at decode m=1 and the chunked expansion path at m>1. Numeric-class
758 // door, run-gen argmax gate + boot battery (see docs/FLAGS.md).
759 if full_prec_enabled() || crate::Engine::bf16_mmv_on() {
760 // Only bf16 sources take the resident-bf16 arm; F16/F32 fall through to f32 Float
761 // (exact, and tiny/absent in the bf16 ST checkpoints this mode targets). The 1M
762 // threshold keeps small tensors (norms, gate_inp) on the proven f32 path — only
763 // the big trunk matrices need the 2 B/w VRAM saving. The MMV door uses 2M:
764 // the MoE router (288x4096 = 1.18M) is consumed via float_data() and its
765 // logits pick the routes — it stays exact-f32 so routing never moves.
766 let threshold = if full_prec_enabled() {
767 1_000_000
768 } else {
769 2_000_000
770 };
771 if v.ggml_type == GgmlType::BF16 && v.ne.len() == 2 && n >= threshold {
772 let data = e.htod_bytes(&v.bytes)?; // raw bf16 bytes, u16 LE pairs
773 return Ok(GpuTensor::FloatBf16 {
774 data,
775 ne: v.ne.clone(),
776 });
777 }
778 if full_prec_enabled() {
779 let f32v = dequant::dequantize(v.ggml_type, &v.bytes, n as usize);
780 return Ok(GpuTensor::Float {
781 data: e.htod(&f32v)?,
782 ne: v.ne.clone(),
783 });
784 }
785 }
786 let f32v = dequant::dequantize(v.ggml_type, &v.bytes, n as usize);
787 // ssm_beta/ssm_alpha stored F32 (the 35B GGUF): Q8_0-encode at load. F32 here
788 // fails `mixer_in_q8_1_fast` for the whole linear-attn mixer -> every linear
789 // layer falls off the fused norm+quantize chain onto cuBLAS f32 GEMV pairs
790 // (the NV-27B in_proj_a/b lesson, same all-or-nothing capability check; nsys
791 // 35B: 100 dot+reduce launches/token). Q8_0 of an F32 source is the same
792 // class-lossless step every 9B GGUF already ships for these tensors.
793 if v.ne.len() == 2
794 && v.ne[0] % 32 == 0
795 && (name.ends_with("ssm_beta.weight") || name.ends_with("ssm_alpha.weight")
796 // E4B per_layer_model_proj (F16 [2560, 10752]): matmul-class — the
797 // loader-law recipe (2026-07-12). As Float it rode cuBLAS f32 whose
798 // m=1-vs-m=16 FP-order gap seeds inp_pl noise into EVERY layer's PLE
799 // tail; the 42-layer stack amplifies it to logit maxdiff ~27 and the
800 // chat-prompt prefill-vs-decode argmax gate fails.
801 || name.ends_with("per_layer_model_proj.weight"))
802 {
803 let q8 = memra_gguf::nvfp4_repack::f32_to_q8_0(&f32v);
804 return GpuTensor::from_quant_bytes(
805 e,
806 &q8,
807 GgmlType::Q8_0,
808 v.ne[0],
809 v.ne[1],
810 1.0,
811 );
812 }
813 // LOADER-LAW TRIPWIRE (loadersweep 2026-07-08): a 2D Float tensor with both dims
814 // >= 16 is almost certainly MATMUL-class, and a Float matmul weight (a) rides
815 // cuBLAS f32 GEMV pairs (dot_kernel + reduce_1Block in nsys) and (b) fails
816 // uses_q8_1_fast, poisoning every ALL-OR-NOTHING fast-path predicate it sits on
817 // (mixer_in_q8_1_fast etc.) — the trap that cost measurable perf 4 times (NV-27B
818 // in_proj_a/b BF16, 35B ssm_beta/alpha F32, M3 shexp cousin, M3 BF16 lm_head).
819 // Fix recipe: name-gated f32_to_q8_0 encode at load (see the ssm arm above /
820 // source.rs BF16+F8 gates). Norm-class tensors are 1D or have a dim < 16
821 // (conv1d ne[0]=4) and never reach this warning.
822 if v.ne.len() == 2 && v.ne[0] >= 16 && v.ne[1] >= 16 && !float_2d_audited(name) {
823 warn_float_2d_once(name, &v.ne, v.ggml_type);
824 }
825 // F32/F16/BF16 (or as-yet-unhandled quant): dequant to f32. Small tensors only.
826 Ok(GpuTensor::Float {
827 data: e.htod(&f32v)?,
828 ne: v.ne.clone(),
829 })
830 }
831 }
832 }
833
834 /// Build a Quant tensor directly from raw ggml block bytes (FR-Spec self-trim: byte-level row
835 /// gather from an already-loaded weight — rows in every ggml quant are independent, so a
836 /// contiguous per-row byte copy is a lossless "trim"). `ne0` = in_features, `ne1` = rows.
837 pub fn from_quant_bytes(
838 e: &Engine,
839 bytes: &[u8],
840 ty: GgmlType,
841 ne0: u64,
842 ne1: u64,
843 scale: f32,
844 ) -> Result<Self, Box<dyn std::error::Error>> {
845 let qt = match ty {
846 GgmlType::Q8_0 => QT_Q8_0,
847 GgmlType::Q4_K => QT_Q4_K,
848 GgmlType::Q6_K => QT_Q6_K,
849 GgmlType::Q5_K => QT_Q5_K,
850 GgmlType::Q3_K => QT_Q3_K,
851 GgmlType::IQ4_XS => QT_IQ4_XS,
852 GgmlType::IQ3_S => QT_IQ3_S,
853 GgmlType::NVFP4 => QT_NVFP4,
854 GgmlType::Q4_0 => QT_Q4_0,
855 other => panic!("from_quant_bytes: unsupported dtype {other:?}"),
856 };
857 let row_bytes = bytes.len() / ne1 as usize;
858 // Same A6 repack as load_from_source: callers pass GGUF-layout host bytes (the FR-Spec
859 // self-trim row-gathers from the source file bytes, which are always original layout).
860 let rp = qt == QT_NVFP4 && ne0 % 64 == 0 && row_bytes % 36 == 0 && rp_enabled();
861 let dev = if rp {
862 e.htod_bytes(&repack_nvfp4_split(bytes, ne1 as usize))?
863 } else {
864 e.htod_bytes(bytes)?
865 };
866 Ok(GpuTensor::Quant {
867 bytes: dev,
868 qtype: qt,
869 row_bytes,
870 ne: vec![ne0, ne1],
871 scale,
872 rp,
873 #[cfg(memra_cutlass)]
874 cutlass: None,
875 fp8: None,
876 blk: None,
877 f16: None,
878 rp4: None,
879 })
880 }
881
882 pub fn load_opt(
883 e: &Engine,
884 g: &GgufFile,
885 name: &str,
886 ) -> Result<Option<Self>, Box<dyn std::error::Error>> {
887 Self::load_opt_from_source(e, &GgufSource(g), name)
888 }
889
890 pub fn load_opt_from_source(
891 e: &Engine,
892 src: &dyn TensorSource,
893 name: &str,
894 ) -> Result<Option<Self>, Box<dyn std::error::Error>> {
895 if src.has(name) {
896 Ok(Some(Self::load_from_source(e, src, name)?))
897 } else {
898 Ok(None)
899 }
900 }
901
902 /// Accessor for tensors that MUST be f32 (norm weights). Panics if quantized.
903 pub fn float_data(&self) -> &CudaSlice<f32> {
904 match self {
905 GpuTensor::Float { data, .. } => data,
906 GpuTensor::Quant { .. } => panic!("expected float tensor (norm), got quantized"),
907 GpuTensor::FloatBf16 { .. } => {
908 panic!("expected f32 float tensor (norm), got bf16-resident matmul weight")
909 }
910 }
911 }
912}
913
914pub struct Layer {
915 pub attn_norm: GpuTensor,
916 pub wq: GpuTensor,
917 pub wk: GpuTensor,
918 pub wv: GpuTensor,
919 pub wo: GpuTensor,
920 pub q_norm: Option<GpuTensor>,
921 pub k_norm: Option<GpuTensor>,
922 pub ffn_norm: GpuTensor,
923 /// FFN: dense SwiGLU or routed MoE (OLMoE — dense attention + MoE FFN). Reuses the hybrid
924 /// `Ffn` enum + `load_ffn` so the routed-expert forward is shared with `HybridModel::moe_ffn`.
925 pub ffn: crate::hybrid::Ffn,
926}
927
928/// Host-resident embedding table for row gather (dequant only the needed token rows).
929pub struct EmbedHost {
930 pub raw: Vec<u8>,
931 pub ggml_type: GgmlType,
932 pub n_embd: usize,
933}
934impl EmbedHost {
935 pub fn from_gguf(g: &GgufFile, name: &str) -> Self {
936 Self::from_source(&GgufSource(g), name)
937 }
938 pub fn from_source(src: &dyn TensorSource, name: &str) -> Self {
939 let v = src
940 .find(name)
941 .unwrap_or_else(|| panic!("missing embed {name}"));
942 EmbedHost {
943 raw: v.bytes.to_vec(),
944 ggml_type: v.ggml_type,
945 n_embd: v.ne[0] as usize,
946 }
947 }
948 /// QT int + row_bytes for this embed table's dtype (for the device embed-gather kernel).
949 /// CUDA-GRAPH-PLAN Phase 1. Mirrors the GpuTensor qtype mapping.
950 pub fn qt_and_row_bytes(&self, n_embd: usize) -> (i32, usize) {
951 let (blk, tsize) = self.ggml_type.block_and_type_size();
952 let row_bytes = (n_embd as u64 / blk * tsize) as usize;
953 let qt = match self.ggml_type {
954 GgmlType::Q8_0 => QT_Q8_0,
955 GgmlType::Q4_K => QT_Q4_K,
956 GgmlType::Q6_K => QT_Q6_K,
957 GgmlType::Q5_K => QT_Q5_K,
958 GgmlType::Q3_K => QT_Q3_K,
959 GgmlType::IQ4_XS => QT_IQ4_XS,
960 GgmlType::IQ3_S => QT_IQ3_S,
961 GgmlType::NVFP4 => QT_NVFP4,
962 GgmlType::F32 => QT_F32,
963 // BF16 embed table (FULL_PREC research mode: qwen35-9b-hf) — device gather does the
964 // exact bits<<16 expansion; 2 B/elem resident instead of an f32-doubled table.
965 GgmlType::BF16 => QT_BF16,
966 other => panic!("embed_gather: unsupported dtype {other:?}"),
967 };
968 (qt, row_bytes)
969 }
970
971 /// Gather rows for tokens -> [T, n_embd] f32. Dequant per-row from raw bytes.
972 pub fn gather(&self, n_embd: usize, tokens: &[u32]) -> Vec<f32> {
973 let (blk, tsize) = self.ggml_type.block_and_type_size();
974 let row_bytes = (n_embd as u64 / blk * tsize) as usize;
975 let mut x = vec![0f32; tokens.len() * n_embd];
976 for (ti, &tok) in tokens.iter().enumerate() {
977 let off = tok as usize * row_bytes;
978 let row = dequant::dequantize(self.ggml_type, &self.raw[off..off + row_bytes], n_embd);
979 x[ti * n_embd..ti * n_embd + n_embd].copy_from_slice(&row);
980 }
981 x
982 }
983}
984
985pub struct Model {
986 pub cfg: ModelConfig,
987 pub embd: EmbedHost,
988 pub output_norm: GpuTensor,
989 pub output: GpuTensor,
990 pub layers: Vec<Layer>,
991}
992
993impl Model {
994 /// Load a dense (vanilla-transformer) model from GGUF. Thin wrapper over
995 /// `load_dense_from_source`. Panics if the arch has SSM/MoE layers.
996 pub fn load_dense(e: &Engine, g: &GgufFile) -> Result<Self, Box<dyn std::error::Error>> {
997 Self::load_dense_from_source(e, &GgufSource(g))
998 }
999
1000 /// Load a dense-attention model from any `TensorSource` — GGUF or a safetensors HF checkpoint.
1001 /// The whole loop speaks ggml names; the source maps them. The FFN is dense SwiGLU OR routed MoE
1002 /// (OLMoE: dense full-attention + MoE FFN). Panics on hybrid (SSM) arches — use the hybrid path.
1003 pub fn load_dense_from_source(
1004 e: &Engine,
1005 src: &dyn TensorSource,
1006 ) -> Result<Self, Box<dyn std::error::Error>> {
1007 let cfg = src.config();
1008 let plan = match memra_gguf::model_packs::for_config(&cfg) {
1009 Some(pack) => pack.compile_plan(&cfg)?,
1010 None => memra_gguf::model_plan::ModelPlan::compile(&cfg)?,
1011 };
1012 if plan.layers.iter().any(|layer| {
1013 !matches!(
1014 layer.attention,
1015 memra_gguf::model_plan::AttentionPlan::Full(_)
1016 )
1017 }) {
1018 return Err("plain executor requires full-attention ModelPlan layers".into());
1019 }
1020 // FP8-KV per-model door: OFF everywhere by default (explicit MEMRA_KV_FP8 wins).
1021 // The 2026-07-12 9B "+0.7-4% scaling with depth" did NOT reproduce on the
1022 // 2026-07-28 build (12k A/B: fp8 117.0/118.2 vs q8 119.3/119.2 = −1%; d1736
1023 // flat; the fa-v3/f16pv/PDL stack moved underneath it). Adoption reverted by
1024 // measurement — fp8-KV's remaining value is bytes (~45% smaller KV) for
1025 // ctx-limited serving, not speed. Gates all green under both formats.
1026 crate::KV_FP8_FORCE.store(0, std::sync::atomic::Ordering::Relaxed);
1027
1028 let embd = EmbedHost::from_source(src, "token_embd.weight");
1029 let output_norm = GpuTensor::load_from_source(e, src, "output_norm.weight")?;
1030 // tied embeddings: fall back to tok_embd if output.weight absent (OLMoE has untied output).
1031 let output = if src.has("output.weight") {
1032 GpuTensor::load_from_source(e, src, "output.weight")?
1033 } else {
1034 GpuTensor::load_from_source(e, src, "token_embd.weight")?
1035 };
1036 let mut resident = crate::hybrid::ResidentPlan::unsharded(e, src, &cfg);
1037 let mut step_runtimes = crate::hybrid::StepParallelRuntimeRegistry::default();
1038
1039 let mut layers = Vec::with_capacity(plan.layers.len());
1040 for (il, layer_plan) in plan.layers.iter().enumerate() {
1041 let il = il as u32;
1042 let p = |s: &str| format!("blk.{il}.{s}");
1043 let ffn = crate::hybrid::load_ffn(
1044 e,
1045 src,
1046 &cfg,
1047 &layer_plan.mlp,
1048 il,
1049 None,
1050 &mut resident,
1051 &mut step_runtimes,
1052 )?;
1053 layers.push(Layer {
1054 attn_norm: GpuTensor::load_from_source(e, src, &p("attn_norm.weight"))?,
1055 wq: GpuTensor::load_from_source(e, src, &p("attn_q.weight"))?,
1056 wk: GpuTensor::load_from_source(e, src, &p("attn_k.weight"))?,
1057 wv: GpuTensor::load_from_source(e, src, &p("attn_v.weight"))?,
1058 wo: GpuTensor::load_from_source(e, src, &p("attn_output.weight"))?,
1059 q_norm: GpuTensor::load_opt_from_source(e, src, &p("attn_q_norm.weight"))?,
1060 k_norm: GpuTensor::load_opt_from_source(e, src, &p("attn_k_norm.weight"))?,
1061 ffn_norm: GpuTensor::load_from_source(e, src, &p("ffn_norm.weight"))?,
1062 ffn,
1063 });
1064 }
1065 Ok(Model {
1066 cfg,
1067 embd,
1068 output_norm,
1069 output,
1070 layers,
1071 })
1072 }
1073
1074 /// Largest expert block (bytes) across all MoE layers — the fixed cache-slot size (mirrors
1075 /// `HybridModel::max_moe_block`). 0 for a dense (non-MoE) model.
1076 pub(crate) fn max_moe_block(&self) -> usize {
1077 use crate::hybrid::Ffn;
1078 let mut mx = 0usize;
1079 for l in &self.layers {
1080 if let Ffn::Moe(m) = &l.ffn {
1081 mx = mx
1082 .max(m.gate_exps.max_expert_bytes())
1083 .max(m.up_exps.max_expert_bytes())
1084 .max(m.down_exps.max_expert_bytes());
1085 }
1086 }
1087 mx
1088 }
1089
1090 /// Gather embedding rows into f32 [T, n_embd] (token-major) by dequantizing only the needed
1091 /// rows from the host-side embedding bytes (token_embd is [n_embd, n_vocab], row per token).
1092 pub fn embed_tokens(
1093 &self,
1094 e: &Engine,
1095 tokens: &[u32],
1096 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1097 let n_embd = self.cfg.n_embd as usize;
1098 let x = self.embd.gather(n_embd, tokens);
1099 Ok(e.htod(&x)?)
1100 }
1101}
1102
1103pub type TensorMap = HashMap<String, GpuTensor>;
1104
1105/// One layer's stacked 256-expert tensor, raw GGUF quant bytes held HOST-RESIDENT.
1106///
1107/// EDGE-1: these bytes are NEVER uploaded at load (uploading 29.75GB would OOM a 24GB GPU —
1108/// this is BUG-4). Per token, only the 8 routed experts are staged H2D into a small GPU scratch.
1109///
1110/// ne = [in_f, out_f, n_expert]; the expert axis (ne[2]) is the slowest/highest-stride axis, so
1111/// expert `e` occupies the CONTIGUOUS byte block `bytes[e*expert_stride .. (e+1)*expert_stride]`.
1112///
1113/// THE 3D FIX: GpuTensor::load computes `row_bytes = raw.len()/ne[1]`, which for a stacked 3D
1114/// tensor ignores the 256-expert axis and is 256x too large (gate_exps -> 430080 instead of 1680).
1115/// load() here uses `row_bytes = raw.len() / (out_f * n_expert)` (= 1680 gate/up, 544 down).
1116/// Host byte storage for the expert blocks. Default = a pageable `Vec<u8>` (current behavior). Under
1117/// MEMRA_MOE_PINNED (auto-on when MEMRA_MOE_CACHE is set), the bytes live in CUDA pinned host memory so
1118/// the miss-path `memcpy_htod` is a true DMA, not a pageable bounce copy (MOE-SLRU-PLAN §C.1).
1119///
1120/// CAVEAT (§C.1): `alloc_pinned` uses CU_MEMHOSTALLOC_WRITECOMBINED — great for H2D-only (the expert
1121/// bytes are never read by the CPU on the hot path), but write-combined memory is SLOW for CPU reads.
1122/// A future CPU-VNNI cold-expert fallback must NOT read from this buffer.
1123pub enum HostBuf {
1124 Paged(Vec<u8>),
1125 /// Pinned host memory. We keep the `PinnedHostSlice` alive (it owns the allocation; Drop frees it)
1126 /// AND cache its raw base pointer + len so the hot-path `as_bytes()` needs no per-call event sync.
1127 Pinned {
1128 slice: std::sync::Arc<cudarc::driver::PinnedHostSlice<u8>>,
1129 base: *const u8,
1130 len: usize,
1131 },
1132 /// Alias into a shared pinned slab (ST pinned tier): `owner` keeps the slab alive; `base`/`len`
1133 /// select this expert's window. Same DMA class as `Pinned`.
1134 PinnedAlias {
1135 owner: std::sync::Arc<HostBuf>,
1136 base: *const u8,
1137 len: usize,
1138 },
1139 /// SPILLING-PLAN §1, Tier 2 (disk): the bytes live in an mmap'd region of the GGUF file, NOT in
1140 /// RAM. `map` is `MAP_SHARED`, no `MAP_POPULATE` — zero upfront copy. The first `memcpy_htod` of
1141 /// this slice page-faults → NVMe read → DMA (the demand-fault disk path). `off`/`len` select this
1142 /// expert's contiguous block within the shared file mmap. Bit-identical to `Paged`/`Pinned` —
1143 /// those copied FROM exactly these on-disk bytes, so the GEMM result is unchanged.
1144 Mmap {
1145 map: std::sync::Arc<memmap2::Mmap>,
1146 /// The same opened inode backing `map`. It must outlive the loader source so future explicit
1147 /// positioned reads cannot accidentally reopen a replaced path.
1148 file: std::sync::Arc<std::fs::File>,
1149 /// Absolute byte offset within both the whole-file mmap and `file`.
1150 off: usize,
1151 len: usize,
1152 },
1153}
1154// SAFETY: `base` is a stable pinned-host pointer owned by `slice`; the buffer is written once at load
1155// then only READ for H2D. HostExps is shared `&` across the (single per-Engine) forward, so Send/Sync
1156// mirror the underlying PinnedHostSlice (which is already Send+Sync). The `Mmap` arm holds
1157// `Arc<Mmap>` + `Arc<File>` (both Send+Sync) plus plain usize fields, so it does not weaken bounds.
1158unsafe impl Send for HostBuf {}
1159unsafe impl Sync for HostBuf {}
1160impl HostBuf {
1161 #[inline]
1162 pub fn as_bytes(&self) -> &[u8] {
1163 match self {
1164 HostBuf::Paged(v) => v.as_slice(),
1165 // SAFETY: base+len are the pinned allocation's stable extent; written once at load, then
1166 // read-only. We avoid `as_slice()` here because it would synchronize the buffer's event
1167 // on every hot-path call.
1168 HostBuf::Pinned { base, len, .. } => unsafe { std::slice::from_raw_parts(*base, *len) },
1169 HostBuf::PinnedAlias { base, len, .. } => unsafe {
1170 std::slice::from_raw_parts(*base, *len)
1171 },
1172 // Slicing the mmap is the same `&[u8]` the kernel DMAs; the read page-faults the NVMe.
1173 HostBuf::Mmap { map, off, len, .. } => &map[*off..*off + *len],
1174 }
1175 }
1176 #[inline]
1177 pub fn len(&self) -> usize {
1178 match self {
1179 HostBuf::Paged(v) => v.len(),
1180 HostBuf::Pinned { len, .. } => *len,
1181 HostBuf::PinnedAlias { len, .. } => *len,
1182 HostBuf::Mmap { len, .. } => *len,
1183 }
1184 }
1185
1186 /// Best-effort OS read-ahead for a future mmap-backed expert range. This does not touch or
1187 /// copy the bytes, so the zero-copy ownership contract is unchanged. Non-mmap buffers are
1188 /// already resident and need no advice. Kept fallible-at-the-OS but non-fatal at the call site:
1189 /// an unsupported/pressured kernel simply leaves the normal demand-fault path in place.
1190 #[inline]
1191 pub fn advise_willneed(&self, rel_off: usize, len: usize) -> bool {
1192 let HostBuf::Mmap {
1193 map,
1194 off,
1195 len: extent,
1196 ..
1197 } = self
1198 else {
1199 return false;
1200 };
1201 if len == 0 || rel_off > *extent || len > *extent - rel_off {
1202 return false;
1203 }
1204 #[cfg(unix)]
1205 {
1206 map.advise_range(memmap2::Advice::WillNeed, *off + rel_off, len)
1207 .is_ok()
1208 }
1209 #[cfg(not(unix))]
1210 {
1211 let _ = (map, off);
1212 false
1213 }
1214 }
1215
1216 #[inline]
1217 fn expert_source(&self, rel_off: usize, len: usize) -> ExpertSource<'_> {
1218 debug_assert!(rel_off <= self.len() && len <= self.len() - rel_off);
1219 match self {
1220 HostBuf::Mmap { map, file, off, .. } => {
1221 let offset = *off + rel_off;
1222 ExpertSource::Disk {
1223 file,
1224 offset: offset as u64,
1225 len,
1226 fallback: &map[offset..offset + len],
1227 keepalive: ExpertKeepalive::Mmap(map.clone()),
1228 }
1229 }
1230 HostBuf::Pinned { slice, .. } => ExpertSource::Memory {
1231 bytes: &self.as_bytes()[rel_off..rel_off + len],
1232 keepalive: Some(ExpertKeepalive::Pinned(slice.clone())),
1233 },
1234 HostBuf::PinnedAlias { owner, .. } => ExpertSource::Memory {
1235 bytes: &self.as_bytes()[rel_off..rel_off + len],
1236 keepalive: Some(ExpertKeepalive::Buffer(owner.clone())),
1237 },
1238 HostBuf::Paged(_) => ExpertSource::Memory {
1239 bytes: &self.as_bytes()[rel_off..rel_off + len],
1240 // CUDA stages pageable input before returning from the async-copy API. Only true
1241 // pinned and mmap-backed sources need an explicit lifetime owner in the cache.
1242 keepalive: None,
1243 },
1244 }
1245 }
1246}
1247
1248/// Clonable ownership retained by asynchronous cache transfers. The payload is intentionally never
1249/// read: keeping it alive is the contract.
1250#[allow(dead_code)]
1251pub(crate) enum ExpertKeepalive {
1252 Pinned(std::sync::Arc<cudarc::driver::PinnedHostSlice<u8>>),
1253 Buffer(std::sync::Arc<HostBuf>),
1254 Mmap(std::sync::Arc<memmap2::Mmap>),
1255}
1256
1257/// Source-aware view of one expert block. The mmap fallback remains the byte oracle; retaining the
1258/// opened file enables a later explicit-read backend without changing tensor layout or numerics.
1259pub(crate) enum ExpertSource<'a> {
1260 Memory {
1261 bytes: &'a [u8],
1262 keepalive: Option<ExpertKeepalive>,
1263 },
1264 Disk {
1265 file: &'a std::sync::Arc<std::fs::File>,
1266 offset: u64,
1267 len: usize,
1268 fallback: &'a [u8],
1269 keepalive: ExpertKeepalive,
1270 },
1271}
1272
1273/// One layer's stacked 256-expert tensor, raw GGUF quant bytes held HOST-RESIDENT.
1274///
1275/// EDGE-1: these bytes are NEVER uploaded at load (uploading 29.75GB would OOM a 24GB GPU —
1276/// this is BUG-4). Per token, only the 8 routed experts are staged H2D into a small GPU scratch.
1277///
1278/// ne = [in_f, out_f, n_expert]; the expert axis (ne[2]) is the slowest/highest-stride axis, so
1279/// expert `e` occupies the CONTIGUOUS byte block `bytes[e*expert_stride .. (e+1)*expert_stride]`.
1280///
1281/// THE 3D FIX: GpuTensor::load computes `row_bytes = raw.len()/ne[1]`, which for a stacked 3D
1282/// tensor ignores the 256-expert axis and is 256x too large (gate_exps -> 430080 instead of 1680).
1283/// load() here uses `row_bytes = raw.len() / (out_f * n_expert)` (= 1680 gate/up, 544 down).
1284#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1285pub struct ExpertLayout {
1286 pub offset: usize,
1287 pub len: usize,
1288 pub qtype: i32,
1289 pub row_bytes: usize,
1290}
1291
1292fn staged_expert_qtype(ty: GgmlType) -> Option<i32> {
1293 Some(match ty {
1294 GgmlType::Q8_0 => QT_Q8_0,
1295 GgmlType::Q2_K => QT_Q2_K,
1296 GgmlType::Q4_K => QT_Q4_K,
1297 GgmlType::Q6_K => QT_Q6_K,
1298 GgmlType::Q5_K => QT_Q5_K,
1299 GgmlType::Q3_K => QT_Q3_K,
1300 GgmlType::IQ4_XS => QT_IQ4_XS,
1301 GgmlType::IQ3_S => QT_IQ3_S,
1302 GgmlType::NVFP4 => QT_NVFP4,
1303 GgmlType::F32 => QT_F32,
1304 GgmlType::BF16 => QT_BF16,
1305 _ => return None,
1306 })
1307}
1308
1309fn staged_expert_row_bytes(ty: GgmlType, in_f: usize) -> Option<usize> {
1310 staged_expert_qtype(ty)?;
1311 let (block, type_size) = ty.block_and_type_size();
1312 assert_eq!(
1313 in_f as u64 % block,
1314 0,
1315 "expert row width {in_f} is not divisible by {ty:?} block {block}"
1316 );
1317 Some((in_f as u64 / block * type_size) as usize)
1318}
1319
1320fn find_expert_disk_strict(
1321 src: &dyn TensorSource,
1322 name: &str,
1323) -> Result<Option<DiskExtent>, Box<dyn std::error::Error>> {
1324 if let Some(extent) = src.find_expert_disk(name) {
1325 return Ok(Some(extent));
1326 }
1327 if src.find_expert_mmap(name).is_some() {
1328 return Err(std::io::Error::new(
1329 std::io::ErrorKind::InvalidData,
1330 format!(
1331 "expert tensor {name} exposes legacy find_expert_mmap without find_expert_disk; \
1332 disk-backed expert loading requires a retained Arc<File>"
1333 ),
1334 )
1335 .into());
1336 }
1337 Ok(None)
1338}
1339
1340pub struct HostExps {
1341 pub bytes: HostBuf, // raw GGUF block bytes (host); per-token DMA src for the 8 routed exps
1342 /// SPILLING-PLAN §1.1: per-expert backing tier. `None` => the layer fits in one `bytes` store and
1343 /// every expert slices it (the unchanged in-RAM path). `Some` => per-expert split: the hottest
1344 /// experts are `Pinned` (Tier 1, fast async DMA), the rest `Mmap` into the GGUF (Tier 2, disk
1345 /// demand-fault). `expert_bytes(e)` resolves `tiers[e]` if present, else slices `bytes`.
1346 pub tiers: Option<Vec<HostBuf>>,
1347 pub qtype: i32, // QT_Q6_K (gate/up) | QT_Q8_0 (down)
1348 pub in_f: usize, // ne[0] (gate/up = 2048, down = 512)
1349 pub out_f: usize, // ne[1] (gate/up = 512, down = 2048)
1350 pub n_expert: usize, // ne[2] = 256
1351 pub row_bytes: usize, // raw.len()/(out_f*n_expert) -> 1680 (gate/up) / 544 (down)
1352 pub expert_stride: usize, // raw.len()/n_expert -> 860160 (gate/up) / 1114112 (down)
1353 /// Per-expert encoding metadata when experts in this projection do not share one dtype/layout.
1354 /// `None` preserves the existing uniform slab contract and every resident/fused fast path.
1355 /// `Some` routes through the per-expert staged/cache path, using each entry's qtype/row size.
1356 pub layouts: Option<Vec<ExpertLayout>>,
1357 /// Per-expert post-matmul macro-scale (ModelOpt NVFP4 `weight_scale_2`, one scalar per expert
1358 /// tensor). `None` => all 1.0 (GGUF experts; block scales carry everything). The MoE forward
1359 /// folds gate/up macros into the activation epilogue (gs/us) and the down macro into the
1360 /// per-expert accumulate weight.
1361 pub macros: Option<Vec<f32>>,
1362 /// Native block-E4M3 scale plane for a uniform stacked expert bank. Scales are
1363 /// `[expert, output_block, input_block]` in checkpoint order.
1364 pub fp8_blk: Option<HostExpertFp8BlockScales>,
1365}
1366
1367pub struct HostExpertFp8BlockScales {
1368 pub scales: Vec<f32>,
1369 pub rows: usize,
1370 pub cols: usize,
1371 pub expert_stride: usize,
1372}
1373
1374impl HostExps {
1375 /// Load a stacked 3D expert tensor, keeping its quant bytes on the HOST. `e` supplies the CUDA
1376 /// context for the optional pinned allocation (§C.1). Default storage is pageable `Vec<u8>`
1377 /// (identical to the prior behavior); pinned is chosen when MEMRA_MOE_PINNED or MEMRA_MOE_CACHE is set.
1378 pub fn load(e: &Engine, g: &GgufFile, name: &str) -> Result<Self, Box<dyn std::error::Error>> {
1379 Self::load_stacked_from_source(e, &GgufSource(g), name)
1380 }
1381
1382 /// Load a STACKED 3D expert tensor (`ne=[in_f,out_f,n_expert]`) from any source. GGUF stores the
1383 /// experts this way; the source returns the same mmap bytes (`GgufSource::find` == `tensor_data`),
1384 /// so the GGUF path is byte-identical to the prior direct-`GgufFile` loader. (Safetensors stores N
1385 /// 2D tensors instead — those go through `load_from_source`, which gathers them.)
1386 /// Row-range variant for FUSED stacked tensors (gemma4 ffn_gate_up_exps: gate = rows
1387 /// [0,ff), up = [ff,2ff) per expert — llama-graph view convention). Copies only the range.
1388 pub fn load_stacked_split_from_source(
1389 e: &Engine,
1390 src: &dyn TensorSource,
1391 name: &str,
1392 row0: usize,
1393 row1: usize,
1394 ) -> Result<Self, Box<dyn std::error::Error>> {
1395 let t = src
1396 .find(name)
1397 .unwrap_or_else(|| panic!("missing exps tensor {name}"));
1398 assert_eq!(t.ne.len(), 3, "{name} is not 3D (ne={:?})", t.ne);
1399 let qtype = match t.ggml_type {
1400 GgmlType::Q8_0 => QT_Q8_0,
1401 GgmlType::Q4_K => QT_Q4_K,
1402 GgmlType::Q6_K => QT_Q6_K,
1403 GgmlType::Q5_K => QT_Q5_K,
1404 GgmlType::Q3_K => QT_Q3_K,
1405 GgmlType::IQ4_XS => QT_IQ4_XS,
1406 GgmlType::IQ3_S => QT_IQ3_S,
1407 GgmlType::NVFP4 => QT_NVFP4,
1408 GgmlType::Q4_0 => QT_Q4_0,
1409 other => panic!("exps {name} unsupported quant {other:?}"),
1410 };
1411 let raw: &[u8] = &t.bytes;
1412 let in_f = t.ne[0] as usize;
1413 let out_full = t.ne[1] as usize;
1414 let n_expert = t.ne[2] as usize;
1415 let full_stride = raw.len() / n_expert;
1416 let row_bytes = raw.len() / (out_full * n_expert);
1417 assert_eq!(full_stride, out_full * row_bytes, "{name} stride mismatch");
1418 let out_f = row1 - row0;
1419 let expert_stride = out_f * row_bytes;
1420 let mut buf = vec![0u8; n_expert * expert_stride];
1421 for ex in 0..n_expert {
1422 let s0 = ex * full_stride + row0 * row_bytes;
1423 buf[ex * expert_stride..(ex + 1) * expert_stride]
1424 .copy_from_slice(&raw[s0..s0 + expert_stride]);
1425 }
1426 let pinned = std::env::var("MEMRA_MOE_PINNED").is_ok()
1427 || std::env::var("MEMRA_MOE_CACHE").as_deref() != Ok("0");
1428 let bytes = if pinned {
1429 let mut pn = unsafe { e.ctx().alloc_pinned::<u8>(buf.len())? };
1430 {
1431 let dst = pn.as_mut_slice()?;
1432 dst.copy_from_slice(&buf);
1433 }
1434 let base = pn.as_ptr()? as *const u8;
1435 let len = buf.len();
1436 HostBuf::Pinned {
1437 slice: std::sync::Arc::new(pn),
1438 base,
1439 len,
1440 }
1441 } else {
1442 HostBuf::Paged(buf)
1443 };
1444 Ok(HostExps {
1445 bytes,
1446 tiers: None,
1447 qtype,
1448 in_f,
1449 out_f,
1450 n_expert,
1451 row_bytes,
1452 expert_stride,
1453 layouts: None,
1454 macros: None,
1455 fp8_blk: None,
1456 })
1457 }
1458
1459 /// Stacked per-expert macro-scale sidecar: `blk.N.ffn_{proj}_exps.scale` f32 [n_expert]
1460 /// (the qwen3.6 NVFP4 converter emits one per stacked expert tensor — compressed-tensors
1461 /// global scales, inverted to multipliers). Absent (every k-quant GGUF) => None.
1462 /// NOTE gemma4 consumes ffn_down_exps.scale through its OWN router-fold (Gemma4MoeBits) —
1463 /// its MoE forward does not read HostExps::macros, so a Some here is inert there.
1464 fn stacked_macros(src: &dyn TensorSource, name: &str) -> Option<Vec<f32>> {
1465 let stem = name.strip_suffix(".weight")?;
1466 let sv = src.find(&format!("{stem}.scale"))?;
1467 if sv.ggml_type != GgmlType::F32 {
1468 return None;
1469 }
1470 let macros: Vec<f32> = sv
1471 .bytes
1472 .chunks_exact(4)
1473 .map(|c| f32::from_le_bytes(c.try_into().unwrap()))
1474 .collect();
1475 if macros.iter().all(|&m| m == 1.0) {
1476 None
1477 } else {
1478 Some(macros)
1479 }
1480 }
1481
1482 /// STACKED NVFP4-NATIVE ARM (Step-3.7-Flash-NVFP4 class, 2026-08-20): the checkpoint stores
1483 /// each routed projection as ONE stacked modelopt tensor `[E, out, in/2]` (not per-expert 2-D
1484 /// tensors — that class rides PATH B in `load_from_source`). Repack per expert into the GGUF
1485 /// 36B-block layout the staged qmatvec decodes, streaming into the same `.memra-repack`
1486 /// disk-cache tier PATH B uses (peak RAM = one expert), and mmap the cache. Per-expert
1487 /// `weight_scale_2` macros go to `macros` — the MoE forward folds them post-matmul; dropping
1488 /// them (~1e-5..1e-4 in the official artifact) produces garbage.
1489 fn load_nvfp4_stacked_native(
1490 src: &dyn TensorSource,
1491 name: &str,
1492 ) -> Result<Option<Self>, Box<dyn std::error::Error>> {
1493 let Some(bank) = src.find_nvfp4_stacked_native(name) else {
1494 return Ok(None);
1495 };
1496 let (n_expert, out_f, in_f) = (bank.n_expert, bank.out_f, bank.in_f);
1497 if in_f % 64 != 0 {
1498 return Err(
1499 format!("{name} stacked NVFP4 in_features {in_f} is not 64-aligned").into(),
1500 );
1501 }
1502 let row_bytes = in_f / 64 * 36;
1503 let expert_stride = out_f * row_bytes;
1504 let total = n_expert * expert_stride;
1505 let code_stride = out_f * in_f / 2;
1506 let scale_stride = out_f * in_f / 16;
1507 let macros = bank.macros.clone();
1508 let cache_path = src.st_dir().map(|dir| {
1509 let cache_dir = dir.join(".memra-repack");
1510 let _ = std::fs::create_dir_all(&cache_dir);
1511 cache_dir.join(format!(
1512 "{}-stacked-{n_expert}x{out_f}x{in_f}.nvfp4",
1513 name.replace(['.', '/'], "-")
1514 ))
1515 });
1516 let bytes = if let Some(cache) = cache_path.as_ref() {
1517 let fresh = std::fs::metadata(cache)
1518 .map(|meta| meta.len() as usize == total)
1519 .unwrap_or(false);
1520 if !fresh {
1521 use std::io::Write;
1522 let mut out = std::io::BufWriter::new(std::fs::File::create(cache)?);
1523 for expert in 0..n_expert {
1524 out.write_all(&memra_gguf::nvfp4_repack::repack_modelopt_to_gguf(
1525 &bank.codes[expert * code_stride..(expert + 1) * code_stride],
1526 &bank.scales[expert * scale_stride..(expert + 1) * scale_stride],
1527 out_f,
1528 in_f,
1529 ))?;
1530 }
1531 out.flush()?;
1532 }
1533 let file = std::sync::Arc::new(std::fs::File::open(cache)?);
1534 let map = unsafe { memmap2::Mmap::map(file.as_ref())? };
1535 assert_eq!(map.len(), total, "repack cache {cache:?} size mismatch");
1536 let _ = memra_gguf::source::apply_expert_mmap_advice(&map);
1537 HostBuf::Mmap {
1538 map: std::sync::Arc::new(map),
1539 file,
1540 off: 0,
1541 len: total,
1542 }
1543 } else {
1544 let mut buf: Vec<u8> = Vec::with_capacity(total);
1545 for expert in 0..n_expert {
1546 buf.extend_from_slice(&memra_gguf::nvfp4_repack::repack_modelopt_to_gguf(
1547 &bank.codes[expert * code_stride..(expert + 1) * code_stride],
1548 &bank.scales[expert * scale_stride..(expert + 1) * scale_stride],
1549 out_f,
1550 in_f,
1551 ));
1552 }
1553 assert_eq!(buf.len(), total);
1554 HostBuf::Paged(buf)
1555 };
1556 let all_one = macros.iter().all(|&value| value == 1.0);
1557 Ok(Some(HostExps {
1558 bytes,
1559 tiers: None,
1560 qtype: QT_NVFP4,
1561 in_f,
1562 out_f,
1563 n_expert,
1564 row_bytes,
1565 expert_stride,
1566 layouts: None,
1567 macros: if all_one { None } else { Some(macros) },
1568 fp8_blk: None,
1569 }))
1570 }
1571
1572 fn load_fp8_stacked_native_with_policy(
1573 src: &dyn TensorSource,
1574 name: &str,
1575 native_enabled: bool,
1576 ) -> Result<Option<Self>, Box<dyn std::error::Error>> {
1577 let Some(f8) = src.find_fp8_stacked_native(name) else {
1578 return Ok(None);
1579 };
1580 if f8.scale_rows != f8.out_f.div_ceil(128) || f8.scale_cols != f8.in_f.div_ceil(128) {
1581 return Err(format!(
1582 "{name} FP8 scale geometry mismatch: got {}x{}, expected {}x{}",
1583 f8.scale_rows,
1584 f8.scale_cols,
1585 f8.out_f.div_ceil(128),
1586 f8.in_f.div_ceil(128)
1587 )
1588 .into());
1589 }
1590 if f8.bytes.iter().any(|code| code & 0x7f == 0x7f) {
1591 return Err(format!("{name} FP8 code slab contains non-finite E4M3 values").into());
1592 }
1593 let scale_stride = f8.scale_rows * f8.scale_cols;
1594 if !native_enabled {
1595 if f8.in_f % 32 != 0 {
1596 return Err(format!(
1597 "{name} FP8 rollback requires an input width divisible by 32, got {}",
1598 f8.in_f
1599 )
1600 .into());
1601 }
1602 let mut q8 = Vec::new();
1603 for expert in 0..f8.n_expert {
1604 let mut data = Vec::with_capacity(f8.out_f * f8.in_f);
1605 for output in 0..f8.out_f {
1606 let row = (expert * f8.out_f + output) * f8.in_f;
1607 for input in 0..f8.in_f {
1608 let scale = f8.scales
1609 [expert * scale_stride + (output / 128) * f8.scale_cols + input / 128];
1610 data.push(
1611 memra_gguf::nvfp4_repack::fp8_e4m3_to_f32(f8.bytes[row + input])
1612 * scale,
1613 );
1614 }
1615 }
1616 q8.extend_from_slice(&memra_gguf::nvfp4_repack::f32_to_q8_0(&data));
1617 }
1618 let row_bytes = f8.in_f / 32 * 34;
1619 let expert_stride = f8.out_f * row_bytes;
1620 assert_eq!(q8.len(), f8.n_expert * expert_stride);
1621 return Ok(Some(HostExps {
1622 bytes: HostBuf::Paged(q8),
1623 tiers: None,
1624 qtype: QT_Q8_0,
1625 in_f: f8.in_f,
1626 out_f: f8.out_f,
1627 n_expert: f8.n_expert,
1628 row_bytes,
1629 expert_stride,
1630 layouts: None,
1631 macros: None,
1632 fp8_blk: None,
1633 }));
1634 }
1635
1636 assert_eq!(
1637 f8.bytes.len(),
1638 f8.n_expert * f8.out_f * f8.in_f,
1639 "{name} FP8 code slab length mismatch"
1640 );
1641 assert_eq!(
1642 f8.scales.len(),
1643 f8.n_expert * scale_stride,
1644 "{name} FP8 scale slab length mismatch"
1645 );
1646 let expert_stride = f8.out_f * f8.in_f;
1647 let bytes = match find_expert_disk_strict(src, name)? {
1648 Some(extent) => {
1649 if extent.len != f8.bytes.len() {
1650 return Err(format!(
1651 "{name} FP8 mmap length mismatch: extent={} tensor={}",
1652 extent.len,
1653 f8.bytes.len()
1654 )
1655 .into());
1656 }
1657 let off = usize::try_from(extent.offset).map_err(|_| {
1658 format!(
1659 "{name} FP8 mmap offset {} does not fit usize",
1660 extent.offset
1661 )
1662 })?;
1663 HostBuf::Mmap {
1664 map: extent.map,
1665 file: extent.file,
1666 off,
1667 len: extent.len,
1668 }
1669 }
1670 None => HostBuf::Paged(f8.bytes.to_vec()),
1671 };
1672 Ok(Some(HostExps {
1673 bytes,
1674 tiers: None,
1675 qtype: crate::QT_F8_E4M3_BLK,
1676 in_f: f8.in_f,
1677 out_f: f8.out_f,
1678 n_expert: f8.n_expert,
1679 row_bytes: f8.in_f,
1680 expert_stride,
1681 layouts: None,
1682 macros: None,
1683 fp8_blk: Some(HostExpertFp8BlockScales {
1684 scales: f8.scales,
1685 rows: f8.scale_rows,
1686 cols: f8.scale_cols,
1687 expert_stride: scale_stride,
1688 }),
1689 }))
1690 }
1691
1692 pub fn load_stacked_from_source(
1693 e: &Engine,
1694 src: &dyn TensorSource,
1695 name: &str,
1696 ) -> Result<Self, Box<dyn std::error::Error>> {
1697 if let Some(exps) = Self::load_fp8_stacked_native_with_policy(
1698 src,
1699 name,
1700 crate::fp8_ffi::st_e4m3_blk_enabled(),
1701 )? {
1702 return Ok(exps);
1703 }
1704 if let Some(exps) = Self::load_nvfp4_stacked_native(src, name)? {
1705 return Ok(exps);
1706 }
1707
1708 let t = src
1709 .find(name)
1710 .unwrap_or_else(|| panic!("missing exps tensor {name}"));
1711 assert_eq!(
1712 t.ne.len(),
1713 3,
1714 "{name} is not a 3D stacked-expert tensor (ne={:?})",
1715 t.ne
1716 );
1717 // MMAP-BACKED SPILL TIER (Hy3 repack dir, 2026-07-09): when the source's on-disk layout IS
1718 // already the engine's expert layout (one expert-axis-slowest slab file per (layer, proj),
1719 // the transcoder's contract), back the HostExps with `HostBuf::Mmap` directly — ZERO host
1720 // copy. The default copy path below would pin/allocate the WHOLE stacked slab (80.5 GB for
1721 // Hy3-REAP50 on a 60 GB host = the M3 first-load OOM class); the mmap tier instead lets the
1722 // page cache carry the hot expert mass (RAM tier) and demand-faults the overflow from NVMe,
1723 // exactly like the proven M3 `.memra-repack` path (model.rs NVFP4 disk arm). Bit-identity:
1724 // `expert_bytes(e)` slices the same on-disk bytes the copy would have staged. The SLRU VRAM
1725 // cache stacks on top unchanged. The configured whole-map advice is applied at source open.
1726 if let Some(DiskExtent {
1727 map,
1728 file,
1729 offset,
1730 len,
1731 }) = find_expert_disk_strict(src, name)?
1732 {
1733 let off = usize::try_from(offset)
1734 .map_err(|_| format!("{name} disk offset {offset} does not fit usize"))?;
1735 let qtype = match t.ggml_type {
1736 GgmlType::Q8_0 => QT_Q8_0,
1737 GgmlType::Q4_K => QT_Q4_K,
1738 GgmlType::Q6_K => QT_Q6_K,
1739 GgmlType::Q5_K => QT_Q5_K,
1740 GgmlType::Q3_K => QT_Q3_K,
1741 GgmlType::IQ4_XS => QT_IQ4_XS,
1742 GgmlType::IQ3_S => QT_IQ3_S,
1743 GgmlType::NVFP4 => QT_NVFP4,
1744 GgmlType::Q4_0 => QT_Q4_0,
1745 other => panic!("exps {name} unsupported quant {other:?}"),
1746 };
1747 let in_f = t.ne[0] as usize;
1748 let out_f = t.ne[1] as usize;
1749 let n_expert = t.ne[2] as usize;
1750 let expert_stride = len / n_expert;
1751 let row_bytes = len / (out_f * n_expert);
1752 assert_eq!(
1753 expert_stride,
1754 out_f * row_bytes,
1755 "{name} stride mismatch: stride={expert_stride} out_f={out_f} row_bytes={row_bytes}"
1756 );
1757 assert_eq!(
1758 len,
1759 n_expert * expert_stride,
1760 "{name} mmap len != n_expert*stride"
1761 );
1762 return Ok(HostExps {
1763 bytes: HostBuf::Mmap {
1764 map,
1765 file,
1766 off,
1767 len,
1768 },
1769 tiers: None,
1770 qtype,
1771 in_f,
1772 out_f,
1773 n_expert,
1774 row_bytes,
1775 expert_stride,
1776 layouts: None,
1777 macros: Self::stacked_macros(src, name),
1778 fp8_blk: None,
1779 });
1780 }
1781 let raw: &[u8] = &t.bytes;
1782 // All quant types the staged-expert qmatvec can decode (dp4a-fast or Stage-A f32).
1783 let qtype = match t.ggml_type {
1784 GgmlType::Q8_0 => QT_Q8_0,
1785 GgmlType::Q4_K => QT_Q4_K,
1786 GgmlType::Q6_K => QT_Q6_K,
1787 GgmlType::Q5_K => QT_Q5_K,
1788 GgmlType::Q3_K => QT_Q3_K,
1789 GgmlType::IQ4_XS => QT_IQ4_XS,
1790 GgmlType::IQ3_S => QT_IQ3_S,
1791 GgmlType::NVFP4 => QT_NVFP4,
1792 GgmlType::Q4_0 => QT_Q4_0,
1793 other => panic!("exps {name} unsupported quant {other:?}"),
1794 };
1795 let in_f = t.ne[0] as usize;
1796 let out_f = t.ne[1] as usize;
1797 let n_expert = t.ne[2] as usize;
1798 // VERIFIED: gate/up Q6_K total/256 = 860160; row = total/(512*256) = 1680.
1799 // down Q8_0 total/256 = 1114112; row = total/(2048*256) = 544.
1800 let expert_stride = raw.len() / n_expert;
1801 let row_bytes = raw.len() / (out_f * n_expert);
1802 // sanity: expert_stride must equal out_f * row_bytes exactly (catches a dim mixup)
1803 assert_eq!(
1804 expert_stride,
1805 out_f * row_bytes,
1806 "{name} stride mismatch: stride={expert_stride} out_f={out_f} row_bytes={row_bytes}"
1807 );
1808
1809 let pinned = std::env::var("MEMRA_MOE_PINNED").is_ok()
1810 || std::env::var("MEMRA_MOE_CACHE").as_deref() != Ok("0");
1811 let bytes = if pinned {
1812 // alloc pinned host memory, copy the GGUF block bytes in once, cache the base pointer.
1813 let mut p = unsafe { e.ctx().alloc_pinned::<u8>(raw.len())? };
1814 {
1815 let dst = p.as_mut_slice()?;
1816 dst.copy_from_slice(raw);
1817 }
1818 let base = p.as_ptr()? as *const u8; // syncs once here at load; stable afterward
1819 let len = raw.len();
1820 HostBuf::Pinned {
1821 slice: std::sync::Arc::new(p),
1822 base,
1823 len,
1824 }
1825 } else {
1826 HostBuf::Paged(raw.to_vec())
1827 };
1828 Ok(HostExps {
1829 bytes,
1830 tiers: None,
1831 qtype,
1832 in_f,
1833 out_f,
1834 n_expert,
1835 row_bytes,
1836 expert_stride,
1837 layouts: None,
1838 macros: Self::stacked_macros(src, name),
1839 fp8_blk: None,
1840 })
1841 }
1842
1843 /// SPILLING-PLAN §1.1, §2 step 4: load a stacked 3D expert tensor with a PER-EXPERT tier split.
1844 /// Under `MEMRA_SPILL_DISK`, the hottest experts (greedy in expert order, until the shared pinned
1845 /// budget in `ctx` is exhausted) get `HostBuf::Pinned` (Tier 1, fast async DMA); every remaining
1846 /// expert is `HostBuf::Mmap` into the GGUF (Tier 2, demand-faulted from disk on first H2D). The
1847 /// resulting bytes are bit-identical to the in-RAM path either way — `qmatvec_view` is untouched.
1848 ///
1849 /// `ctx.file_map` is ONE shared `MAP_SHARED` mmap of the whole GGUF (`Arc`-cloned per spilled
1850 /// expert), so the 120 expert tensors of a 40-layer MoE never open the file more than once.
1851 pub fn load_tiered(
1852 e: &Engine,
1853 g: &GgufFile,
1854 name: &str,
1855 ctx: &mut crate::spill::SpillCtx,
1856 ) -> Result<Self, Box<dyn std::error::Error>> {
1857 let t = g
1858 .find(name)
1859 .unwrap_or_else(|| panic!("missing exps tensor {name}"));
1860 assert_eq!(
1861 t.ne.len(),
1862 3,
1863 "{name} is not a 3D stacked-expert tensor (ne={:?})",
1864 t.ne
1865 );
1866 let raw = g.tensor_data(t);
1867 let qtype = match t.ggml_type {
1868 GgmlType::Q8_0 => QT_Q8_0,
1869 GgmlType::Q4_K => QT_Q4_K,
1870 GgmlType::Q6_K => QT_Q6_K,
1871 GgmlType::Q5_K => QT_Q5_K,
1872 GgmlType::Q3_K => QT_Q3_K,
1873 GgmlType::IQ4_XS => QT_IQ4_XS,
1874 GgmlType::IQ3_S => QT_IQ3_S,
1875 GgmlType::NVFP4 => QT_NVFP4,
1876 GgmlType::Q4_0 => QT_Q4_0,
1877 other => panic!("exps {name} unsupported quant {other:?}"),
1878 };
1879 let in_f = t.ne[0] as usize;
1880 let out_f = t.ne[1] as usize;
1881 let n_expert = t.ne[2] as usize;
1882 let expert_stride = raw.len() / n_expert;
1883 let row_bytes = raw.len() / (out_f * n_expert);
1884 assert_eq!(
1885 expert_stride,
1886 out_f * row_bytes,
1887 "{name} stride mismatch: stride={expert_stride} out_f={out_f} row_bytes={row_bytes}"
1888 );
1889
1890 // Byte offset of this tensor's data (start of expert 0) WITHIN ITS OWN SHARD's file; each
1891 // expert is the next `expert_stride` bytes. The `Mmap` arm slices `ctx.file_maps[t.shard]`
1892 // at these offsets — a split model's offsets are per-shard, not global.
1893 let (file_start, _file_end) = g.tensor_file_range(t);
1894
1895 // Per-expert tier decision under the shared running budget. `bytes` keeps a 0-byte sentinel
1896 // (`Paged(empty)`) since every read now goes through `tiers`.
1897 let mut tiers = Vec::with_capacity(n_expert);
1898 for ex in 0..n_expert {
1899 let blk = &raw[ex * expert_stride..(ex + 1) * expert_stride];
1900 let file_off = file_start + ex * expert_stride;
1901 tiers.push(crate::spill::place_expert(ctx, e, blk, file_off, t.shard)?);
1902 }
1903 Ok(HostExps {
1904 bytes: HostBuf::Paged(Vec::new()), // unused when `tiers` is Some
1905 tiers: Some(tiers),
1906 qtype,
1907 in_f,
1908 out_f,
1909 n_expert,
1910 row_bytes,
1911 expert_stride,
1912 layouts: None,
1913 macros: Self::stacked_macros(&GgufSource(g), name),
1914 fp8_blk: None,
1915 })
1916 }
1917
1918 /// MoE expert GATHER from a `TensorSource` (the safetensors path; ST-MOE-PLAN §1.3). GGUF stacks
1919 /// all experts into ONE 3D tensor; HF stores them as N separate 2D tensors
1920 /// `model.layers.{il}.mlp.experts.{e}.{gate,up,down}_proj.weight`. `find` returns `None` for the
1921 /// ggml `*_exps` name on purpose, so the experts are gathered out-of-band here.
1922 ///
1923 /// PATH A (load-time only, no quantize): each HF 2D expert tensor is dequantized to f32 and the
1924 /// per-expert blocks are concatenated expert-axis-slowest into ONE contiguous buffer — exactly the
1925 /// layout `expert_bytes(e)` slices and the staged `qmatvec_view` (qtype=QT_F32) reads. The same
1926 /// `expert_stride == out_f*row_bytes` invariant as the GGUF path is asserted at the end.
1927 ///
1928 /// `ggml_exps_name` is `blk.{il}.ffn_{gate,up,down}_exps.weight`; it is split to recover `il` and
1929 /// the proj. `n_expert` comes from `cfg.moe`. The HF per-expert literal `mlp.experts.{e}.{p}_proj`
1930 /// is the qwen3moe / olmoe layout (a future arch with `block_sparse_moe.experts.*` would need a
1931 /// branch in `hf_expert_name`).
1932 pub fn load_from_source(
1933 e: &Engine,
1934 src: &dyn TensorSource,
1935 ggml_exps_name: &str,
1936 n_expert: usize,
1937 ) -> Result<Self, Box<dyn std::error::Error>> {
1938 // Recover il + proj from `blk.{il}.ffn_{gate,up,down}_exps.weight`.
1939 let rest = ggml_exps_name
1940 .strip_prefix("blk.")
1941 .unwrap_or_else(|| panic!("not a blk.* name: {ggml_exps_name}"));
1942 let (il_s, suffix) = rest.split_once('.').unwrap();
1943 let il: u32 = il_s.parse().unwrap();
1944 let proj = match suffix {
1945 "ffn_gate_exps.weight" => "gate",
1946 "ffn_up_exps.weight" => "up",
1947 "ffn_down_exps.weight" => "down",
1948 other => panic!("not a *_exps suffix: {other}"),
1949 };
1950
1951 // A mixed-precision safetensors/repack source exposes experts as separate 2D tensors.
1952 // Detect a dtype/layout change before the uniform gather paths normalize the whole layer
1953 // to one encoding. Uniform checkpoints take the unchanged optimized path below.
1954 let mut signatures = Vec::with_capacity(n_expert);
1955 let active = src.active_experts(il);
1956 for ex in 0..n_expert {
1957 if active.is_some_and(|mask| !mask[ex]) {
1958 signatures.push((i32::MIN, 0));
1959 continue;
1960 }
1961 let name = format!("blk.{il}.ffn_{proj}_exps.{ex}.weight");
1962 if let Some(nv) = src.find_nvfp4_native(&name) {
1963 signatures.push((QT_NVFP4, nv.in_f / 64 * 36));
1964 } else {
1965 let v = src
1966 .find(&name)
1967 .unwrap_or_else(|| panic!("missing expert tensor {name}"));
1968 let in_f = v.ne[0] as usize;
1969 signatures.push(match staged_expert_row_bytes(v.ggml_type, in_f) {
1970 Some(row_bytes) => (staged_expert_qtype(v.ggml_type).unwrap(), row_bytes),
1971 None => (QT_F32, in_f * 4),
1972 });
1973 }
1974 }
1975 let mixed_layout = signatures.windows(2).any(|pair| pair[0] != pair[1]);
1976 if src.preserve_expert_encodings() && !mixed_layout {
1977 if let Some(uniform) = Self::load_uniform_mmap_from_source(src, il, proj, n_expert)? {
1978 return Ok(uniform);
1979 }
1980 }
1981 if src.preserve_expert_encodings() || mixed_layout {
1982 return Self::load_mixed_from_source(src, il, proj, n_expert);
1983 }
1984
1985 // PATH B (NVFP4-NATIVE GATHER, 2026-07-05): when the source exposes the experts as packed
1986 // ModelOpt/Reza NVFP4 (find_nvfp4_native), keep them QUANTIZED — repack each expert's
1987 // modelopt bytes to the GGUF 36B-block layout the staged qmatvec decodes, and concatenate.
1988 // No f32 blow-up: a 129GB checkpoint gathers to ~the same bytes instead of ~8x (which is
1989 // what makes MiniMax-M3 REAP50 loadable on a 60GB-RAM host at all, with spill on top).
1990 // Per-expert `weight_scale_2` macros go to `macros` (folded post-matmul by the MoE forward).
1991 {
1992 let name0 = format!("blk.{il}.ffn_{proj}_exps.0.weight");
1993 if let Some(nv0) = src.find_nvfp4_native(&name0) {
1994 let (in_f, out_f) = (nv0.in_f, nv0.out_f);
1995 let row_bytes = in_f / 64 * 36;
1996 let expert_stride = out_f * row_bytes;
1997 // ST DISK TIER (2026-07-06, the MiniMax OOM fix): when the total expert bytes
1998 // exceed host RAM (M3 REAP50 = 122GB repacked on a 60GB host, first-load host-OOM
1999 // at layer ~24), repack each layer ONCE into an on-disk cache file next to the
2000 // checkpoint and mmap it (HostBuf::Mmap, MAP_SHARED no-populate — the same tier-2
2001 // mechanism the GGUF spill path uses). Reloads hit the cache (size-checked), pay
2002 // zero repack. MEMRA_ST_REPACK_DISK=0 forces the old in-RAM gather.
2003 let disk = std::env::var("MEMRA_ST_REPACK_DISK")
2004 .map(|v| v != "0")
2005 .unwrap_or(true)
2006 && src.st_dir().is_some();
2007 let cache_path = src.st_dir().map(|d| {
2008 let cd = d.join(".memra-repack");
2009 let _ = std::fs::create_dir_all(&cd);
2010 cd.join(format!("blk{il}-{proj}-{n_expert}x{out_f}x{in_f}.nvfp4"))
2011 });
2012 let total = n_expert * expert_stride;
2013 let mut macros = vec![1.0f32; n_expert];
2014 let read_macros = |macros: &mut Vec<f32>| {
2015 for ex in 0..n_expert {
2016 let stem = format!("blk.{il}.ffn_{proj}_exps.{ex}");
2017 if let Some(sv) = src.find(&format!("{stem}.scale")) {
2018 macros[ex] = f32::from_le_bytes(sv.bytes[..4].try_into().unwrap());
2019 }
2020 }
2021 };
2022 let bytes = if disk {
2023 let cp = cache_path.as_ref().unwrap();
2024 let fresh = std::fs::metadata(cp)
2025 .map(|m| m.len() as usize == total)
2026 .unwrap_or(false);
2027 if !fresh {
2028 // stream one expert at a time to disk — peak RAM = one expert (~8MB)
2029 use std::io::Write;
2030 let mut f = std::io::BufWriter::new(std::fs::File::create(cp)?);
2031 for ex in 0..n_expert {
2032 let name = format!("blk.{il}.ffn_{proj}_exps.{ex}.weight");
2033 let nv = src.find_nvfp4_native(&name).unwrap_or_else(|| {
2034 panic!("expert {name} lost NVFP4-native mid-gather")
2035 });
2036 assert_eq!(
2037 (nv.in_f, nv.out_f),
2038 (in_f, out_f),
2039 "expert {ex} dims ({},{}) != expert 0 ({in_f},{out_f})",
2040 nv.in_f,
2041 nv.out_f
2042 );
2043 f.write_all(&memra_gguf::nvfp4_repack::repack_modelopt_to_gguf(
2044 nv.wbytes, nv.wscale, out_f, in_f,
2045 ))?;
2046 }
2047 f.flush()?;
2048 }
2049 read_macros(&mut macros);
2050 let file = std::sync::Arc::new(std::fs::File::open(cp)?);
2051 let map = unsafe { memmap2::Mmap::map(file.as_ref())? };
2052 assert_eq!(map.len(), total, "repack cache {cp:?} size mismatch");
2053 // Default random preserves the original policy; normal lets Linux readahead
2054 // within each multi-megabyte expert on the spill-bound path.
2055 let _ = memra_gguf::source::apply_expert_mmap_advice(&map);
2056 let map = std::sync::Arc::new(map);
2057 // ST PINNED TIER (2026-07-07, the M3 1.5-tok/s lever): mmap-only backing makes
2058 // every SLRU miss a page-cache (or NVMe) synchronous read into the H2D copy.
2059 // Pin as many experts as the live budget allows (same MemBudget probe + 0.6
2060 // MemAvailable cap as the GGUF spill tier) — pinned pages upload via true
2061 // async DMA at full PCIe. Budget is GLOBAL across layers (first-come: earlier
2062 // layers pin first; routing is roughly uniform so early-layer bias is benign).
2063 // MEMRA_ST_PINNED=0 disables (pure-mmap, the 2026-07-06 behavior).
2064 // DEFAULT OFF (2026-07-07 measured): with a 122GB expert set on 60GB RAM,
2065 // pinning 26GB EVICTED the page cache backing the mmap tier — every unpinned
2066 // expert faulted cold from NVMe and gen fell 1.5 -> 0.05 tok/s (30x WORSE).
2067 // Pinning only pays when (total - pinned) fits page cache; here it never can.
2068 // MEMRA_ST_PINNED=1 opt-in for fits-in-RAM checkpoints (e.g. REAP-heavier cuts).
2069 let tiers = if std::env::var("MEMRA_ST_PINNED")
2070 .map(|v| v == "1")
2071 .unwrap_or(false)
2072 {
2073 static PIN_BUDGET: std::sync::OnceLock<std::sync::Mutex<usize>> =
2074 std::sync::OnceLock::new();
2075 let budget = PIN_BUDGET.get_or_init(|| {
2076 let b = crate::spill::MemBudget::probe(e)
2077 .map(|b| b.free_pinnable_ram)
2078 .unwrap_or(0);
2079 eprintln!("[st-spill] free_pinnable_ram={} MiB", b >> 20);
2080 std::sync::Mutex::new(b)
2081 });
2082 let mut rem = budget.lock().unwrap();
2083 // ONE pinned slab per file prefix (n_pin experts contiguous): 1 alloc +
2084 // 1 bulk copy instead of n_pin small allocs (per-expert cudaHostAllocs
2085 // stalled the 122GB M3 load >10min).
2086 let n_pin = (*rem / expert_stride).min(n_expert);
2087 if n_pin == 0 {
2088 None
2089 } else {
2090 let slab_len = n_pin * expert_stride;
2091 let mut pn = unsafe { e.ctx().alloc_pinned::<u8>(slab_len)? };
2092 {
2093 let dst = pn.as_mut_slice()?;
2094 dst.copy_from_slice(&map[..slab_len]);
2095 }
2096 let base = pn.as_ptr()? as *const u8;
2097 *rem -= slab_len;
2098 let slab = std::sync::Arc::new(HostBuf::Pinned {
2099 slice: std::sync::Arc::new(pn),
2100 base,
2101 len: slab_len,
2102 });
2103 let mut tiers: Vec<HostBuf> = Vec::with_capacity(n_expert);
2104 for ex in 0..n_expert {
2105 let off = ex * expert_stride;
2106 if ex < n_pin {
2107 tiers.push(HostBuf::PinnedAlias {
2108 owner: slab.clone(),
2109 base: unsafe { base.add(off) },
2110 len: expert_stride,
2111 });
2112 } else {
2113 tiers.push(HostBuf::Mmap {
2114 map: map.clone(),
2115 file: file.clone(),
2116 off,
2117 len: expert_stride,
2118 });
2119 }
2120 }
2121 Some(tiers)
2122 }
2123 } else {
2124 None
2125 };
2126 if let Some(tiers) = tiers {
2127 let all_one = macros.iter().all(|&m| m == 1.0);
2128 return Ok(HostExps {
2129 bytes: HostBuf::Mmap {
2130 map,
2131 file,
2132 off: 0,
2133 len: total,
2134 },
2135 tiers: Some(tiers),
2136 qtype: QT_NVFP4,
2137 in_f,
2138 out_f,
2139 n_expert,
2140 row_bytes,
2141 expert_stride,
2142 layouts: None,
2143 macros: if all_one { None } else { Some(macros) },
2144 fp8_blk: None,
2145 });
2146 }
2147 HostBuf::Mmap {
2148 map,
2149 file,
2150 off: 0,
2151 len: total,
2152 }
2153 } else {
2154 let mut buf: Vec<u8> = Vec::with_capacity(total);
2155 for ex in 0..n_expert {
2156 let name = format!("blk.{il}.ffn_{proj}_exps.{ex}.weight");
2157 let nv = src.find_nvfp4_native(&name).unwrap_or_else(|| {
2158 panic!("expert {name} lost NVFP4-native mid-gather")
2159 });
2160 assert_eq!(
2161 (nv.in_f, nv.out_f),
2162 (in_f, out_f),
2163 "expert {ex} dims ({},{}) != expert 0 ({in_f},{out_f})",
2164 nv.in_f,
2165 nv.out_f
2166 );
2167 buf.extend_from_slice(&memra_gguf::nvfp4_repack::repack_modelopt_to_gguf(
2168 nv.wbytes, nv.wscale, out_f, in_f,
2169 ));
2170 }
2171 assert_eq!(buf.len(), total);
2172 read_macros(&mut macros);
2173 let pinned = std::env::var("MEMRA_MOE_PINNED").is_ok()
2174 || std::env::var("MEMRA_MOE_CACHE").as_deref() != Ok("0");
2175 if pinned {
2176 let mut p = unsafe { e.ctx().alloc_pinned::<u8>(buf.len())? };
2177 {
2178 let dst = p.as_mut_slice()?;
2179 dst.copy_from_slice(&buf);
2180 }
2181 let base = p.as_ptr()? as *const u8;
2182 let len = buf.len();
2183 HostBuf::Pinned {
2184 slice: std::sync::Arc::new(p),
2185 base,
2186 len,
2187 }
2188 } else {
2189 HostBuf::Paged(buf)
2190 }
2191 };
2192 let all_one = macros.iter().all(|&m| m == 1.0);
2193 return Ok(HostExps {
2194 bytes,
2195 tiers: None,
2196 qtype: QT_NVFP4,
2197 in_f,
2198 out_f,
2199 n_expert,
2200 row_bytes,
2201 expert_stride,
2202 layouts: None,
2203 macros: if all_one { None } else { Some(macros) },
2204 fp8_blk: None,
2205 });
2206 }
2207 }
2208
2209 // expert 0 fixes (in_f, out_f); every later expert must match (catches a layer/arch mixup).
2210 let mut buf: Vec<u8> = Vec::new();
2211 let mut in_f = 0usize;
2212 let mut out_f = 0usize;
2213 for ex in 0..n_expert {
2214 // Per-expert ggml name; the source maps it to the HF expert tensor (ST-MOE-PLAN §1.3).
2215 let name = format!("blk.{il}.ffn_{proj}_exps.{ex}.weight");
2216 let v = src
2217 .find(&name)
2218 .unwrap_or_else(|| panic!("missing expert tensor {name}"));
2219 assert_eq!(v.ne.len(), 2, "expert {name} is not 2D (ne={:?})", v.ne);
2220 let (cur_in, cur_out) = (v.ne[0] as usize, v.ne[1] as usize);
2221 if ex == 0 {
2222 in_f = cur_in;
2223 out_f = cur_out;
2224 } else {
2225 assert_eq!(
2226 (cur_in, cur_out),
2227 (in_f, out_f),
2228 "expert {ex} dims {:?} != expert 0 [{in_f},{out_f}]",
2229 (cur_in, cur_out)
2230 );
2231 }
2232 // PATH A: dequant the 2D expert (F32/F16/BF16) to f32, append its bytes verbatim. The
2233 // dequantized [out_f, in_f] row-major f32 block is exactly one expert_stride slow→fast.
2234 let n = cur_in * cur_out;
2235 let f32v = dequant::dequantize(v.ggml_type, &v.bytes, n);
2236 buf.reserve(n * 4);
2237 for f in &f32v {
2238 buf.extend_from_slice(&f.to_le_bytes());
2239 }
2240 }
2241 let row_bytes = in_f * 4; // one out-row = in_f contiguous f32s
2242 let expert_stride = out_f * row_bytes;
2243 assert_eq!(
2244 buf.len(),
2245 n_expert * expert_stride,
2246 "{ggml_exps_name} gather size {} != n_expert*stride {}",
2247 buf.len(),
2248 n_expert * expert_stride
2249 );
2250 // Hold to the identical invariant as the GGUF path (ST-MOE-PLAN §1.3 step 4).
2251 assert_eq!(
2252 expert_stride,
2253 out_f * row_bytes,
2254 "{ggml_exps_name} stride mismatch: stride={expert_stride} out_f={out_f} row_bytes={row_bytes}"
2255 );
2256
2257 // Same pinned-vs-paged choice as the GGUF loader (the bytes are H2D-only on the hot path).
2258 let pinned = std::env::var("MEMRA_MOE_PINNED").is_ok()
2259 || std::env::var("MEMRA_MOE_CACHE").as_deref() != Ok("0");
2260 let bytes = if pinned {
2261 let mut p = unsafe { e.ctx().alloc_pinned::<u8>(buf.len())? };
2262 {
2263 let dst = p.as_mut_slice()?;
2264 dst.copy_from_slice(&buf);
2265 }
2266 let base = p.as_ptr()? as *const u8;
2267 let len = buf.len();
2268 HostBuf::Pinned {
2269 slice: std::sync::Arc::new(p),
2270 base,
2271 len,
2272 }
2273 } else {
2274 HostBuf::Paged(buf)
2275 };
2276 Ok(HostExps {
2277 bytes,
2278 tiers: None,
2279 qtype: QT_F32,
2280 in_f,
2281 out_f,
2282 n_expert,
2283 row_bytes,
2284 expert_stride,
2285 layouts: None,
2286 macros: None,
2287 fp8_blk: None,
2288 })
2289 }
2290
2291 /// Coalesce a uniform v2 overlay back into the existing stacked-slab contract without copying.
2292 /// The artifact stores one record per original expert for coverage validation, but a full-bank
2293 /// uniform arm writes those records contiguously into one file. Keeping `layouts=None` preserves
2294 /// the uniform fused kernels while `HostBuf::Mmap` keeps the >RAM artifact zero-copy.
2295 fn load_uniform_mmap_from_source(
2296 src: &dyn TensorSource,
2297 il: u32,
2298 proj: &str,
2299 n_expert: usize,
2300 ) -> Result<Option<Self>, Box<dyn std::error::Error>> {
2301 if src
2302 .active_experts(il)
2303 .is_some_and(|mask| mask.iter().any(|&active| !active))
2304 {
2305 return Ok(None);
2306 }
2307 let mut first_map = None;
2308 let mut first_file = None;
2309 let mut base_offset = 0u64;
2310 let mut expert_stride = 0usize;
2311 let mut in_f = 0usize;
2312 let mut out_f = 0usize;
2313 let mut qtype = 0i32;
2314 let mut row_bytes = 0usize;
2315 let mut macros = vec![1.0f32; n_expert];
2316 for ex in 0..n_expert {
2317 let stem = format!("blk.{il}.ffn_{proj}_exps.{ex}");
2318 let name = format!("{stem}.weight");
2319 let Some(DiskExtent {
2320 map,
2321 file,
2322 offset,
2323 len,
2324 }) = find_expert_disk_strict(src, &name)?
2325 else {
2326 return Ok(None);
2327 };
2328 let Some(v) = src.find(&name) else {
2329 return Ok(None);
2330 };
2331 if v.ne.len() != 2 {
2332 return Ok(None);
2333 }
2334 let (cur_in, cur_out) = (v.ne[0] as usize, v.ne[1] as usize);
2335 let Some(cur_row_bytes) = staged_expert_row_bytes(v.ggml_type, cur_in) else {
2336 return Ok(None);
2337 };
2338 let cur_qtype = staged_expert_qtype(v.ggml_type).unwrap();
2339 if ex == 0 {
2340 base_offset = offset;
2341 expert_stride = len;
2342 in_f = cur_in;
2343 out_f = cur_out;
2344 qtype = cur_qtype;
2345 row_bytes = cur_row_bytes;
2346 first_map = Some(map);
2347 first_file = Some(file);
2348 } else if !std::sync::Arc::ptr_eq(first_map.as_ref().unwrap(), &map)
2349 || !std::sync::Arc::ptr_eq(first_file.as_ref().unwrap(), &file)
2350 || offset != base_offset + (ex * expert_stride) as u64
2351 || len != expert_stride
2352 || (cur_in, cur_out, cur_qtype, cur_row_bytes) != (in_f, out_f, qtype, row_bytes)
2353 {
2354 return Ok(None);
2355 }
2356 if let Some(scale) = src.find(&format!("{stem}.scale")) {
2357 macros[ex] = f32::from_le_bytes(scale.bytes[..4].try_into().unwrap());
2358 }
2359 }
2360 assert_eq!(expert_stride, out_f * row_bytes);
2361 let total = n_expert * expert_stride;
2362 let off = usize::try_from(base_offset)
2363 .map_err(|_| format!("uniform expert disk offset {base_offset} does not fit usize"))?;
2364 let all_one = macros.iter().all(|&scale| scale == 1.0);
2365 Ok(Some(HostExps {
2366 bytes: HostBuf::Mmap {
2367 map: first_map.unwrap(),
2368 file: first_file.unwrap(),
2369 off,
2370 len: total,
2371 },
2372 tiers: None,
2373 qtype,
2374 in_f,
2375 out_f,
2376 n_expert,
2377 row_bytes,
2378 expert_stride,
2379 layouts: None,
2380 macros: if all_one { None } else { Some(macros) },
2381 fp8_blk: None,
2382 }))
2383 }
2384
2385 fn load_mixed_from_source(
2386 src: &dyn TensorSource,
2387 il: u32,
2388 proj: &str,
2389 n_expert: usize,
2390 ) -> Result<Self, Box<dyn std::error::Error>> {
2391 let mut tiers = Vec::with_capacity(n_expert);
2392 let mut layouts = Vec::with_capacity(n_expert);
2393 let mut macros = vec![1.0f32; n_expert];
2394 let mut in_f = 0usize;
2395 let mut out_f = 0usize;
2396 let active = src.active_experts(il);
2397 let mut first_active = None;
2398
2399 for ex in 0..n_expert {
2400 if active.is_some_and(|mask| !mask[ex]) {
2401 layouts.push(ExpertLayout {
2402 offset: 0,
2403 len: 0,
2404 qtype: QT_F32,
2405 row_bytes: 0,
2406 });
2407 tiers.push(HostBuf::Paged(Vec::new()));
2408 continue;
2409 }
2410 let name = format!("blk.{il}.ffn_{proj}_exps.{ex}.weight");
2411 let stem = format!("blk.{il}.ffn_{proj}_exps.{ex}");
2412 if let Some(scale) = src.find(&format!("{stem}.scale")) {
2413 macros[ex] = f32::from_le_bytes(scale.bytes[..4].try_into().unwrap());
2414 }
2415 let (host, byte_len, qtype, row_bytes, cur_in, cur_out) = if let Some(DiskExtent {
2416 map,
2417 file,
2418 offset,
2419 len,
2420 }) =
2421 find_expert_disk_strict(src, &name)?
2422 {
2423 let v = src
2424 .find(&name)
2425 .unwrap_or_else(|| panic!("missing expert tensor {name}"));
2426 assert_eq!(v.ne.len(), 2, "expert {name} is not 2D (ne={:?})", v.ne);
2427 let (cur_in, cur_out) = (v.ne[0] as usize, v.ne[1] as usize);
2428 let row_bytes = staged_expert_row_bytes(v.ggml_type, cur_in).ok_or_else(|| {
2429 format!("mmap expert {name} has unsupported qtype {:?}", v.ggml_type)
2430 })?;
2431 let off = usize::try_from(offset).map_err(|_| {
2432 format!("expert {name} disk offset {offset} does not fit usize")
2433 })?;
2434 (
2435 HostBuf::Mmap {
2436 map,
2437 file,
2438 off,
2439 len,
2440 },
2441 len,
2442 staged_expert_qtype(v.ggml_type).unwrap(),
2443 row_bytes,
2444 cur_in,
2445 cur_out,
2446 )
2447 } else if let Some(nv) = src.find_nvfp4_native(&name) {
2448 let bytes = memra_gguf::nvfp4_repack::repack_modelopt_to_gguf(
2449 nv.wbytes, nv.wscale, nv.out_f, nv.in_f,
2450 );
2451 let row_bytes = nv.in_f / 64 * 36;
2452 let byte_len = bytes.len();
2453 (
2454 HostBuf::Paged(bytes),
2455 byte_len,
2456 QT_NVFP4,
2457 row_bytes,
2458 nv.in_f,
2459 nv.out_f,
2460 )
2461 } else {
2462 let v = src
2463 .find(&name)
2464 .unwrap_or_else(|| panic!("missing expert tensor {name}"));
2465 assert_eq!(v.ne.len(), 2, "expert {name} is not 2D (ne={:?})", v.ne);
2466 let (cur_in, cur_out) = (v.ne[0] as usize, v.ne[1] as usize);
2467 if let Some(row_bytes) = staged_expert_row_bytes(v.ggml_type, cur_in) {
2468 let bytes = v.bytes.into_owned();
2469 let byte_len = bytes.len();
2470 (
2471 HostBuf::Paged(bytes),
2472 byte_len,
2473 staged_expert_qtype(v.ggml_type).unwrap(),
2474 row_bytes,
2475 cur_in,
2476 cur_out,
2477 )
2478 } else {
2479 let f32v = dequant::dequantize(v.ggml_type, &v.bytes, cur_in * cur_out);
2480 let mut bytes = Vec::with_capacity(f32v.len() * 4);
2481 for f in f32v {
2482 bytes.extend_from_slice(&f.to_le_bytes());
2483 }
2484 let byte_len = bytes.len();
2485 (
2486 HostBuf::Paged(bytes),
2487 byte_len,
2488 QT_F32,
2489 cur_in * 4,
2490 cur_in,
2491 cur_out,
2492 )
2493 }
2494 };
2495
2496 if first_active.is_none() {
2497 in_f = cur_in;
2498 out_f = cur_out;
2499 first_active = Some(ex);
2500 } else {
2501 assert_eq!(
2502 (cur_in, cur_out),
2503 (in_f, out_f),
2504 "expert {ex} dims ({cur_in},{cur_out}) != first active expert ({in_f},{out_f})"
2505 );
2506 }
2507 assert_eq!(
2508 byte_len,
2509 cur_out * row_bytes,
2510 "expert {name} bytes {byte_len} != out_f*row_bytes {}",
2511 cur_out * row_bytes
2512 );
2513 layouts.push(ExpertLayout {
2514 offset: 0,
2515 len: byte_len,
2516 qtype,
2517 row_bytes,
2518 });
2519 tiers.push(host);
2520 }
2521
2522 let first = layouts[*first_active
2523 .as_ref()
2524 .expect("expert mask pruned every expert")];
2525 let expert_stride = layouts.iter().map(|layout| layout.len).max().unwrap_or(0);
2526 let all_one = macros.iter().all(|&scale| scale == 1.0);
2527 Ok(HostExps {
2528 bytes: HostBuf::Paged(Vec::new()),
2529 tiers: Some(tiers),
2530 qtype: first.qtype,
2531 in_f,
2532 out_f,
2533 n_expert,
2534 row_bytes: first.row_bytes,
2535 expert_stride,
2536 layouts: Some(layouts),
2537 macros: if all_one { None } else { Some(macros) },
2538 fp8_blk: None,
2539 })
2540 }
2541
2542 /// Host byte slice for expert `e` (the H2D DMA source). Contiguous block, offset honored.
2543 /// Resolves the per-expert tier when spilling is active (`tiers` Some), else slices the single
2544 /// Per-expert post-matmul macro-scale (1.0 when absent).
2545 #[inline]
2546 pub fn macro_scale(&self, e: usize) -> f32 {
2547 self.macros.as_ref().map(|m| m[e]).unwrap_or(1.0)
2548 }
2549
2550 #[inline]
2551 pub fn is_uniform_layout(&self) -> bool {
2552 self.layouts.is_none()
2553 }
2554
2555 #[inline]
2556 pub fn expert_layout(&self, e: usize) -> ExpertLayout {
2557 debug_assert!(
2558 e < self.n_expert,
2559 "expert index {e} >= n_expert {}",
2560 self.n_expert
2561 );
2562 self.layouts
2563 .as_ref()
2564 .map(|layouts| layouts[e])
2565 .unwrap_or(ExpertLayout {
2566 offset: e * self.expert_stride,
2567 len: self.expert_stride,
2568 qtype: self.qtype,
2569 row_bytes: self.row_bytes,
2570 })
2571 }
2572
2573 #[inline]
2574 pub fn max_expert_bytes(&self) -> usize {
2575 self.layouts
2576 .as_ref()
2577 .and_then(|layouts| layouts.iter().map(|layout| layout.len).max())
2578 .unwrap_or(self.expert_stride)
2579 }
2580
2581 /// backing store (unchanged in-RAM path). Each `tiers[e]` is exactly one expert's stride.
2582 #[inline]
2583 pub fn expert_bytes(&self, e: usize) -> &[u8] {
2584 let layout = self.expert_layout(e);
2585 match &self.tiers {
2586 Some(tiers) => {
2587 debug_assert_eq!(tiers[e].len(), layout.len);
2588 tiers[e].as_bytes()
2589 }
2590 None => &self.bytes.as_bytes()[layout.offset..layout.offset + layout.len],
2591 }
2592 }
2593
2594 /// Source-aware twin of `expert_bytes`. Per-expert tiers already point at one exact block, while
2595 /// a uniform slab needs the expert layout offset added to its base. Keeping those cases separate
2596 /// prevents expert `e` from being offset twice when a tier vector is present.
2597 #[inline]
2598 pub(crate) fn expert_source(&self, e: usize) -> ExpertSource<'_> {
2599 let layout = self.expert_layout(e);
2600 match &self.tiers {
2601 Some(tiers) => tiers[e].expert_source(0, layout.len),
2602 None => self.bytes.expert_source(layout.offset, layout.len),
2603 }
2604 }
2605
2606 /// Hint that expert `e` will be staged soon. Uniform slabs advise only this expert's window;
2607 /// mixed/pruned layouts advise the selected per-expert mmap. Returns false for resident or
2608 /// empty buffers and on unsupported kernels; callers always retain the demand-fault fallback.
2609 #[inline]
2610 pub fn prefetch_expert_pages(&self, e: usize) -> bool {
2611 let layout = self.expert_layout(e);
2612 match &self.tiers {
2613 Some(tiers) => tiers[e].advise_willneed(0, layout.len),
2614 None => self.bytes.advise_willneed(layout.offset, layout.len),
2615 }
2616 }
2617}
2618
2619#[cfg(test)]
2620mod tests {
2621 use super::{
2622 ExpertKeepalive, ExpertSource, HostBuf, HostExps, QT_BF16, QT_NVFP4, QT_Q2_K, QT_Q4_K,
2623 repack_nvfp4_split, unpack_nvfp4_split,
2624 };
2625 use memra_gguf::nvfp4_repack::{repack_modelopt_to_gguf, repack_modelopt_to_split};
2626 use memra_gguf::source::{DiskExtent, Fp8StackedNative, TensorSource, TensorView};
2627 use memra_gguf::{GgmlType, config::ModelConfig};
2628 use std::borrow::Cow;
2629
2630 struct MixedExpertSource {
2631 bf16: Vec<u8>,
2632 q4k: Vec<u8>,
2633 }
2634
2635 impl TensorSource for MixedExpertSource {
2636 fn config(&self) -> ModelConfig {
2637 panic!("unused by HostExps mixed-loader test")
2638 }
2639
2640 fn find(&self, name: &str) -> Option<TensorView<'_>> {
2641 let (bytes, ggml_type) = if name == "blk.0.ffn_gate_exps.0.weight" {
2642 (&self.bf16, GgmlType::BF16)
2643 } else if name == "blk.0.ffn_gate_exps.1.weight" {
2644 (&self.q4k, GgmlType::Q4_K)
2645 } else {
2646 return None;
2647 };
2648 Some(TensorView {
2649 bytes: Cow::Borrowed(bytes),
2650 ggml_type,
2651 ne: vec![256, 2],
2652 })
2653 }
2654 }
2655
2656 struct PrunedExpertSource {
2657 q2k: Vec<u8>,
2658 nvfp4: Vec<u8>,
2659 active: Vec<bool>,
2660 }
2661
2662 struct MmapExpertSource {
2663 file: std::sync::Arc<std::fs::File>,
2664 map: std::sync::Arc<memmap2::Mmap>,
2665 base_offset: usize,
2666 expert_len: usize,
2667 }
2668
2669 struct LegacyMmapExpertSource {
2670 map: std::sync::Arc<memmap2::Mmap>,
2671 expert_len: usize,
2672 }
2673
2674 struct StackedFp8Source {
2675 file: std::sync::Arc<std::fs::File>,
2676 map: std::sync::Arc<memmap2::Mmap>,
2677 offset: usize,
2678 len: usize,
2679 scales: Vec<f32>,
2680 }
2681
2682 impl TensorSource for StackedFp8Source {
2683 fn config(&self) -> ModelConfig {
2684 panic!("unused by stacked FP8 ownership test")
2685 }
2686
2687 fn find(&self, _name: &str) -> Option<TensorView<'_>> {
2688 None
2689 }
2690
2691 fn find_fp8_stacked_native(&self, name: &str) -> Option<Fp8StackedNative<'_>> {
2692 (name == "blk.0.ffn_gate_exps.weight").then(|| Fp8StackedNative {
2693 bytes: &self.map[self.offset..self.offset + self.len],
2694 scales: self.scales.clone(),
2695 n_expert: 2,
2696 out_f: 2,
2697 in_f: 32,
2698 scale_rows: 1,
2699 scale_cols: 1,
2700 })
2701 }
2702
2703 fn find_expert_disk(&self, name: &str) -> Option<DiskExtent> {
2704 (name == "blk.0.ffn_gate_exps.weight").then(|| DiskExtent {
2705 map: self.map.clone(),
2706 file: self.file.clone(),
2707 offset: self.offset as u64,
2708 len: self.len,
2709 })
2710 }
2711 }
2712
2713 impl TensorSource for MmapExpertSource {
2714 fn config(&self) -> ModelConfig {
2715 panic!("unused by HostExps mmap-loader test")
2716 }
2717 fn preserve_expert_encodings(&self) -> bool {
2718 true
2719 }
2720 fn find(&self, name: &str) -> Option<TensorView<'_>> {
2721 let ex = match name {
2722 "blk.0.ffn_gate_exps.0.weight" => 0,
2723 "blk.0.ffn_gate_exps.1.weight" => 1,
2724 _ => return None,
2725 };
2726 let off = self.base_offset + ex * self.expert_len;
2727 Some(TensorView {
2728 bytes: Cow::Borrowed(&self.map[off..off + self.expert_len]),
2729 ggml_type: GgmlType::Q2_K,
2730 ne: vec![256, 2],
2731 })
2732 }
2733 fn find_expert_disk(&self, name: &str) -> Option<DiskExtent> {
2734 let ex = match name {
2735 "blk.0.ffn_gate_exps.0.weight" => 0,
2736 "blk.0.ffn_gate_exps.1.weight" => 1,
2737 _ => return None,
2738 };
2739 Some(DiskExtent {
2740 map: self.map.clone(),
2741 file: self.file.clone(),
2742 offset: (self.base_offset + ex * self.expert_len) as u64,
2743 len: self.expert_len,
2744 })
2745 }
2746 }
2747
2748 impl TensorSource for LegacyMmapExpertSource {
2749 fn config(&self) -> ModelConfig {
2750 panic!("unused by legacy mmap guard test")
2751 }
2752 fn preserve_expert_encodings(&self) -> bool {
2753 true
2754 }
2755 fn find(&self, name: &str) -> Option<TensorView<'_>> {
2756 let ex = match name {
2757 "blk.0.ffn_gate_exps.0.weight" => 0,
2758 "blk.0.ffn_gate_exps.1.weight" => 1,
2759 _ => return None,
2760 };
2761 let off = ex * self.expert_len;
2762 Some(TensorView {
2763 bytes: Cow::Borrowed(&self.map[off..off + self.expert_len]),
2764 ggml_type: GgmlType::Q2_K,
2765 ne: vec![256, 2],
2766 })
2767 }
2768 fn find_expert_mmap(
2769 &self,
2770 name: &str,
2771 ) -> Option<(std::sync::Arc<memmap2::Mmap>, usize, usize)> {
2772 let ex = match name {
2773 "blk.0.ffn_gate_exps.0.weight" => 0,
2774 "blk.0.ffn_gate_exps.1.weight" => 1,
2775 _ => return None,
2776 };
2777 Some((self.map.clone(), ex * self.expert_len, self.expert_len))
2778 }
2779 }
2780
2781 impl TensorSource for PrunedExpertSource {
2782 fn config(&self) -> ModelConfig {
2783 panic!("unused by HostExps pruned-loader test")
2784 }
2785 fn active_experts(&self, layer: u32) -> Option<&[bool]> {
2786 (layer == 0).then_some(self.active.as_slice())
2787 }
2788 fn find(&self, name: &str) -> Option<TensorView<'_>> {
2789 let (bytes, ggml_type) = match name {
2790 "blk.0.ffn_gate_exps.0.weight" => (&self.q2k, GgmlType::Q2_K),
2791 "blk.0.ffn_gate_exps.2.weight" => (&self.nvfp4, GgmlType::NVFP4),
2792 _ => return None,
2793 };
2794 Some(TensorView {
2795 bytes: Cow::Borrowed(bytes),
2796 ggml_type,
2797 ne: vec![256, 2],
2798 })
2799 }
2800 }
2801
2802 #[test]
2803 fn stacked_fp8_experts_retain_owned_mmap_and_scale_geometry() {
2804 let path = std::env::temp_dir().join(format!("memra-stacked-fp8-{}", std::process::id()));
2805 let offset = 11usize;
2806 let len = 2 * 2 * 32;
2807 let mut file_bytes = vec![0xA5; offset];
2808 file_bytes.extend((0..len).map(|i| (i % 127) as u8));
2809 std::fs::write(&path, &file_bytes).unwrap();
2810 let file = std::sync::Arc::new(std::fs::File::open(&path).unwrap());
2811 let map = std::sync::Arc::new(unsafe { memmap2::Mmap::map(file.as_ref()).unwrap() });
2812 let source = StackedFp8Source {
2813 file,
2814 map,
2815 offset,
2816 len,
2817 scales: vec![0.5, 0.25],
2818 };
2819
2820 let exps = HostExps::load_fp8_stacked_native_with_policy(
2821 &source,
2822 "blk.0.ffn_gate_exps.weight",
2823 true,
2824 )
2825 .unwrap()
2826 .unwrap();
2827 assert_eq!(exps.qtype, crate::QT_F8_E4M3_BLK);
2828 assert_eq!((exps.n_expert, exps.out_f, exps.in_f), (2, 2, 32));
2829 assert_eq!(exps.expert_stride, 64);
2830 assert!(matches!(exps.bytes, HostBuf::Mmap { .. }));
2831 assert_eq!(exps.expert_bytes(0), &file_bytes[offset..offset + 64]);
2832 assert_eq!(exps.expert_bytes(1), &file_bytes[offset + 64..offset + len]);
2833 let fp8 = exps.fp8_blk.as_ref().unwrap();
2834 assert_eq!((fp8.rows, fp8.cols, fp8.expert_stride), (1, 1, 1));
2835 assert_eq!(fp8.scales, vec![0.5, 0.25]);
2836
2837 drop(source);
2838 assert_eq!(exps.expert_bytes(1), &file_bytes[offset + 64..offset + len]);
2839 std::fs::remove_file(path).ok();
2840 }
2841
2842 #[test]
2843 fn stacked_fp8_experts_reject_non_finite_codes() {
2844 let path =
2845 std::env::temp_dir().join(format!("memra-stacked-fp8-nan-{}", std::process::id()));
2846 let len = 2 * 2 * 32;
2847 let mut file_bytes = vec![0x12; len];
2848 file_bytes[73] = 0x7f;
2849 std::fs::write(&path, &file_bytes).unwrap();
2850 let file = std::sync::Arc::new(std::fs::File::open(&path).unwrap());
2851 let map = std::sync::Arc::new(unsafe { memmap2::Mmap::map(file.as_ref()).unwrap() });
2852 let source = StackedFp8Source {
2853 file,
2854 map,
2855 offset: 0,
2856 len,
2857 scales: vec![0.5, 0.25],
2858 };
2859
2860 let err = match HostExps::load_fp8_stacked_native_with_policy(
2861 &source,
2862 "blk.0.ffn_gate_exps.weight",
2863 true,
2864 ) {
2865 Ok(_) => panic!("non-finite E4M3 code was accepted"),
2866 Err(err) => err,
2867 };
2868 assert!(err.to_string().contains("non-finite E4M3"));
2869 std::fs::remove_file(path).ok();
2870 }
2871
2872 /// A1 direct-import gate (engine side): the fused modelopt->split repack must be byte-for-byte
2873 /// the composition of the two passes it replaces (modelopt->GGUF blocks, then the A6
2874 /// split-plane repack). Also pins the split roundtrip on the same buffers.
2875 #[test]
2876 fn direct_split_equals_chained() {
2877 for (out_f, in_f) in [(1usize, 64usize), (3, 128), (5, 320), (8, 1024)] {
2878 let mut w = vec![0u8; out_f * in_f / 2];
2879 let mut s = vec![0u8; out_f * in_f / 16];
2880 for (i, b) in w.iter_mut().enumerate() {
2881 *b = ((i * 41 + 7) & 0xFF) as u8;
2882 }
2883 for (i, b) in s.iter_mut().enumerate() {
2884 *b = (0x20 + ((i * 11 + 5) % 0x50)) as u8;
2885 }
2886 let gguf = repack_modelopt_to_gguf(&w, &s, out_f, in_f);
2887 let chained = repack_nvfp4_split(&gguf, out_f);
2888 let direct = repack_modelopt_to_split(&w, &s, out_f, in_f);
2889 assert_eq!(
2890 direct, chained,
2891 "fused != chained at out_f={out_f} in_f={in_f}"
2892 );
2893 assert_eq!(
2894 unpack_nvfp4_split(&direct, out_f),
2895 gguf,
2896 "split roundtrip broken at out_f={out_f} in_f={in_f}"
2897 );
2898 }
2899 }
2900
2901 #[test]
2902 fn mixed_expert_loader_keeps_each_encoding_and_extent() {
2903 let source = MixedExpertSource {
2904 bf16: vec![0x5a; 256 * 2 * 2],
2905 q4k: vec![0xa5; 2 * 144],
2906 };
2907 let exps = HostExps::load_mixed_from_source(&source, 0, "gate", 2).unwrap();
2908 assert!(!exps.is_uniform_layout());
2909 assert_eq!(exps.max_expert_bytes(), 1024);
2910 assert_eq!(exps.expert_layout(0).qtype, QT_BF16);
2911 assert_eq!(exps.expert_layout(0).row_bytes, 512);
2912 assert_eq!(exps.expert_layout(0).len, 1024);
2913 assert_eq!(exps.expert_layout(1).qtype, QT_Q4_K);
2914 assert_eq!(exps.expert_layout(1).row_bytes, 144);
2915 assert_eq!(exps.expert_layout(1).len, 288);
2916 assert_eq!(exps.expert_bytes(0), source.bf16);
2917 assert_eq!(exps.expert_bytes(1), source.q4k);
2918 match exps.expert_source(1) {
2919 ExpertSource::Memory { bytes, .. } => assert_eq!(bytes, source.q4k),
2920 ExpertSource::Disk { .. } => panic!("paged expert unexpectedly became disk-backed"),
2921 }
2922 }
2923
2924 #[test]
2925 fn mixed_expert_loader_omits_masked_expert_bytes() {
2926 let source = PrunedExpertSource {
2927 q2k: vec![0x22; 2 * 84],
2928 nvfp4: vec![0x44; 2 * 4 * 36],
2929 active: vec![true, false, true],
2930 };
2931 let exps = HostExps::load_mixed_from_source(&source, 0, "gate", 3).unwrap();
2932 assert_eq!(exps.expert_layout(0).qtype, QT_Q2_K);
2933 assert_eq!(exps.expert_layout(0).row_bytes, 84);
2934 assert_eq!(exps.expert_layout(1).len, 0);
2935 assert_eq!(exps.expert_bytes(1), &[]);
2936 assert_eq!(exps.expert_layout(2).qtype, QT_NVFP4);
2937 assert_eq!(exps.expert_layout(2).row_bytes, 4 * 36);
2938 }
2939
2940 #[test]
2941 fn mixed_expert_loader_keeps_mmap_backing_zero_copy() {
2942 let path = std::env::temp_dir().join(format!("memra-mixed-mmap-{}", std::process::id()));
2943 let base_offset = 3usize;
2944 let expert_len = 2 * 84;
2945 let mut bytes = vec![0xE1; base_offset];
2946 bytes.extend(vec![0x31; expert_len]);
2947 bytes.extend(vec![0x72; expert_len]);
2948 std::fs::write(&path, &bytes).unwrap();
2949 let file = std::sync::Arc::new(std::fs::File::open(&path).unwrap());
2950 let map = std::sync::Arc::new(unsafe { memmap2::Mmap::map(file.as_ref()).unwrap() });
2951 let source = MmapExpertSource {
2952 file: file.clone(),
2953 map,
2954 base_offset,
2955 expert_len,
2956 };
2957 let exps = HostExps::load_mixed_from_source(&source, 0, "gate", 2).unwrap();
2958 assert!(matches!(
2959 exps.tiers.as_ref().unwrap()[0],
2960 HostBuf::Mmap { .. }
2961 ));
2962 assert!(matches!(
2963 exps.tiers.as_ref().unwrap()[1],
2964 HostBuf::Mmap { .. }
2965 ));
2966 assert_eq!(
2967 exps.expert_bytes(0),
2968 &bytes[base_offset..base_offset + expert_len]
2969 );
2970 assert_eq!(exps.expert_bytes(1), &bytes[base_offset + expert_len..]);
2971 match exps.expert_source(1) {
2972 ExpertSource::Disk {
2973 file: got_file,
2974 offset,
2975 len,
2976 fallback,
2977 keepalive,
2978 } => {
2979 assert!(std::sync::Arc::ptr_eq(got_file, &file));
2980 assert_eq!(offset, (base_offset + expert_len) as u64);
2981 assert_eq!(len, expert_len);
2982 assert_eq!(fallback, &bytes[base_offset + expert_len..]);
2983 match keepalive {
2984 ExpertKeepalive::Mmap(owner) => {
2985 assert!(std::sync::Arc::ptr_eq(&owner, &source.map));
2986 }
2987 _ => panic!("mmap expert did not retain its mmap owner"),
2988 }
2989 }
2990 ExpertSource::Memory { .. } => panic!("mixed mmap tier lost its disk extent"),
2991 }
2992 #[cfg(unix)]
2993 assert!(exps.prefetch_expert_pages(1));
2994 std::fs::remove_file(path).ok();
2995 }
2996
2997 #[test]
2998 fn tiered_expert_source_does_not_double_apply_layout_offset() {
2999 let path =
3000 std::env::temp_dir().join(format!("memra-tiered-source-offset-{}", std::process::id()));
3001 let base_offset = 7usize;
3002 let expert_len = 2 * 84;
3003 let mut bytes = vec![0xE3; base_offset];
3004 bytes.extend(vec![0x41; expert_len]);
3005 bytes.extend(vec![0x82; expert_len]);
3006 std::fs::write(&path, &bytes).unwrap();
3007 let file = std::sync::Arc::new(std::fs::File::open(&path).unwrap());
3008 let map = std::sync::Arc::new(unsafe { memmap2::Mmap::map(file.as_ref()).unwrap() });
3009 let exps = HostExps {
3010 bytes: HostBuf::Paged(Vec::new()),
3011 tiers: Some(vec![
3012 HostBuf::Mmap {
3013 map: map.clone(),
3014 file: file.clone(),
3015 off: base_offset,
3016 len: expert_len,
3017 },
3018 HostBuf::Mmap {
3019 map,
3020 file: file.clone(),
3021 off: base_offset + expert_len,
3022 len: expert_len,
3023 },
3024 ]),
3025 qtype: QT_Q2_K,
3026 in_f: 256,
3027 out_f: 2,
3028 n_expert: 2,
3029 row_bytes: 84,
3030 expert_stride: expert_len,
3031 layouts: None,
3032 macros: None,
3033 fp8_blk: None,
3034 };
3035
3036 // `expert_layout(1).offset == expert_len`, but tier 1 already starts at expert 1.
3037 assert_eq!(exps.expert_layout(1).offset, expert_len);
3038 match exps.expert_source(1) {
3039 ExpertSource::Disk {
3040 offset,
3041 len,
3042 fallback,
3043 ..
3044 } => {
3045 assert_eq!(offset, (base_offset + expert_len) as u64);
3046 assert_eq!(len, expert_len);
3047 assert_eq!(fallback, &bytes[base_offset + expert_len..]);
3048 }
3049 ExpertSource::Memory { .. } => panic!("tiered mmap expert lost its disk extent"),
3050 }
3051 std::fs::remove_file(path).ok();
3052 }
3053
3054 #[test]
3055 fn legacy_mmap_source_requires_retained_file_extent() {
3056 let path =
3057 std::env::temp_dir().join(format!("memra-legacy-mmap-source-{}", std::process::id()));
3058 let expert_len = 2 * 84;
3059 std::fs::write(&path, vec![0x64; 2 * expert_len]).unwrap();
3060 let file = std::fs::File::open(&path).unwrap();
3061 let map = std::sync::Arc::new(unsafe { memmap2::Mmap::map(&file).unwrap() });
3062 let source = LegacyMmapExpertSource { map, expert_len };
3063
3064 let err = match HostExps::load_uniform_mmap_from_source(&source, 0, "gate", 2) {
3065 Ok(_) => panic!("legacy mmap-only source silently fell back instead of failing"),
3066 Err(err) => err,
3067 };
3068 let message = err.to_string();
3069 assert!(
3070 message.contains("legacy find_expert_mmap without find_expert_disk"),
3071 "{message}"
3072 );
3073 assert!(message.contains("retained Arc<File>"), "{message}");
3074 std::fs::remove_file(path).ok();
3075 }
3076
3077 #[test]
3078 fn uniform_expert_loader_coalesces_contiguous_mmap() {
3079 let path = std::env::temp_dir().join(format!("memra-uniform-mmap-{}", std::process::id()));
3080 let base_offset = 5usize;
3081 let expert_len = 2 * 84;
3082 let mut bytes = vec![0xE2; base_offset];
3083 bytes.extend(vec![0x19; expert_len]);
3084 bytes.extend(vec![0x91; expert_len]);
3085 std::fs::write(&path, &bytes).unwrap();
3086 let file = std::sync::Arc::new(std::fs::File::open(&path).unwrap());
3087 let map = std::sync::Arc::new(unsafe { memmap2::Mmap::map(file.as_ref()).unwrap() });
3088 let source = MmapExpertSource {
3089 file: file.clone(),
3090 map,
3091 base_offset,
3092 expert_len,
3093 };
3094 let exps = HostExps::load_uniform_mmap_from_source(&source, 0, "gate", 2)
3095 .unwrap()
3096 .expect("contiguous mmap should coalesce");
3097 assert!(exps.is_uniform_layout());
3098 assert!(matches!(&exps.bytes, HostBuf::Mmap { .. }));
3099 assert_eq!(exps.expert_stride, expert_len);
3100 assert_eq!(
3101 exps.expert_bytes(0),
3102 &bytes[base_offset..base_offset + expert_len]
3103 );
3104 assert_eq!(exps.expert_bytes(1), &bytes[base_offset + expert_len..]);
3105 match exps.expert_source(1) {
3106 ExpertSource::Disk {
3107 file: got_file,
3108 offset,
3109 len,
3110 fallback,
3111 ..
3112 } => {
3113 assert!(std::sync::Arc::ptr_eq(got_file, &file));
3114 assert_eq!(offset, (base_offset + expert_len) as u64);
3115 assert_eq!(len, expert_len);
3116 assert_eq!(fallback, &bytes[base_offset + expert_len..]);
3117 }
3118 ExpertSource::Memory { .. } => panic!("uniform mmap slab lost its disk extent"),
3119 }
3120 #[cfg(unix)]
3121 assert!(exps.prefetch_expert_pages(1));
3122 std::fs::remove_file(path).ok();
3123 }
3124}