1use std::collections::BTreeMap;
27use std::os::raw::c_void;
28use std::path::Path;
29
30use cudarc::driver::{CudaSlice, CudaStream, DevicePtr, DevicePtrMut};
31use memra_gguf::dsv4_forward::{
32 ActQuantVariant, Dsv4Model, FreqsCis, compress_topk_idxs, hc_split_sinkhorn,
33 precompute_freqs_cis, window_topk_idxs,
34};
35
36use crate::dsv4_ffi as k;
37use crate::dsv4_ffi::ck;
38
39type Res<T> = Result<T, String>;
40
41fn e<E: std::fmt::Display>(what: &str) -> impl FnOnce(E) -> String + '_ {
42 move |err| format!("{what}: {err}")
43}
44
45#[inline]
48fn sigmoid_f32(x: f32) -> f32 {
49 1.0 / (1.0 + (-x).exp())
50}
51
52#[inline]
54fn softplus_f32(x: f32) -> f32 {
55 if x > 20.0 { x } else { x.exp().ln_1p() }
56}
57
58pub struct Stage {
62 pub dev: usize,
63 pub gpu: memra_runtime::Gpu,
64 pub layers: Vec<LayerDev>,
65 pub embed: Option<CudaSlice<u8>>, pub head: Option<CudaSlice<u8>>, pub trunk_norm: Option<CudaSlice<f32>>,
68 pub hc_head_fn: Option<CudaSlice<f32>>, pub fc_yarn: CudaSlice<f32>, pub fc_plain: CudaSlice<f32>, pub ws: CudaSlice<u8>, pub deq: [CudaSlice<u8>; 3], pub loaded_bytes: u64, pub hc_head_base_dev: Option<CudaSlice<f32>>,
76 pub hc_head_scale_dev: Option<CudaSlice<f32>>,
77}
78
79pub struct CmpDev {
80 pub ratio: usize,
81 pub d: usize,
82 pub latent: usize,
83 pub overlap: bool,
84 pub rotate: bool,
85 pub wkv: CudaSlice<f32>, pub wgate: CudaSlice<f32>, pub norm: CudaSlice<f32>,
88 pub ape: CudaSlice<f32>, }
90
91pub struct IdxDev {
92 pub wq_b: DenseBf16, pub weights_proj: DenseBf16, pub wq_b_fp8: Option<Fp8Dense>,
95 pub weights_proj_fp8: Option<Fp8Dense>,
96 pub cmp: CmpDev,
97 pub heads: usize,
98 pub hd: usize,
99 pub topk: usize,
100}
101
102pub struct Fp8Dense {
112 pub codes: CudaSlice<u8>, pub scales: CudaSlice<f32>, pub sc_cols: usize, pub rows: usize,
116 pub cols: usize,
117}
118
119pub enum DenseBf16 {
132 Dev(CudaSlice<u8>),
133 Host(Vec<u8>),
134}
135
136impl DenseBf16 {
137 pub fn dev(&self) -> &CudaSlice<u8> {
142 match self {
143 DenseBf16::Dev(d) => d,
144 DenseBf16::Host(_) => unreachable!(
145 "bf16 dense slab is host-staged (fp8 dense arm): this consumer must \
146 ride the fp8 twins (dwsel) or the staged prefill view"
147 ),
148 }
149 }
150
151 fn staged(&self, stream: &std::sync::Arc<CudaStream>) -> Res<DenseView<'_>> {
155 Ok(match self {
156 DenseBf16::Dev(d) => DenseView::Res(d),
157 DenseBf16::Host(b) => DenseView::Tmp(upload_u8(stream, b)?),
158 })
159 }
160}
161
162pub enum DenseView<'a> {
164 Res(&'a CudaSlice<u8>),
165 Tmp(CudaSlice<u8>),
166}
167
168impl DenseView<'_> {
169 fn slab(&self) -> &CudaSlice<u8> {
170 match self {
171 DenseView::Res(d) => d,
172 DenseView::Tmp(d) => d,
173 }
174 }
175}
176
177#[derive(Clone, Copy)]
181pub enum DW {
182 Bf16(*const c_void),
183 Fp8 {
184 codes: *const c_void,
185 scales: *const f32,
186 sc_cols: i32,
187 },
188}
189
190impl DW {
191 fn offset_rows(self, rows_off: usize, cols: usize) -> DW {
195 match self {
196 DW::Bf16(p) => DW::Bf16((p as usize + rows_off * cols * 2) as *const c_void),
197 DW::Fp8 {
198 codes,
199 scales,
200 sc_cols,
201 } => {
202 assert_eq!(
203 rows_off % 128,
204 0,
205 "fp8 dense arm: grouped row offset {rows_off} not on the 128-row \
206 scale-grid boundary"
207 );
208 DW::Fp8 {
209 codes: (codes as usize + rows_off * cols) as *const c_void,
210 scales: unsafe { scales.add((rows_off / 128) * sc_cols as usize) },
211 sc_cols,
212 }
213 }
214 }
215 }
216}
217
218fn dwsel(
222 active: bool,
223 stream: &cudarc::driver::CudaStream,
224 w_bf16: &DenseBf16,
225 fp8: &Option<Fp8Dense>,
226) -> DW {
227 match fp8 {
228 Some(f) if active => DW::Fp8 {
229 codes: f.codes.device_ptr(stream).0 as *const c_void,
230 scales: f.scales.device_ptr(stream).0 as *const f32,
231 sc_cols: f.sc_cols as i32,
232 },
233 _ => DW::Bf16(w_bf16.dev().device_ptr(stream).0 as *const c_void),
236 }
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq)]
243pub enum ExpertKind {
244 Nvfp4,
245 Mxfp4,
246}
247
248#[derive(Debug, Clone, Copy, PartialEq, Eq)]
256pub enum ExpertArm {
257 Bf16Dequant,
258 Native,
259}
260
261#[derive(Debug, Clone, Copy, PartialEq, Eq)]
272pub enum DecodePath {
273 Legacy,
274 Device { host_math: bool },
275}
276
277pub struct LayerDev {
278 pub il: u32,
279 pub ratio: usize,
280 pub expert_kind: ExpertKind,
281 pub wq_a: DenseBf16,
283 pub wq_b: DenseBf16,
284 pub wkv: DenseBf16,
285 pub wo_a: DenseBf16, pub wo_b: DenseBf16,
287 pub q_norm: CudaSlice<f32>,
288 pub kv_norm: CudaSlice<f32>,
289 pub attn_norm: CudaSlice<f32>,
290 pub ffn_norm: CudaSlice<f32>,
291 pub sink: CudaSlice<f32>,
292 pub cmp: Option<CmpDev>,
293 pub idx: Option<IdxDev>,
294 pub hc_attn_fn: CudaSlice<f32>,
296 pub hc_ffn_fn: CudaSlice<f32>,
297 pub hc_attn_base: Vec<f32>,
298 pub hc_attn_scale: Vec<f32>,
299 pub hc_ffn_base: Vec<f32>,
300 pub hc_ffn_scale: Vec<f32>,
301 pub hc_attn_base_dev: CudaSlice<f32>,
303 pub hc_attn_scale_dev: CudaSlice<f32>,
304 pub hc_ffn_base_dev: CudaSlice<f32>,
305 pub hc_ffn_scale_dev: CudaSlice<f32>,
306 pub gate_bias_dev: Option<CudaSlice<f32>>,
307 pub tid2eid_dev: Option<CudaSlice<i32>>,
310 pub experts_s2_dev: CudaSlice<f32>,
311 pub gate_w: CudaSlice<f32>, pub gate_bias: Option<Vec<f32>>,
314 pub tid2eid: Option<Vec<i64>>, pub experts_w: CudaSlice<u8>, pub experts_sc: CudaSlice<u8>, pub experts_s2: Vec<f32>, pub shared_w: [DenseBf16; 3], pub wq_a_fp8: Option<Fp8Dense>,
323 pub wq_b_fp8: Option<Fp8Dense>,
324 pub wkv_fp8: Option<Fp8Dense>,
325 pub wo_a_fp8: Option<Fp8Dense>,
326 pub wo_b_fp8: Option<Fp8Dense>,
327 pub shared_fp8: [Option<Fp8Dense>; 3],
328}
329
330#[derive(Default)]
332pub struct GpuCapture {
333 pub embed_out: Option<Vec<f32>>,
334 pub layer_out: BTreeMap<u32, Vec<f32>>,
335 pub attn_out: BTreeMap<u32, Vec<f32>>,
336 pub x_dbg: BTreeMap<u32, Vec<f32>>,
339 pub q_dbg: BTreeMap<u32, Vec<f32>>,
340 pub kv_dbg: BTreeMap<u32, Vec<f32>>,
341 pub o_dbg: BTreeMap<u32, Vec<f32>>,
342 pub compressor_kv: BTreeMap<u32, (Vec<f32>, usize)>,
343 pub indexer_kv: BTreeMap<u32, (Vec<f32>, usize)>,
344 pub index_score: BTreeMap<u32, (Vec<f32>, usize)>,
345 pub moe_x: BTreeMap<u32, Vec<f32>>,
348 pub want: std::collections::BTreeSet<u32>,
349}
350
351pub struct MtpDev {
354 pub layer: LayerDev, pub enorm: CudaSlice<f32>,
356 pub hnorm: CudaSlice<f32>,
357 pub norm: CudaSlice<f32>,
358 pub e_proj: CudaSlice<u8>, pub h_proj: CudaSlice<u8>, pub hc_head_fn: CudaSlice<f32>,
361 pub hc_head_base: Vec<f32>,
362 pub hc_head_scale: Vec<f32>,
363}
364
365pub struct DsparkDev {
371 pub blocks: Vec<LayerDev>,
373 pub main_proj: CudaSlice<u8>, pub main_norm: CudaSlice<f32>,
375 pub norm: CudaSlice<f32>, pub markov_w1: CudaSlice<f32>, pub markov_w2: CudaSlice<f32>, pub markov_w1_host: Vec<f32>, pub conf_w: CudaSlice<f32>, pub hc_head_fn: CudaSlice<f32>, pub hc_head_base: Vec<f32>,
384 pub hc_head_scale: Vec<f32>,
385 pub block_size: usize,
386 pub noise_token: u32,
387 pub targets: Vec<usize>, pub rank: usize,
389 pub vocab: usize,
390}
391
392pub struct DsparkState {
399 pub rings: Vec<CudaSlice<f32>>,
400 pub taps: CudaSlice<f32>,
404}
405
406pub struct DsparkProposal {
409 pub out_ids: Vec<u32>,
410 pub confidence: Vec<f32>,
411 pub margins: Vec<f32>,
412 pub top1_logits: Vec<f32>,
413 pub capture: Option<DsparkCaptureOut>,
416}
417
418pub struct DsparkCaptureOut {
419 pub main_hidden: Vec<f32>,
423 pub main_x: Vec<f32>,
424 pub block_outs: Vec<Vec<f32>>,
425 pub x_collapsed: Vec<f32>,
426 pub logits_pre: Vec<f32>,
428 pub logits_post: Vec<f32>,
429 pub markov_embed: Vec<f32>,
430}
431
432pub struct Dsv4Gpu {
433 pub model: Dsv4Model,
434 pub stages: Vec<Stage>,
435 pub layer_stage: Vec<usize>, pub split_at: u32, pub max_seq: usize,
438 pub variant: ActQuantVariant,
439 pub fc_yarn_host: FreqsCis,
440 pub fc_plain_host: FreqsCis,
441 pub mtp: Option<MtpDev>,
442 pub dspark: Option<DsparkDev>,
445 pub expert_arm: ExpertArm,
446 pub decode_path: DecodePath,
447 pub dots_f32: bool,
452 pub chains_f32: bool,
460 pub dspark_head_f32: bool,
466 pub dense_fp8: bool,
475 boundary_ev: Vec<cudarc::driver::CudaEvent>,
478 hc_head_base: Vec<f32>,
479 hc_head_scale: Vec<f32>,
480}
481
482pub struct ForwardOut {
485 pub logits: Vec<f32>,
486 pub h_last: CudaSlice<f32>,
487}
488
489pub struct LayerCache {
498 pub kvc: CudaSlice<f32>,
499 pub n_blocks: usize,
500 pub pend_kv: Option<CudaSlice<f32>>,
501 pub pend_score: Option<CudaSlice<f32>>,
502 pub ikvc: Option<CudaSlice<f32>>,
505 pub i_blocks: usize,
506 pub ipend_kv: Option<CudaSlice<f32>>,
507 pub ipend_score: Option<CudaSlice<f32>>,
508}
509
510pub struct StepWs {
516 pub h_a: CudaSlice<f32>, pub h_b: CudaSlice<f32>, pub h_rx: CudaSlice<f32>, pub emb: CudaSlice<f32>, pub mixes: CudaSlice<f32>, pub pre: CudaSlice<f32>, pub post: CudaSlice<f32>, pub comb: CudaSlice<f32>, pub y_hc: CudaSlice<f32>, pub x: CudaSlice<f32>, pub xf: CudaSlice<f32>, pub qr: CudaSlice<f32>, pub qr_b: CudaSlice<u8>, pub q: CudaSlice<f32>, pub kv: CudaSlice<f32>, pub qi: CudaSlice<f32>, pub wproj: CudaSlice<f32>, pub score: CudaSlice<f32>, pub idx: CudaSlice<i32>, pub o: CudaSlice<f32>, pub o_b: CudaSlice<u8>, pub og: CudaSlice<f32>, pub attn_out: CudaSlice<f32>, pub gemm_xb: CudaSlice<u8>, pub raw: CudaSlice<f32>, pub sel: CudaSlice<i32>, pub selw: CudaSlice<f32>, pub order: CudaSlice<i32>, pub xq: CudaSlice<u8>, pub xs: CudaSlice<f32>, pub g1: CudaSlice<f32>, pub g3: CudaSlice<f32>, pub hbuf: CudaSlice<f32>, pub hq: CudaSlice<u8>, pub hs: CudaSlice<f32>, pub contrib: CudaSlice<f32>, pub y: CudaSlice<f32>, pub xb: CudaSlice<u8>, pub sg1: CudaSlice<f32>, pub sg3: CudaSlice<f32>,
557 pub shbuf: CudaSlice<f32>,
558 pub shb16: CudaSlice<u8>, pub sh_out: CudaSlice<f32>, pub cmp_kv_row: CudaSlice<f32>, pub cmp_sc_row: CudaSlice<f32>, pub cmp_emit: CudaSlice<f32>, pub cmp_shift: CudaSlice<f32>, pub sink_scores: CudaSlice<f32>,
567 pub sink_evals: CudaSlice<f32>,
568 pub sink_den: CudaSlice<f64>,
569 pub head_mixes: CudaSlice<f32>, pub head_pre: CudaSlice<f32>, pub collapsed: CudaSlice<f32>, pub logits: CudaSlice<f32>, pub argmax: CudaSlice<i32>, pub tok: CudaSlice<i32>, }
577
578pub struct DecodeState {
581 pub caches: Vec<LayerCache>,
582 pub pos: usize,
583 pub cache_bytes: Vec<u64>,
585 pub ws: Option<Vec<StepWs>>,
587}
588
589fn sp(stream: &CudaStream) -> *mut c_void {
592 stream.cu_stream() as *mut c_void
593}
594
595fn upload_f32(stream: &std::sync::Arc<CudaStream>, v: &[f32]) -> Res<CudaSlice<f32>> {
596 let mut d = stream.alloc_zeros::<f32>(v.len()).map_err(e("alloc f32"))?;
597 stream.memcpy_htod(v, &mut d).map_err(e("htod f32"))?;
598 Ok(d)
599}
600
601fn upload_i32(stream: &std::sync::Arc<CudaStream>, v: &[i32]) -> Res<CudaSlice<i32>> {
602 let mut d = stream.alloc_zeros::<i32>(v.len()).map_err(e("alloc i32"))?;
603 stream.memcpy_htod(v, &mut d).map_err(e("htod i32"))?;
604 Ok(d)
605}
606
607fn upload_u8(stream: &std::sync::Arc<CudaStream>, v: &[u8]) -> Res<CudaSlice<u8>> {
608 let mut d = stream.alloc_zeros::<u8>(v.len()).map_err(e("alloc u8"))?;
609 stream.memcpy_htod(v, &mut d).map_err(e("htod u8"))?;
610 Ok(d)
611}
612
613fn dtoh_f32(stream: &std::sync::Arc<CudaStream>, d: &CudaSlice<f32>) -> Res<Vec<f32>> {
614 let mut v = vec![0f32; d.len()];
615 stream.memcpy_dtoh(d, &mut v[..]).map_err(e("dtoh"))?;
616 stream.synchronize().map_err(e("sync dtoh"))?;
617 Ok(v)
618}
619
620#[derive(Default, Clone)]
645struct Dsv4PhaseAcc {
646 rows: Vec<(&'static str, u64, u64, u64)>,
648 stack: Vec<(usize, u64)>,
650}
651
652thread_local! {
653 static DSV4_PHASES: std::cell::RefCell<Dsv4PhaseAcc> =
654 std::cell::RefCell::new(Dsv4PhaseAcc::default());
655}
656
657fn dsv4_prof_sync() -> bool {
658 static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
659 *V.get_or_init(|| std::env::var("MEMRA_DSV4_ROUND_PROFILE").as_deref() == Ok("1"))
660}
661
662fn dsv4_prof_nvtx() -> bool {
663 static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
664 *V.get_or_init(|| std::env::var("MEMRA_DSV4_NVTX").as_deref() == Ok("1"))
665}
666
667pub fn dsv4_prof_on() -> bool {
670 dsv4_prof_sync() || dsv4_prof_nvtx()
671}
672
673pub struct Dsv4Phase<'a> {
676 stream: Option<&'a std::sync::Arc<CudaStream>>,
677 t0: std::time::Instant,
678 nvtx: bool,
679}
680
681impl<'a> Dsv4Phase<'a> {
682 pub fn new(name: &'static str, stream: Option<&'a std::sync::Arc<CudaStream>>) -> Option<Self> {
686 if !dsv4_prof_on() {
687 return None;
688 }
689 let nvtx = dsv4_prof_nvtx();
690 if nvtx {
691 unsafe {
692 k::memra_dsv4_nvtx_push(name.as_ptr() as *const std::os::raw::c_char);
693 }
694 }
695 let label = &name[..name.len() - 1];
696 DSV4_PHASES.with(|p| {
697 let mut p = p.borrow_mut();
698 let idx = match p.rows.iter().position(|r| r.0 == label) {
699 Some(i) => i,
700 None => {
701 p.rows.push((label, 0, 0, 0));
702 p.rows.len() - 1
703 }
704 };
705 p.stack.push((idx, 0));
706 });
707 Some(Dsv4Phase {
708 stream: if dsv4_prof_sync() { stream } else { None },
709 t0: std::time::Instant::now(),
710 nvtx,
711 })
712 }
713}
714
715impl Drop for Dsv4Phase<'_> {
716 fn drop(&mut self) {
717 if let Some(s) = self.stream {
720 let _ = s.synchronize();
721 }
722 let us = self.t0.elapsed().as_micros() as u64;
723 if self.nvtx {
724 unsafe {
725 k::memra_dsv4_nvtx_pop();
726 }
727 }
728 DSV4_PHASES.with(|p| {
729 let mut p = p.borrow_mut();
730 if let Some((idx, child)) = p.stack.pop() {
731 let r = &mut p.rows[idx];
732 r.1 += us;
733 r.2 += child;
734 r.3 += 1;
735 if let Some(top) = p.stack.last_mut() {
736 top.1 += us;
737 }
738 }
739 });
740 }
741}
742
743macro_rules! phase {
746 ($name:literal, $stream:expr) => {
747 crate::dsv4_gpu::Dsv4Phase::new(concat!($name, "\0"), $stream)
748 };
749}
750
751pub fn dsv4_phase_report(tag: &str, rounds: u64, plain_us: f64) {
754 DSV4_PHASES.with(|p| {
755 let p = p.borrow();
756 if p.rows.is_empty() {
757 return;
758 }
759 let mode = if dsv4_prof_sync() {
760 "sync-bracketed (PERTURBS: compare the round total against the unbracketed A/B)"
761 } else {
762 "nvtx-only (host wall; GPU-busy comes from nsys nvtx_gpu_proj_sum)"
763 };
764 println!("\n[phase] === F ITEMISATION: {tag} ===");
765 println!("[phase] rounds={rounds} plain step={plain_us:.1} us mode={mode}");
766 println!(
767 "[phase] {:<26} {:>11} {:>11} {:>9} {:>12} {:>12}",
768 "phase", "incl_us/rd", "self_us/rd", "calls/rd", "self_plainstp", "incl_plainstp"
769 );
770 let mut rows = p.rows.clone();
771 rows.sort_by(|a, b| (b.1.saturating_sub(b.2)).cmp(&(a.1.saturating_sub(a.2))));
772 let r = rounds.max(1) as f64;
773 let mut leaf_sum = 0f64;
774 for (name, incl, child, calls) in rows {
775 let selfus = incl.saturating_sub(child) as f64 / r;
776 let inclus = incl as f64 / r;
777 leaf_sum += selfus;
778 let (sp, ip) = if plain_us > 0.0 {
779 (selfus / plain_us, inclus / plain_us)
780 } else {
781 (0.0, 0.0)
782 };
783 println!(
784 "[phase] {name:<26} {inclus:>11.1} {selfus:>11.1} {:>9.2} {sp:>12.4} {ip:>12.4}",
785 calls as f64 / r
786 );
787 }
788 println!(
789 "[phase] {:<26} {:>11} {:>11.1} {:>9} {:>12.4}",
790 "SUM of self",
791 "",
792 leaf_sum,
793 "",
794 if plain_us > 0.0 {
795 leaf_sum / plain_us
796 } else {
797 0.0
798 }
799 );
800 });
801}
802
803fn dsv4_dspark_chain_device() -> bool {
808 static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
809 *V.get_or_init(|| {
810 let on = std::env::var("MEMRA_DSV4_DSPARK_CHAIN").as_deref() == Ok("device");
811 if on {
812 println!(
813 "[spec] DSpark markov chain RESIDENT ON DEVICE (MEMRA_DSV4_DSPARK_CHAIN=device): \
814 one D2H per round instead of 2 x block_size"
815 );
816 }
817 on
818 })
819}
820
821fn dsv4_dspark_markov_rowblk() -> bool {
827 static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
828 *V.get_or_init(|| {
829 let on = std::env::var("MEMRA_DSV4_DSPARK_MARKOV").as_deref() == Ok("rowblk");
830 if on {
831 println!(
832 "[spec] DSpark markov bias GEMV on the ROW-BLOCKED dots twin \
833 (MEMRA_DSV4_DSPARK_MARKOV=rowblk; bit-identical, geometry only)"
834 );
835 }
836 on
837 })
838}
839
840pub fn dsv4_phase_reset() {
843 DSV4_PHASES.with(|p| *p.borrow_mut() = Dsv4PhaseAcc::default());
844}
845
846macro_rules! dp {
847 ($slice:expr, $stream:expr) => {{ $slice.device_ptr($stream).0 as *const c_void }};
848}
849macro_rules! dpf {
850 ($slice:expr, $stream:expr) => {{ $slice.device_ptr($stream).0 as *const f32 }};
851}
852macro_rules! dpm {
853 ($slice:expr, $stream:expr) => {{ $slice.device_ptr_mut($stream).0 as *mut f32 }};
854}
855
856fn f32_to_bf16_exact(name: &str, v: &[f32]) -> Vec<u8> {
862 let mut out = Vec::with_capacity(v.len() * 2);
863 for (i, x) in v.iter().enumerate() {
864 let bits = x.to_bits();
865 assert!(
866 bits & 0xFFFF == 0,
867 "{name}: element {i} = {x} not exactly representable in bf16 — lane-4 rung \
868 exactness violated"
869 );
870 out.extend_from_slice(&((bits >> 16) as u16).to_le_bytes());
871 }
872 out
873}
874
875impl Dsv4Gpu {
876 fn tensor_bf16(&mut self, stage: usize, name: &str) -> Res<CudaSlice<u8>> {
879 let raw_name = format!("{name}.weight");
880 let is_bf16_raw = self
881 .model
882 .st
883 .raw(&raw_name)
884 .map(|(i, _)| i.dtype == "BF16")
885 .unwrap_or(false)
886 || self
887 .model
888 .st
889 .raw(name)
890 .map(|(i, _)| i.dtype == "BF16")
891 .unwrap_or(false);
892 let stream = self.stages[stage].gpu.stream();
893 let bytes: u64;
894 let out = if is_bf16_raw {
895 let (_, raw) = self
896 .model
897 .st
898 .raw(&raw_name)
899 .or_else(|| self.model.st.raw(name))
900 .unwrap();
901 bytes = raw.len() as u64;
902 upload_u8(&stream, raw)?
903 } else {
904 let (_, v) = self.model.tensor_f32(name);
905 let b = f32_to_bf16_exact(name, &v);
906 bytes = b.len() as u64;
907 upload_u8(&stream, &b)?
908 };
909 self.stages[stage].loaded_bytes += bytes;
910 Ok(out)
911 }
912
913 fn tensor_dense(
925 &mut self,
926 stage: usize,
927 name: &str,
928 fp8_ok: bool,
929 ) -> Res<(DenseBf16, Option<Fp8Dense>)> {
930 let raw_name = format!("{name}.weight");
931 let is_bf16_raw = self
932 .model
933 .st
934 .raw(&raw_name)
935 .map(|(i, _)| i.dtype == "BF16")
936 .unwrap_or(false)
937 || self
938 .model
939 .st
940 .raw(name)
941 .map(|(i, _)| i.dtype == "BF16")
942 .unwrap_or(false);
943 if is_bf16_raw || !fp8_ok || !self.dense_fp8 {
944 return Ok((DenseBf16::Dev(self.tensor_bf16(stage, name)?), None));
945 }
946 let (wi, wraw, stem) = if let Some((i, r)) = self.model.st.raw(&raw_name) {
948 (i.clone(), r.to_vec(), name.to_string())
949 } else {
950 let (i, r) = self
951 .model
952 .st
953 .raw(name)
954 .unwrap_or_else(|| panic!("missing dense tensor {name}"));
955 let stem = name.strip_suffix(".weight").unwrap_or(name).to_string();
956 (i.clone(), r.to_vec(), stem)
957 };
958 if wi.dtype != "F8_E4M3" {
959 return Ok((DenseBf16::Dev(self.tensor_bf16(stage, name)?), None));
961 }
962 assert_eq!(wi.shape.len(), 2, "{name}: fp8 dense tensor must be 2-D");
963 let rows = wi.shape[0] as usize;
964 let cols = wi.shape[1] as usize;
965 assert_eq!(cols % 8, 0, "{name}: fp8 dense cols {cols} % 8 != 0");
966 assert_eq!(wraw.len(), rows * cols, "{name}: fp8 byte count");
967 let scale_name = format!("{stem}.scale");
968 let (si, sraw) = self
969 .model
970 .st
971 .raw(&scale_name)
972 .unwrap_or_else(|| panic!("{name}: F8_E4M3 weight without {scale_name}"));
973 assert_eq!(si.dtype, "F8_E8M0", "{scale_name}: dtype");
974 let sc_rows = rows.div_ceil(128);
975 let sc_cols = cols.div_ceil(128);
976 assert_eq!(
977 (si.shape[0] as usize, si.shape[1] as usize),
978 (sc_rows, sc_cols),
979 "{scale_name}: scale grid shape vs [ceil({rows}/128), ceil({cols}/128)]"
980 );
981 let sc_f32: Vec<f32> = sraw
982 .iter()
983 .map(|&b| {
984 assert_ne!(b, 0xFF, "{scale_name}: e8m0 NaN code");
985 memra_gguf::dsv4::e8m0_to_f32(b)
986 })
987 .collect();
988 let (_, v) = self.model.tensor_f32(name);
990 assert_eq!(v.len(), rows * cols, "{name}: dequant len");
991 let step = (v.len() / 1024).max(1);
992 for idx in (0..v.len()).step_by(step) {
993 let (r, c) = (idx / cols, idx % cols);
994 let got = memra_gguf::nvfp4_repack::fp8_e4m3_to_f32(wraw[idx])
995 * sc_f32[(r / 128) * sc_cols + c / 128];
996 assert_eq!(
997 got.to_bits(),
998 v[idx].to_bits(),
999 "{name}: fp8 arm layout check failed at [{r},{c}] ({got} vs {})",
1000 v[idx]
1001 );
1002 }
1003 let b = f32_to_bf16_exact(name, &v);
1004 let stream = self.stages[stage].gpu.stream();
1005 let codes = upload_u8(&stream, &wraw)?;
1009 let scales = upload_f32(&stream, &sc_f32)?;
1010 self.stages[stage].loaded_bytes += (wraw.len() + sc_f32.len() * 4) as u64;
1011 Ok((
1012 DenseBf16::Host(b),
1013 Some(Fp8Dense {
1014 codes,
1015 scales,
1016 sc_cols,
1017 rows,
1018 cols,
1019 }),
1020 ))
1021 }
1022
1023 fn tensor_f32_dev(&mut self, stage: usize, name: &str) -> Res<CudaSlice<f32>> {
1026 let (_, v) = self.model.tensor_f32(name);
1027 let stream = self.stages[stage].gpu.stream();
1028 self.stages[stage].loaded_bytes += (v.len() * 4) as u64;
1029 upload_f32(&stream, &v)
1030 }
1031
1032 fn load_cmp(
1033 &mut self,
1034 stage: usize,
1035 prefix: &str,
1036 ratio: usize,
1037 d: usize,
1038 rotate: bool,
1039 ) -> Res<CmpDev> {
1040 let (wkv_shape, _) = self.model.tensor_f32(&format!("{prefix}.wkv.weight"));
1041 let latent = wkv_shape[0];
1042 let overlap = ratio == 4;
1043 assert_eq!(latent, if overlap { 2 * d } else { d }, "{prefix} latent");
1044 Ok(CmpDev {
1045 ratio,
1046 d,
1047 latent,
1048 overlap,
1049 rotate,
1050 wkv: self.tensor_f32_dev(stage, &format!("{prefix}.wkv.weight"))?,
1051 wgate: self.tensor_f32_dev(stage, &format!("{prefix}.wgate.weight"))?,
1052 norm: self.tensor_f32_dev(stage, &format!("{prefix}.norm.weight"))?,
1053 ape: self.tensor_f32_dev(stage, &format!("{prefix}.ape"))?,
1054 })
1055 }
1056
1057 fn load_layer(&mut self, stage: usize, il: u32, prefix: &str) -> Res<LayerDev> {
1060 let d = self.model.cfg().clone();
1061 let moe = self.model.mc.moe.clone().expect("moe block");
1062 let ratio = d.compress_ratio(il) as usize;
1063 let hd = d.head_dim as usize;
1064 let p = prefix.to_string();
1065 let hash = d.is_hash_layer(il);
1066 let ne = moe.expert_count as usize;
1067 let inter = moe.expert_ff_length as usize;
1068 let hidden = self.model.mc.n_embd as usize;
1069
1070 let hc_load = |m: &Dsv4Model, fam: &str| -> (Vec<f32>, Vec<f32>, Vec<f32>) {
1072 let fn_w = m.tensor_f32(&format!("{p}.hc_{fam}_fn")).1;
1073 let base = m.tensor_f32(&format!("{p}.hc_{fam}_base")).1;
1074 let scale = m.tensor_f32(&format!("{p}.hc_{fam}_scale")).1;
1075 (fn_w, base, scale)
1076 };
1077 let (attn_fn, attn_base, attn_scale) = hc_load(&self.model, "attn");
1078 let (ffn_fn, ffn_base, ffn_scale) = hc_load(&self.model, "ffn");
1079 let stream = self.stages[stage].gpu.stream();
1080 let hc_attn_fn = upload_f32(&stream, &attn_fn)?;
1081 let hc_ffn_fn = upload_f32(&stream, &ffn_fn)?;
1082 self.stages[stage].loaded_bytes += ((attn_fn.len() + ffn_fn.len()) * 4) as u64;
1083
1084 let (wi0, _) = self
1088 .model
1089 .st
1090 .raw(&format!("{p}.ffn.experts.0.w1.weight"))
1091 .unwrap_or_else(|| panic!("missing {p}.ffn.experts.0.w1.weight"));
1092 let expert_kind = match wi0.dtype.as_str() {
1093 "U8" => ExpertKind::Nvfp4,
1094 "I8" => ExpertKind::Mxfp4,
1095 other => panic!("{p}: unexpected expert weight dtype {other}"),
1096 };
1097 let wbytes = inter * hidden / 2; let sbytes = match expert_kind {
1099 ExpertKind::Nvfp4 => inter * hidden / 16,
1100 ExpertKind::Mxfp4 => inter * hidden / 32,
1101 };
1102 let mut experts_w = stream
1103 .alloc_zeros::<u8>(ne * 3 * wbytes)
1104 .map_err(e("alloc expert slab"))?;
1105 let mut experts_sc = stream
1106 .alloc_zeros::<u8>(ne * 3 * sbytes)
1107 .map_err(e("alloc expert scale slab"))?;
1108 let mut experts_s2 = Vec::with_capacity(ne * 3);
1109 for ex in 0..ne {
1110 for (pi, pname) in ["w1", "w2", "w3"].iter().enumerate() {
1111 let base = format!("{p}.ffn.experts.{ex}.{pname}");
1112 let (wi, wb) = self
1113 .model
1114 .st
1115 .raw(&format!("{base}.weight"))
1116 .unwrap_or_else(|| panic!("missing {base}.weight"));
1117 assert_eq!(wb.len(), wbytes, "{base}: weight bytes");
1118 let sb = match expert_kind {
1119 ExpertKind::Nvfp4 => {
1120 assert_eq!(wi.dtype, "U8", "{base}: expected NVFP4 U8 weight");
1121 let (_, sb) = self
1122 .model
1123 .st
1124 .raw(&format!("{base}.weight_scale"))
1125 .unwrap_or_else(|| panic!("missing {base}.weight_scale"));
1126 let (_, s2b) = self
1127 .model
1128 .st
1129 .raw(&format!("{base}.weight_scale_2"))
1130 .unwrap_or_else(|| panic!("missing {base}.weight_scale_2"));
1131 let s2 = f32::from_le_bytes(s2b.try_into().expect("scale_2 4B"));
1132 assert!(
1135 s2 > 0.0 && s2.to_bits() & 0x007F_FFFF == 0,
1136 "{base}: scale_2 {s2} not a power of two — rung exactness violated"
1137 );
1138 experts_s2.push(s2);
1139 sb
1140 }
1141 ExpertKind::Mxfp4 => {
1142 assert_eq!(wi.dtype, "I8", "{base}: expected MXFP4 I8 weight");
1143 let (si, sb) = self
1144 .model
1145 .st
1146 .raw(&format!("{base}.scale"))
1147 .unwrap_or_else(|| panic!("missing {base}.scale"));
1148 assert_eq!(si.dtype, "F8_E8M0", "{base}: expected E8M0 scale");
1149 assert!(
1151 !sb.contains(&0xFFu8),
1152 "{base}: E8M0 NaN scale code — refusing"
1153 );
1154 experts_s2.push(1.0);
1155 sb
1156 }
1157 };
1158 assert_eq!(sb.len(), sbytes, "{base}: scale bytes");
1159 let off = (ex * 3 + pi) * wbytes;
1160 let mut view = experts_w.slice_mut(off..off + wbytes);
1161 stream
1162 .memcpy_htod(wb, &mut view)
1163 .map_err(e("htod expert w"))?;
1164 let soff = (ex * 3 + pi) * sbytes;
1165 let mut sview = experts_sc.slice_mut(soff..soff + sbytes);
1166 stream
1167 .memcpy_htod(sb, &mut sview)
1168 .map_err(e("htod expert sc"))?;
1169 }
1170 }
1171 self.stages[stage].loaded_bytes +=
1172 (ne * 3 * (wbytes + sbytes)) as u64 + (ne * 3 * 4) as u64;
1173
1174 let cmp = if ratio != 0 {
1175 Some(self.load_cmp(stage, &format!("{p}.attn.compressor"), ratio, hd, false)?)
1176 } else {
1177 None
1178 };
1179 let fp8_ok = p.starts_with("layers.");
1182 let idx = if d.has_indexer(il) {
1183 let heads = d.index_n_heads as usize;
1184 let ihd = d.index_head_dim as usize;
1185 let (iwq_b, iwq_b_fp8) =
1186 self.tensor_dense(stage, &format!("{p}.attn.indexer.wq_b"), fp8_ok)?;
1187 let (iwp, iwp_fp8) = self.tensor_dense(
1188 stage,
1189 &format!("{p}.attn.indexer.weights_proj.weight"),
1190 fp8_ok,
1191 )?;
1192 Some(IdxDev {
1193 wq_b: iwq_b,
1194 weights_proj: iwp,
1195 wq_b_fp8: iwq_b_fp8,
1196 weights_proj_fp8: iwp_fp8,
1197 cmp: self.load_cmp(
1198 stage,
1199 &format!("{p}.attn.indexer.compressor"),
1200 ratio,
1201 ihd,
1202 true,
1203 )?,
1204 heads,
1205 hd: ihd,
1206 topk: d.index_topk as usize,
1207 })
1208 } else {
1209 None
1210 };
1211
1212 let stream = self.stages[stage].gpu.stream();
1216 let hc_attn_base_dev = upload_f32(&stream, &attn_base)?;
1217 let hc_attn_scale_dev = upload_f32(&stream, &attn_scale)?;
1218 let hc_ffn_base_dev = upload_f32(&stream, &ffn_base)?;
1219 let hc_ffn_scale_dev = upload_f32(&stream, &ffn_scale)?;
1220 let gate_bias_host: Option<Vec<f32>> = if hash {
1221 None
1222 } else {
1223 Some(self.model.tensor_f32(&format!("{p}.ffn.gate.bias")).1)
1224 };
1225 let gate_bias_dev = match &gate_bias_host {
1226 Some(b) => Some(upload_f32(&stream, b)?),
1227 None => None,
1228 };
1229 let tid2eid_host: Option<Vec<i64>> = if hash {
1230 Some(self.model.tensor_i64(&format!("{p}.ffn.gate.tid2eid")).1)
1231 } else {
1232 None
1233 };
1234 let tid2eid_dev = match &tid2eid_host {
1235 Some(t) => {
1236 let topk = moe.expert_used_count as usize;
1237 assert_eq!(t.len() % topk, 0, "{p}: tid2eid rows");
1238 let mut t32 = Vec::with_capacity(t.len());
1239 for row in t.chunks(topk) {
1240 let mut seen = std::collections::BTreeSet::new();
1241 for &ex in row {
1242 assert!(
1243 (0..ne as i64).contains(&ex),
1244 "{p}: tid2eid out of range at load"
1245 );
1246 assert!(seen.insert(ex), "{p}: duplicate expert id in tid2eid row");
1247 t32.push(ex as i32);
1248 }
1249 }
1250 Some(upload_i32(&stream, &t32)?)
1251 }
1252 None => None,
1253 };
1254 let experts_s2_dev = upload_f32(&stream, &experts_s2)?;
1255
1256 let (wq_a, wq_a_fp8) = self.tensor_dense(stage, &format!("{p}.attn.wq_a"), fp8_ok)?;
1257 let (wq_b, wq_b_fp8) = self.tensor_dense(stage, &format!("{p}.attn.wq_b"), fp8_ok)?;
1258 let (wkv, wkv_fp8) = self.tensor_dense(stage, &format!("{p}.attn.wkv"), fp8_ok)?;
1259 let (wo_a, wo_a_fp8) = self.tensor_dense(stage, &format!("{p}.attn.wo_a"), fp8_ok)?;
1260 let (wo_b, wo_b_fp8) = self.tensor_dense(stage, &format!("{p}.attn.wo_b"), fp8_ok)?;
1261 let (sw1, sw1_fp8) =
1262 self.tensor_dense(stage, &format!("{p}.ffn.shared_experts.w1"), fp8_ok)?;
1263 let (sw2, sw2_fp8) =
1264 self.tensor_dense(stage, &format!("{p}.ffn.shared_experts.w2"), fp8_ok)?;
1265 let (sw3, sw3_fp8) =
1266 self.tensor_dense(stage, &format!("{p}.ffn.shared_experts.w3"), fp8_ok)?;
1267
1268 Ok(LayerDev {
1269 il,
1270 ratio,
1271 expert_kind,
1272 hc_attn_base_dev,
1273 hc_attn_scale_dev,
1274 hc_ffn_base_dev,
1275 hc_ffn_scale_dev,
1276 gate_bias_dev,
1277 tid2eid_dev,
1278 experts_s2_dev,
1279 wq_a,
1280 wq_b,
1281 wkv,
1282 wo_a,
1283 wo_b,
1284 wq_a_fp8,
1285 wq_b_fp8,
1286 wkv_fp8,
1287 wo_a_fp8,
1288 wo_b_fp8,
1289 q_norm: self.tensor_f32_dev(stage, &format!("{p}.attn.q_norm.weight"))?,
1290 kv_norm: self.tensor_f32_dev(stage, &format!("{p}.attn.kv_norm.weight"))?,
1291 attn_norm: self.tensor_f32_dev(stage, &format!("{p}.attn_norm.weight"))?,
1292 ffn_norm: self.tensor_f32_dev(stage, &format!("{p}.ffn_norm.weight"))?,
1293 sink: self.tensor_f32_dev(stage, &format!("{p}.attn.attn_sink"))?,
1294 cmp,
1295 idx,
1296 hc_attn_fn,
1297 hc_ffn_fn,
1298 hc_attn_base: attn_base,
1299 hc_attn_scale: attn_scale,
1300 hc_ffn_base: ffn_base,
1301 hc_ffn_scale: ffn_scale,
1302 gate_w: self.tensor_f32_dev(stage, &format!("{p}.ffn.gate.weight"))?,
1303 gate_bias: gate_bias_host,
1304 tid2eid: tid2eid_host,
1305 experts_w,
1306 experts_sc,
1307 experts_s2,
1308 shared_w: [sw1, sw2, sw3],
1309 shared_fp8: [sw1_fp8, sw2_fp8, sw3_fp8],
1310 })
1311 }
1312
1313 pub fn load(
1316 dir: &Path,
1317 devices: &[usize],
1318 variant: ActQuantVariant,
1319 max_seq: usize,
1320 ) -> Res<Self> {
1321 assert_eq!(devices.len(), 2, "lane 4 placement is a 2-card layer split");
1322 let model = Dsv4Model::open(dir);
1323 let d = model.cfg().clone();
1324 let mc = model.mc.clone();
1325 let n_trunk = mc.n_layer - mc.nextn_predict_layers;
1326 let rd = d.qk_rope_head_dim as usize;
1327
1328 let layer_bytes = |il: u32| -> u64 {
1331 let ratio = d.compress_ratio(il);
1332 let base = 3_875_000_000u64; match ratio {
1334 4 => base + 66_000_000,
1335 _ => base,
1336 }
1337 };
1338 let total: u64 = (0..n_trunk).map(layer_bytes).sum();
1339 let mut acc = 0u64;
1340 let mut split_at = n_trunk / 2;
1341 for il in 0..n_trunk {
1342 acc += layer_bytes(il);
1343 if acc * 2 >= total {
1344 split_at = il + 1;
1345 break;
1346 }
1347 }
1348
1349 let fc_yarn_host = precompute_freqs_cis(
1350 rd,
1351 max_seq,
1352 d.rope_yarn_orig_ctx,
1353 d.compress_rope_theta,
1354 d.rope_yarn_factor,
1355 d.rope_yarn_beta_fast,
1356 d.rope_yarn_beta_slow,
1357 );
1358 let fc_plain_host = precompute_freqs_cis(
1359 rd,
1360 max_seq,
1361 0,
1362 mc.rope_freq_base,
1363 d.rope_yarn_factor,
1364 d.rope_yarn_beta_fast,
1365 d.rope_yarn_beta_slow,
1366 );
1367 let flat =
1368 |fc: &FreqsCis| -> Vec<f32> { fc.cs.iter().flat_map(|&(c, s)| [c, s]).collect() };
1369
1370 let inter = mc.moe.as_ref().expect("moe").expert_ff_length as usize;
1371 let hidden = mc.n_embd as usize;
1372 let mut stages = Vec::new();
1373 for &dev in devices {
1374 let gpu = memra_runtime::Gpu::new(dev).map_err(e("Gpu::new"))?;
1375 unsafe { gpu.ctx.disable_event_tracking() };
1378 let stream = gpu.stream();
1379 let fc_yarn = upload_f32(&stream, &flat(&fc_yarn_host))?;
1380 let fc_plain = upload_f32(&stream, &flat(&fc_plain_host))?;
1381 let ws = stream.alloc_zeros::<u8>(64 << 20).map_err(e("ws alloc"))?;
1382 let deq = [
1383 stream
1384 .alloc_zeros::<u8>(inter * hidden * 2)
1385 .map_err(e("deq"))?,
1386 stream
1387 .alloc_zeros::<u8>(inter * hidden * 2)
1388 .map_err(e("deq"))?,
1389 stream
1390 .alloc_zeros::<u8>(inter * hidden * 2)
1391 .map_err(e("deq"))?,
1392 ];
1393 stages.push(Stage {
1394 dev,
1395 gpu,
1396 layers: Vec::new(),
1397 embed: None,
1398 head: None,
1399 trunk_norm: None,
1400 hc_head_fn: None,
1401 fc_yarn,
1402 fc_plain,
1403 ws,
1404 deq,
1405 loaded_bytes: 0,
1406 hc_head_base_dev: None,
1407 hc_head_scale_dev: None,
1408 });
1409 }
1410
1411 let decode_path = match std::env::var("MEMRA_DSV4_DECODE_PATH").as_deref() {
1413 Err(_) | Ok("") | Ok("legacy") => DecodePath::Legacy,
1414 Ok("device-hostmath") => DecodePath::Device { host_math: true },
1415 Ok("device") => DecodePath::Device { host_math: false },
1416 Ok(other) => {
1417 return Err(format!(
1418 "MEMRA_DSV4_DECODE_PATH '{other}' unknown (legacy | device | device-hostmath)"
1419 ));
1420 }
1421 };
1422 let on_device = matches!(decode_path, DecodePath::Device { .. });
1440 let (dots_f32, chains_f32) = match std::env::var("MEMRA_DSV4_DOTS_ARM").as_deref() {
1441 Err(_) | Ok("") => {
1442 if on_device {
1444 (true, true)
1445 } else {
1446 (false, false)
1447 }
1448 }
1449 Ok(explicit @ ("f32x" | "f32")) if !on_device => {
1450 return Err(format!(
1451 "MEMRA_DSV4_DOTS_ARM={explicit} requires MEMRA_DSV4_DECODE_PATH=device \
1452 (the f32 dots arms exist on the device decode path only)"
1453 ));
1454 }
1455 Ok("f32x") => (true, true),
1456 Ok("f64") => (false, false),
1457 Ok("f32") => (true, false),
1458 Ok(other) => {
1459 return Err(format!(
1460 "MEMRA_DSV4_DOTS_ARM '{other}' unknown (f64 | f32 | f32x)"
1461 ));
1462 }
1463 };
1464
1465 let dspark_head_f32 = match std::env::var("MEMRA_DSV4_DSPARK_HEAD_ARM").as_deref() {
1483 Err(_) | Ok("") | Ok("f32x") => true,
1484 Ok("f64") => false,
1485 Ok(other) => {
1486 return Err(format!(
1487 "MEMRA_DSV4_DSPARK_HEAD_ARM '{other}' unknown (f64 | f32x)"
1488 ));
1489 }
1490 };
1491
1492 let dense_fp8 = resolve_dense_arm(
1507 std::env::var("MEMRA_DSV4_DENSE_ARM").ok().as_deref(),
1508 on_device,
1509 )?;
1510
1511 let mut me = Dsv4Gpu {
1512 model,
1513 stages,
1514 layer_stage: (0..n_trunk).map(|il| usize::from(il >= split_at)).collect(),
1515 split_at,
1516 max_seq,
1517 variant,
1518 fc_yarn_host,
1519 fc_plain_host,
1520 mtp: None,
1521 expert_arm: if memra_gguf::dsv4_forward::expert_arm_native() {
1522 ExpertArm::Native
1523 } else {
1524 ExpertArm::Bf16Dequant
1525 },
1526 decode_path,
1527 dots_f32,
1528 chains_f32,
1529 dspark_head_f32,
1530 dense_fp8,
1531 dspark: None,
1532 boundary_ev: Vec::new(),
1533 hc_head_base: Vec::new(),
1534 hc_head_scale: Vec::new(),
1535 };
1536 eprintln!(
1537 "[load] expert arm: {:?} | decode path: {:?} | dots arm: {}",
1538 me.expert_arm,
1539 me.decode_path,
1540 if me.chains_f32 {
1541 "f32x (dots + sink/norm/indexer chains)"
1542 } else if me.dots_f32 {
1543 "f32"
1544 } else {
1545 "f64"
1546 }
1547 );
1548 eprintln!(
1549 "[load] dspark exit-head dots arm: {} (rung-4c fork; drafts only, never the \
1550 emitted stream)",
1551 if me.dspark_head_f32 { "f32x" } else { "f64" }
1552 );
1553 eprintln!(
1554 "[load] dense arm: {} (iteration-5; fp8 = FP8-blk linears as-stored on the \
1555 device decode/verify paths, bit-identical twins)",
1556 if me.dense_fp8 { "fp8" } else { "bf16" }
1557 );
1558 if matches!(me.decode_path, DecodePath::Device { .. }) && me.expert_arm != ExpertArm::Native
1559 {
1560 return Err(
1565 "MEMRA_DSV4_DECODE_PATH=device requires MEMRA_DSV4_EXPERT_ARM=native".to_string(),
1566 );
1567 }
1568
1569 if matches!(me.decode_path, DecodePath::Device { .. }) && me.stages.len() > 1 {
1573 use cudarc::driver::sys as cus;
1574 for a in 0..me.stages.len() {
1575 for b in 0..me.stages.len() {
1576 if a == b || me.stages[a].dev == me.stages[b].dev {
1577 continue;
1578 }
1579 me.stages[a]
1580 .gpu
1581 .ctx
1582 .bind_to_thread()
1583 .map_err(e("peer bind"))?;
1584 let rc =
1585 unsafe { cus::cuCtxEnablePeerAccess(me.stages[b].gpu.ctx.cu_ctx(), 0) };
1586 if rc != cus::cudaError_enum::CUDA_SUCCESS
1587 && rc != cus::cudaError_enum::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED
1588 {
1589 return Err(format!(
1590 "cuCtxEnablePeerAccess(dev{} -> dev{}) failed: {rc:?}",
1591 me.stages[a].dev, me.stages[b].dev
1592 ));
1593 }
1594 let dev = cudarc::driver::result::device::get(me.stages[a].dev as i32)
1595 .map_err(e("device get"))?;
1596 let mut pool: cus::CUmemoryPool = std::ptr::null_mut();
1597 unsafe {
1598 cus::cuDeviceGetDefaultMemPool(&mut pool, dev)
1599 .result()
1600 .map_err(e("default pool"))?;
1601 }
1602 let desc = cus::CUmemAccessDesc {
1603 location: cus::CUmemLocation {
1604 type_: cus::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
1605 id: me.stages[b].dev as i32,
1606 },
1607 flags: cus::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
1608 };
1609 let rc = unsafe { cus::cuMemPoolSetAccess(pool, &desc, 1) };
1610 if rc != cus::cudaError_enum::CUDA_SUCCESS {
1611 return Err(format!(
1612 "cuMemPoolSetAccess(dev{} pool -> dev{}) failed: {rc:?}",
1613 me.stages[a].dev, me.stages[b].dev
1614 ));
1615 }
1616 }
1617 }
1618 for bnd in 0..me.stages.len() - 1 {
1619 let ev = me.stages[bnd]
1620 .gpu
1621 .ctx
1622 .new_event(None)
1623 .map_err(e("boundary event"))?;
1624 me.boundary_ev.push(ev);
1625 }
1626 eprintln!(
1627 "[load] lane-8 peer transport enabled ({} boundaries)",
1628 me.boundary_ev.len()
1629 );
1630 }
1631
1632 me.stages[0].embed = Some({
1634 let (_, raw) = me.model.st.raw("embed.weight").expect("embed.weight");
1635 let stream = me.stages[0].gpu.stream();
1636 me.stages[0].loaded_bytes += raw.len() as u64;
1637 upload_u8(&stream, raw)?
1638 });
1639 let last = me.stages.len() - 1;
1640 me.stages[last].head = Some({
1641 let (_, raw) = me.model.st.raw("head.weight").expect("head.weight");
1642 let stream = me.stages[last].gpu.stream();
1643 me.stages[last].loaded_bytes += raw.len() as u64;
1644 upload_u8(&stream, raw)?
1645 });
1646 me.stages[last].trunk_norm = Some(me.tensor_f32_dev(last, "norm.weight")?);
1647 me.stages[last].hc_head_fn = Some(me.tensor_f32_dev(last, "hc_head_fn")?);
1648 me.hc_head_base = me.model.tensor_f32("hc_head_base").1;
1649 me.hc_head_scale = me.model.tensor_f32("hc_head_scale").1;
1650 {
1651 let stream = me.stages[last].gpu.stream();
1652 let base_dev = upload_f32(&stream, &me.hc_head_base)?;
1653 let scale_dev = upload_f32(&stream, &me.hc_head_scale)?;
1654 me.stages[last].hc_head_base_dev = Some(base_dev);
1655 me.stages[last].hc_head_scale_dev = Some(scale_dev);
1656 }
1657
1658 let t0 = std::time::Instant::now();
1659 for il in 0..n_trunk {
1660 let stage = me.layer_stage[il as usize];
1661 let l = me.load_layer(stage, il, &format!("layers.{il}"))?;
1662 me.stages[stage].layers.push(l);
1663 if il % 4 == 3 || il + 1 == n_trunk {
1664 eprintln!(
1665 "[load] layer {il} -> dev{} done t={:.0}s",
1666 me.stages[stage].dev,
1667 t0.elapsed().as_secs_f64()
1668 );
1669 }
1670 }
1671 let nextn = me.model.mc.nextn_predict_layers;
1682 if nextn > 0 && me.model.has("mtp.0.e_proj.weight") {
1683 assert_eq!(
1684 nextn, 1,
1685 "multi-NextN chains not wired (single MTP layer expected)"
1686 );
1687 let p = "mtp.0";
1688 let layer = me.load_layer(last, n_trunk, p)?;
1689 assert_eq!(
1690 layer.expert_kind,
1691 ExpertKind::Mxfp4,
1692 "MTP experts must be MXFP4"
1693 );
1694 let mtp = MtpDev {
1695 layer,
1696 enorm: me.tensor_f32_dev(last, &format!("{p}.enorm.weight"))?,
1697 hnorm: me.tensor_f32_dev(last, &format!("{p}.hnorm.weight"))?,
1698 norm: me.tensor_f32_dev(last, &format!("{p}.norm.weight"))?,
1699 e_proj: me.tensor_bf16(last, &format!("{p}.e_proj"))?,
1700 h_proj: me.tensor_bf16(last, &format!("{p}.h_proj"))?,
1701 hc_head_fn: me.tensor_f32_dev(last, &format!("{p}.hc_head_fn"))?,
1702 hc_head_base: me.model.tensor_f32(&format!("{p}.hc_head_base")).1,
1703 hc_head_scale: me.model.tensor_f32(&format!("{p}.hc_head_scale")).1,
1704 };
1705 me.mtp = Some(mtp);
1706 } else if nextn > 0 {
1707 if std::env::var("MEMRA_DSV4_DRAFTER").as_deref() == Ok("dspark") {
1708 let cfg = memra_gguf::dsv4_dspark::DsparkConfig::load(dir, &me.model);
1713 let hidden = me.model.mc.n_embd as usize;
1714 let mut blocks = Vec::with_capacity(cfg.n_blocks);
1715 for k in 0..cfg.n_blocks {
1716 let layer = me.load_layer(last, n_trunk + k as u32, &format!("mtp.{k}"))?;
1717 assert_eq!(layer.ratio, 0, "dspark block mtp.{k} must be ratio 0");
1718 assert_eq!(
1719 layer.expert_kind,
1720 ExpertKind::Mxfp4,
1721 "dspark experts must be MXFP4"
1722 );
1723 blocks.push(layer);
1724 }
1725 let last_p = format!("mtp.{}", cfg.n_blocks - 1);
1726 let (mp_shape, _) = me.model.tensor_f32("mtp.0.main_proj");
1727 assert_eq!(
1728 mp_shape,
1729 vec![hidden, cfg.target_layer_ids.len() * hidden],
1730 "main_proj shape"
1731 );
1732 let (w1_shape, w1) = me
1733 .model
1734 .tensor_f32(&format!("{last_p}.markov_head.markov_w1.weight"));
1735 let (w2_shape, w2) = me
1736 .model
1737 .tensor_f32(&format!("{last_p}.markov_head.markov_w2.weight"));
1738 let vocab = w1_shape[0];
1739 assert_eq!(w1_shape[1], cfg.markov_rank, "markov_w1 rank");
1740 assert_eq!(w2_shape, vec![vocab, cfg.markov_rank], "markov_w2 shape");
1741 let (cf_shape, _) = me
1742 .model
1743 .tensor_f32(&format!("{last_p}.confidence_head.proj.weight"));
1744 assert_eq!(
1745 cf_shape,
1746 vec![1, hidden + cfg.markov_rank],
1747 "confidence proj shape"
1748 );
1749 let st_stream = me.stages[last].gpu.stream();
1750 let markov_w1 = upload_f32(&st_stream, &w1)?;
1751 let markov_w2 = upload_f32(&st_stream, &w2)?;
1752 let dspark = DsparkDev {
1753 blocks,
1754 main_proj: me.tensor_bf16(last, "mtp.0.main_proj")?,
1755 main_norm: me.tensor_f32_dev(last, "mtp.0.main_norm.weight")?,
1756 norm: me.tensor_f32_dev(last, &format!("{last_p}.norm.weight"))?,
1757 markov_w1,
1758 markov_w2,
1759 markov_w1_host: w1,
1760 conf_w: me
1761 .tensor_f32_dev(last, &format!("{last_p}.confidence_head.proj.weight"))?,
1762 hc_head_fn: me.tensor_f32_dev(last, &format!("{last_p}.hc_head_fn"))?,
1763 hc_head_base: me.model.tensor_f32(&format!("{last_p}.hc_head_base")).1,
1764 hc_head_scale: me.model.tensor_f32(&format!("{last_p}.hc_head_scale")).1,
1765 block_size: cfg.block_size,
1766 noise_token: cfg.noise_token_id,
1767 targets: cfg.target_layer_ids.clone(),
1768 rank: cfg.markov_rank,
1769 vocab,
1770 };
1771 eprintln!(
1772 "[load] drafter: DSpark ({} blocks, block_size {}, targets {:?}) \
1773 resident on stage {last}",
1774 cfg.n_blocks, cfg.block_size, cfg.target_layer_ids
1775 );
1776 me.dspark = Some(dspark);
1777 } else {
1778 eprintln!(
1779 "[load] drafter: {nextn} DSpark block(s) (mtp.0.e_proj absent) — GPU \
1780 drafter path off (set MEMRA_DSV4_DRAFTER=dspark); trunk-only"
1781 );
1782 }
1783 }
1784 for st in &me.stages {
1785 st.gpu.stream().synchronize().map_err(e("load sync"))?;
1786 }
1787 Ok(me)
1788 }
1789
1790 pub fn vram_report(&self) -> Res<Vec<(usize, u64, u64, u64)>> {
1792 let mut out = Vec::new();
1793 for st in &self.stages {
1794 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx"))?;
1795 let (free, total) = st.gpu.ctx.mem_get_info().map_err(e("mem_get_info"))?;
1796 out.push((st.dev, free as u64, total as u64, st.loaded_bytes));
1797 }
1798 Ok(out)
1799 }
1800
1801 #[allow(clippy::too_many_arguments)]
1806 fn gemm(
1807 st: &Stage,
1808 x_f32: &CudaSlice<f32>,
1809 w_bf16: &CudaSlice<u8>,
1810 w_off_elems: usize,
1811 m: usize,
1812 n: usize,
1813 kdim: usize,
1814 y: &mut CudaSlice<f32>,
1815 ) -> Res<()> {
1816 let stream = st.gpu.stream();
1817 let mut xb = stream
1818 .alloc_zeros::<u8>(m * kdim * 2)
1819 .map_err(e("alloc xb"))?;
1820 unsafe {
1821 ck(
1822 "cvt_bf16",
1823 k::memra_dsv4_cvt_bf16(
1824 dpf!(x_f32, &stream),
1825 xb.device_ptr_mut(&stream).0 as *mut c_void,
1826 (m * kdim) as i64,
1827 sp(&stream),
1828 ),
1829 )?;
1830 ck(
1831 "gemm_bf16",
1832 k::memra_dsv4_gemm_bf16(
1833 (w_bf16.device_ptr(&stream).0 as usize + w_off_elems * 2) as *const c_void,
1834 dp!(xb, &stream),
1835 dpm!(y, &stream),
1836 m as i32,
1837 n as i32,
1838 kdim as i32,
1839 st.dev as i32,
1840 st.ws.device_ptr(&stream).0 as *mut c_void,
1841 st.ws.len(),
1842 sp(&stream),
1843 ),
1844 )?;
1845 }
1846 Ok(())
1847 }
1848
1849 #[allow(clippy::too_many_arguments)]
1851 fn gemm_pre(
1852 st: &Stage,
1853 xb: &CudaSlice<u8>,
1854 w_bf16_ptr: *const c_void,
1855 m: usize,
1856 n: usize,
1857 kdim: usize,
1858 y: &mut CudaSlice<f32>,
1859 ) -> Res<()> {
1860 let stream = st.gpu.stream();
1861 unsafe {
1862 ck(
1863 "gemm_bf16",
1864 k::memra_dsv4_gemm_bf16(
1865 w_bf16_ptr,
1866 dp!(xb, &stream),
1867 dpm!(y, &stream),
1868 m as i32,
1869 n as i32,
1870 kdim as i32,
1871 st.dev as i32,
1872 st.ws.device_ptr(&stream).0 as *mut c_void,
1873 st.ws.len(),
1874 sp(&stream),
1875 ),
1876 )?;
1877 }
1878 Ok(())
1879 }
1880
1881 fn dots(
1883 st: &Stage,
1884 x: &CudaSlice<f32>,
1885 w_f32: &CudaSlice<f32>,
1886 s: usize,
1887 kdim: usize,
1888 n: usize,
1889 y: &mut CudaSlice<f32>,
1890 ) -> Res<()> {
1891 let stream = st.gpu.stream();
1892 unsafe {
1893 ck(
1894 "dots_f32",
1895 k::memra_dsv4_dots_f32(
1896 dpf!(x, &stream),
1897 dp!(w_f32, &stream),
1898 0,
1899 dpm!(y, &stream),
1900 s as i32,
1901 kdim as i32,
1902 n as i32,
1903 sp(&stream),
1904 ),
1905 )?;
1906 }
1907 Ok(())
1908 }
1909
1910 fn dots_dev(
1915 &self,
1916 st: &Stage,
1917 x: &CudaSlice<f32>,
1918 w_f32: &CudaSlice<f32>,
1919 s: usize,
1920 kdim: usize,
1921 n: usize,
1922 y: &mut CudaSlice<f32>,
1923 ) -> Res<()> {
1924 if !self.dots_f32 {
1925 return Self::dots(st, x, w_f32, s, kdim, n, y);
1926 }
1927 let stream = st.gpu.stream();
1928 unsafe {
1929 ck(
1930 "dots_f32acc",
1931 k::memra_dsv4_dots_f32acc(
1932 dpf!(x, &stream),
1933 dp!(w_f32, &stream),
1934 0,
1935 dpm!(y, &stream),
1936 s as i32,
1937 kdim as i32,
1938 n as i32,
1939 sp(&stream),
1940 ),
1941 )?;
1942 }
1943 Ok(())
1944 }
1945
1946 #[allow(clippy::too_many_arguments)]
1951 fn compressor(
1952 &self,
1953 st: &Stage,
1954 cmp: &CmpDev,
1955 x: &CudaSlice<f32>, s: usize,
1957 hidden: usize,
1958 fc_dev: &CudaSlice<f32>,
1959 rd: usize,
1960 eps: f32,
1961 ) -> Res<(
1962 Option<(CudaSlice<f32>, usize)>,
1963 CudaSlice<f32>,
1964 CudaSlice<f32>,
1965 )> {
1966 let stream = st.gpu.stream();
1967 let mut kv = stream
1968 .alloc_zeros::<f32>(s * cmp.latent)
1969 .map_err(e("cmp kv"))?;
1970 let mut score = stream
1971 .alloc_zeros::<f32>(s * cmp.latent)
1972 .map_err(e("cmp score"))?;
1973 Self::dots(st, x, &cmp.wkv, s, hidden, cmp.latent, &mut kv)?;
1974 Self::dots(st, x, &cmp.wgate, s, hidden, cmp.latent, &mut score)?;
1975 if s < cmp.ratio {
1976 return Ok((None, kv, score));
1977 }
1978 let cutoff = s - s % cmp.ratio;
1979 let nb = cutoff / cmp.ratio;
1980 let mut pooled = stream
1981 .alloc_zeros::<f32>(nb * cmp.d)
1982 .map_err(e("cmp out"))?;
1983 unsafe {
1984 ck(
1985 "compressor_pool",
1986 k::memra_dsv4_compressor_pool(
1987 dpf!(kv, &stream),
1988 dpf!(score, &stream),
1989 dpf!(cmp.ape, &stream),
1990 dpm!(pooled, &stream),
1991 nb as i32,
1992 cmp.ratio as i32,
1993 cmp.d as i32,
1994 cmp.latent as i32,
1995 cmp.overlap as i32,
1996 sp(&stream),
1997 ),
1998 )?;
1999 ck(
2000 "rmsnorm cmp",
2001 k::memra_dsv4_rmsnorm(
2002 dpf!(pooled, &stream),
2003 dpf!(cmp.norm, &stream),
2004 dpm!(pooled, &stream),
2005 nb as i32,
2006 cmp.d as i32,
2007 eps,
2008 sp(&stream),
2009 ),
2010 )?;
2011 let positions: Vec<i32> = (0..nb).map(|j| (j * cmp.ratio) as i32).collect();
2012 let pos_dev = upload_i32(&stream, &positions)?;
2013 ck(
2014 "rope cmp",
2015 k::memra_dsv4_rope(
2016 dpm!(pooled, &stream),
2017 nb as i32,
2018 1,
2019 cmp.d as i32,
2020 rd as i32,
2021 dpf!(fc_dev, &stream),
2022 pos_dev.device_ptr(&stream).0 as *const i32,
2023 0,
2024 sp(&stream),
2025 ),
2026 )?;
2027 if cmp.rotate {
2028 let scale = (cmp.d as f32).powf(-0.5);
2030 ck(
2031 "hadamard cmp",
2032 k::memra_dsv4_hadamard(
2033 dpm!(pooled, &stream),
2034 nb as i32,
2035 cmp.d as i32,
2036 scale,
2037 sp(&stream),
2038 ),
2039 )?;
2040 ck(
2041 "fp4 cmp",
2042 k::memra_dsv4_fp4_act_quant(
2043 dpm!(pooled, &stream),
2044 nb as i32,
2045 cmp.d as i64,
2046 cmp.d as i32,
2047 sp(&stream),
2048 ),
2049 )?;
2050 } else {
2051 ck(
2052 "act_quant cmp",
2053 k::memra_dsv4_act_quant(
2054 dpm!(pooled, &stream),
2055 nb as i32,
2056 cmp.d as i64,
2057 (cmp.d - rd) as i32,
2058 64,
2059 (self.variant == ActQuantVariant::ClampOnly) as i32,
2060 sp(&stream),
2061 ),
2062 )?;
2063 }
2064 }
2065 Ok((Some((pooled, nb)), kv, score))
2066 }
2067
2068 #[allow(clippy::too_many_arguments)]
2073 fn populate_cmp_cache(
2074 stream: &std::sync::Arc<CudaStream>,
2075 s: usize,
2076 cmp_ratio: usize,
2077 latent: usize,
2078 d: usize,
2079 pooled: &Option<(CudaSlice<f32>, usize)>,
2080 kv_raw: &CudaSlice<f32>,
2081 score_raw: &CudaSlice<f32>,
2082 store: &mut CudaSlice<f32>,
2083 row0: usize,
2084 blocks: &mut usize,
2085 pend_kv: &mut CudaSlice<f32>,
2086 pend_score: &mut CudaSlice<f32>,
2087 overlap: bool,
2088 ) -> Res<()> {
2089 *blocks = 0;
2090 if let Some((buf, nb)) = pooled {
2091 let src = buf.slice(0..nb * d);
2092 let mut dst = store.slice_mut(row0 * d..(row0 + nb) * d);
2093 stream.memcpy_dtod(&src, &mut dst).map_err(e("cmp store"))?;
2094 *blocks = *nb;
2095 }
2096 let cutoff = s - s % cmp_ratio;
2097 let rem = s - cutoff;
2098 if overlap {
2099 if cutoff >= cmp_ratio {
2100 let a = (cutoff - cmp_ratio) * latent;
2101 let b = cutoff * latent;
2102 let src = kv_raw.slice(a..b);
2103 let mut dst = pend_kv.slice_mut(0..cmp_ratio * latent);
2104 stream
2105 .memcpy_dtod(&src, &mut dst)
2106 .map_err(e("pend kv prev"))?;
2107 let src = score_raw.slice(a..b);
2108 let mut dst = pend_score.slice_mut(0..cmp_ratio * latent);
2109 stream
2110 .memcpy_dtod(&src, &mut dst)
2111 .map_err(e("pend sc prev"))?;
2112 }
2113 if rem > 0 {
2114 let a = cutoff * latent;
2115 let src = kv_raw.slice(a..s * latent);
2116 let mut dst = pend_kv.slice_mut(cmp_ratio * latent..(cmp_ratio + rem) * latent);
2117 stream
2118 .memcpy_dtod(&src, &mut dst)
2119 .map_err(e("pend kv cur"))?;
2120 let src = score_raw.slice(a..s * latent);
2121 let mut dst = pend_score.slice_mut(cmp_ratio * latent..(cmp_ratio + rem) * latent);
2122 stream
2123 .memcpy_dtod(&src, &mut dst)
2124 .map_err(e("pend sc cur"))?;
2125 }
2126 } else if rem > 0 {
2127 let a = cutoff * latent;
2128 let src = kv_raw.slice(a..s * latent);
2129 let mut dst = pend_kv.slice_mut(0..rem * latent);
2130 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend kv"))?;
2131 let src = score_raw.slice(a..s * latent);
2132 let mut dst = pend_score.slice_mut(0..rem * latent);
2133 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend sc"))?;
2134 }
2135 Ok(())
2136 }
2137
2138 #[allow(clippy::too_many_arguments)]
2141 fn hc_pre(
2142 st: &Stage,
2143 h: &CudaSlice<f32>, fn_w: &CudaSlice<f32>,
2145 base: &[f32],
2146 scale: &[f32],
2147 s: usize,
2148 hc: usize,
2149 hidden: usize,
2150 iters: u32,
2151 hc_eps: f32,
2152 ) -> Res<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)> {
2153 let stream = st.gpu.stream();
2154 let w = hc * hidden;
2155 let rows = (2 + hc) * hc;
2156 let mut mixes = stream.alloc_zeros::<f32>(s * rows).map_err(e("mixes"))?;
2157 Self::dots(st, h, fn_w, s, w, rows, &mut mixes)?;
2158 unsafe {
2159 ck(
2160 "rowsq_scale",
2161 k::memra_dsv4_rowsq_scale(
2162 dpf!(h, &stream),
2163 dpm!(mixes, &stream),
2164 s as i32,
2165 w as i32,
2166 rows as i32,
2167 hc_eps,
2168 sp(&stream),
2169 ),
2170 )?;
2171 }
2172 let mixes_h = dtoh_f32(&stream, &mixes)?;
2173 let (pre, post, comb) = hc_split_sinkhorn(&mixes_h, s, hc, scale, base, iters, hc_eps);
2174 let pre_d = upload_f32(&stream, &pre)?;
2175 let post_d = upload_f32(&stream, &post)?;
2176 let comb_d = upload_f32(&stream, &comb)?;
2177 let mut y = stream.alloc_zeros::<f32>(s * hidden).map_err(e("hc y"))?;
2178 unsafe {
2179 ck(
2180 "hc_collapse",
2181 k::memra_dsv4_hc_collapse(
2182 dpf!(h, &stream),
2183 dpf!(pre_d, &stream),
2184 dpm!(y, &stream),
2185 s as i32,
2186 hc as i32,
2187 hidden as i32,
2188 sp(&stream),
2189 ),
2190 )?;
2191 }
2192 Ok((y, post_d, comb_d))
2193 }
2194
2195 #[allow(clippy::too_many_arguments)]
2197 fn route_host(
2198 layer: &LayerDev,
2199 raw_scores: &[f32], ids: &[u32],
2201 s: usize,
2202 ne: usize,
2203 topk: usize,
2204 route_scale: f32,
2205 ) -> (Vec<usize>, Vec<f32>) {
2206 let mut scores = raw_scores.to_vec();
2207 for v in &mut scores {
2208 *v = softplus_f32(*v).sqrt();
2209 }
2210 let mut indices = vec![0usize; s * topk];
2211 if let Some(tid2eid) = &layer.tid2eid {
2212 for t in 0..s {
2213 let row = &tid2eid[ids[t] as usize * topk..(ids[t] as usize + 1) * topk];
2214 let mut seen = std::collections::BTreeSet::new();
2215 for (kk, &ex) in row.iter().enumerate() {
2216 assert!(
2217 (0..ne as i64).contains(&ex),
2218 "layer {}: tid2eid out of range",
2219 layer.il
2220 );
2221 assert!(
2222 seen.insert(ex),
2223 "layer {}: duplicate expert id in tid2eid row {}",
2224 layer.il,
2225 ids[t]
2226 );
2227 indices[t * topk + kk] = ex as usize;
2228 }
2229 }
2230 } else {
2231 let bias = layer.gate_bias.as_ref().expect("score layer needs bias");
2232 for t in 0..s {
2233 let biased: Vec<f32> = (0..ne).map(|ex| scores[t * ne + ex] + bias[ex]).collect();
2234 let mut order: Vec<usize> = (0..ne).collect();
2235 order.sort_by(|&a, &b| {
2236 biased[b]
2237 .partial_cmp(&biased[a])
2238 .unwrap_or(std::cmp::Ordering::Equal)
2239 .then(a.cmp(&b))
2240 });
2241 for kk in 0..topk {
2242 indices[t * topk + kk] = order[kk];
2243 }
2244 }
2245 }
2246 let mut weights = vec![0f32; s * topk];
2247 for t in 0..s {
2248 let mut sum = 0f32;
2249 for kk in 0..topk {
2250 let w = scores[t * ne + indices[t * topk + kk]];
2251 weights[t * topk + kk] = w;
2252 sum += w;
2253 }
2254 for kk in 0..topk {
2255 weights[t * topk + kk] = weights[t * topk + kk] / sum * route_scale;
2256 }
2257 }
2258 (indices, weights)
2259 }
2260
2261 #[allow(clippy::too_many_arguments)]
2264 fn block_forward(
2265 &self,
2266 st: &Stage,
2267 layer: &LayerDev,
2268 h: &CudaSlice<f32>,
2269 s: usize,
2270 ids: &[u32],
2271 mut capture: Option<&mut GpuCapture>,
2272 mut cache: Option<&mut LayerCache>,
2273 ) -> Res<CudaSlice<f32>> {
2274 let d = self.model.cfg();
2275 let mc = &self.model.mc;
2276 let hc = d.hc_mult as usize;
2277 let hidden = mc.n_embd as usize;
2278 let heads = mc.n_head as usize;
2279 let hd = d.head_dim as usize;
2280 let rd = d.qk_rope_head_dim as usize;
2281 let q_lora = d.q_lora_rank as usize;
2282 let win = d.sliding_window as usize;
2283 let o_groups = d.o_groups as usize;
2284 let o_lora = d.o_lora_rank as usize;
2285 let eps = mc.rms_eps;
2286 let iters = d.hc_sinkhorn_iters;
2287 let hc_eps = d.hc_eps;
2288 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx"))?;
2292 let stream = st.gpu.stream();
2293 let fc_dev = if layer.ratio != 0 {
2294 &st.fc_yarn
2295 } else {
2296 &st.fc_plain
2297 };
2298 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
2299
2300 let (y, post, comb) = Self::hc_pre(
2302 st,
2303 h,
2304 &layer.hc_attn_fn,
2305 &layer.hc_attn_base,
2306 &layer.hc_attn_scale,
2307 s,
2308 hc,
2309 hidden,
2310 iters,
2311 hc_eps,
2312 )?;
2313 let mut x = stream.alloc_zeros::<f32>(s * hidden).map_err(e("x"))?;
2314 unsafe {
2315 ck(
2316 "rmsnorm attn",
2317 k::memra_dsv4_rmsnorm(
2318 dpf!(y, &stream),
2319 dpf!(layer.attn_norm, &stream),
2320 dpm!(x, &stream),
2321 s as i32,
2322 hidden as i32,
2323 eps,
2324 sp(&stream),
2325 ),
2326 )?;
2327 }
2328
2329 let wq_a_v = layer.wq_a.staged(&stream)?;
2334 let mut qr = stream.alloc_zeros::<f32>(s * q_lora).map_err(e("qr"))?;
2335 Self::gemm(st, &x, wq_a_v.slab(), 0, s, q_lora, hidden, &mut qr)?;
2336 unsafe {
2337 ck(
2338 "rmsnorm q",
2339 k::memra_dsv4_rmsnorm(
2340 dpf!(qr, &stream),
2341 dpf!(layer.q_norm, &stream),
2342 dpm!(qr, &stream),
2343 s as i32,
2344 q_lora as i32,
2345 eps,
2346 sp(&stream),
2347 ),
2348 )?;
2349 }
2350 let mut qr_b = stream
2352 .alloc_zeros::<u8>(s * q_lora * 2)
2353 .map_err(e("qr_b"))?;
2354 unsafe {
2355 ck(
2356 "cvt qr",
2357 k::memra_dsv4_cvt_bf16(
2358 dpf!(qr, &stream),
2359 qr_b.device_ptr_mut(&stream).0 as *mut c_void,
2360 (s * q_lora) as i64,
2361 sp(&stream),
2362 ),
2363 )?;
2364 }
2365 let wq_b_v = layer.wq_b.staged(&stream)?;
2366 let mut q = stream.alloc_zeros::<f32>(s * heads * hd).map_err(e("q"))?;
2367 Self::gemm_pre(
2368 st,
2369 &qr_b,
2370 wq_b_v.slab().device_ptr(&stream).0 as *const c_void,
2371 s,
2372 heads * hd,
2373 q_lora,
2374 &mut q,
2375 )?;
2376 let positions: Vec<i32> = (0..s as i32).collect();
2377 let pos_dev = upload_i32(&stream, &positions)?;
2378 unsafe {
2379 ck(
2380 "headrms",
2381 k::memra_dsv4_headrms(
2382 dpm!(q, &stream),
2383 (s * heads) as i32,
2384 hd as i32,
2385 eps,
2386 sp(&stream),
2387 ),
2388 )?;
2389 ck(
2390 "rope q",
2391 k::memra_dsv4_rope(
2392 dpm!(q, &stream),
2393 s as i32,
2394 heads as i32,
2395 hd as i32,
2396 rd as i32,
2397 dpf!(fc_dev, &stream),
2398 pos_dev.device_ptr(&stream).0 as *const i32,
2399 0,
2400 sp(&stream),
2401 ),
2402 )?;
2403 }
2404
2405 let wkv_v = layer.wkv.staged(&stream)?;
2407 let mut kv = stream.alloc_zeros::<f32>(s * hd).map_err(e("kv"))?;
2408 Self::gemm(st, &x, wkv_v.slab(), 0, s, hd, hidden, &mut kv)?;
2409 unsafe {
2410 ck(
2411 "rmsnorm kv",
2412 k::memra_dsv4_rmsnorm(
2413 dpf!(kv, &stream),
2414 dpf!(layer.kv_norm, &stream),
2415 dpm!(kv, &stream),
2416 s as i32,
2417 hd as i32,
2418 eps,
2419 sp(&stream),
2420 ),
2421 )?;
2422 ck(
2423 "rope kv",
2424 k::memra_dsv4_rope(
2425 dpm!(kv, &stream),
2426 s as i32,
2427 1,
2428 hd as i32,
2429 rd as i32,
2430 dpf!(fc_dev, &stream),
2431 pos_dev.device_ptr(&stream).0 as *const i32,
2432 0,
2433 sp(&stream),
2434 ),
2435 )?;
2436 ck(
2437 "act_quant kv",
2438 k::memra_dsv4_act_quant(
2439 dpm!(kv, &stream),
2440 s as i32,
2441 hd as i64,
2442 (hd - rd) as i32,
2443 64,
2444 clamp_only,
2445 sp(&stream),
2446 ),
2447 )?;
2448 }
2449 if let Some(c) = cache.as_deref_mut() {
2453 for p in s.saturating_sub(win)..s {
2454 let slot = p % win;
2455 let src = kv.slice(p * hd..(p + 1) * hd);
2456 let mut dst = c.kvc.slice_mut(slot * hd..(slot + 1) * hd);
2457 stream.memcpy_dtod(&src, &mut dst).map_err(e("ring copy"))?;
2458 }
2459 }
2460
2461 let (widx, wslots) = window_topk_idxs(win, s);
2463 let mut idxs: Vec<i64> = widx;
2464 let mut slots = wslots;
2465 let mut n_kv = s;
2466 let mut kv_full = kv;
2467 let mut cap_cmp: Option<(Vec<f32>, usize)> = None;
2468 let mut cap_ikv: Option<(Vec<f32>, usize)> = None;
2469 let mut cap_isc: Option<(Vec<f32>, usize)> = None;
2470 let want_cap = capture
2471 .as_ref()
2472 .map(|c| c.want.contains(&layer.il))
2473 .unwrap_or(false);
2474 if layer.ratio != 0 {
2475 let offset = s;
2476 let (cidx, cslots) = if let Some(ix) = &layer.idx {
2477 let mut qi = stream
2479 .alloc_zeros::<f32>(s * ix.heads * ix.hd)
2480 .map_err(e("qi"))?;
2481 let iwq_b_v = ix.wq_b.staged(&stream)?;
2482 Self::gemm_pre(
2483 st,
2484 &qr_b,
2485 iwq_b_v.slab().device_ptr(&stream).0 as *const c_void,
2486 s,
2487 ix.heads * ix.hd,
2488 q_lora,
2489 &mut qi,
2490 )?;
2491 unsafe {
2492 ck(
2493 "rope qi",
2494 k::memra_dsv4_rope(
2495 dpm!(qi, &stream),
2496 s as i32,
2497 ix.heads as i32,
2498 ix.hd as i32,
2499 rd as i32,
2500 dpf!(fc_dev, &stream),
2501 pos_dev.device_ptr(&stream).0 as *const i32,
2502 0,
2503 sp(&stream),
2504 ),
2505 )?;
2506 let scale = (ix.hd as f32).powf(-0.5);
2507 ck(
2508 "hadamard qi",
2509 k::memra_dsv4_hadamard(
2510 dpm!(qi, &stream),
2511 (s * ix.heads) as i32,
2512 ix.hd as i32,
2513 scale,
2514 sp(&stream),
2515 ),
2516 )?;
2517 ck(
2518 "fp4 qi",
2519 k::memra_dsv4_fp4_act_quant(
2520 dpm!(qi, &stream),
2521 (s * ix.heads) as i32,
2522 ix.hd as i64,
2523 ix.hd as i32,
2524 sp(&stream),
2525 ),
2526 )?;
2527 }
2528 let (ckv_i, ikv_raw, isc_raw) =
2530 self.compressor(st, &ix.cmp, &x, s, hidden, fc_dev, rd, eps)?;
2531 if want_cap {
2532 if let Some((buf, nb)) = &ckv_i {
2533 cap_ikv = Some((dtoh_f32(&stream, buf)?, *nb));
2534 }
2535 }
2536 if let Some(c) = cache.as_deref_mut() {
2537 let mut i_blocks = c.i_blocks;
2538 Self::populate_cmp_cache(
2539 &stream,
2540 s,
2541 ix.cmp.ratio,
2542 ix.cmp.latent,
2543 ix.cmp.d,
2544 &ckv_i,
2545 &ikv_raw,
2546 &isc_raw,
2547 c.ikvc.as_mut().expect("fine layer has indexer store"),
2548 0,
2549 &mut i_blocks,
2550 c.ipend_kv.as_mut().expect("ipend"),
2551 c.ipend_score.as_mut().expect("ipend"),
2552 ix.cmp.overlap,
2553 )?;
2554 c.i_blocks = i_blocks;
2555 }
2556 let iwp_v = ix.weights_proj.staged(&stream)?;
2558 let mut wproj = stream.alloc_zeros::<f32>(s * ix.heads).map_err(e("wp"))?;
2559 Self::gemm(st, &x, iwp_v.slab(), 0, s, ix.heads, hidden, &mut wproj)?;
2560 if let Some((ckv, nb)) = &ckv_i {
2561 let wscale = ((ix.hd as f64).powf(-0.5) * (ix.heads as f64).powf(-0.5)) as f32;
2562 let mut score = stream.alloc_zeros::<f32>(s * nb).map_err(e("iscore"))?;
2563 unsafe {
2564 ck(
2565 "indexer_score",
2566 k::memra_dsv4_indexer_score(
2567 dpf!(qi, &stream),
2568 dpf!(ckv, &stream),
2569 dpf!(wproj, &stream),
2570 wscale,
2571 dpm!(score, &stream),
2572 s as i32,
2573 ix.heads as i32,
2574 ix.hd as i32,
2575 *nb as i32,
2576 layer.ratio as i32,
2577 -1, sp(&stream),
2579 ),
2580 )?;
2581 }
2582 let score_h = dtoh_f32(&stream, &score)?;
2583 if want_cap {
2584 cap_isc = Some((score_h.clone(), *nb));
2585 }
2586 let kk = ix.topk.min(*nb);
2588 let mut cidx = vec![-1i64; s * kk];
2589 for t in 0..s {
2590 let lim = (t + 1) / layer.ratio;
2591 let mut order: Vec<usize> = (0..*nb).collect();
2592 order.sort_by(|&a, &b| {
2593 score_h[t * nb + b]
2594 .partial_cmp(&score_h[t * nb + a])
2595 .unwrap_or(std::cmp::Ordering::Equal)
2596 .then(a.cmp(&b))
2597 });
2598 for (slot, &j) in order.iter().take(kk).enumerate() {
2599 cidx[t * kk + slot] = if j >= lim { -1 } else { (j + offset) as i64 };
2600 }
2601 }
2602 (cidx, kk)
2603 } else {
2604 (Vec::new(), 0)
2605 }
2606 } else {
2607 compress_topk_idxs(layer.ratio, s, offset)
2608 };
2609 if cslots > 0 {
2610 let mut merged = vec![-1i64; s * (slots + cslots)];
2611 for t in 0..s {
2612 merged[t * (slots + cslots)..t * (slots + cslots) + slots]
2613 .copy_from_slice(&idxs[t * slots..(t + 1) * slots]);
2614 merged[t * (slots + cslots) + slots..(t + 1) * (slots + cslots)]
2615 .copy_from_slice(&cidx[t * cslots..(t + 1) * cslots]);
2616 }
2617 idxs = merged;
2618 slots += cslots;
2619 }
2620 let acmp = layer.cmp.as_ref().expect("ratio!=0 has compressor");
2622 let (ckv, akv_raw, asc_raw) =
2623 self.compressor(st, acmp, &x, s, hidden, fc_dev, rd, eps)?;
2624 if want_cap {
2625 if let Some((buf, nb)) = &ckv {
2626 cap_cmp = Some((dtoh_f32(&stream, buf)?, *nb));
2627 }
2628 }
2629 if let Some(c) = cache.as_deref_mut() {
2630 let mut n_blocks = c.n_blocks;
2631 Self::populate_cmp_cache(
2632 &stream,
2633 s,
2634 acmp.ratio,
2635 acmp.latent,
2636 acmp.d,
2637 &ckv,
2638 &akv_raw,
2639 &asc_raw,
2640 &mut c.kvc,
2641 win,
2642 &mut n_blocks,
2643 c.pend_kv.as_mut().expect("pend"),
2644 c.pend_score.as_mut().expect("pend"),
2645 acmp.overlap,
2646 )?;
2647 c.n_blocks = n_blocks;
2648 }
2649 if let Some((ckv_buf, nb)) = ckv {
2650 let mut merged_kv = stream
2651 .alloc_zeros::<f32>((s + nb) * hd)
2652 .map_err(e("kv_full"))?;
2653 {
2654 let mut head_view = merged_kv.slice_mut(0..s * hd);
2655 stream
2656 .memcpy_dtod(&kv_full.slice(0..s * hd), &mut head_view)
2657 .map_err(e("kv copy"))?;
2658 }
2659 {
2660 let mut tail = merged_kv.slice_mut(s * hd..(s + nb) * hd);
2661 stream
2662 .memcpy_dtod(&ckv_buf.slice(0..nb * hd), &mut tail)
2663 .map_err(e("ckv copy"))?;
2664 }
2665 kv_full = merged_kv;
2666 n_kv += nb;
2667 }
2668 }
2669 let _ = n_kv;
2670 let idxs_i32: Vec<i32> = idxs.iter().map(|&v| v as i32).collect();
2671 let idx_dev = upload_i32(&stream, &idxs_i32)?;
2672
2673 let mut o = stream.alloc_zeros::<f32>(s * heads * hd).map_err(e("o"))?;
2675 let scale = (hd as f64).powf(-0.5) as f32;
2676 unsafe {
2677 ck(
2678 "sink_attn",
2679 k::memra_dsv4_sink_attn(
2680 dpf!(q, &stream),
2681 dpf!(kv_full, &stream),
2682 idx_dev.device_ptr(&stream).0 as *const i32,
2683 dpf!(layer.sink, &stream),
2684 dpm!(o, &stream),
2685 s as i32,
2686 heads as i32,
2687 hd as i32,
2688 slots as i32,
2689 scale,
2690 sp(&stream),
2691 ),
2692 )?;
2693 ck(
2694 "rope o inv",
2695 k::memra_dsv4_rope(
2696 dpm!(o, &stream),
2697 s as i32,
2698 heads as i32,
2699 hd as i32,
2700 rd as i32,
2701 dpf!(fc_dev, &stream),
2702 pos_dev.device_ptr(&stream).0 as *const i32,
2703 1,
2704 sp(&stream),
2705 ),
2706 )?;
2707 }
2708
2709 let gw = heads / o_groups * hd;
2711 let mut og = stream
2712 .alloc_zeros::<f32>(s * o_groups * o_lora)
2713 .map_err(e("og"))?;
2714 let mut o_grp = stream.alloc_zeros::<f32>(s * gw).map_err(e("o_grp"))?;
2715 let mut y_grp = stream.alloc_zeros::<f32>(s * o_lora).map_err(e("y_grp"))?;
2716 let wo_a_v = layer.wo_a.staged(&stream)?; for g in 0..o_groups {
2718 unsafe {
2719 ck(
2720 "take_cols",
2721 k::memra_dsv4_take_cols(
2722 dpf!(o, &stream),
2723 dpm!(o_grp, &stream),
2724 s as i32,
2725 gw as i32,
2726 (heads * hd) as i64,
2727 (g * gw) as i64,
2728 sp(&stream),
2729 ),
2730 )?;
2731 }
2732 Self::gemm(
2733 st,
2734 &o_grp,
2735 wo_a_v.slab(),
2736 g * o_lora * gw,
2737 s,
2738 o_lora,
2739 gw,
2740 &mut y_grp,
2741 )?;
2742 unsafe {
2743 ck(
2744 "place_cols",
2745 k::memra_dsv4_place_cols(
2746 dpf!(y_grp, &stream),
2747 dpm!(og, &stream),
2748 s as i32,
2749 o_lora as i32,
2750 (o_groups * o_lora) as i64,
2751 (g * o_lora) as i64,
2752 sp(&stream),
2753 ),
2754 )?;
2755 }
2756 }
2757 let wo_b_v = layer.wo_b.staged(&stream)?;
2758 let mut attn_out = stream.alloc_zeros::<f32>(s * hidden).map_err(e("ao"))?;
2759 Self::gemm(
2760 st,
2761 &og,
2762 wo_b_v.slab(),
2763 0,
2764 s,
2765 hidden,
2766 o_groups * o_lora,
2767 &mut attn_out,
2768 )?;
2769
2770 let mut cap_attn: Option<Vec<f32>> = None;
2771 if want_cap {
2772 cap_attn = Some(dtoh_f32(&stream, &attn_out)?);
2773 }
2774
2775 let mut h2 = stream
2777 .alloc_zeros::<f32>(s * hc * hidden)
2778 .map_err(e("h2"))?;
2779 unsafe {
2780 ck(
2781 "hc_post attn",
2782 k::memra_dsv4_hc_post(
2783 dpf!(attn_out, &stream),
2784 dpf!(h, &stream),
2785 dpf!(post, &stream),
2786 dpf!(comb, &stream),
2787 dpm!(h2, &stream),
2788 s as i32,
2789 hc as i32,
2790 hidden as i32,
2791 sp(&stream),
2792 ),
2793 )?;
2794 }
2795
2796 let (y2, post2, comb2) = Self::hc_pre(
2798 st,
2799 &h2,
2800 &layer.hc_ffn_fn,
2801 &layer.hc_ffn_base,
2802 &layer.hc_ffn_scale,
2803 s,
2804 hc,
2805 hidden,
2806 iters,
2807 hc_eps,
2808 )?;
2809 let mut xf = stream.alloc_zeros::<f32>(s * hidden).map_err(e("xf"))?;
2810 unsafe {
2811 ck(
2812 "rmsnorm ffn",
2813 k::memra_dsv4_rmsnorm(
2814 dpf!(y2, &stream),
2815 dpf!(layer.ffn_norm, &stream),
2816 dpm!(xf, &stream),
2817 s as i32,
2818 hidden as i32,
2819 eps,
2820 sp(&stream),
2821 ),
2822 )?;
2823 }
2824 if let Some(c) = capture.as_deref_mut() {
2825 if c.want.contains(&layer.il) {
2826 c.moe_x.insert(layer.il, dtoh_f32(&stream, &xf)?);
2827 }
2828 }
2829 let moe_out = self.moe_forward(st, layer, &xf, s, ids)?;
2830 let mut h3 = stream
2831 .alloc_zeros::<f32>(s * hc * hidden)
2832 .map_err(e("h3"))?;
2833 unsafe {
2834 ck(
2835 "hc_post ffn",
2836 k::memra_dsv4_hc_post(
2837 dpf!(moe_out, &stream),
2838 dpf!(h2, &stream),
2839 dpf!(post2, &stream),
2840 dpf!(comb2, &stream),
2841 dpm!(h3, &stream),
2842 s as i32,
2843 hc as i32,
2844 hidden as i32,
2845 sp(&stream),
2846 ),
2847 )?;
2848 }
2849
2850 if let Some(c) = capture {
2851 if c.want.contains(&layer.il) {
2852 c.layer_out.insert(layer.il, dtoh_f32(&stream, &h3)?);
2853 c.x_dbg.insert(layer.il, dtoh_f32(&stream, &x)?);
2854 c.q_dbg.insert(layer.il, dtoh_f32(&stream, &q)?);
2855 {
2856 let mut kvh = vec![0f32; s * hd];
2857 stream
2858 .memcpy_dtoh(&kv_full.slice(0..s * hd), &mut kvh[..])
2859 .map_err(e("dtoh kv_dbg"))?;
2860 stream.synchronize().map_err(e("sync kv_dbg"))?;
2861 c.kv_dbg.insert(layer.il, kvh);
2862 }
2863 c.o_dbg.insert(layer.il, dtoh_f32(&stream, &o)?);
2864 if let Some(a) = cap_attn {
2865 c.attn_out.insert(layer.il, a);
2866 }
2867 if let Some(v) = cap_cmp {
2868 c.compressor_kv.insert(layer.il, v);
2869 }
2870 if let Some(v) = cap_ikv {
2871 c.indexer_kv.insert(layer.il, v);
2872 }
2873 if let Some(v) = cap_isc {
2874 c.index_score.insert(layer.il, v);
2875 }
2876 }
2877 }
2878 Ok(h3)
2879 }
2880
2881 fn moe_forward(
2885 &self,
2886 st: &Stage,
2887 layer: &LayerDev,
2888 x: &CudaSlice<f32>, s: usize,
2890 ids: &[u32],
2891 ) -> Res<CudaSlice<f32>> {
2892 let mc = &self.model.mc;
2893 let d = self.model.cfg();
2894 let moe = mc.moe.as_ref().expect("moe");
2895 let hidden = mc.n_embd as usize;
2896 let ne = moe.expert_count as usize;
2897 let topk = moe.expert_used_count as usize;
2898 let inter = moe.expert_ff_length as usize;
2899 let limit = d.swiglu_limit;
2900 let stream = st.gpu.stream();
2901
2902 let mut raw = stream.alloc_zeros::<f32>(s * ne).map_err(e("gate raw"))?;
2903 Self::dots(st, x, &layer.gate_w, s, hidden, ne, &mut raw)?;
2904 let raw_h = dtoh_f32(&stream, &raw)?;
2905 let (indices, weights) =
2906 Self::route_host(layer, &raw_h, ids, s, ne, topk, d.routed_scaling_factor);
2907
2908 let mut xb = stream
2910 .alloc_zeros::<u8>(s * hidden * 2)
2911 .map_err(e("xb moe"))?;
2912 unsafe {
2913 ck(
2914 "cvt moe x",
2915 k::memra_dsv4_cvt_bf16(
2916 dpf!(x, &stream),
2917 xb.device_ptr_mut(&stream).0 as *mut c_void,
2918 (s * hidden) as i64,
2919 sp(&stream),
2920 ),
2921 )?;
2922 }
2923 let mut y = stream.alloc_zeros::<f32>(s * hidden).map_err(e("moe y"))?;
2924
2925 let wbytes = inter * hidden / 2;
2926 let sbytes = match layer.expert_kind {
2927 ExpertKind::Nvfp4 => inter * hidden / 16,
2928 ExpertKind::Mxfp4 => inter * hidden / 32,
2929 };
2930 let mut uniq: Vec<usize> = indices.clone();
2931 uniq.sort_unstable();
2932 uniq.dedup();
2933 if self.expert_arm == ExpertArm::Native {
2934 let kind = match layer.expert_kind {
2939 ExpertKind::Nvfp4 => 0i32,
2940 ExpertKind::Mxfp4 => 1i32,
2941 };
2942 let kq_x = hidden / 128;
2943 let kq_h = inter / 128;
2944 let mut xq = stream.alloc_zeros::<u8>(s * hidden).map_err(e("xq"))?;
2945 let mut xs = stream.alloc_zeros::<f32>(s * kq_x).map_err(e("xs"))?;
2946 unsafe {
2947 ck(
2948 "act_quant_fp8 x",
2949 k::memra_dsv4_act_quant_fp8(
2950 dpf!(x, &stream),
2951 xq.device_ptr_mut(&stream).0 as *mut c_void,
2952 dpm!(xs, &stream),
2953 s as i32,
2954 hidden as i32,
2955 sp(&stream),
2956 ),
2957 )?;
2958 }
2959 let mut xgq = stream.alloc_zeros::<u8>(s * hidden).map_err(e("xgq"))?;
2960 let mut xgs = stream.alloc_zeros::<f32>(s * kq_x).map_err(e("xgs"))?;
2961 let mut g1 = stream.alloc_zeros::<f32>(s * inter).map_err(e("g1"))?;
2962 let mut g3 = stream.alloc_zeros::<f32>(s * inter).map_err(e("g3"))?;
2963 let mut hbuf = stream.alloc_zeros::<f32>(s * inter).map_err(e("hbuf"))?;
2964 let mut hq = stream.alloc_zeros::<u8>(s * inter).map_err(e("hq"))?;
2965 let mut hs = stream.alloc_zeros::<f32>(s * kq_h).map_err(e("hs"))?;
2966 let mut contrib = stream
2967 .alloc_zeros::<f32>(s * hidden)
2968 .map_err(e("contrib"))?;
2969 for &ex in &uniq {
2970 let toks: Vec<(usize, usize)> = (0..s * topk)
2971 .filter(|i| indices[*i] == ex)
2972 .map(|i| (i / topk, i % topk))
2973 .collect();
2974 let g = toks.len();
2975 let tok_rows: Vec<i32> = toks.iter().map(|&(t, _)| t as i32).collect();
2976 let wrow: Vec<f32> = toks.iter().map(|&(t, kk)| weights[t * topk + kk]).collect();
2977 let rows_dev = upload_i32(&stream, &tok_rows)?;
2978 let wrow_dev = upload_f32(&stream, &wrow)?;
2979 unsafe {
2980 ck(
2981 "gather xq",
2982 k::memra_dsv4_gather_rows_u8(
2983 dp!(xq, &stream),
2984 rows_dev.device_ptr(&stream).0 as *const i32,
2985 xgq.device_ptr_mut(&stream).0 as *mut c_void,
2986 g as i32,
2987 hidden as i64,
2988 sp(&stream),
2989 ),
2990 )?;
2991 ck(
2992 "gather xs",
2993 k::memra_dsv4_gather_rows_u8(
2994 xs.device_ptr(&stream).0 as *const c_void,
2995 rows_dev.device_ptr(&stream).0 as *const i32,
2996 xgs.device_ptr_mut(&stream).0 as *mut c_void,
2997 g as i32,
2998 (kq_x * 4) as i64,
2999 sp(&stream),
3000 ),
3001 )?;
3002 for (pi, dst) in [(0usize, &mut g1), (2usize, &mut g3)] {
3004 let woff = (ex * 3 + pi) * wbytes;
3005 let soff = (ex * 3 + pi) * sbytes;
3006 ck(
3007 "fp4_gemm w1/w3",
3008 k::memra_dsv4_fp4_gemm(
3009 dp!(xgq, &stream),
3010 dpf!(xgs, &stream),
3011 (layer.experts_w.device_ptr(&stream).0 as usize + woff)
3012 as *const c_void,
3013 (layer.experts_sc.device_ptr(&stream).0 as usize + soff)
3014 as *const c_void,
3015 layer.experts_s2[ex * 3 + pi],
3016 kind,
3017 dpm!(*dst, &stream),
3018 g as i32,
3019 inter as i32,
3020 hidden as i32,
3021 sp(&stream),
3022 ),
3023 )?;
3024 }
3025 ck(
3026 "swiglu",
3027 k::memra_dsv4_swiglu(
3028 dpf!(g1, &stream),
3029 dpf!(g3, &stream),
3030 dpm!(hbuf, &stream),
3031 g as i32,
3032 inter as i32,
3033 limit,
3034 wrow_dev.device_ptr(&stream).0 as *const f32,
3035 sp(&stream),
3036 ),
3037 )?;
3038 ck(
3039 "act_quant_fp8 h",
3040 k::memra_dsv4_act_quant_fp8(
3041 dpf!(hbuf, &stream),
3042 hq.device_ptr_mut(&stream).0 as *mut c_void,
3043 dpm!(hs, &stream),
3044 g as i32,
3045 inter as i32,
3046 sp(&stream),
3047 ),
3048 )?;
3049 let woff2 = (ex * 3 + 1) * wbytes;
3050 let soff2 = (ex * 3 + 1) * sbytes;
3051 ck(
3052 "fp4_gemm w2",
3053 k::memra_dsv4_fp4_gemm(
3054 dp!(hq, &stream),
3055 dpf!(hs, &stream),
3056 (layer.experts_w.device_ptr(&stream).0 as usize + woff2)
3057 as *const c_void,
3058 (layer.experts_sc.device_ptr(&stream).0 as usize + soff2)
3059 as *const c_void,
3060 layer.experts_s2[ex * 3 + 1],
3061 kind,
3062 dpm!(contrib, &stream),
3063 g as i32,
3064 hidden as i32,
3065 inter as i32,
3066 sp(&stream),
3067 ),
3068 )?;
3069 ck(
3070 "scatter",
3071 k::memra_dsv4_scatter_add(
3072 dpm!(y, &stream),
3073 dpf!(contrib, &stream),
3074 rows_dev.device_ptr(&stream).0 as *const i32,
3075 g as i32,
3076 hidden as i32,
3077 sp(&stream),
3078 ),
3079 )?;
3080 }
3081 }
3082 return self.moe_shared_and_finish(st, layer, &xb, s, y);
3083 }
3084 let mut xg = stream.alloc_zeros::<u8>(s * hidden * 2).map_err(e("xg"))?;
3086 let mut g1 = stream.alloc_zeros::<f32>(s * inter).map_err(e("g1"))?;
3087 let mut g3 = stream.alloc_zeros::<f32>(s * inter).map_err(e("g3"))?;
3088 let mut hbuf = stream.alloc_zeros::<f32>(s * inter).map_err(e("hbuf"))?;
3089 let mut hb = stream.alloc_zeros::<u8>(s * inter * 2).map_err(e("hb"))?;
3090 let mut contrib = stream
3091 .alloc_zeros::<f32>(s * hidden)
3092 .map_err(e("contrib"))?;
3093 for &ex in &uniq {
3094 let toks: Vec<(usize, usize)> = (0..s * topk)
3095 .filter(|i| indices[*i] == ex)
3096 .map(|i| (i / topk, i % topk))
3097 .collect();
3098 let g = toks.len();
3099 let tok_rows: Vec<i32> = toks.iter().map(|&(t, _)| t as i32).collect();
3100 let wrow: Vec<f32> = toks.iter().map(|&(t, kk)| weights[t * topk + kk]).collect();
3101 let rows_dev = upload_i32(&stream, &tok_rows)?;
3102 let wrow_dev = upload_f32(&stream, &wrow)?;
3103 unsafe {
3104 ck(
3105 "gather",
3106 k::memra_dsv4_gather_bf16(
3107 dp!(xb, &stream),
3108 rows_dev.device_ptr(&stream).0 as *const i32,
3109 xg.device_ptr_mut(&stream).0 as *mut c_void,
3110 g as i32,
3111 hidden as i32,
3112 sp(&stream),
3113 ),
3114 )?;
3115 for (pi, (rows, cols)) in [(inter, hidden), (hidden, inter), (inter, hidden)]
3117 .iter()
3118 .enumerate()
3119 {
3120 let woff = (ex * 3 + pi) * wbytes;
3121 let soff = (ex * 3 + pi) * sbytes;
3122 let wp =
3123 (layer.experts_w.device_ptr(&stream).0 as usize + woff) as *const c_void;
3124 let scp =
3125 (layer.experts_sc.device_ptr(&stream).0 as usize + soff) as *const c_void;
3126 let dst = st.deq[pi].device_ptr(&stream).0 as *mut c_void;
3127 match layer.expert_kind {
3128 ExpertKind::Nvfp4 => ck(
3129 "nvfp4 deq",
3130 k::memra_dsv4_nvfp4_deq_bf16(
3131 wp,
3132 scp,
3133 layer.experts_s2[ex * 3 + pi],
3134 *rows as i32,
3135 *cols as i32,
3136 dst,
3137 sp(&stream),
3138 ),
3139 )?,
3140 ExpertKind::Mxfp4 => ck(
3141 "mxfp4 deq",
3142 k::memra_dsv4_mxfp4_deq_bf16(
3143 wp,
3144 scp,
3145 *rows as i32,
3146 *cols as i32,
3147 dst,
3148 sp(&stream),
3149 ),
3150 )?,
3151 }
3152 }
3153 ck(
3154 "gemm w1",
3155 k::memra_dsv4_gemm_bf16(
3156 st.deq[0].device_ptr(&stream).0 as *const c_void,
3157 dp!(xg, &stream),
3158 dpm!(g1, &stream),
3159 g as i32,
3160 inter as i32,
3161 hidden as i32,
3162 st.dev as i32,
3163 st.ws.device_ptr(&stream).0 as *mut c_void,
3164 st.ws.len(),
3165 sp(&stream),
3166 ),
3167 )?;
3168 ck(
3169 "gemm w3",
3170 k::memra_dsv4_gemm_bf16(
3171 st.deq[2].device_ptr(&stream).0 as *const c_void,
3172 dp!(xg, &stream),
3173 dpm!(g3, &stream),
3174 g as i32,
3175 inter as i32,
3176 hidden as i32,
3177 st.dev as i32,
3178 st.ws.device_ptr(&stream).0 as *mut c_void,
3179 st.ws.len(),
3180 sp(&stream),
3181 ),
3182 )?;
3183 ck(
3184 "swiglu",
3185 k::memra_dsv4_swiglu(
3186 dpf!(g1, &stream),
3187 dpf!(g3, &stream),
3188 dpm!(hbuf, &stream),
3189 g as i32,
3190 inter as i32,
3191 limit,
3192 wrow_dev.device_ptr(&stream).0 as *const f32,
3193 sp(&stream),
3194 ),
3195 )?;
3196 ck(
3197 "cvt h",
3198 k::memra_dsv4_cvt_bf16(
3199 dpf!(hbuf, &stream),
3200 hb.device_ptr_mut(&stream).0 as *mut c_void,
3201 (g * inter) as i64,
3202 sp(&stream),
3203 ),
3204 )?;
3205 ck(
3206 "gemm w2",
3207 k::memra_dsv4_gemm_bf16(
3208 st.deq[1].device_ptr(&stream).0 as *const c_void,
3209 dp!(hb, &stream),
3210 dpm!(contrib, &stream),
3211 g as i32,
3212 hidden as i32,
3213 inter as i32,
3214 st.dev as i32,
3215 st.ws.device_ptr(&stream).0 as *mut c_void,
3216 st.ws.len(),
3217 sp(&stream),
3218 ),
3219 )?;
3220 ck(
3221 "scatter",
3222 k::memra_dsv4_scatter_add(
3223 dpm!(y, &stream),
3224 dpf!(contrib, &stream),
3225 rows_dev.device_ptr(&stream).0 as *const i32,
3226 g as i32,
3227 hidden as i32,
3228 sp(&stream),
3229 ),
3230 )?;
3231 }
3232 }
3233 self.moe_shared_and_finish(st, layer, &xb, s, y)
3234 }
3235
3236 fn moe_shared_and_finish(
3240 &self,
3241 st: &Stage,
3242 layer: &LayerDev,
3243 xb: &CudaSlice<u8>,
3244 s: usize,
3245 mut y: CudaSlice<f32>,
3246 ) -> Res<CudaSlice<f32>> {
3247 let d = self.model.cfg();
3248 let hidden = self.model.mc.n_embd as usize;
3249 let limit = d.swiglu_limit;
3250 let stream = st.gpu.stream();
3251 let sh_inter = {
3252 let (shape, _) = self
3254 .model
3255 .st
3256 .raw("layers.0.ffn.shared_experts.w1.weight")
3257 .map(|(i, _)| (i.shape.clone(), ()))
3258 .expect("shared w1");
3259 shape[0] as usize
3260 };
3261 let mut sg1 = stream.alloc_zeros::<f32>(s * sh_inter).map_err(e("sg1"))?;
3262 let mut sg3 = stream.alloc_zeros::<f32>(s * sh_inter).map_err(e("sg3"))?;
3263 let mut shbuf = stream.alloc_zeros::<f32>(s * sh_inter).map_err(e("shb"))?;
3264 let mut shb16 = stream
3265 .alloc_zeros::<u8>(s * sh_inter * 2)
3266 .map_err(e("shb16"))?;
3267 let mut sh_out = stream.alloc_zeros::<f32>(s * hidden).map_err(e("sh_out"))?;
3268 let sw = [
3270 layer.shared_w[0].staged(&stream)?,
3271 layer.shared_w[1].staged(&stream)?,
3272 layer.shared_w[2].staged(&stream)?,
3273 ];
3274 Self::gemm_pre(
3275 st,
3276 xb,
3277 sw[0].slab().device_ptr(&stream).0 as *const c_void,
3278 s,
3279 sh_inter,
3280 hidden,
3281 &mut sg1,
3282 )?;
3283 Self::gemm_pre(
3284 st,
3285 xb,
3286 sw[2].slab().device_ptr(&stream).0 as *const c_void,
3287 s,
3288 sh_inter,
3289 hidden,
3290 &mut sg3,
3291 )?;
3292 unsafe {
3293 ck(
3294 "swiglu sh",
3295 k::memra_dsv4_swiglu(
3296 dpf!(sg1, &stream),
3297 dpf!(sg3, &stream),
3298 dpm!(shbuf, &stream),
3299 s as i32,
3300 sh_inter as i32,
3301 limit,
3302 std::ptr::null(),
3303 sp(&stream),
3304 ),
3305 )?;
3306 ck(
3307 "cvt sh",
3308 k::memra_dsv4_cvt_bf16(
3309 dpf!(shbuf, &stream),
3310 shb16.device_ptr_mut(&stream).0 as *mut c_void,
3311 (s * sh_inter) as i64,
3312 sp(&stream),
3313 ),
3314 )?;
3315 }
3316 Self::gemm_pre(
3317 st,
3318 &shb16,
3319 sw[1].slab().device_ptr(&stream).0 as *const c_void,
3320 s,
3321 hidden,
3322 sh_inter,
3323 &mut sh_out,
3324 )?;
3325 unsafe {
3326 ck(
3327 "add shared",
3328 k::memra_dsv4_add_inplace(
3329 dpm!(y, &stream),
3330 dpf!(sh_out, &stream),
3331 (s * hidden) as i64,
3332 sp(&stream),
3333 ),
3334 )?;
3335 }
3336 Ok(y)
3337 }
3338
3339 pub fn forward(
3342 &self,
3343 ids: &[u32],
3344 capture: Option<&mut GpuCapture>,
3345 early_exit_after: Option<u32>,
3346 ) -> Res<Option<ForwardOut>> {
3347 self.forward_impl(ids, capture, early_exit_after, None)
3348 }
3349
3350 pub fn prefill_with_cache(&self, ids: &[u32], state: &mut DecodeState) -> Res<ForwardOut> {
3353 assert_eq!(state.pos, 0, "prefill_with_cache needs a fresh DecodeState");
3354 assert!(!ids.is_empty(), "empty prompt");
3355 let out = self
3356 .forward_impl(ids, None, None, Some(state))?
3357 .expect("prefill logits");
3358 state.pos = ids.len();
3359 Ok(out)
3360 }
3361
3362 fn forward_impl(
3363 &self,
3364 ids: &[u32],
3365 mut capture: Option<&mut GpuCapture>,
3366 early_exit_after: Option<u32>,
3367 mut state: Option<&mut DecodeState>,
3368 ) -> Res<Option<ForwardOut>> {
3369 let mc = &self.model.mc;
3370 let d = self.model.cfg();
3371 let s = ids.len();
3372 assert!(s <= self.max_seq, "seq {s} > max_seq {}", self.max_seq);
3373 let hidden = mc.n_embd as usize;
3374 let hc = d.hc_mult as usize;
3375 let n_trunk = mc.n_layer - mc.nextn_predict_layers;
3376
3377 let st0 = &self.stages[0];
3379 st0.gpu.ctx.bind_to_thread().map_err(e("bind ctx0"))?;
3380 let stream0 = st0.gpu.stream();
3381 let ids_i32: Vec<i32> = ids.iter().map(|&x| x as i32).collect();
3382 let ids_dev = upload_i32(&stream0, &ids_i32)?;
3383 let mut emb = stream0.alloc_zeros::<f32>(s * hidden).map_err(e("emb"))?;
3384 unsafe {
3385 ck(
3386 "embed_rows",
3387 k::memra_dsv4_embed_rows(
3388 st0.embed
3389 .as_ref()
3390 .expect("embed on stage 0")
3391 .device_ptr(&stream0)
3392 .0 as *const c_void,
3393 ids_dev.device_ptr(&stream0).0 as *const i32,
3394 dpm!(emb, &stream0),
3395 s as i32,
3396 hidden as i32,
3397 sp(&stream0),
3398 ),
3399 )?;
3400 }
3401 if let Some(c) = capture.as_deref_mut() {
3402 if c.embed_out.is_none() {
3403 c.embed_out = Some(dtoh_f32(&stream0, &emb)?);
3404 }
3405 }
3406 let mut h = stream0
3407 .alloc_zeros::<f32>(s * hc * hidden)
3408 .map_err(e("h0"))?;
3409 unsafe {
3410 ck(
3411 "repeat_hc",
3412 k::memra_dsv4_repeat_hc(
3413 dpf!(emb, &stream0),
3414 dpm!(h, &stream0),
3415 s as i32,
3416 hc as i32,
3417 hidden as i32,
3418 sp(&stream0),
3419 ),
3420 )?;
3421 }
3422
3423 let mut cur_stage = 0usize;
3425 for il in 0..n_trunk {
3426 let stage = self.layer_stage[il as usize];
3427 if stage != cur_stage {
3428 let src_stream = self.stages[cur_stage].gpu.stream();
3429 let host = dtoh_f32(&src_stream, &h)?;
3430 let dst_stream = self.stages[stage].gpu.stream();
3431 self.stages[stage]
3432 .gpu
3433 .ctx
3434 .bind_to_thread()
3435 .map_err(e("bind"))?;
3436 h = upload_f32(&dst_stream, &host)?;
3437 cur_stage = stage;
3438 }
3439 let st = &self.stages[stage];
3440 let lidx = st
3441 .layers
3442 .iter()
3443 .position(|l| l.il == il)
3444 .unwrap_or_else(|| panic!("layer {il} not on stage {stage}"));
3445 let layer_cache = state.as_deref_mut().map(|ds| &mut ds.caches[il as usize]);
3446 h = self.block_forward(
3447 st,
3448 &st.layers[lidx],
3449 &h,
3450 s,
3451 ids,
3452 capture.as_deref_mut(),
3453 layer_cache,
3454 )?;
3455 if early_exit_after == Some(il) {
3456 self.stages[cur_stage]
3457 .gpu
3458 .stream()
3459 .synchronize()
3460 .map_err(e("sync"))?;
3461 return Ok(None);
3462 }
3463 }
3464
3465 let last = self.stages.len() - 1;
3467 if cur_stage != last {
3468 let src_stream = self.stages[cur_stage].gpu.stream();
3469 let host = dtoh_f32(&src_stream, &h)?;
3470 let dst_stream = self.stages[last].gpu.stream();
3471 h = upload_f32(&dst_stream, &host)?;
3472 }
3473 let hc_head_fn = self.stages[last].hc_head_fn.as_ref().expect("hc_head_fn");
3474 let trunk_norm = self.stages[last].trunk_norm.as_ref().expect("trunk norm");
3475 let logits = self.head_logits_from(
3476 &h,
3477 s,
3478 hc_head_fn,
3479 &self.hc_head_base,
3480 &self.hc_head_scale,
3481 trunk_norm,
3482 )?;
3483 Ok(Some(ForwardOut { logits, h_last: h }))
3484 }
3485
3486 fn head_logits_from(
3490 &self,
3491 h: &CudaSlice<f32>,
3492 s: usize,
3493 fn_w: &CudaSlice<f32>,
3494 base: &[f32],
3495 scale: &[f32],
3496 norm: &CudaSlice<f32>,
3497 ) -> Res<Vec<f32>> {
3498 self.head_logits_row(h, s, s - 1, fn_w, base, scale, norm)
3499 }
3500
3501 #[allow(clippy::too_many_arguments)]
3505 fn head_logits_row(
3506 &self,
3507 h: &CudaSlice<f32>,
3508 s: usize,
3509 row: usize,
3510 fn_w: &CudaSlice<f32>,
3511 base: &[f32],
3512 scale: &[f32],
3513 norm: &CudaSlice<f32>,
3514 ) -> Res<Vec<f32>> {
3515 let d = self.model.cfg();
3516 let mc = &self.model.mc;
3517 let hc = d.hc_mult as usize;
3518 let hidden = mc.n_embd as usize;
3519 let eps = mc.rms_eps;
3520 let last = self.stages.len() - 1;
3521 let st = &self.stages[last];
3522 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx head"))?;
3523 let stream = st.gpu.stream();
3524 let w = hc * hidden;
3525 let mut mixes = stream.alloc_zeros::<f32>(s * hc).map_err(e("hm"))?;
3526 Self::dots(st, h, fn_w, s, w, hc, &mut mixes)?;
3527 unsafe {
3528 ck(
3529 "rowsq head",
3530 k::memra_dsv4_rowsq_scale(
3531 dpf!(h, &stream),
3532 dpm!(mixes, &stream),
3533 s as i32,
3534 w as i32,
3535 hc as i32,
3536 eps,
3537 sp(&stream),
3538 ),
3539 )?;
3540 }
3541 let mut mixes_h = dtoh_f32(&stream, &mixes)?;
3544 for t in 0..s {
3545 for c in 0..hc {
3546 let m = mixes_h[t * hc + c];
3547 mixes_h[t * hc + c] = sigmoid_f32(m * scale[0] + base[c]) + d.hc_eps;
3548 }
3549 }
3550 let pre_d = upload_f32(&stream, &mixes_h)?;
3551 let mut collapsed = stream.alloc_zeros::<f32>(s * hidden).map_err(e("col"))?;
3552 unsafe {
3553 ck(
3554 "hc_collapse head",
3555 k::memra_dsv4_hc_collapse(
3556 dpf!(h, &stream),
3557 dpf!(pre_d, &stream),
3558 dpm!(collapsed, &stream),
3559 s as i32,
3560 hc as i32,
3561 hidden as i32,
3562 sp(&stream),
3563 ),
3564 )?;
3565 ck(
3566 "rmsnorm head",
3567 k::memra_dsv4_rmsnorm(
3568 dpf!(collapsed, &stream),
3569 dpf!(norm, &stream),
3570 dpm!(collapsed, &stream),
3571 s as i32,
3572 hidden as i32,
3573 eps,
3574 sp(&stream),
3575 ),
3576 )?;
3577 }
3578 assert!(row < s, "logits row {row} out of range (s = {s})");
3580 let vocab = {
3581 let (info, _) = self.model.st.raw("head.weight").expect("head");
3582 info.shape[0] as usize
3583 };
3584 let last_row = collapsed.slice(row * hidden..(row + 1) * hidden);
3585 let mut logits = stream.alloc_zeros::<f32>(vocab).map_err(e("logits"))?;
3586 unsafe {
3587 ck(
3588 "head dots",
3589 k::memra_dsv4_dots_f32(
3590 last_row.device_ptr(&stream).0 as *const f32,
3591 st.head.as_ref().expect("head").device_ptr(&stream).0 as *const c_void,
3592 1,
3593 dpm!(logits, &stream),
3594 1,
3595 hidden as i32,
3596 vocab as i32,
3597 sp(&stream),
3598 ),
3599 )?;
3600 }
3601 dtoh_f32(&stream, &logits)
3602 }
3603
3604 pub fn mtp_logits_last(&self, h_trunk: &CudaSlice<f32>, ids: &[u32]) -> Res<Vec<f32>> {
3608 self.mtp_logits_last_cap(h_trunk, ids, None)
3609 }
3610
3611 pub fn mtp_logits_last_cap(
3614 &self,
3615 h_trunk: &CudaSlice<f32>,
3616 ids: &[u32],
3617 capture: Option<&mut GpuCapture>,
3618 ) -> Res<Vec<f32>> {
3619 let mtp = self.mtp.as_ref().expect("MTP not loaded");
3620 let d = self.model.cfg();
3621 let mc = &self.model.mc;
3622 let hc = d.hc_mult as usize;
3623 let hidden = mc.n_embd as usize;
3624 let eps = mc.rms_eps;
3625 let s = ids.len();
3626 let last = self.stages.len() - 1;
3627 let st = &self.stages[last];
3628 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx mtp"))?;
3629 let stream = st.gpu.stream();
3630
3631 let e_host = self.model.embed_rows(ids);
3634 let mut e_dev = upload_f32(&stream, &e_host)?;
3635 unsafe {
3636 ck(
3637 "rmsnorm enorm",
3638 k::memra_dsv4_rmsnorm(
3639 dpf!(e_dev, &stream),
3640 dpf!(mtp.enorm, &stream),
3641 dpm!(e_dev, &stream),
3642 s as i32,
3643 hidden as i32,
3644 eps,
3645 sp(&stream),
3646 ),
3647 )?;
3648 }
3649 let mut xh = stream
3651 .alloc_zeros::<f32>(s * hc * hidden)
3652 .map_err(e("mtp xh"))?;
3653 unsafe {
3654 ck(
3655 "rmsnorm hnorm",
3656 k::memra_dsv4_rmsnorm(
3657 dpf!(h_trunk, &stream),
3658 dpf!(mtp.hnorm, &stream),
3659 dpm!(xh, &stream),
3660 (s * hc) as i32,
3661 hidden as i32,
3662 eps,
3663 sp(&stream),
3664 ),
3665 )?;
3666 }
3667 let mut ep = stream.alloc_zeros::<f32>(s * hidden).map_err(e("mtp ep"))?;
3669 Self::gemm(st, &e_dev, &mtp.e_proj, 0, s, hidden, hidden, &mut ep)?;
3670 let mut hp = stream
3671 .alloc_zeros::<f32>(s * hc * hidden)
3672 .map_err(e("mtp hp"))?;
3673 Self::gemm(st, &xh, &mtp.h_proj, 0, s * hc, hidden, hidden, &mut hp)?;
3674 let mut xm = stream
3676 .alloc_zeros::<f32>(s * hc * hidden)
3677 .map_err(e("mtp xm"))?;
3678 unsafe {
3679 ck(
3680 "repeat ep",
3681 k::memra_dsv4_repeat_hc(
3682 dpf!(ep, &stream),
3683 dpm!(xm, &stream),
3684 s as i32,
3685 hc as i32,
3686 hidden as i32,
3687 sp(&stream),
3688 ),
3689 )?;
3690 ck(
3691 "add hp",
3692 k::memra_dsv4_add_inplace(
3693 dpm!(xm, &stream),
3694 dpf!(hp, &stream),
3695 (s * hc * hidden) as i64,
3696 sp(&stream),
3697 ),
3698 )?;
3699 }
3700 let xm = self.block_forward(st, &mtp.layer, &xm, s, ids, capture, None)?;
3701 self.head_logits_from(
3702 &xm,
3703 s,
3704 &mtp.hc_head_fn,
3705 &mtp.hc_head_base,
3706 &mtp.hc_head_scale,
3707 &mtp.norm,
3708 )
3709 }
3710
3711 pub fn trunk_logits_row(&self, h: &CudaSlice<f32>, s: usize, row: usize) -> Res<Vec<f32>> {
3713 let last = self.stages.len() - 1;
3714 let hc_head_fn = self.stages[last].hc_head_fn.as_ref().expect("hc_head_fn");
3715 let trunk_norm = self.stages[last].trunk_norm.as_ref().expect("trunk norm");
3716 self.head_logits_row(
3717 h,
3718 s,
3719 row,
3720 hc_head_fn,
3721 &self.hc_head_base,
3722 &self.hc_head_scale,
3723 trunk_norm,
3724 )
3725 }
3726
3727 pub fn alloc_decode_state(&self) -> Res<DecodeState> {
3733 let d = self.model.cfg();
3734 let mc = &self.model.mc;
3735 let win = d.sliding_window as usize;
3736 let hd = d.head_dim as usize;
3737 let n_trunk = mc.n_layer - mc.nextn_predict_layers;
3738 let mut caches = Vec::with_capacity(n_trunk as usize);
3739 let mut cache_bytes = vec![0u64; self.stages.len()];
3740 let trans_rows = self.verify_tmax();
3745 for il in 0..n_trunk {
3746 let stage_i = self.layer_stage[il as usize];
3747 let st = &self.stages[stage_i];
3748 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx cache"))?;
3749 let stream = st.gpu.stream();
3750 let lidx = st
3751 .layers
3752 .iter()
3753 .position(|l| l.il == il)
3754 .unwrap_or_else(|| panic!("layer {il} not on stage {stage_i}"));
3755 let layer = &st.layers[lidx];
3756 let ratio = layer.ratio;
3757 let cap_blocks = if ratio != 0 { self.max_seq / ratio } else { 0 };
3758 let kvc_rows = win + cap_blocks + trans_rows;
3759 let mut bytes = (kvc_rows * hd * 4) as u64;
3760 let kvc = stream
3761 .alloc_zeros::<f32>(kvc_rows * hd)
3762 .map_err(e("kvc alloc"))?;
3763 let mk_pend = |latent: usize, slots: usize| -> Res<(CudaSlice<f32>, CudaSlice<f32>)> {
3765 let kv = stream
3766 .alloc_zeros::<f32>(slots * latent)
3767 .map_err(e("pend kv alloc"))?;
3768 let sc = upload_f32(&stream, &vec![f32::NEG_INFINITY; slots * latent])?;
3769 Ok((kv, sc))
3770 };
3771 let (pend_kv, pend_score) = if let Some(cmp) = &layer.cmp {
3772 let slots = if cmp.overlap {
3773 2 * cmp.ratio
3774 } else {
3775 cmp.ratio
3776 };
3777 bytes += (2 * slots * cmp.latent * 4) as u64;
3778 let (a, b) = mk_pend(cmp.latent, slots)?;
3779 (Some(a), Some(b))
3780 } else {
3781 (None, None)
3782 };
3783 let (ikvc, ipend_kv, ipend_score) = if let Some(ix) = &layer.idx {
3784 bytes += (cap_blocks * ix.cmp.d * 4) as u64;
3785 let store = stream
3786 .alloc_zeros::<f32>(cap_blocks * ix.cmp.d)
3787 .map_err(e("ikvc alloc"))?;
3788 let slots = if ix.cmp.overlap {
3789 2 * ix.cmp.ratio
3790 } else {
3791 ix.cmp.ratio
3792 };
3793 bytes += (2 * slots * ix.cmp.latent * 4) as u64;
3794 let (a, b) = mk_pend(ix.cmp.latent, slots)?;
3795 (Some(store), Some(a), Some(b))
3796 } else {
3797 (None, None, None)
3798 };
3799 cache_bytes[stage_i] += bytes;
3800 caches.push(LayerCache {
3801 kvc,
3802 n_blocks: 0,
3803 pend_kv,
3804 pend_score,
3805 ikvc,
3806 i_blocks: 0,
3807 ipend_kv,
3808 ipend_score,
3809 });
3810 }
3811 let ws = if matches!(self.decode_path, DecodePath::Device { .. }) {
3812 Some(self.alloc_step_ws()?)
3813 } else {
3814 None
3815 };
3816 for st in &self.stages {
3817 st.gpu.stream().synchronize().map_err(e("cache sync"))?;
3818 }
3819 Ok(DecodeState {
3820 caches,
3821 pos: 0,
3822 cache_bytes,
3823 ws,
3824 })
3825 }
3826
3827 fn alloc_step_ws(&self) -> Res<Vec<StepWs>> {
3829 let d = self.model.cfg();
3830 let mc = &self.model.mc;
3831 let moe = mc.moe.as_ref().expect("moe");
3832 let hc = d.hc_mult as usize;
3833 let hidden = mc.n_embd as usize;
3834 let heads = mc.n_head as usize;
3835 let hd = d.head_dim as usize;
3836 let q_lora = d.q_lora_rank as usize;
3837 let win = d.sliding_window as usize;
3838 let o_groups = d.o_groups as usize;
3839 let o_lora = d.o_lora_rank as usize;
3840 let iheads = d.index_n_heads as usize;
3841 let ihd = d.index_head_dim as usize;
3842 let topk = moe.expert_used_count as usize;
3843 let ne = moe.expert_count as usize;
3844 let inter = moe.expert_ff_length as usize;
3845 let itopk = d.index_topk as usize;
3846 let vocab = {
3847 let (info, _) = self.model.st.raw("head.weight").expect("head");
3848 info.shape[0] as usize
3849 };
3850 let sh_inter = {
3851 let (info, _) = self
3852 .model
3853 .st
3854 .raw("layers.0.ffn.shared_experts.w1.weight")
3855 .expect("shared w1");
3856 info.shape[0] as usize
3857 };
3858 let mut max_latent = 0usize;
3860 let mut max_d = 0usize;
3861 let mut max_shift = 0usize;
3862 let mut min_ratio = usize::MAX;
3863 for st in &self.stages {
3864 for l in &st.layers {
3865 for cmp in l.cmp.iter().chain(l.idx.as_ref().map(|ix| &ix.cmp)) {
3866 max_latent = max_latent.max(cmp.latent);
3867 max_d = max_d.max(cmp.d);
3868 if cmp.overlap {
3869 max_shift = max_shift.max(cmp.ratio * cmp.latent);
3870 }
3871 min_ratio = min_ratio.min(cmp.ratio);
3872 }
3873 }
3874 }
3875 assert!(min_ratio != usize::MAX, "no compressor layers?");
3876 let score_cap = self.max_seq / min_ratio + 1;
3877 let idx_tail = itopk.max(self.max_seq / 128 + 1);
3878 let max_gemm_k = (o_groups * o_lora).max(hidden).max(q_lora).max(sh_inter);
3881 let mut out = Vec::with_capacity(self.stages.len());
3882 for st in &self.stages {
3883 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx ws"))?;
3884 let s = st.gpu.stream();
3885 let f = |n: usize| s.alloc_zeros::<f32>(n).map_err(e("ws f32"));
3886 let b = |n: usize| s.alloc_zeros::<u8>(n).map_err(e("ws u8"));
3887 let i = |n: usize| s.alloc_zeros::<i32>(n).map_err(e("ws i32"));
3888 out.push(StepWs {
3889 h_a: f(hc * hidden)?,
3890 h_b: f(hc * hidden)?,
3891 h_rx: f(hc * hidden)?,
3892 emb: f(hidden)?,
3893 mixes: f((2 + hc) * hc)?,
3894 pre: f(hc)?,
3895 post: f(hc)?,
3896 comb: f(hc * hc)?,
3897 y_hc: f(hidden)?,
3898 x: f(hidden)?,
3899 xf: f(hidden)?,
3900 qr: f(q_lora)?,
3901 qr_b: b(q_lora * 2)?,
3902 q: f(heads * hd)?,
3903 kv: f(hd)?,
3904 qi: f(iheads * ihd)?,
3905 wproj: f(iheads)?,
3906 score: f(score_cap)?,
3907 idx: i(win + idx_tail)?,
3908 o: f(heads * hd)?,
3909 o_b: b(heads * hd * 2)?,
3910 og: f(o_groups * o_lora)?,
3911 attn_out: f(hidden)?,
3912 gemm_xb: b(max_gemm_k * 2)?,
3913 raw: f(ne)?,
3914 sel: i(topk)?,
3915 selw: f(topk)?,
3916 order: i(topk)?,
3917 xq: b(hidden)?,
3918 xs: f(hidden / 128)?,
3919 g1: f(topk * inter)?,
3920 g3: f(topk * inter)?,
3921 hbuf: f(topk * inter)?,
3922 hq: b(topk * inter)?,
3923 hs: f(topk * inter / 128)?,
3924 contrib: f(topk * hidden)?,
3925 y: f(hidden)?,
3926 xb: b(hidden * 2)?,
3927 sg1: f(sh_inter)?,
3928 sg3: f(sh_inter)?,
3929 shbuf: f(sh_inter)?,
3930 shb16: b(sh_inter * 2)?,
3931 sh_out: f(hidden)?,
3932 cmp_kv_row: f(max_latent)?,
3933 cmp_sc_row: f(max_latent)?,
3934 cmp_emit: f(2 * max_d)?,
3935 cmp_shift: f(max_shift.max(1))?,
3936 sink_scores: f(heads * (win + idx_tail))?,
3937 sink_evals: f(heads * (win + idx_tail))?,
3938 sink_den: s.alloc_zeros::<f64>(heads).map_err(e("ws f64"))?,
3939 head_mixes: f(hc)?,
3940 head_pre: f(hc)?,
3941 collapsed: f(hidden)?,
3942 logits: f(vocab)?,
3943 argmax: i(1)?,
3944 tok: i(1)?,
3945 });
3946 }
3947 Ok(out)
3948 }
3949
3950 #[allow(clippy::too_many_arguments)]
3958 fn cmp_decode(
3959 &self,
3960 st: &Stage,
3961 cmp: &CmpDev,
3962 x: &CudaSlice<f32>, pos: usize,
3964 hidden: usize,
3965 fc_dev: &CudaSlice<f32>,
3966 rd: usize,
3967 eps: f32,
3968 pend_kv: &mut CudaSlice<f32>,
3969 pend_score: &mut CudaSlice<f32>,
3970 store: &mut CudaSlice<f32>,
3971 row0: usize,
3972 blocks: &mut usize,
3973 ) -> Res<()> {
3974 let stream = st.gpu.stream();
3975 let (ratio, d, latent) = (cmp.ratio, cmp.d, cmp.latent);
3976 let mut kv_row = stream.alloc_zeros::<f32>(latent).map_err(e("dkv"))?;
3977 let mut sc_row = stream.alloc_zeros::<f32>(latent).map_err(e("dsc"))?;
3978 Self::dots(st, x, &cmp.wkv, 1, hidden, latent, &mut kv_row)?;
3979 Self::dots(st, x, &cmp.wgate, 1, hidden, latent, &mut sc_row)?;
3980 let slot = if cmp.overlap {
3981 ratio + pos % ratio
3982 } else {
3983 pos % ratio
3984 };
3985 {
3986 let src = kv_row.slice(0..latent);
3987 let mut dst = pend_kv.slice_mut(slot * latent..(slot + 1) * latent);
3988 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend kv"))?;
3989 let src = sc_row.slice(0..latent);
3990 let mut dst = pend_score.slice_mut(slot * latent..(slot + 1) * latent);
3991 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend sc"))?;
3992 }
3993 if (pos + 1) % ratio != 0 {
3994 return Ok(());
3995 }
3996 let j = pos / ratio;
3997 let nb_launch = if cmp.overlap { 2usize } else { 1 };
3998 let row_off = if cmp.overlap { d } else { 0 };
3999 let mut out = stream
4000 .alloc_zeros::<f32>(nb_launch * d)
4001 .map_err(e("emit"))?;
4002 unsafe {
4003 ck(
4004 "compressor_pool dec",
4005 k::memra_dsv4_compressor_pool(
4006 dpf!(pend_kv, &stream),
4007 dpf!(pend_score, &stream),
4008 dpf!(cmp.ape, &stream),
4009 dpm!(out, &stream),
4010 nb_launch as i32,
4011 ratio as i32,
4012 d as i32,
4013 latent as i32,
4014 cmp.overlap as i32,
4015 sp(&stream),
4016 ),
4017 )?;
4018 let row_c = (out.device_ptr(&stream).0 as usize + row_off * 4) as *const f32;
4020 let row_m = (out.device_ptr_mut(&stream).0 as usize + row_off * 4) as *mut f32;
4021 ck(
4022 "rmsnorm dec cmp",
4023 k::memra_dsv4_rmsnorm(
4024 row_c,
4025 dpf!(cmp.norm, &stream),
4026 row_m,
4027 1,
4028 d as i32,
4029 eps,
4030 sp(&stream),
4031 ),
4032 )?;
4033 let pos_dev = upload_i32(&stream, &[(j * ratio) as i32])?;
4034 ck(
4035 "rope dec cmp",
4036 k::memra_dsv4_rope(
4037 row_m,
4038 1,
4039 1,
4040 d as i32,
4041 rd as i32,
4042 dpf!(fc_dev, &stream),
4043 pos_dev.device_ptr(&stream).0 as *const i32,
4044 0,
4045 sp(&stream),
4046 ),
4047 )?;
4048 if cmp.rotate {
4049 let scale = (d as f32).powf(-0.5);
4050 ck(
4051 "hadamard dec cmp",
4052 k::memra_dsv4_hadamard(row_m, 1, d as i32, scale, sp(&stream)),
4053 )?;
4054 ck(
4055 "fp4 dec cmp",
4056 k::memra_dsv4_fp4_act_quant(row_m, 1, d as i64, d as i32, sp(&stream)),
4057 )?;
4058 } else {
4059 ck(
4060 "act_quant dec cmp",
4061 k::memra_dsv4_act_quant(
4062 row_m,
4063 1,
4064 d as i64,
4065 (d - rd) as i32,
4066 64,
4067 (self.variant == ActQuantVariant::ClampOnly) as i32,
4068 sp(&stream),
4069 ),
4070 )?;
4071 }
4072 }
4073 {
4074 let src = out.slice(row_off..row_off + d);
4075 let mut dst = store.slice_mut((row0 + j) * d..(row0 + j + 1) * d);
4076 stream
4077 .memcpy_dtod(&src, &mut dst)
4078 .map_err(e("emit store"))?;
4079 }
4080 if cmp.overlap {
4081 let mut tmp = stream
4083 .alloc_zeros::<f32>(ratio * latent)
4084 .map_err(e("shift tmp"))?;
4085 {
4086 let src = pend_kv.slice(ratio * latent..2 * ratio * latent);
4087 stream.memcpy_dtod(&src, &mut tmp).map_err(e("shift1"))?;
4088 }
4089 {
4090 let mut dst = pend_kv.slice_mut(0..ratio * latent);
4091 stream
4092 .memcpy_dtod(&tmp.slice(0..ratio * latent), &mut dst)
4093 .map_err(e("shift2"))?;
4094 }
4095 {
4096 let src = pend_score.slice(ratio * latent..2 * ratio * latent);
4097 stream.memcpy_dtod(&src, &mut tmp).map_err(e("shift3"))?;
4098 }
4099 {
4100 let mut dst = pend_score.slice_mut(0..ratio * latent);
4101 stream
4102 .memcpy_dtod(&tmp.slice(0..ratio * latent), &mut dst)
4103 .map_err(e("shift4"))?;
4104 }
4105 }
4106 *blocks = j + 1;
4107 Ok(())
4108 }
4109
4110 #[allow(clippy::too_many_arguments)]
4116 fn block_decode(
4117 &self,
4118 st: &Stage,
4119 layer: &LayerDev,
4120 cache: &mut LayerCache,
4121 h: &CudaSlice<f32>,
4122 pos: usize,
4123 tok: u32,
4124 mut dump: Option<&mut Vec<(String, Vec<f32>)>>,
4125 ) -> Res<CudaSlice<f32>> {
4126 let d = self.model.cfg();
4127 let mc = &self.model.mc;
4128 let hc = d.hc_mult as usize;
4129 let hidden = mc.n_embd as usize;
4130 let heads = mc.n_head as usize;
4131 let hd = d.head_dim as usize;
4132 let rd = d.qk_rope_head_dim as usize;
4133 let q_lora = d.q_lora_rank as usize;
4134 let win = d.sliding_window as usize;
4135 let o_groups = d.o_groups as usize;
4136 let o_lora = d.o_lora_rank as usize;
4137 let eps = mc.rms_eps;
4138 let iters = d.hc_sinkhorn_iters;
4139 let hc_eps = d.hc_eps;
4140 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx"))?;
4141 let stream = st.gpu.stream();
4142 let fc_dev = if layer.ratio != 0 {
4143 &st.fc_yarn
4144 } else {
4145 &st.fc_plain
4146 };
4147 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
4148 let LayerCache {
4149 kvc,
4150 n_blocks,
4151 pend_kv,
4152 pend_score,
4153 ikvc,
4154 i_blocks,
4155 ipend_kv,
4156 ipend_score,
4157 } = cache;
4158
4159 let (y, post, comb) = Self::hc_pre(
4161 st,
4162 h,
4163 &layer.hc_attn_fn,
4164 &layer.hc_attn_base,
4165 &layer.hc_attn_scale,
4166 1,
4167 hc,
4168 hidden,
4169 iters,
4170 hc_eps,
4171 )?;
4172 let mut x = stream.alloc_zeros::<f32>(hidden).map_err(e("x"))?;
4173 unsafe {
4174 ck(
4175 "rmsnorm attn",
4176 k::memra_dsv4_rmsnorm(
4177 dpf!(y, &stream),
4178 dpf!(layer.attn_norm, &stream),
4179 dpm!(x, &stream),
4180 1,
4181 hidden as i32,
4182 eps,
4183 sp(&stream),
4184 ),
4185 )?;
4186 }
4187 if let Some(dm) = dump.as_deref_mut() {
4188 dm.push((format!("layer{}.x", layer.il), dtoh_f32(&stream, &x)?));
4189 }
4190
4191 let mut qr = stream.alloc_zeros::<f32>(q_lora).map_err(e("qr"))?;
4194 Self::gemm(st, &x, layer.wq_a.dev(), 0, 1, q_lora, hidden, &mut qr)?;
4195 unsafe {
4196 ck(
4197 "rmsnorm q",
4198 k::memra_dsv4_rmsnorm(
4199 dpf!(qr, &stream),
4200 dpf!(layer.q_norm, &stream),
4201 dpm!(qr, &stream),
4202 1,
4203 q_lora as i32,
4204 eps,
4205 sp(&stream),
4206 ),
4207 )?;
4208 }
4209 let mut qr_b = stream.alloc_zeros::<u8>(q_lora * 2).map_err(e("qr_b"))?;
4210 unsafe {
4211 ck(
4212 "cvt qr",
4213 k::memra_dsv4_cvt_bf16(
4214 dpf!(qr, &stream),
4215 qr_b.device_ptr_mut(&stream).0 as *mut c_void,
4216 q_lora as i64,
4217 sp(&stream),
4218 ),
4219 )?;
4220 }
4221 let mut q = stream.alloc_zeros::<f32>(heads * hd).map_err(e("q"))?;
4222 Self::gemm_pre(
4223 st,
4224 &qr_b,
4225 layer.wq_b.dev().device_ptr(&stream).0 as *const c_void,
4226 1,
4227 heads * hd,
4228 q_lora,
4229 &mut q,
4230 )?;
4231 let pos_dev = upload_i32(&stream, &[pos as i32])?;
4232 unsafe {
4233 ck(
4234 "headrms",
4235 k::memra_dsv4_headrms(dpm!(q, &stream), heads as i32, hd as i32, eps, sp(&stream)),
4236 )?;
4237 ck(
4238 "rope q",
4239 k::memra_dsv4_rope(
4240 dpm!(q, &stream),
4241 1,
4242 heads as i32,
4243 hd as i32,
4244 rd as i32,
4245 dpf!(fc_dev, &stream),
4246 pos_dev.device_ptr(&stream).0 as *const i32,
4247 0,
4248 sp(&stream),
4249 ),
4250 )?;
4251 }
4252
4253 if let Some(dm) = dump.as_deref_mut() {
4254 dm.push((format!("layer{}.q", layer.il), dtoh_f32(&stream, &q)?));
4255 }
4256 let mut kv = stream.alloc_zeros::<f32>(hd).map_err(e("kv"))?;
4258 Self::gemm(st, &x, layer.wkv.dev(), 0, 1, hd, hidden, &mut kv)?;
4259 unsafe {
4260 ck(
4261 "rmsnorm kv",
4262 k::memra_dsv4_rmsnorm(
4263 dpf!(kv, &stream),
4264 dpf!(layer.kv_norm, &stream),
4265 dpm!(kv, &stream),
4266 1,
4267 hd as i32,
4268 eps,
4269 sp(&stream),
4270 ),
4271 )?;
4272 ck(
4273 "rope kv",
4274 k::memra_dsv4_rope(
4275 dpm!(kv, &stream),
4276 1,
4277 1,
4278 hd as i32,
4279 rd as i32,
4280 dpf!(fc_dev, &stream),
4281 pos_dev.device_ptr(&stream).0 as *const i32,
4282 0,
4283 sp(&stream),
4284 ),
4285 )?;
4286 ck(
4287 "act_quant kv",
4288 k::memra_dsv4_act_quant(
4289 dpm!(kv, &stream),
4290 1,
4291 hd as i64,
4292 (hd - rd) as i32,
4293 64,
4294 clamp_only,
4295 sp(&stream),
4296 ),
4297 )?;
4298 }
4299 {
4300 let slot = pos % win;
4301 let src = kv.slice(0..hd);
4302 let mut dst = kvc.slice_mut(slot * hd..(slot + 1) * hd);
4303 stream
4304 .memcpy_dtod(&src, &mut dst)
4305 .map_err(e("ring write"))?;
4306 }
4307 if let Some(dm) = dump.as_deref_mut() {
4308 dm.push((format!("layer{}.kv", layer.il), dtoh_f32(&stream, &kv)?));
4309 }
4310
4311 let mut idxs: Vec<i64> = vec![-1; win];
4313 if pos >= win - 1 {
4314 let sp_ = pos % win;
4315 let mut k_ = 0usize;
4316 for s_ in (sp_ + 1)..win {
4317 idxs[k_] = s_ as i64;
4318 k_ += 1;
4319 }
4320 for s_ in 0..=sp_ {
4321 idxs[k_] = s_ as i64;
4322 k_ += 1;
4323 }
4324 } else {
4325 for (p, v) in idxs.iter_mut().enumerate().take(pos + 1) {
4326 *v = p as i64;
4327 }
4328 }
4329
4330 if layer.ratio != 0 {
4331 let cidx: Vec<i64> = if let Some(ix) = &layer.idx {
4332 let mut qi = stream
4334 .alloc_zeros::<f32>(ix.heads * ix.hd)
4335 .map_err(e("qi"))?;
4336 Self::gemm_pre(
4337 st,
4338 &qr_b,
4339 ix.wq_b.dev().device_ptr(&stream).0 as *const c_void,
4340 1,
4341 ix.heads * ix.hd,
4342 q_lora,
4343 &mut qi,
4344 )?;
4345 unsafe {
4346 ck(
4347 "rope qi",
4348 k::memra_dsv4_rope(
4349 dpm!(qi, &stream),
4350 1,
4351 ix.heads as i32,
4352 ix.hd as i32,
4353 rd as i32,
4354 dpf!(fc_dev, &stream),
4355 pos_dev.device_ptr(&stream).0 as *const i32,
4356 0,
4357 sp(&stream),
4358 ),
4359 )?;
4360 let scale = (ix.hd as f32).powf(-0.5);
4361 ck(
4362 "hadamard qi",
4363 k::memra_dsv4_hadamard(
4364 dpm!(qi, &stream),
4365 ix.heads as i32,
4366 ix.hd as i32,
4367 scale,
4368 sp(&stream),
4369 ),
4370 )?;
4371 ck(
4372 "fp4 qi",
4373 k::memra_dsv4_fp4_act_quant(
4374 dpm!(qi, &stream),
4375 ix.heads as i32,
4376 ix.hd as i64,
4377 ix.hd as i32,
4378 sp(&stream),
4379 ),
4380 )?;
4381 }
4382 self.cmp_decode(
4384 st,
4385 &ix.cmp,
4386 &x,
4387 pos,
4388 hidden,
4389 fc_dev,
4390 rd,
4391 eps,
4392 ipend_kv.as_mut().expect("ipend"),
4393 ipend_score.as_mut().expect("ipend"),
4394 ikvc.as_mut().expect("ikvc"),
4395 0,
4396 i_blocks,
4397 )?;
4398 let nb = *i_blocks;
4399 debug_assert_eq!(nb, (pos + 1) / layer.ratio, "indexer block count");
4400 if nb > 0 {
4401 let mut wproj = stream.alloc_zeros::<f32>(ix.heads).map_err(e("wp"))?;
4402 Self::gemm(
4403 st,
4404 &x,
4405 ix.weights_proj.dev(),
4406 0,
4407 1,
4408 ix.heads,
4409 hidden,
4410 &mut wproj,
4411 )?;
4412 let wscale = ((ix.hd as f64).powf(-0.5) * (ix.heads as f64).powf(-0.5)) as f32;
4413 let mut score = stream.alloc_zeros::<f32>(nb).map_err(e("iscore"))?;
4414 unsafe {
4415 ck(
4416 "indexer_score dec",
4417 k::memra_dsv4_indexer_score(
4418 dpf!(qi, &stream),
4419 dpf!(ikvc.as_ref().expect("ikvc"), &stream),
4420 dpf!(wproj, &stream),
4421 wscale,
4422 dpm!(score, &stream),
4423 1,
4424 ix.heads as i32,
4425 ix.hd as i32,
4426 nb as i32,
4427 layer.ratio as i32,
4428 nb as i32, sp(&stream),
4430 ),
4431 )?;
4432 }
4433 let score_h = dtoh_f32(&stream, &score)?;
4434 let kk = ix.topk.min(nb);
4436 let mut order: Vec<usize> = (0..nb).collect();
4437 order.sort_by(|&a, &b| {
4438 score_h[b]
4439 .partial_cmp(&score_h[a])
4440 .unwrap_or(std::cmp::Ordering::Equal)
4441 .then(a.cmp(&b))
4442 });
4443 order
4444 .into_iter()
4445 .take(kk)
4446 .map(|j| (j + win) as i64)
4447 .collect()
4448 } else {
4449 Vec::new()
4450 }
4451 } else {
4452 let nb = (pos + 1) / layer.ratio;
4454 (0..nb).map(|j| (j + win) as i64).collect()
4455 };
4456 self.cmp_decode(
4458 st,
4459 layer.cmp.as_ref().expect("ratio!=0 has compressor"),
4460 &x,
4461 pos,
4462 hidden,
4463 fc_dev,
4464 rd,
4465 eps,
4466 pend_kv.as_mut().expect("pend"),
4467 pend_score.as_mut().expect("pend"),
4468 kvc,
4469 win,
4470 n_blocks,
4471 )?;
4472 debug_assert_eq!(*n_blocks, (pos + 1) / layer.ratio, "attn block count");
4473 idxs.extend_from_slice(&cidx);
4474 }
4475 let slots = idxs.len();
4476 let idxs_i32: Vec<i32> = idxs.iter().map(|&v| v as i32).collect();
4477 let idx_dev = upload_i32(&stream, &idxs_i32)?;
4478
4479 let mut o = stream.alloc_zeros::<f32>(heads * hd).map_err(e("o"))?;
4481 let scale = (hd as f64).powf(-0.5) as f32;
4482 unsafe {
4483 ck(
4484 "sink_attn dec",
4485 k::memra_dsv4_sink_attn(
4486 dpf!(q, &stream),
4487 dpf!(kvc, &stream),
4488 idx_dev.device_ptr(&stream).0 as *const i32,
4489 dpf!(layer.sink, &stream),
4490 dpm!(o, &stream),
4491 1,
4492 heads as i32,
4493 hd as i32,
4494 slots as i32,
4495 scale,
4496 sp(&stream),
4497 ),
4498 )?;
4499 ck(
4500 "rope o inv",
4501 k::memra_dsv4_rope(
4502 dpm!(o, &stream),
4503 1,
4504 heads as i32,
4505 hd as i32,
4506 rd as i32,
4507 dpf!(fc_dev, &stream),
4508 pos_dev.device_ptr(&stream).0 as *const i32,
4509 1,
4510 sp(&stream),
4511 ),
4512 )?;
4513 }
4514
4515 if let Some(dm) = dump.as_deref_mut() {
4516 dm.push((format!("layer{}.o", layer.il), dtoh_f32(&stream, &o)?));
4517 }
4518 let gw = heads / o_groups * hd;
4520 let mut og = stream
4521 .alloc_zeros::<f32>(o_groups * o_lora)
4522 .map_err(e("og"))?;
4523 let mut o_grp = stream.alloc_zeros::<f32>(gw).map_err(e("o_grp"))?;
4524 let mut y_grp = stream.alloc_zeros::<f32>(o_lora).map_err(e("y_grp"))?;
4525 for g in 0..o_groups {
4526 unsafe {
4527 ck(
4528 "take_cols",
4529 k::memra_dsv4_take_cols(
4530 dpf!(o, &stream),
4531 dpm!(o_grp, &stream),
4532 1,
4533 gw as i32,
4534 (heads * hd) as i64,
4535 (g * gw) as i64,
4536 sp(&stream),
4537 ),
4538 )?;
4539 }
4540 Self::gemm(
4541 st,
4542 &o_grp,
4543 layer.wo_a.dev(),
4544 g * o_lora * gw,
4545 1,
4546 o_lora,
4547 gw,
4548 &mut y_grp,
4549 )?;
4550 unsafe {
4551 ck(
4552 "place_cols",
4553 k::memra_dsv4_place_cols(
4554 dpf!(y_grp, &stream),
4555 dpm!(og, &stream),
4556 1,
4557 o_lora as i32,
4558 (o_groups * o_lora) as i64,
4559 (g * o_lora) as i64,
4560 sp(&stream),
4561 ),
4562 )?;
4563 }
4564 }
4565 let mut attn_out = stream.alloc_zeros::<f32>(hidden).map_err(e("ao"))?;
4566 Self::gemm(
4567 st,
4568 &og,
4569 layer.wo_b.dev(),
4570 0,
4571 1,
4572 hidden,
4573 o_groups * o_lora,
4574 &mut attn_out,
4575 )?;
4576
4577 if let Some(dm) = dump.as_deref_mut() {
4578 dm.push((
4579 format!("layer{}.attn_out", layer.il),
4580 dtoh_f32(&stream, &attn_out)?,
4581 ));
4582 }
4583 let mut h2 = stream.alloc_zeros::<f32>(hc * hidden).map_err(e("h2"))?;
4585 unsafe {
4586 ck(
4587 "hc_post attn",
4588 k::memra_dsv4_hc_post(
4589 dpf!(attn_out, &stream),
4590 dpf!(h, &stream),
4591 dpf!(post, &stream),
4592 dpf!(comb, &stream),
4593 dpm!(h2, &stream),
4594 1,
4595 hc as i32,
4596 hidden as i32,
4597 sp(&stream),
4598 ),
4599 )?;
4600 }
4601
4602 let (y2, post2, comb2) = Self::hc_pre(
4604 st,
4605 &h2,
4606 &layer.hc_ffn_fn,
4607 &layer.hc_ffn_base,
4608 &layer.hc_ffn_scale,
4609 1,
4610 hc,
4611 hidden,
4612 iters,
4613 hc_eps,
4614 )?;
4615 let mut xf = stream.alloc_zeros::<f32>(hidden).map_err(e("xf"))?;
4616 unsafe {
4617 ck(
4618 "rmsnorm ffn",
4619 k::memra_dsv4_rmsnorm(
4620 dpf!(y2, &stream),
4621 dpf!(layer.ffn_norm, &stream),
4622 dpm!(xf, &stream),
4623 1,
4624 hidden as i32,
4625 eps,
4626 sp(&stream),
4627 ),
4628 )?;
4629 }
4630 let moe_out = self.moe_forward(st, layer, &xf, 1, &[tok])?;
4631 if let Some(dm) = dump.as_deref_mut() {
4632 dm.push((
4633 format!("layer{}.moe_out", layer.il),
4634 dtoh_f32(&stream, &moe_out)?,
4635 ));
4636 }
4637 let mut h3 = stream.alloc_zeros::<f32>(hc * hidden).map_err(e("h3"))?;
4638 unsafe {
4639 ck(
4640 "hc_post ffn",
4641 k::memra_dsv4_hc_post(
4642 dpf!(moe_out, &stream),
4643 dpf!(h2, &stream),
4644 dpf!(post2, &stream),
4645 dpf!(comb2, &stream),
4646 dpm!(h3, &stream),
4647 1,
4648 hc as i32,
4649 hidden as i32,
4650 sp(&stream),
4651 ),
4652 )?;
4653 }
4654 if let Some(dm) = dump.as_deref_mut() {
4655 dm.push((format!("layer{}.h3", layer.il), dtoh_f32(&stream, &h3)?));
4656 }
4657 Ok(h3)
4658 }
4659
4660 pub fn decode_step(&self, tok: u32, state: &mut DecodeState) -> Res<Vec<f32>> {
4665 self.decode_step_impl(tok, state, None)
4666 }
4667
4668 pub fn decode_step_probe(
4670 &self,
4671 tok: u32,
4672 state: &mut DecodeState,
4673 ) -> Res<(Vec<f32>, Vec<(String, Vec<f32>)>)> {
4674 let mut dump = Vec::new();
4675 let logits = self.decode_step_impl(tok, state, Some(&mut dump))?;
4676 Ok((logits, dump))
4677 }
4678
4679 #[allow(clippy::too_many_arguments)]
4685 fn gemm_dev(
4686 st: &Stage,
4687 x_f32: *const f32,
4688 xb: &mut CudaSlice<u8>,
4689 w: DW,
4690 m: usize,
4691 n: usize,
4692 kdim: usize,
4693 y_ptr: *mut f32,
4694 ) -> Res<()> {
4695 assert_eq!(m, 1, "gemm_dev is the m=1 decode path");
4696 let stream = st.gpu.stream();
4697 unsafe {
4698 ck(
4699 "cvt_bf16 dev",
4700 k::memra_dsv4_cvt_bf16(
4701 x_f32,
4702 xb.device_ptr_mut(&stream).0 as *mut c_void,
4703 kdim as i64,
4704 sp(&stream),
4705 ),
4706 )?;
4707 }
4708 let xb_ptr = xb.device_ptr(&stream).0 as *const c_void;
4709 Self::gemv_pre_dev(st, xb_ptr, w, n, kdim, y_ptr)
4710 }
4711
4712 fn gemv_pre_dev(
4716 st: &Stage,
4717 xb_ptr: *const c_void,
4718 w: DW,
4719 n: usize,
4720 kdim: usize,
4721 y_ptr: *mut f32,
4722 ) -> Res<()> {
4723 let stream = st.gpu.stream();
4724 unsafe {
4725 match w {
4726 DW::Bf16(w_ptr) => ck(
4727 "gemv_bf16 pre dev",
4728 k::memra_dsv4_gemv_bf16(
4729 w_ptr,
4730 xb_ptr,
4731 y_ptr,
4732 n as i32,
4733 kdim as i32,
4734 sp(&stream),
4735 ),
4736 )?,
4737 DW::Fp8 {
4738 codes,
4739 scales,
4740 sc_cols,
4741 } => ck(
4742 "gemv_fp8 pre dev",
4743 k::memra_dsv4_gemv_fp8(
4744 codes,
4745 scales,
4746 sc_cols,
4747 xb_ptr,
4748 y_ptr,
4749 n as i32,
4750 kdim as i32,
4751 sp(&stream),
4752 ),
4753 )?,
4754 }
4755 }
4756 Ok(())
4757 }
4758
4759 #[allow(clippy::too_many_arguments)]
4764 #[allow(clippy::too_many_arguments)]
4769 unsafe fn rmsnorm_arm(
4770 &self,
4771 x: *const f32,
4772 w: *const f32,
4773 dst: *mut f32,
4774 rows: i32,
4775 ncols: i32,
4776 eps: f32,
4777 sv: *mut c_void,
4778 ) -> i32 {
4779 unsafe {
4780 if self.chains_f32 {
4781 k::memra_dsv4_rmsnorm_f32acc(x, w, dst, rows, ncols, eps, sv)
4782 } else {
4783 k::memra_dsv4_rmsnorm(x, w, dst, rows, ncols, eps, sv)
4784 }
4785 }
4786 }
4787
4788 unsafe fn headrms_arm(&self, x: *mut f32, rows: i32, d: i32, eps: f32, sv: *mut c_void) -> i32 {
4789 unsafe {
4790 if self.chains_f32 {
4791 k::memra_dsv4_headrms_f32acc(x, rows, d, eps, sv)
4792 } else {
4793 k::memra_dsv4_headrms(x, rows, d, eps, sv)
4794 }
4795 }
4796 }
4797
4798 #[allow(clippy::too_many_arguments)]
4799 unsafe fn rowsq_scale_arm(
4800 &self,
4801 x: *const f32,
4802 mixes: *mut f32,
4803 s: i32,
4804 w: i32,
4805 rows: i32,
4806 eps: f32,
4807 sv: *mut c_void,
4808 ) -> i32 {
4809 unsafe {
4810 if self.chains_f32 {
4811 k::memra_dsv4_rowsq_scale_f32acc(x, mixes, s, w, rows, eps, sv)
4812 } else {
4813 k::memra_dsv4_rowsq_scale(x, mixes, s, w, rows, eps, sv)
4814 }
4815 }
4816 }
4817
4818 #[allow(clippy::too_many_arguments)]
4819 unsafe fn indexer_score_arm(
4820 &self,
4821 q: *const f32,
4822 ckv: *const f32,
4823 w: *const f32,
4824 wscale: f32,
4825 score: *mut f32,
4826 s: i32,
4827 heads: i32,
4828 hd: i32,
4829 nb: i32,
4830 ratio: i32,
4831 lim0: i32,
4832 sv: *mut c_void,
4833 ) -> i32 {
4834 unsafe {
4835 if self.chains_f32 {
4836 k::memra_dsv4_indexer_score_f32acc(
4837 q, ckv, w, wscale, score, s, heads, hd, nb, ratio, lim0, sv,
4838 )
4839 } else {
4840 k::memra_dsv4_indexer_score(
4841 q, ckv, w, wscale, score, s, heads, hd, nb, ratio, lim0, sv,
4842 )
4843 }
4844 }
4845 }
4846
4847 #[allow(clippy::too_many_arguments)]
4850 unsafe fn sink_attn_dec_arm(
4851 &self,
4852 q: *const f32,
4853 kv: *const f32,
4854 idxs: *const i32,
4855 sink: *const f32,
4856 scores: *mut f32,
4857 evals: *mut f32,
4858 den: *mut f64,
4859 o: *mut f32,
4860 heads: i32,
4861 hd: i32,
4862 slots: i32,
4863 scale: f32,
4864 sv: *mut c_void,
4865 ) -> i32 {
4866 unsafe {
4867 if self.chains_f32 {
4868 k::memra_dsv4_sink_attn_dec_f32acc(
4869 q,
4870 kv,
4871 idxs,
4872 sink,
4873 scores,
4874 evals,
4875 den as *mut f32,
4876 o,
4877 heads,
4878 hd,
4879 slots,
4880 scale,
4881 sv,
4882 )
4883 } else {
4884 k::memra_dsv4_sink_attn_dec(
4885 q, kv, idxs, sink, scores, evals, den, o, heads, hd, slots, scale, sv,
4886 )
4887 }
4888 }
4889 }
4890
4891 fn hc_pre_dev(
4892 &self,
4893 st: &Stage,
4894 h: &CudaSlice<f32>,
4895 fn_w: &CudaSlice<f32>,
4896 base_host: &[f32],
4897 scale_host: &[f32],
4898 base_dev: &CudaSlice<f32>,
4899 scale_dev: &CudaSlice<f32>,
4900 mixes: &mut CudaSlice<f32>,
4901 pre: &mut CudaSlice<f32>,
4902 post: &mut CudaSlice<f32>,
4903 comb: &mut CudaSlice<f32>,
4904 y_hc: &mut CudaSlice<f32>,
4905 hc: usize,
4906 hidden: usize,
4907 iters: u32,
4908 hc_eps: f32,
4909 host_math: bool,
4910 ) -> Res<()> {
4911 let stream = st.gpu.stream();
4912 let w = hc * hidden;
4913 let rows = (2 + hc) * hc;
4914 self.dots_dev(st, h, fn_w, 1, w, rows, mixes)?;
4915 unsafe {
4916 ck(
4917 "rowsq_scale dev",
4918 self.rowsq_scale_arm(
4919 dpf!(h, &stream),
4920 dpm!(*mixes, &stream),
4921 1,
4922 w as i32,
4923 rows as i32,
4924 hc_eps,
4925 sp(&stream),
4926 ),
4927 )?;
4928 }
4929 if host_math {
4930 let mixes_h = dtoh_f32(&stream, mixes)?;
4931 let (pre_h, post_h, comb_h) =
4932 hc_split_sinkhorn(&mixes_h, 1, hc, scale_host, base_host, iters, hc_eps);
4933 stream.memcpy_htod(&pre_h, pre).map_err(e("htod pre"))?;
4934 stream.memcpy_htod(&post_h, post).map_err(e("htod post"))?;
4935 stream.memcpy_htod(&comb_h, comb).map_err(e("htod comb"))?;
4936 } else {
4937 unsafe {
4938 ck(
4939 "hc_sinkhorn",
4940 k::memra_dsv4_hc_sinkhorn(
4941 dpf!(*mixes, &stream),
4942 dpf!(scale_dev, &stream),
4943 dpf!(base_dev, &stream),
4944 dpm!(*pre, &stream),
4945 dpm!(*post, &stream),
4946 dpm!(*comb, &stream),
4947 hc as i32,
4948 iters as i32,
4949 hc_eps,
4950 sp(&stream),
4951 ),
4952 )?;
4953 }
4954 }
4955 unsafe {
4956 ck(
4957 "hc_collapse dev",
4958 k::memra_dsv4_hc_collapse(
4959 dpf!(h, &stream),
4960 dpf!(*pre, &stream),
4961 dpm!(*y_hc, &stream),
4962 1,
4963 hc as i32,
4964 hidden as i32,
4965 sp(&stream),
4966 ),
4967 )?;
4968 }
4969 Ok(())
4970 }
4971
4972 #[allow(clippy::too_many_arguments)]
4976 fn cmp_decode_dev(
4977 &self,
4978 st: &Stage,
4979 cmp: &CmpDev,
4980 x: &CudaSlice<f32>,
4981 pos: usize,
4982 hidden: usize,
4983 fc_dev: &CudaSlice<f32>,
4984 rd: usize,
4985 eps: f32,
4986 kv_row: &mut CudaSlice<f32>,
4987 sc_row: &mut CudaSlice<f32>,
4988 emit: &mut CudaSlice<f32>,
4989 shift: &mut CudaSlice<f32>,
4990 pend_kv: &mut CudaSlice<f32>,
4991 pend_score: &mut CudaSlice<f32>,
4992 store: &mut CudaSlice<f32>,
4993 row0: usize,
4994 blocks: &mut usize,
4995 ) -> Res<()> {
4996 let stream = st.gpu.stream();
4997 let (ratio, d, latent) = (cmp.ratio, cmp.d, cmp.latent);
4998 self.dots_dev(st, x, &cmp.wkv, 1, hidden, latent, kv_row)?;
4999 self.dots_dev(st, x, &cmp.wgate, 1, hidden, latent, sc_row)?;
5000 let slot = if cmp.overlap {
5001 ratio + pos % ratio
5002 } else {
5003 pos % ratio
5004 };
5005 {
5006 let src = kv_row.slice(0..latent);
5007 let mut dst = pend_kv.slice_mut(slot * latent..(slot + 1) * latent);
5008 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend kv"))?;
5009 let src = sc_row.slice(0..latent);
5010 let mut dst = pend_score.slice_mut(slot * latent..(slot + 1) * latent);
5011 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend sc"))?;
5012 }
5013 if (pos + 1) % ratio != 0 {
5014 return Ok(());
5015 }
5016 let j = pos / ratio;
5017 let nb_launch = if cmp.overlap { 2usize } else { 1 };
5018 let row_off = if cmp.overlap { d } else { 0 };
5019 unsafe {
5020 ck(
5021 "compressor_pool dec",
5022 k::memra_dsv4_compressor_pool(
5023 dpf!(*pend_kv, &stream),
5024 dpf!(*pend_score, &stream),
5025 dpf!(cmp.ape, &stream),
5026 dpm!(*emit, &stream),
5027 nb_launch as i32,
5028 ratio as i32,
5029 d as i32,
5030 latent as i32,
5031 cmp.overlap as i32,
5032 sp(&stream),
5033 ),
5034 )?;
5035 let row_c = (emit.device_ptr(&stream).0 as usize + row_off * 4) as *const f32;
5036 let row_m = (emit.device_ptr_mut(&stream).0 as usize + row_off * 4) as *mut f32;
5037 ck(
5038 "rmsnorm dec cmp",
5039 self.rmsnorm_arm(
5040 row_c,
5041 dpf!(cmp.norm, &stream),
5042 row_m,
5043 1,
5044 d as i32,
5045 eps,
5046 sp(&stream),
5047 ),
5048 )?;
5049 ck(
5050 "rope_at dec cmp",
5051 k::memra_dsv4_rope_at(
5052 row_m,
5053 1,
5054 d as i32,
5055 rd as i32,
5056 dpf!(fc_dev, &stream),
5057 (j * ratio) as i32,
5058 0,
5059 sp(&stream),
5060 ),
5061 )?;
5062 if cmp.rotate {
5063 let scale = (d as f32).powf(-0.5);
5064 ck(
5065 "hadamard dec cmp",
5066 k::memra_dsv4_hadamard(row_m, 1, d as i32, scale, sp(&stream)),
5067 )?;
5068 ck(
5069 "fp4 dec cmp",
5070 k::memra_dsv4_fp4_act_quant(row_m, 1, d as i64, d as i32, sp(&stream)),
5071 )?;
5072 } else {
5073 ck(
5074 "act_quant dec cmp",
5075 k::memra_dsv4_act_quant(
5076 row_m,
5077 1,
5078 d as i64,
5079 (d - rd) as i32,
5080 64,
5081 (self.variant == ActQuantVariant::ClampOnly) as i32,
5082 sp(&stream),
5083 ),
5084 )?;
5085 }
5086 }
5087 {
5088 let src = emit.slice(row_off..row_off + d);
5089 let mut dst = store.slice_mut((row0 + j) * d..(row0 + j + 1) * d);
5090 stream
5091 .memcpy_dtod(&src, &mut dst)
5092 .map_err(e("emit store"))?;
5093 }
5094 if cmp.overlap {
5095 {
5096 let src = pend_kv.slice(ratio * latent..2 * ratio * latent);
5097 let mut dst = shift.slice_mut(0..ratio * latent);
5098 stream.memcpy_dtod(&src, &mut dst).map_err(e("shift1"))?;
5099 }
5100 {
5101 let src = shift.slice(0..ratio * latent);
5102 let mut dst = pend_kv.slice_mut(0..ratio * latent);
5103 stream.memcpy_dtod(&src, &mut dst).map_err(e("shift2"))?;
5104 }
5105 {
5106 let src = pend_score.slice(ratio * latent..2 * ratio * latent);
5107 let mut dst = shift.slice_mut(0..ratio * latent);
5108 stream.memcpy_dtod(&src, &mut dst).map_err(e("shift3"))?;
5109 }
5110 {
5111 let src = shift.slice(0..ratio * latent);
5112 let mut dst = pend_score.slice_mut(0..ratio * latent);
5113 stream.memcpy_dtod(&src, &mut dst).map_err(e("shift4"))?;
5114 }
5115 }
5116 *blocks = j + 1;
5117 Ok(())
5118 }
5119
5120 #[allow(clippy::too_many_arguments)]
5125 fn block_decode_dev(
5126 &self,
5127 st: &Stage,
5128 layer: &LayerDev,
5129 cache: &mut LayerCache,
5130 ws: &mut StepWs,
5131 input_rx: bool,
5132 pos: usize,
5133 tok: u32,
5134 host_math: bool,
5135 ) -> Res<()> {
5136 let d = self.model.cfg();
5137 let mc = &self.model.mc;
5138 let hc = d.hc_mult as usize;
5139 let hidden = mc.n_embd as usize;
5140 let heads = mc.n_head as usize;
5141 let hd = d.head_dim as usize;
5142 let rd = d.qk_rope_head_dim as usize;
5143 let q_lora = d.q_lora_rank as usize;
5144 let win = d.sliding_window as usize;
5145 let o_groups = d.o_groups as usize;
5146 let o_lora = d.o_lora_rank as usize;
5147 let eps = mc.rms_eps;
5148 let iters = d.hc_sinkhorn_iters;
5149 let hc_eps = d.hc_eps;
5150 let stream = st.gpu.stream();
5151 let fc_dev: *const f32 = if layer.ratio != 0 {
5152 st.fc_yarn.device_ptr(&stream).0 as *const f32
5153 } else {
5154 st.fc_plain.device_ptr(&stream).0 as *const f32
5155 };
5156 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
5157 let LayerCache {
5158 kvc,
5159 n_blocks,
5160 pend_kv,
5161 pend_score,
5162 ikvc,
5163 i_blocks,
5164 ipend_kv,
5165 ipend_score,
5166 } = cache;
5167
5168 {
5170 let StepWs {
5172 h_a,
5173 h_rx,
5174 mixes,
5175 pre,
5176 post,
5177 comb,
5178 y_hc,
5179 ..
5180 } = ws;
5181 let h_in: &CudaSlice<f32> = if input_rx { h_rx } else { h_a };
5182 self.hc_pre_dev(
5183 st,
5184 h_in,
5185 &layer.hc_attn_fn,
5186 &layer.hc_attn_base,
5187 &layer.hc_attn_scale,
5188 &layer.hc_attn_base_dev,
5189 &layer.hc_attn_scale_dev,
5190 mixes,
5191 pre,
5192 post,
5193 comb,
5194 y_hc,
5195 hc,
5196 hidden,
5197 iters,
5198 hc_eps,
5199 host_math,
5200 )?;
5201 }
5202 unsafe {
5203 ck(
5204 "rmsnorm attn dev",
5205 self.rmsnorm_arm(
5206 dpf!(ws.y_hc, &stream),
5207 dpf!(layer.attn_norm, &stream),
5208 dpm!(ws.x, &stream),
5209 1,
5210 hidden as i32,
5211 eps,
5212 sp(&stream),
5213 ),
5214 )?;
5215 }
5216
5217 Self::gemm_dev(
5219 st,
5220 ws.x.device_ptr(&stream).0 as *const f32,
5221 &mut ws.gemm_xb,
5222 dwsel(self.dense_fp8, &stream, &layer.wq_a, &layer.wq_a_fp8),
5223 1,
5224 q_lora,
5225 hidden,
5226 ws.qr.device_ptr_mut(&stream).0 as *mut f32,
5227 )?;
5228 unsafe {
5229 ck(
5230 "rmsnorm q dev",
5231 self.rmsnorm_arm(
5232 dpf!(ws.qr, &stream),
5233 dpf!(layer.q_norm, &stream),
5234 dpm!(ws.qr, &stream),
5235 1,
5236 q_lora as i32,
5237 eps,
5238 sp(&stream),
5239 ),
5240 )?;
5241 ck(
5242 "cvt qr dev",
5243 k::memra_dsv4_cvt_bf16(
5244 dpf!(ws.qr, &stream),
5245 ws.qr_b.device_ptr_mut(&stream).0 as *mut c_void,
5246 q_lora as i64,
5247 sp(&stream),
5248 ),
5249 )?;
5250 }
5251 Self::gemv_pre_dev(
5252 st,
5253 ws.qr_b.device_ptr(&stream).0 as *const c_void,
5254 dwsel(self.dense_fp8, &stream, &layer.wq_b, &layer.wq_b_fp8),
5255 heads * hd,
5256 q_lora,
5257 ws.q.device_ptr_mut(&stream).0 as *mut f32,
5258 )?;
5259 unsafe {
5260 ck(
5261 "headrms dev",
5262 self.headrms_arm(
5263 dpm!(ws.q, &stream),
5264 heads as i32,
5265 hd as i32,
5266 eps,
5267 sp(&stream),
5268 ),
5269 )?;
5270 ck(
5271 "rope_at q dev",
5272 k::memra_dsv4_rope_at(
5273 dpm!(ws.q, &stream),
5274 heads as i32,
5275 hd as i32,
5276 rd as i32,
5277 fc_dev,
5278 pos as i32,
5279 0,
5280 sp(&stream),
5281 ),
5282 )?;
5283 }
5284
5285 Self::gemm_dev(
5287 st,
5288 ws.x.device_ptr(&stream).0 as *const f32,
5289 &mut ws.gemm_xb,
5290 dwsel(self.dense_fp8, &stream, &layer.wkv, &layer.wkv_fp8),
5291 1,
5292 hd,
5293 hidden,
5294 ws.kv.device_ptr_mut(&stream).0 as *mut f32,
5295 )?;
5296 unsafe {
5297 ck(
5298 "rmsnorm kv dev",
5299 self.rmsnorm_arm(
5300 dpf!(ws.kv, &stream),
5301 dpf!(layer.kv_norm, &stream),
5302 dpm!(ws.kv, &stream),
5303 1,
5304 hd as i32,
5305 eps,
5306 sp(&stream),
5307 ),
5308 )?;
5309 ck(
5310 "rope_at kv dev",
5311 k::memra_dsv4_rope_at(
5312 dpm!(ws.kv, &stream),
5313 1,
5314 hd as i32,
5315 rd as i32,
5316 fc_dev,
5317 pos as i32,
5318 0,
5319 sp(&stream),
5320 ),
5321 )?;
5322 ck(
5323 "act_quant kv dev",
5324 k::memra_dsv4_act_quant(
5325 dpm!(ws.kv, &stream),
5326 1,
5327 hd as i64,
5328 (hd - rd) as i32,
5329 64,
5330 clamp_only,
5331 sp(&stream),
5332 ),
5333 )?;
5334 }
5335 {
5336 let slot = pos % win;
5337 let src = ws.kv.slice(0..hd);
5338 let mut dst = kvc.slice_mut(slot * hd..(slot + 1) * hd);
5339 stream
5340 .memcpy_dtod(&src, &mut dst)
5341 .map_err(e("ring write"))?;
5342 }
5343
5344 let mut slots = win;
5346 if layer.ratio != 0 {
5347 if let Some(ix) = &layer.idx {
5348 Self::gemv_pre_dev(
5350 st,
5351 ws.qr_b.device_ptr(&stream).0 as *const c_void,
5352 dwsel(self.dense_fp8, &stream, &ix.wq_b, &ix.wq_b_fp8),
5353 ix.heads * ix.hd,
5354 q_lora,
5355 ws.qi.device_ptr_mut(&stream).0 as *mut f32,
5356 )?;
5357 unsafe {
5358 ck(
5359 "rope_at qi dev",
5360 k::memra_dsv4_rope_at(
5361 dpm!(ws.qi, &stream),
5362 ix.heads as i32,
5363 ix.hd as i32,
5364 rd as i32,
5365 fc_dev,
5366 pos as i32,
5367 0,
5368 sp(&stream),
5369 ),
5370 )?;
5371 let scale = (ix.hd as f32).powf(-0.5);
5372 ck(
5373 "hadamard qi dev",
5374 k::memra_dsv4_hadamard(
5375 dpm!(ws.qi, &stream),
5376 ix.heads as i32,
5377 ix.hd as i32,
5378 scale,
5379 sp(&stream),
5380 ),
5381 )?;
5382 ck(
5383 "fp4 qi dev",
5384 k::memra_dsv4_fp4_act_quant(
5385 dpm!(ws.qi, &stream),
5386 ix.heads as i32,
5387 ix.hd as i64,
5388 ix.hd as i32,
5389 sp(&stream),
5390 ),
5391 )?;
5392 }
5393 {
5395 let StepWs {
5396 x,
5397 cmp_kv_row,
5398 cmp_sc_row,
5399 cmp_emit,
5400 cmp_shift,
5401 ..
5402 } = ws;
5403 self.cmp_decode_dev(
5404 st,
5405 &ix.cmp,
5406 x,
5407 pos,
5408 hidden,
5409 if layer.ratio != 0 {
5410 &st.fc_yarn
5411 } else {
5412 &st.fc_plain
5413 },
5414 rd,
5415 eps,
5416 cmp_kv_row,
5417 cmp_sc_row,
5418 cmp_emit,
5419 cmp_shift,
5420 ipend_kv.as_mut().expect("ipend"),
5421 ipend_score.as_mut().expect("ipend"),
5422 ikvc.as_mut().expect("ikvc"),
5423 0,
5424 i_blocks,
5425 )?;
5426 }
5427 let nb = *i_blocks;
5428 debug_assert_eq!(nb, (pos + 1) / layer.ratio, "indexer block count");
5429 unsafe {
5431 ck(
5432 "build_idx win",
5433 k::memra_dsv4_build_idx(
5434 ws.idx.device_ptr_mut(&stream).0 as *mut i32,
5435 pos as i32,
5436 win as i32,
5437 -1,
5438 win as i32,
5439 sp(&stream),
5440 ),
5441 )?;
5442 }
5443 if nb > 0 {
5444 Self::gemm_dev(
5445 st,
5446 ws.x.device_ptr(&stream).0 as *const f32,
5447 &mut ws.gemm_xb,
5448 dwsel(
5449 self.dense_fp8,
5450 &stream,
5451 &ix.weights_proj,
5452 &ix.weights_proj_fp8,
5453 ),
5454 1,
5455 ix.heads,
5456 hidden,
5457 ws.wproj.device_ptr_mut(&stream).0 as *mut f32,
5458 )?;
5459 let wscale = ((ix.hd as f64).powf(-0.5) * (ix.heads as f64).powf(-0.5)) as f32;
5460 unsafe {
5461 ck(
5462 "indexer_score dev",
5463 self.indexer_score_arm(
5464 dpf!(ws.qi, &stream),
5465 dpf!(ikvc.as_ref().expect("ikvc"), &stream),
5466 dpf!(ws.wproj, &stream),
5467 wscale,
5468 dpm!(ws.score, &stream),
5469 1,
5470 ix.heads as i32,
5471 ix.hd as i32,
5472 nb as i32,
5473 layer.ratio as i32,
5474 nb as i32,
5475 sp(&stream),
5476 ),
5477 )?;
5478 }
5479 let kk = ix.topk.min(nb);
5480 if host_math {
5481 let score_h = {
5484 let view = ws.score.slice(0..nb);
5485 let mut v = vec![0f32; nb];
5486 stream
5487 .memcpy_dtoh(&view, &mut v[..])
5488 .map_err(e("dtoh sc"))?;
5489 stream.synchronize().map_err(e("sync sc"))?;
5490 v
5491 };
5492 let mut order: Vec<usize> = (0..nb).collect();
5493 order.sort_by(|&a, &b| {
5494 score_h[b]
5495 .partial_cmp(&score_h[a])
5496 .unwrap_or(std::cmp::Ordering::Equal)
5497 .then(a.cmp(&b))
5498 });
5499 let cidx: Vec<i32> = order
5500 .into_iter()
5501 .take(kk)
5502 .map(|j| (j + win) as i32)
5503 .collect();
5504 let mut dst = ws.idx.slice_mut(win..win + kk);
5505 stream.memcpy_htod(&cidx, &mut dst).map_err(e("htod idx"))?;
5506 } else {
5507 unsafe {
5508 let idx_tail =
5509 (ws.idx.device_ptr_mut(&stream).0 as usize + win * 4) as *mut i32;
5510 ck(
5511 "topk_idx dev",
5512 k::memra_dsv4_topk_idx(
5513 dpf!(ws.score, &stream),
5514 nb as i32,
5515 kk as i32,
5516 win as i32,
5517 idx_tail,
5518 sp(&stream),
5519 ),
5520 )?;
5521 }
5522 }
5523 slots = win + kk;
5524 }
5525 } else {
5526 let nb = (pos + 1) / layer.ratio;
5529 unsafe {
5530 ck(
5531 "build_idx coarse",
5532 k::memra_dsv4_build_idx(
5533 ws.idx.device_ptr_mut(&stream).0 as *mut i32,
5534 pos as i32,
5535 win as i32,
5536 nb as i32,
5537 (win + nb) as i32,
5538 sp(&stream),
5539 ),
5540 )?;
5541 }
5542 slots = win + nb;
5543 }
5544 {
5546 let StepWs {
5547 x,
5548 cmp_kv_row,
5549 cmp_sc_row,
5550 cmp_emit,
5551 cmp_shift,
5552 ..
5553 } = ws;
5554 self.cmp_decode_dev(
5555 st,
5556 layer.cmp.as_ref().expect("ratio!=0 has compressor"),
5557 x,
5558 pos,
5559 hidden,
5560 &st.fc_yarn,
5561 rd,
5562 eps,
5563 cmp_kv_row,
5564 cmp_sc_row,
5565 cmp_emit,
5566 cmp_shift,
5567 pend_kv.as_mut().expect("pend"),
5568 pend_score.as_mut().expect("pend"),
5569 kvc,
5570 win,
5571 n_blocks,
5572 )?;
5573 }
5574 debug_assert_eq!(*n_blocks, (pos + 1) / layer.ratio, "attn block count");
5575 } else {
5576 unsafe {
5578 ck(
5579 "build_idx window-only",
5580 k::memra_dsv4_build_idx(
5581 ws.idx.device_ptr_mut(&stream).0 as *mut i32,
5582 pos as i32,
5583 win as i32,
5584 -1,
5585 win as i32,
5586 sp(&stream),
5587 ),
5588 )?;
5589 }
5590 }
5591
5592 let scale = (hd as f64).powf(-0.5) as f32;
5595 unsafe {
5596 ck(
5597 "sink_attn_dec dev",
5598 self.sink_attn_dec_arm(
5599 dpf!(ws.q, &stream),
5600 dpf!(kvc, &stream),
5601 ws.idx.device_ptr(&stream).0 as *const i32,
5602 dpf!(layer.sink, &stream),
5603 dpm!(ws.sink_scores, &stream),
5604 dpm!(ws.sink_evals, &stream),
5605 ws.sink_den.device_ptr_mut(&stream).0 as *mut f64,
5606 dpm!(ws.o, &stream),
5607 heads as i32,
5608 hd as i32,
5609 slots as i32,
5610 scale,
5611 sp(&stream),
5612 ),
5613 )?;
5614 ck(
5615 "rope_at o inv dev",
5616 k::memra_dsv4_rope_at(
5617 dpm!(ws.o, &stream),
5618 heads as i32,
5619 hd as i32,
5620 rd as i32,
5621 fc_dev,
5622 pos as i32,
5623 1,
5624 sp(&stream),
5625 ),
5626 )?;
5627 }
5628
5629 let gw = heads / o_groups * hd;
5633 unsafe {
5634 ck(
5635 "cvt o dev",
5636 k::memra_dsv4_cvt_bf16(
5637 dpf!(ws.o, &stream),
5638 ws.o_b.device_ptr_mut(&stream).0 as *mut c_void,
5639 (heads * hd) as i64,
5640 sp(&stream),
5641 ),
5642 )?;
5643 }
5644 let wo_a_dw = dwsel(self.dense_fp8, &stream, &layer.wo_a, &layer.wo_a_fp8);
5645 for g in 0..o_groups {
5646 Self::gemv_pre_dev(
5647 st,
5648 (ws.o_b.device_ptr(&stream).0 as usize + g * gw * 2) as *const c_void,
5649 wo_a_dw.offset_rows(g * o_lora, gw),
5650 o_lora,
5651 gw,
5652 (ws.og.device_ptr_mut(&stream).0 as usize + g * o_lora * 4) as *mut f32,
5653 )?;
5654 }
5655 Self::gemm_dev(
5656 st,
5657 ws.og.device_ptr(&stream).0 as *const f32,
5658 &mut ws.gemm_xb,
5659 dwsel(self.dense_fp8, &stream, &layer.wo_b, &layer.wo_b_fp8),
5660 1,
5661 hidden,
5662 o_groups * o_lora,
5663 ws.attn_out.device_ptr_mut(&stream).0 as *mut f32,
5664 )?;
5665
5666 {
5668 let StepWs {
5669 h_a,
5670 h_b,
5671 h_rx,
5672 attn_out,
5673 post,
5674 comb,
5675 ..
5676 } = ws;
5677 let h_in: &CudaSlice<f32> = if input_rx { h_rx } else { h_a };
5678 unsafe {
5679 ck(
5680 "hc_post attn dev",
5681 k::memra_dsv4_hc_post(
5682 dpf!(attn_out, &stream),
5683 dpf!(h_in, &stream),
5684 dpf!(post, &stream),
5685 dpf!(comb, &stream),
5686 dpm!(*h_b, &stream),
5687 1,
5688 hc as i32,
5689 hidden as i32,
5690 sp(&stream),
5691 ),
5692 )?;
5693 }
5694 }
5695
5696 {
5698 let StepWs {
5699 h_b,
5700 mixes,
5701 pre,
5702 post,
5703 comb,
5704 y_hc,
5705 ..
5706 } = ws;
5707 self.hc_pre_dev(
5708 st,
5709 h_b,
5710 &layer.hc_ffn_fn,
5711 &layer.hc_ffn_base,
5712 &layer.hc_ffn_scale,
5713 &layer.hc_ffn_base_dev,
5714 &layer.hc_ffn_scale_dev,
5715 mixes,
5716 pre,
5717 post,
5718 comb,
5719 y_hc,
5720 hc,
5721 hidden,
5722 iters,
5723 hc_eps,
5724 host_math,
5725 )?;
5726 }
5727 unsafe {
5728 ck(
5729 "rmsnorm ffn dev",
5730 self.rmsnorm_arm(
5731 dpf!(ws.y_hc, &stream),
5732 dpf!(layer.ffn_norm, &stream),
5733 dpm!(ws.xf, &stream),
5734 1,
5735 hidden as i32,
5736 eps,
5737 sp(&stream),
5738 ),
5739 )?;
5740 }
5741 self.moe_forward_dev(st, layer, ws, tok, host_math)?;
5742 {
5743 let StepWs {
5744 h_a,
5745 h_b,
5746 y,
5747 post,
5748 comb,
5749 ..
5750 } = ws;
5751 unsafe {
5752 ck(
5753 "hc_post ffn dev",
5754 k::memra_dsv4_hc_post(
5755 dpf!(y, &stream),
5756 dpf!(h_b, &stream),
5757 dpf!(post, &stream),
5758 dpf!(comb, &stream),
5759 dpm!(*h_a, &stream),
5760 1,
5761 hc as i32,
5762 hidden as i32,
5763 sp(&stream),
5764 ),
5765 )?;
5766 }
5767 }
5768 Ok(())
5769 }
5770
5771 fn moe_forward_dev(
5777 &self,
5778 st: &Stage,
5779 layer: &LayerDev,
5780 ws: &mut StepWs,
5781 tok: u32,
5782 host_math: bool,
5783 ) -> Res<()> {
5784 let mc = &self.model.mc;
5785 let d = self.model.cfg();
5786 let moe = mc.moe.as_ref().expect("moe");
5787 let hidden = mc.n_embd as usize;
5788 let ne = moe.expert_count as usize;
5789 let topk = moe.expert_used_count as usize;
5790 let inter = moe.expert_ff_length as usize;
5791 let limit = d.swiglu_limit;
5792 let stream = st.gpu.stream();
5793 let kind = match layer.expert_kind {
5794 ExpertKind::Nvfp4 => 0i32,
5795 ExpertKind::Mxfp4 => 1i32,
5796 };
5797 let wstride = (inter * hidden / 2) as i64;
5798 let sstride = match layer.expert_kind {
5799 ExpertKind::Nvfp4 => (inter * hidden / 16) as i64,
5800 ExpertKind::Mxfp4 => (inter * hidden / 32) as i64,
5801 };
5802
5803 self.dots_dev(st, &ws.xf, &layer.gate_w, 1, hidden, ne, &mut ws.raw)?;
5804 if host_math {
5805 let raw_h = dtoh_f32(&stream, &ws.raw)?;
5806 let (indices, weights) =
5807 Self::route_host(layer, &raw_h, &[tok], 1, ne, topk, d.routed_scaling_factor);
5808 let sel: Vec<i32> = indices.iter().map(|&x| x as i32).collect();
5809 let mut order: Vec<i32> = (0..topk as i32).collect();
5810 order.sort_by_key(|&s| indices[s as usize]);
5811 stream
5812 .memcpy_htod(&sel, &mut ws.sel)
5813 .map_err(e("htod sel"))?;
5814 stream
5815 .memcpy_htod(&weights, &mut ws.selw)
5816 .map_err(e("htod selw"))?;
5817 stream
5818 .memcpy_htod(&order, &mut ws.order)
5819 .map_err(e("htod order"))?;
5820 } else {
5821 unsafe {
5822 ck(
5823 "route dev",
5824 k::memra_dsv4_route(
5825 dpf!(ws.raw, &stream),
5826 layer
5827 .gate_bias_dev
5828 .as_ref()
5829 .map(|b| b.device_ptr(&stream).0 as *const f32)
5830 .unwrap_or(std::ptr::null()),
5831 layer
5832 .tid2eid_dev
5833 .as_ref()
5834 .map(|t| t.device_ptr(&stream).0 as *const i32)
5835 .unwrap_or(std::ptr::null()),
5836 ws.tok.device_ptr(&stream).0 as *const i32,
5837 ne as i32,
5838 topk as i32,
5839 d.routed_scaling_factor,
5840 ws.sel.device_ptr_mut(&stream).0 as *mut i32,
5841 ws.selw.device_ptr_mut(&stream).0 as *mut f32,
5842 ws.order.device_ptr_mut(&stream).0 as *mut i32,
5843 sp(&stream),
5844 ),
5845 )?;
5846 }
5847 }
5848
5849 unsafe {
5850 ck(
5851 "act_quant_fp8 x dev",
5852 k::memra_dsv4_act_quant_fp8(
5853 dpf!(ws.xf, &stream),
5854 ws.xq.device_ptr_mut(&stream).0 as *mut c_void,
5855 dpm!(ws.xs, &stream),
5856 1,
5857 hidden as i32,
5858 sp(&stream),
5859 ),
5860 )?;
5861 for (proj, dst) in [(0i32, &mut ws.g1), (2i32, &mut ws.g3)] {
5862 ck(
5863 "fp4_gemm_sel w1/w3",
5864 k::memra_dsv4_fp4_gemm_sel(
5865 dp!(ws.xq, &stream),
5866 dpf!(ws.xs, &stream),
5867 dp!(layer.experts_w, &stream),
5868 dp!(layer.experts_sc, &stream),
5869 dpf!(layer.experts_s2_dev, &stream),
5870 ws.sel.device_ptr(&stream).0 as *const i32,
5871 proj,
5872 0,
5873 kind,
5874 dpm!(*dst, &stream),
5875 topk as i32,
5876 inter as i32,
5877 hidden as i32,
5878 wstride,
5879 sstride,
5880 sp(&stream),
5881 ),
5882 )?;
5883 }
5884 ck(
5885 "swiglu dev",
5886 k::memra_dsv4_swiglu(
5887 dpf!(ws.g1, &stream),
5888 dpf!(ws.g3, &stream),
5889 dpm!(ws.hbuf, &stream),
5890 topk as i32,
5891 inter as i32,
5892 limit,
5893 ws.selw.device_ptr(&stream).0 as *const f32,
5894 sp(&stream),
5895 ),
5896 )?;
5897 ck(
5898 "act_quant_fp8 h dev",
5899 k::memra_dsv4_act_quant_fp8(
5900 dpf!(ws.hbuf, &stream),
5901 ws.hq.device_ptr_mut(&stream).0 as *mut c_void,
5902 dpm!(ws.hs, &stream),
5903 topk as i32,
5904 inter as i32,
5905 sp(&stream),
5906 ),
5907 )?;
5908 ck(
5909 "fp4_gemm_sel w2",
5910 k::memra_dsv4_fp4_gemm_sel(
5911 dp!(ws.hq, &stream),
5912 dpf!(ws.hs, &stream),
5913 dp!(layer.experts_w, &stream),
5914 dp!(layer.experts_sc, &stream),
5915 dpf!(layer.experts_s2_dev, &stream),
5916 ws.sel.device_ptr(&stream).0 as *const i32,
5917 1,
5918 1,
5919 kind,
5920 dpm!(ws.contrib, &stream),
5921 topk as i32,
5922 hidden as i32,
5923 inter as i32,
5924 wstride,
5925 sstride,
5926 sp(&stream),
5927 ),
5928 )?;
5929 ck(
5930 "combine dev",
5931 k::memra_dsv4_combine_rows(
5932 dpf!(ws.contrib, &stream),
5933 ws.order.device_ptr(&stream).0 as *const i32,
5934 topk as i32,
5935 dpm!(ws.y, &stream),
5936 hidden as i64,
5937 sp(&stream),
5938 ),
5939 )?;
5940 ck(
5942 "cvt xb dev",
5943 k::memra_dsv4_cvt_bf16(
5944 dpf!(ws.xf, &stream),
5945 ws.xb.device_ptr_mut(&stream).0 as *mut c_void,
5946 hidden as i64,
5947 sp(&stream),
5948 ),
5949 )?;
5950 }
5951 let sh_inter = ws.sg1.len();
5952 Self::gemv_pre_dev(
5953 st,
5954 ws.xb.device_ptr(&stream).0 as *const c_void,
5955 dwsel(
5956 self.dense_fp8,
5957 &stream,
5958 &layer.shared_w[0],
5959 &layer.shared_fp8[0],
5960 ),
5961 sh_inter,
5962 hidden,
5963 ws.sg1.device_ptr_mut(&stream).0 as *mut f32,
5964 )?;
5965 Self::gemv_pre_dev(
5966 st,
5967 ws.xb.device_ptr(&stream).0 as *const c_void,
5968 dwsel(
5969 self.dense_fp8,
5970 &stream,
5971 &layer.shared_w[2],
5972 &layer.shared_fp8[2],
5973 ),
5974 sh_inter,
5975 hidden,
5976 ws.sg3.device_ptr_mut(&stream).0 as *mut f32,
5977 )?;
5978 unsafe {
5979 ck(
5980 "swiglu sh dev",
5981 k::memra_dsv4_swiglu(
5982 dpf!(ws.sg1, &stream),
5983 dpf!(ws.sg3, &stream),
5984 dpm!(ws.shbuf, &stream),
5985 1,
5986 sh_inter as i32,
5987 limit,
5988 std::ptr::null(),
5989 sp(&stream),
5990 ),
5991 )?;
5992 ck(
5993 "cvt sh dev",
5994 k::memra_dsv4_cvt_bf16(
5995 dpf!(ws.shbuf, &stream),
5996 ws.shb16.device_ptr_mut(&stream).0 as *mut c_void,
5997 sh_inter as i64,
5998 sp(&stream),
5999 ),
6000 )?;
6001 }
6002 Self::gemv_pre_dev(
6003 st,
6004 ws.shb16.device_ptr(&stream).0 as *const c_void,
6005 dwsel(
6006 self.dense_fp8,
6007 &stream,
6008 &layer.shared_w[1],
6009 &layer.shared_fp8[1],
6010 ),
6011 hidden,
6012 sh_inter,
6013 ws.sh_out.device_ptr_mut(&stream).0 as *mut f32,
6014 )?;
6015 unsafe {
6016 ck(
6017 "add shared dev",
6018 k::memra_dsv4_add_inplace(
6019 dpm!(ws.y, &stream),
6020 dpf!(ws.sh_out, &stream),
6021 hidden as i64,
6022 sp(&stream),
6023 ),
6024 )?;
6025 }
6026 Ok(())
6027 }
6028
6029 fn head_logits_dev(&self, ws: &mut StepWs, host_math: bool) -> Res<()> {
6033 let d = self.model.cfg();
6034 let mc = &self.model.mc;
6035 let hc = d.hc_mult as usize;
6036 let hidden = mc.n_embd as usize;
6037 let eps = mc.rms_eps;
6038 let last = self.stages.len() - 1;
6039 let st = &self.stages[last];
6040 let stream = st.gpu.stream();
6041 let w = hc * hidden;
6042 let fn_w = st.hc_head_fn.as_ref().expect("hc_head_fn");
6043 let norm = st.trunk_norm.as_ref().expect("trunk norm");
6044 self.dots_dev(st, &ws.h_a, fn_w, 1, w, hc, &mut ws.head_mixes)?;
6045 unsafe {
6046 ck(
6047 "rowsq head dev",
6048 self.rowsq_scale_arm(
6049 dpf!(ws.h_a, &stream),
6050 dpm!(ws.head_mixes, &stream),
6051 1,
6052 w as i32,
6053 hc as i32,
6054 eps,
6055 sp(&stream),
6056 ),
6057 )?;
6058 }
6059 if host_math {
6060 let mut mixes_h = dtoh_f32(&stream, &ws.head_mixes)?;
6061 for c in 0..hc {
6062 let m = mixes_h[c];
6063 mixes_h[c] =
6064 sigmoid_f32(m * self.hc_head_scale[0] + self.hc_head_base[c]) + d.hc_eps;
6065 }
6066 stream
6067 .memcpy_htod(&mixes_h, &mut ws.head_pre)
6068 .map_err(e("htod head pre"))?;
6069 } else {
6070 unsafe {
6071 ck(
6072 "hc_head_pre dev",
6073 k::memra_dsv4_hc_head_pre(
6074 dpf!(ws.head_mixes, &stream),
6075 st.hc_head_scale_dev
6076 .as_ref()
6077 .expect("head scale dev")
6078 .device_ptr(&stream)
6079 .0 as *const f32,
6080 st.hc_head_base_dev
6081 .as_ref()
6082 .expect("head base dev")
6083 .device_ptr(&stream)
6084 .0 as *const f32,
6085 dpm!(ws.head_pre, &stream),
6086 hc as i32,
6087 d.hc_eps,
6088 sp(&stream),
6089 ),
6090 )?;
6091 }
6092 }
6093 unsafe {
6094 ck(
6095 "hc_collapse head dev",
6096 k::memra_dsv4_hc_collapse(
6097 dpf!(ws.h_a, &stream),
6098 dpf!(ws.head_pre, &stream),
6099 dpm!(ws.collapsed, &stream),
6100 1,
6101 hc as i32,
6102 hidden as i32,
6103 sp(&stream),
6104 ),
6105 )?;
6106 ck(
6107 "rmsnorm head dev",
6108 self.rmsnorm_arm(
6109 dpf!(ws.collapsed, &stream),
6110 dpf!(norm, &stream),
6111 dpm!(ws.collapsed, &stream),
6112 1,
6113 hidden as i32,
6114 eps,
6115 sp(&stream),
6116 ),
6117 )?;
6118 let head_ptr = st.head.as_ref().expect("head").device_ptr(&stream).0 as *const c_void;
6119 if self.dots_f32 {
6120 ck(
6121 "head dots f32acc dev",
6122 k::memra_dsv4_dots_f32acc(
6123 dpf!(ws.collapsed, &stream),
6124 head_ptr,
6125 1,
6126 dpm!(ws.logits, &stream),
6127 1,
6128 hidden as i32,
6129 ws.logits.len() as i32,
6130 sp(&stream),
6131 ),
6132 )?;
6133 } else {
6134 ck(
6135 "head dots dev",
6136 k::memra_dsv4_dots_f32(
6137 dpf!(ws.collapsed, &stream),
6138 head_ptr,
6139 1,
6140 dpm!(ws.logits, &stream),
6141 1,
6142 hidden as i32,
6143 ws.logits.len() as i32,
6144 sp(&stream),
6145 ),
6146 )?;
6147 }
6148 }
6149 Ok(())
6150 }
6151
6152 fn decode_step_fast(
6156 &self,
6157 tok: u32,
6158 state: &mut DecodeState,
6159 want_logits: bool,
6160 host_math: bool,
6161 ) -> Res<(Option<Vec<f32>>, u32)> {
6162 self.decode_step_fast_tap(tok, state, want_logits, host_math, None)
6163 }
6164
6165 fn decode_step_fast_tap(
6171 &self,
6172 tok: u32,
6173 state: &mut DecodeState,
6174 want_logits: bool,
6175 host_math: bool,
6176 mut taps: Option<(&mut CudaSlice<f32>, usize)>,
6177 ) -> Res<(Option<Vec<f32>>, u32)> {
6178 let mc = &self.model.mc;
6179 let d = self.model.cfg();
6180 let pos = state.pos;
6181 assert!(pos > 0, "decode_step needs prefill_with_cache first");
6182 assert!(pos < self.max_seq, "pos {pos} >= max_seq {}", self.max_seq);
6183 let hidden = mc.n_embd as usize;
6184 let hc = d.hc_mult as usize;
6185 let n_trunk = mc.n_layer - mc.nextn_predict_layers;
6186 let ws_all = state.ws.as_mut().expect("device path needs StepWs");
6187
6188 let st0 = &self.stages[0];
6190 st0.gpu.ctx.bind_to_thread().map_err(e("bind ctx0"))?;
6191 let stream0 = st0.gpu.stream();
6192 {
6193 let ws0 = &mut ws_all[0];
6194 stream0
6195 .memcpy_htod(&[tok as i32], &mut ws0.tok)
6196 .map_err(e("htod tok"))?;
6197 unsafe {
6198 ck(
6199 "embed_rows dev",
6200 k::memra_dsv4_embed_rows(
6201 st0.embed
6202 .as_ref()
6203 .expect("embed on stage 0")
6204 .device_ptr(&stream0)
6205 .0 as *const c_void,
6206 ws0.tok.device_ptr(&stream0).0 as *const i32,
6207 dpm!(ws0.emb, &stream0),
6208 1,
6209 hidden as i32,
6210 sp(&stream0),
6211 ),
6212 )?;
6213 ck(
6214 "repeat_hc dev",
6215 k::memra_dsv4_repeat_hc(
6216 dpf!(ws0.emb, &stream0),
6217 dpm!(ws0.h_a, &stream0),
6218 1,
6219 hc as i32,
6220 hidden as i32,
6221 sp(&stream0),
6222 ),
6223 )?;
6224 }
6225 }
6226
6227 let mut cur_stage = 0usize;
6228 let mut input_rx = false;
6229 for il in 0..n_trunk {
6230 let stage = self.layer_stage[il as usize];
6231 if stage != cur_stage {
6232 let bytes = hc * hidden * std::mem::size_of::<f32>();
6235 let src_stream = self.stages[cur_stage].gpu.stream();
6236 let dst_stream = self.stages[stage].gpu.stream();
6237 let (ws_src, ws_dst) = ws_all.split_at_mut(stage);
6238 let src_ws = &ws_src[cur_stage];
6239 let dst_ws = &mut ws_dst[0];
6240 self.stages[cur_stage]
6241 .gpu
6242 .ctx
6243 .bind_to_thread()
6244 .map_err(e("bind tx"))?;
6245 let (sp_, _g0) = src_ws.h_a.device_ptr(&src_stream);
6246 let (dp_, _g1) = dst_ws.h_rx.device_ptr_mut(&src_stream);
6247 unsafe {
6248 cudarc::driver::result::memcpy_peer_async(
6249 self.stages[stage].gpu.ctx.cu_ctx(),
6250 dp_,
6251 self.stages[cur_stage].gpu.ctx.cu_ctx(),
6252 sp_,
6253 bytes,
6254 src_stream.cu_stream(),
6255 )
6256 .map_err(e("peer copy h"))?;
6257 }
6258 let bnd = stage - 1;
6259 self.boundary_ev[bnd]
6260 .record(&src_stream)
6261 .map_err(e("ev record"))?;
6262 dst_stream
6263 .wait(&self.boundary_ev[bnd])
6264 .map_err(e("ev wait"))?;
6265 self.stages[stage]
6266 .gpu
6267 .ctx
6268 .bind_to_thread()
6269 .map_err(e("bind rx"))?;
6270 cur_stage = stage;
6271 input_rx = true;
6272 }
6273 let st = &self.stages[stage];
6274 let lidx = st
6275 .layers
6276 .iter()
6277 .position(|l| l.il == il)
6278 .unwrap_or_else(|| panic!("layer {il} not on stage {stage}"));
6279 self.block_decode_dev(
6280 st,
6281 &st.layers[lidx],
6282 &mut state.caches[il as usize],
6283 &mut ws_all[stage],
6284 input_rx,
6285 pos,
6286 tok,
6287 host_math,
6288 )?;
6289 input_rx = false;
6290 if let Some((t, base)) = taps.as_mut() {
6293 if let Some(ds) = &self.dspark {
6294 if let Some(k) = ds.targets.iter().position(|&tl| tl == il as usize) {
6295 let stream = self.stages[stage].gpu.stream();
6296 let hidden_i = hidden as i32;
6297 unsafe {
6298 ck(
6299 "hc_mean tap dev",
6300 k::memra_dsv4_hc_mean(
6301 dpf!(ws_all[stage].h_a, &stream),
6302 (t.device_ptr_mut(&stream).0 as usize
6303 + (*base + k * hidden) * 4)
6304 as *mut f32,
6305 1,
6306 hc as i32,
6307 hidden_i,
6308 sp(&stream),
6309 ),
6310 )?;
6311 }
6312 }
6313 }
6314 }
6315 }
6316
6317 let last = self.stages.len() - 1;
6318 assert_eq!(cur_stage, last, "device path expects the head stage last");
6319 self.head_logits_dev(&mut ws_all[last], host_math)?;
6320 let stream_last = self.stages[last].gpu.stream();
6321 state.pos += 1;
6322 if want_logits {
6323 let logits = dtoh_f32(&stream_last, &ws_all[last].logits)?;
6324 let mut best = 0usize;
6325 for i in 1..logits.len() {
6326 if logits[i] > logits[best] {
6327 best = i;
6328 }
6329 }
6330 Ok((Some(logits), best as u32))
6331 } else {
6332 unsafe {
6333 ck(
6334 "argmax dev",
6335 k::memra_dsv4_argmax(
6336 dpf!(ws_all[last].logits, &stream_last),
6337 ws_all[last].logits.len() as i64,
6338 ws_all[last].argmax.device_ptr_mut(&stream_last).0 as *mut i32,
6339 sp(&stream_last),
6340 ),
6341 )?;
6342 }
6343 let mut out = [0i32; 1];
6344 stream_last
6345 .memcpy_dtoh(&ws_all[last].argmax, &mut out[..])
6346 .map_err(e("dtoh argmax"))?;
6347 stream_last.synchronize().map_err(e("sync argmax"))?;
6348 Ok((None, out[0] as u32))
6349 }
6350 }
6351
6352 pub fn decode_step_greedy(&self, tok: u32, state: &mut DecodeState) -> Res<u32> {
6357 match self.decode_path {
6358 DecodePath::Legacy => {
6359 let logits = self.decode_step_impl(tok, state, None)?;
6360 let mut best = 0usize;
6361 for i in 1..logits.len() {
6362 if logits[i] > logits[best] {
6363 best = i;
6364 }
6365 }
6366 Ok(best as u32)
6367 }
6368 DecodePath::Device { host_math } => {
6369 Ok(self.decode_step_fast(tok, state, false, host_math)?.1)
6370 }
6371 }
6372 }
6373
6374 fn decode_step_impl(
6375 &self,
6376 tok: u32,
6377 state: &mut DecodeState,
6378 mut dump: Option<&mut Vec<(String, Vec<f32>)>>,
6379 ) -> Res<Vec<f32>> {
6380 if let DecodePath::Device { host_math } = self.decode_path {
6381 assert!(
6382 dump.is_none(),
6383 "decode_step_probe is a legacy-path diagnostic (set MEMRA_DSV4_DECODE_PATH=legacy)"
6384 );
6385 let (logits, _) = self.decode_step_fast(tok, state, true, host_math)?;
6386 return Ok(logits.expect("want_logits"));
6387 }
6388 let mc = &self.model.mc;
6389 let d = self.model.cfg();
6390 let pos = state.pos;
6391 assert!(pos > 0, "decode_step needs prefill_with_cache first");
6392 assert!(pos < self.max_seq, "pos {pos} >= max_seq {}", self.max_seq);
6393 let hidden = mc.n_embd as usize;
6394 let hc = d.hc_mult as usize;
6395 let n_trunk = mc.n_layer - mc.nextn_predict_layers;
6396
6397 let st0 = &self.stages[0];
6399 st0.gpu.ctx.bind_to_thread().map_err(e("bind ctx0"))?;
6400 let stream0 = st0.gpu.stream();
6401 let ids_dev = upload_i32(&stream0, &[tok as i32])?;
6402 let mut emb = stream0.alloc_zeros::<f32>(hidden).map_err(e("emb"))?;
6403 unsafe {
6404 ck(
6405 "embed_rows",
6406 k::memra_dsv4_embed_rows(
6407 st0.embed
6408 .as_ref()
6409 .expect("embed on stage 0")
6410 .device_ptr(&stream0)
6411 .0 as *const c_void,
6412 ids_dev.device_ptr(&stream0).0 as *const i32,
6413 dpm!(emb, &stream0),
6414 1,
6415 hidden as i32,
6416 sp(&stream0),
6417 ),
6418 )?;
6419 }
6420 let mut h = stream0.alloc_zeros::<f32>(hc * hidden).map_err(e("h0"))?;
6421 unsafe {
6422 ck(
6423 "repeat_hc",
6424 k::memra_dsv4_repeat_hc(
6425 dpf!(emb, &stream0),
6426 dpm!(h, &stream0),
6427 1,
6428 hc as i32,
6429 hidden as i32,
6430 sp(&stream0),
6431 ),
6432 )?;
6433 }
6434
6435 let mut cur_stage = 0usize;
6436 for il in 0..n_trunk {
6437 let stage = self.layer_stage[il as usize];
6438 if stage != cur_stage {
6439 let src_stream = self.stages[cur_stage].gpu.stream();
6440 let host = dtoh_f32(&src_stream, &h)?;
6441 let dst_stream = self.stages[stage].gpu.stream();
6442 self.stages[stage]
6443 .gpu
6444 .ctx
6445 .bind_to_thread()
6446 .map_err(e("bind"))?;
6447 h = upload_f32(&dst_stream, &host)?;
6448 cur_stage = stage;
6449 }
6450 let st = &self.stages[stage];
6451 let lidx = st
6452 .layers
6453 .iter()
6454 .position(|l| l.il == il)
6455 .unwrap_or_else(|| panic!("layer {il} not on stage {stage}"));
6456 h = self.block_decode(
6457 st,
6458 &st.layers[lidx],
6459 &mut state.caches[il as usize],
6460 &h,
6461 pos,
6462 tok,
6463 dump.as_deref_mut(),
6464 )?;
6465 }
6466
6467 let last = self.stages.len() - 1;
6468 if cur_stage != last {
6469 let src_stream = self.stages[cur_stage].gpu.stream();
6470 let host = dtoh_f32(&src_stream, &h)?;
6471 let dst_stream = self.stages[last].gpu.stream();
6472 h = upload_f32(&dst_stream, &host)?;
6473 }
6474 let hc_head_fn = self.stages[last].hc_head_fn.as_ref().expect("hc_head_fn");
6475 let trunk_norm = self.stages[last].trunk_norm.as_ref().expect("trunk norm");
6476 let logits = self.head_logits_from(
6477 &h,
6478 1,
6479 hc_head_fn,
6480 &self.hc_head_base,
6481 &self.hc_head_scale,
6482 trunk_norm,
6483 )?;
6484 state.pos += 1;
6485 Ok(logits)
6486 }
6487}
6488
6489impl Dsv4Gpu {
6498 fn dspark(&self) -> &DsparkDev {
6499 self.dspark
6500 .as_ref()
6501 .expect("MEMRA_DSV4_DRAFTER=dspark not loaded")
6502 }
6503
6504 #[allow(clippy::too_many_arguments)]
6512 fn dspark_head_dots(
6513 &self,
6514 st: &Stage,
6515 x: *const f32,
6516 w: *const c_void,
6517 w_is_bf16: i32,
6518 s: usize,
6519 kdim: usize,
6520 n: usize,
6521 y: *mut f32,
6522 ) -> Res<()> {
6523 let stream = st.gpu.stream();
6524 unsafe {
6525 if self.dspark_head_f32 {
6526 ck(
6527 "dspark head dots f32acc_mrow",
6528 k::memra_dsv4_dots_f32acc_mrow(
6529 x,
6530 w,
6531 w_is_bf16,
6532 y,
6533 s as i32,
6534 kdim as i32,
6535 n as i32,
6536 sp(&stream),
6537 ),
6538 )
6539 } else {
6540 ck(
6541 "dspark head dots f32_mrow",
6542 k::memra_dsv4_dots_f32_mrow(
6543 x,
6544 w,
6545 w_is_bf16,
6546 y,
6547 s as i32,
6548 kdim as i32,
6549 n as i32,
6550 sp(&stream),
6551 ),
6552 )
6553 }
6554 }
6555 }
6556
6557 pub fn dspark_alloc_state(&self) -> Res<DsparkState> {
6560 let ds = self.dspark();
6561 let d = self.model.cfg();
6562 let hd = d.head_dim as usize;
6563 let win = d.sliding_window as usize;
6564 let hidden = self.model.mc.n_embd as usize;
6565 let last = self.stages.len() - 1;
6566 let stream = self.stages[last].gpu.stream();
6567 let mut rings = Vec::with_capacity(ds.blocks.len());
6568 for _ in 0..ds.blocks.len() {
6569 rings.push(
6570 stream
6571 .alloc_zeros::<f32>((win + ds.block_size) * hd)
6572 .map_err(e("dspark ring"))?,
6573 );
6574 }
6575 let taps = stream
6576 .alloc_zeros::<f32>((ds.block_size + 1) * ds.targets.len() * hidden)
6577 .map_err(e("dspark taps"))?;
6578 Ok(DsparkState { rings, taps })
6579 }
6580
6581 fn dspark_main_x(&self, main_hidden: &CudaSlice<f32>, s: usize) -> Res<CudaSlice<f32>> {
6583 let ds = self.dspark();
6584 let hidden = self.model.mc.n_embd as usize;
6585 let k = ds.targets.len() * hidden;
6586 let last = self.stages.len() - 1;
6587 let st = &self.stages[last];
6588 let stream = st.gpu.stream();
6589 let mut mx = stream.alloc_zeros::<f32>(s * hidden).map_err(e("main_x"))?;
6590 Self::gemm(st, main_hidden, &ds.main_proj, 0, s, hidden, k, &mut mx)?;
6591 unsafe {
6592 ck(
6593 "rmsnorm main_x",
6594 k::memra_dsv4_rmsnorm(
6595 dpf!(mx, &stream),
6596 dpf!(ds.main_norm, &stream),
6597 dpm!(mx, &stream),
6598 s as i32,
6599 hidden as i32,
6600 self.model.mc.rms_eps,
6601 sp(&stream),
6602 ),
6603 )?;
6604 }
6605 Ok(mx)
6606 }
6607
6608 fn dspark_main_kv(
6611 &self,
6612 blk: &LayerDev,
6613 main_x: &CudaSlice<f32>,
6614 s: usize,
6615 positions: &[i32],
6616 ) -> Res<CudaSlice<f32>> {
6617 let d = self.model.cfg();
6618 let hd = d.head_dim as usize;
6619 let rd = d.qk_rope_head_dim as usize;
6620 let hidden = self.model.mc.n_embd as usize;
6621 let eps = self.model.mc.rms_eps;
6622 let last = self.stages.len() - 1;
6623 let st = &self.stages[last];
6624 let stream = st.gpu.stream();
6625 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
6626 let mut kv = stream.alloc_zeros::<f32>(s * hd).map_err(e("dspark kv"))?;
6630 Self::gemm(st, main_x, blk.wkv.dev(), 0, s, hd, hidden, &mut kv)?;
6631 let pos_dev = upload_i32(&stream, positions)?;
6632 unsafe {
6633 ck(
6634 "rmsnorm dspark kv",
6635 k::memra_dsv4_rmsnorm(
6636 dpf!(kv, &stream),
6637 dpf!(blk.kv_norm, &stream),
6638 dpm!(kv, &stream),
6639 s as i32,
6640 hd as i32,
6641 eps,
6642 sp(&stream),
6643 ),
6644 )?;
6645 ck(
6646 "rope dspark kv",
6647 k::memra_dsv4_rope(
6648 dpm!(kv, &stream),
6649 s as i32,
6650 1,
6651 hd as i32,
6652 rd as i32,
6653 dpf!(st.fc_plain, &stream),
6654 pos_dev.device_ptr(&stream).0 as *const i32,
6655 0,
6656 sp(&stream),
6657 ),
6658 )?;
6659 ck(
6660 "act_quant dspark kv",
6661 k::memra_dsv4_act_quant(
6662 dpm!(kv, &stream),
6663 s as i32,
6664 hd as i64,
6665 (hd - rd) as i32,
6666 64,
6667 clamp_only,
6668 sp(&stream),
6669 ),
6670 )?;
6671 }
6672 Ok(kv)
6673 }
6674
6675 pub fn dspark_prime_prefill(
6678 &self,
6679 state: &mut DsparkState,
6680 main_hidden: &CudaSlice<f32>,
6681 s: usize,
6682 ) -> Res<()> {
6683 let d = self.model.cfg();
6684 let hd = d.head_dim as usize;
6685 let win = d.sliding_window as usize;
6686 let last = self.stages.len() - 1;
6687 let stream = self.stages[last].gpu.stream();
6688 let mx = self.dspark_main_x(main_hidden, s)?;
6689 let positions: Vec<i32> = (0..s as i32).collect();
6690 let n_blocks = self.dspark().blocks.len();
6691 for bi in 0..n_blocks {
6692 let blk = &self.dspark().blocks[bi];
6693 let kv = self.dspark_main_kv(blk, &mx, s, &positions)?;
6694 for p in s.saturating_sub(win)..s {
6695 let slot = p % win;
6696 let src = kv.slice(p * hd..(p + 1) * hd);
6697 let mut dst = state.rings[bi].slice_mut(slot * hd..(slot + 1) * hd);
6698 stream
6699 .memcpy_dtod(&src, &mut dst)
6700 .map_err(e("prime ring"))?;
6701 }
6702 }
6703 Ok(())
6704 }
6705
6706 pub fn dspark_prefill_prime(
6717 &self,
6718 ids: &[u32],
6719 state: &mut DecodeState,
6720 dstate: &mut DsparkState,
6721 ) -> Res<ForwardOut> {
6722 assert_eq!(
6723 state.pos, 0,
6724 "dspark_prefill_prime needs a fresh DecodeState"
6725 );
6726 assert!(!ids.is_empty(), "empty prompt");
6727 let hidden = self.model.mc.n_embd as usize;
6728 let hc = self.model.cfg().hc_mult as usize;
6729 let s = ids.len();
6730 let targets = self.dspark().targets.clone();
6731 let n_t = targets.len();
6732 let mut cap = GpuCapture {
6733 want: targets.iter().map(|&t| t as u32).collect(),
6734 ..Default::default()
6735 };
6736 let out = self
6737 .forward_impl(ids, Some(&mut cap), None, Some(state))?
6738 .expect("prefill logits");
6739 state.pos = s;
6740
6741 let last = self.stages.len() - 1;
6742 let stream = self.stages[last].gpu.stream();
6743 self.stages[last]
6744 .gpu
6745 .ctx
6746 .bind_to_thread()
6747 .map_err(e("bind ctx prime"))?;
6748 let mut main_hidden = stream
6749 .alloc_zeros::<f32>(s * n_t * hidden)
6750 .map_err(e("prefill main_hidden"))?;
6751 let mut tmp = stream
6752 .alloc_zeros::<f32>(s * hidden)
6753 .map_err(e("tap tmp"))?;
6754 for (k, &il) in targets.iter().enumerate() {
6755 let h = cap
6756 .layer_out
6757 .get(&(il as u32))
6758 .unwrap_or_else(|| panic!("prefill capture missing target layer {il}"));
6759 assert_eq!(
6760 h.len(),
6761 s * hc * hidden,
6762 "target layer {il} capture is not [s, hc, hidden]"
6763 );
6764 let h_dev = upload_f32(&stream, h)?;
6765 unsafe {
6766 ck(
6767 "hc_mean prefill tap",
6768 k::memra_dsv4_hc_mean(
6769 dpf!(h_dev, &stream),
6770 dpm!(tmp, &stream),
6771 s as i32,
6772 hc as i32,
6773 hidden as i32,
6774 sp(&stream),
6775 ),
6776 )?;
6777 ck(
6778 "place_cols prefill tap",
6779 k::memra_dsv4_place_cols(
6780 dpf!(tmp, &stream),
6781 dpm!(main_hidden, &stream),
6782 s as i32,
6783 hidden as i32,
6784 (n_t * hidden) as i64,
6785 (k * hidden) as i64,
6786 sp(&stream),
6787 ),
6788 )?;
6789 }
6790 }
6791 self.dspark_prime_prefill(dstate, &main_hidden, s)?;
6792 {
6797 let src = main_hidden.slice((s - 1) * n_t * hidden..s * n_t * hidden);
6798 let mut dst = dstate.taps.slice_mut(0..n_t * hidden);
6799 stream
6800 .memcpy_dtod(&src, &mut dst)
6801 .map_err(e("seed tap row"))?;
6802 }
6803 stream.synchronize().map_err(e("prime sync"))?;
6804 Ok(out)
6805 }
6806
6807 pub fn dspark_write_rings(
6811 &self,
6812 state: &mut DsparkState,
6813 tap_row: usize,
6814 pos: usize,
6815 ) -> Res<()> {
6816 let d = self.model.cfg();
6817 let hd = d.head_dim as usize;
6818 let win = d.sliding_window as usize;
6819 let hidden = self.model.mc.n_embd as usize;
6820 let n_t = self.dspark().targets.len();
6821 let last = self.stages.len() - 1;
6822 let stream = self.stages[last].gpu.stream();
6823 let tap = {
6824 let mut row = stream
6826 .alloc_zeros::<f32>(n_t * hidden)
6827 .map_err(e("tap row"))?;
6828 let src = state
6829 .taps
6830 .slice(tap_row * n_t * hidden..(tap_row + 1) * n_t * hidden);
6831 stream.memcpy_dtod(&src, &mut row).map_err(e("tap copy"))?;
6832 row
6833 };
6834 let mx = self.dspark_main_x(&tap, 1)?;
6835 let n_blocks = self.dspark().blocks.len();
6836 for bi in 0..n_blocks {
6837 let blk = &self.dspark().blocks[bi];
6838 let kv = self.dspark_main_kv(blk, &mx, 1, &[pos as i32])?;
6839 let slot = pos % win;
6840 let src = kv.slice(0..hd);
6841 let mut dst = state.rings[bi].slice_mut(slot * hd..(slot + 1) * hd);
6842 stream
6843 .memcpy_dtod(&src, &mut dst)
6844 .map_err(e("ring write"))?;
6845 }
6846 Ok(())
6847 }
6848
6849 #[allow(clippy::too_many_arguments)]
6853 fn dspark_block_forward(
6854 &self,
6855 blk: &LayerDev,
6856 ring: &mut CudaSlice<f32>,
6857 h: &CudaSlice<f32>,
6858 block: usize,
6859 pos: usize,
6860 ) -> Res<CudaSlice<f32>> {
6861 let d = self.model.cfg();
6862 let mc = &self.model.mc;
6863 let hc = d.hc_mult as usize;
6864 let hidden = mc.n_embd as usize;
6865 let heads = mc.n_head as usize;
6866 let hd = d.head_dim as usize;
6867 let rd = d.qk_rope_head_dim as usize;
6868 let q_lora = d.q_lora_rank as usize;
6869 let win = d.sliding_window as usize;
6870 let o_groups = d.o_groups as usize;
6871 let o_lora = d.o_lora_rank as usize;
6872 let eps = mc.rms_eps;
6873 let iters = d.hc_sinkhorn_iters;
6874 let hc_eps = d.hc_eps;
6875 let last = self.stages.len() - 1;
6876 let st = &self.stages[last];
6877 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx dspark"))?;
6878 let stream = st.gpu.stream();
6879 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
6880 let positions: Vec<i32> = (1..=block as i32).map(|j| pos as i32 + j).collect();
6882 let pos_dev = upload_i32(&stream, &positions)?;
6883
6884 let (y, post, comb) = Self::hc_pre(
6886 st,
6887 h,
6888 &blk.hc_attn_fn,
6889 &blk.hc_attn_base,
6890 &blk.hc_attn_scale,
6891 block,
6892 hc,
6893 hidden,
6894 iters,
6895 hc_eps,
6896 )?;
6897 let mut x = stream.alloc_zeros::<f32>(block * hidden).map_err(e("x"))?;
6898 unsafe {
6899 ck(
6900 "rmsnorm dspark attn",
6901 k::memra_dsv4_rmsnorm(
6902 dpf!(y, &stream),
6903 dpf!(blk.attn_norm, &stream),
6904 dpm!(x, &stream),
6905 block as i32,
6906 hidden as i32,
6907 eps,
6908 sp(&stream),
6909 ),
6910 )?;
6911 }
6912 let mut qr = stream.alloc_zeros::<f32>(block * q_lora).map_err(e("qr"))?;
6914 Self::gemm(st, &x, blk.wq_a.dev(), 0, block, q_lora, hidden, &mut qr)?;
6915 unsafe {
6916 ck(
6917 "rmsnorm dspark q",
6918 k::memra_dsv4_rmsnorm(
6919 dpf!(qr, &stream),
6920 dpf!(blk.q_norm, &stream),
6921 dpm!(qr, &stream),
6922 block as i32,
6923 q_lora as i32,
6924 eps,
6925 sp(&stream),
6926 ),
6927 )?;
6928 }
6929 let mut qr_b = stream
6930 .alloc_zeros::<u8>(block * q_lora * 2)
6931 .map_err(e("qr_b"))?;
6932 unsafe {
6933 ck(
6934 "cvt dspark qr",
6935 k::memra_dsv4_cvt_bf16(
6936 dpf!(qr, &stream),
6937 qr_b.device_ptr_mut(&stream).0 as *mut c_void,
6938 (block * q_lora) as i64,
6939 sp(&stream),
6940 ),
6941 )?;
6942 }
6943 let mut q = stream
6944 .alloc_zeros::<f32>(block * heads * hd)
6945 .map_err(e("q"))?;
6946 Self::gemm_pre(
6947 st,
6948 &qr_b,
6949 blk.wq_b.dev().device_ptr(&stream).0 as *const c_void,
6950 block,
6951 heads * hd,
6952 q_lora,
6953 &mut q,
6954 )?;
6955 unsafe {
6956 ck(
6957 "headrms dspark",
6958 k::memra_dsv4_headrms(
6959 dpm!(q, &stream),
6960 (block * heads) as i32,
6961 hd as i32,
6962 eps,
6963 sp(&stream),
6964 ),
6965 )?;
6966 ck(
6967 "rope dspark q",
6968 k::memra_dsv4_rope(
6969 dpm!(q, &stream),
6970 block as i32,
6971 heads as i32,
6972 hd as i32,
6973 rd as i32,
6974 dpf!(st.fc_plain, &stream),
6975 pos_dev.device_ptr(&stream).0 as *const i32,
6976 0,
6977 sp(&stream),
6978 ),
6979 )?;
6980 }
6981 {
6983 let mut kv = stream.alloc_zeros::<f32>(block * hd).map_err(e("dkv"))?;
6984 Self::gemm(st, &x, blk.wkv.dev(), 0, block, hd, hidden, &mut kv)?;
6985 unsafe {
6986 ck(
6987 "rmsnorm dspark dkv",
6988 k::memra_dsv4_rmsnorm(
6989 dpf!(kv, &stream),
6990 dpf!(blk.kv_norm, &stream),
6991 dpm!(kv, &stream),
6992 block as i32,
6993 hd as i32,
6994 eps,
6995 sp(&stream),
6996 ),
6997 )?;
6998 ck(
6999 "rope dspark dkv",
7000 k::memra_dsv4_rope(
7001 dpm!(kv, &stream),
7002 block as i32,
7003 1,
7004 hd as i32,
7005 rd as i32,
7006 dpf!(st.fc_plain, &stream),
7007 pos_dev.device_ptr(&stream).0 as *const i32,
7008 0,
7009 sp(&stream),
7010 ),
7011 )?;
7012 ck(
7013 "act_quant dspark dkv",
7014 k::memra_dsv4_act_quant(
7015 dpm!(kv, &stream),
7016 block as i32,
7017 hd as i64,
7018 (hd - rd) as i32,
7019 64,
7020 clamp_only,
7021 sp(&stream),
7022 ),
7023 )?;
7024 }
7025 let src = kv.slice(0..block * hd);
7026 let mut dst = ring.slice_mut(win * hd..(win + block) * hd);
7027 stream.memcpy_dtod(&src, &mut dst).map_err(e("draft kv"))?;
7028 }
7029 let n_ring = win.min(pos + 1);
7033 let mut idx_row: Vec<i32> = (0..n_ring as i32).collect();
7034 idx_row.extend((0..block as i32).map(|j| win as i32 + j));
7035 let slots = idx_row.len();
7036 let mut idxs = Vec::with_capacity(block * slots);
7037 for _ in 0..block {
7038 idxs.extend_from_slice(&idx_row);
7039 }
7040 let idx_dev = upload_i32(&stream, &idxs)?;
7041 let mut o = stream
7042 .alloc_zeros::<f32>(block * heads * hd)
7043 .map_err(e("o"))?;
7044 let scale = (hd as f64).powf(-0.5) as f32;
7045 unsafe {
7046 ck(
7047 "sink_attn dspark",
7048 k::memra_dsv4_sink_attn(
7049 dpf!(q, &stream),
7050 dpf!(ring, &stream),
7051 idx_dev.device_ptr(&stream).0 as *const i32,
7052 dpf!(blk.sink, &stream),
7053 dpm!(o, &stream),
7054 block as i32,
7055 heads as i32,
7056 hd as i32,
7057 slots as i32,
7058 scale,
7059 sp(&stream),
7060 ),
7061 )?;
7062 ck(
7063 "rope dspark o inv",
7064 k::memra_dsv4_rope(
7065 dpm!(o, &stream),
7066 block as i32,
7067 heads as i32,
7068 hd as i32,
7069 rd as i32,
7070 dpf!(st.fc_plain, &stream),
7071 pos_dev.device_ptr(&stream).0 as *const i32,
7072 1,
7073 sp(&stream),
7074 ),
7075 )?;
7076 }
7077 let gw = heads / o_groups * hd;
7079 let mut og = stream
7080 .alloc_zeros::<f32>(block * o_groups * o_lora)
7081 .map_err(e("og"))?;
7082 let mut o_grp = stream.alloc_zeros::<f32>(block * gw).map_err(e("o_grp"))?;
7083 let mut y_grp = stream
7084 .alloc_zeros::<f32>(block * o_lora)
7085 .map_err(e("y_grp"))?;
7086 for g in 0..o_groups {
7087 unsafe {
7088 ck(
7089 "take_cols dspark",
7090 k::memra_dsv4_take_cols(
7091 dpf!(o, &stream),
7092 dpm!(o_grp, &stream),
7093 block as i32,
7094 gw as i32,
7095 (heads * hd) as i64,
7096 (g * gw) as i64,
7097 sp(&stream),
7098 ),
7099 )?;
7100 }
7101 Self::gemm(
7102 st,
7103 &o_grp,
7104 blk.wo_a.dev(),
7105 g * o_lora * gw,
7106 block,
7107 o_lora,
7108 gw,
7109 &mut y_grp,
7110 )?;
7111 unsafe {
7112 ck(
7113 "place_cols dspark",
7114 k::memra_dsv4_place_cols(
7115 dpf!(y_grp, &stream),
7116 dpm!(og, &stream),
7117 block as i32,
7118 o_lora as i32,
7119 (o_groups * o_lora) as i64,
7120 (g * o_lora) as i64,
7121 sp(&stream),
7122 ),
7123 )?;
7124 }
7125 }
7126 let mut attn_out = stream.alloc_zeros::<f32>(block * hidden).map_err(e("ao"))?;
7127 Self::gemm(
7128 st,
7129 &og,
7130 blk.wo_b.dev(),
7131 0,
7132 block,
7133 hidden,
7134 o_groups * o_lora,
7135 &mut attn_out,
7136 )?;
7137 let mut h2 = stream
7138 .alloc_zeros::<f32>(block * hc * hidden)
7139 .map_err(e("h2"))?;
7140 unsafe {
7141 ck(
7142 "hc_post dspark attn",
7143 k::memra_dsv4_hc_post(
7144 dpf!(attn_out, &stream),
7145 dpf!(h, &stream),
7146 dpf!(post, &stream),
7147 dpf!(comb, &stream),
7148 dpm!(h2, &stream),
7149 block as i32,
7150 hc as i32,
7151 hidden as i32,
7152 sp(&stream),
7153 ),
7154 )?;
7155 }
7156 let (y2, post2, comb2) = Self::hc_pre(
7158 st,
7159 &h2,
7160 &blk.hc_ffn_fn,
7161 &blk.hc_ffn_base,
7162 &blk.hc_ffn_scale,
7163 block,
7164 hc,
7165 hidden,
7166 iters,
7167 hc_eps,
7168 )?;
7169 let mut xf = stream.alloc_zeros::<f32>(block * hidden).map_err(e("xf"))?;
7170 unsafe {
7171 ck(
7172 "rmsnorm dspark ffn",
7173 k::memra_dsv4_rmsnorm(
7174 dpf!(y2, &stream),
7175 dpf!(blk.ffn_norm, &stream),
7176 dpm!(xf, &stream),
7177 block as i32,
7178 hidden as i32,
7179 eps,
7180 sp(&stream),
7181 ),
7182 )?;
7183 }
7184 let ids = vec![0u32; block];
7185 let moe_out = self.moe_forward(st, blk, &xf, block, &ids)?;
7186 let mut h3 = stream
7187 .alloc_zeros::<f32>(block * hc * hidden)
7188 .map_err(e("h3"))?;
7189 unsafe {
7190 ck(
7191 "hc_post dspark ffn",
7192 k::memra_dsv4_hc_post(
7193 dpf!(moe_out, &stream),
7194 dpf!(h2, &stream),
7195 dpf!(post2, &stream),
7196 dpf!(comb2, &stream),
7197 dpm!(h3, &stream),
7198 block as i32,
7199 hc as i32,
7200 hidden as i32,
7201 sp(&stream),
7202 ),
7203 )?;
7204 }
7205 Ok(h3)
7206 }
7207
7208 pub fn dspark_forward_spec(
7214 &self,
7215 state: &mut DsparkState,
7216 input_token: u32,
7217 tap_row: usize,
7218 pos: usize,
7219 capture: bool,
7220 ) -> Res<DsparkProposal> {
7221 let ds = self.dspark();
7222 let mc = &self.model.mc;
7223 let d = self.model.cfg();
7224 let hc = d.hc_mult as usize;
7225 let hidden = mc.n_embd as usize;
7226 let eps = mc.rms_eps;
7227 let block = ds.block_size;
7228 let rank = ds.rank;
7229 let vocab = ds.vocab;
7230 let n_t = ds.targets.len();
7231 let last = self.stages.len() - 1;
7232 let st = &self.stages[last];
7233 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx spec"))?;
7234 let stream = st.gpu.stream();
7235
7236 let prof = if dsv4_prof_on() {
7237 Some(stream.clone())
7238 } else {
7239 None
7240 };
7241 let tap = {
7243 let _p = phase!("1a.tap_copy", prof.as_ref());
7244 let mut row = stream.alloc_zeros::<f32>(n_t * hidden).map_err(e("tapr"))?;
7245 let src = state
7246 .taps
7247 .slice(tap_row * n_t * hidden..(tap_row + 1) * n_t * hidden);
7248 stream.memcpy_dtod(&src, &mut row).map_err(e("tap cp"))?;
7249 row
7250 };
7251 let mx = {
7252 let _p = phase!("1b.main_x", prof.as_ref());
7253 self.dspark_main_x(&tap, 1)?
7254 };
7255 let (cap_main_hidden, cap_main_x) = if capture {
7256 (
7257 Some(dtoh_f32(&stream, &tap)?),
7258 Some(dtoh_f32(&stream, &mx)?),
7259 )
7260 } else {
7261 (None, None)
7262 };
7263 let _p_embed = phase!("1c.embed_h2d_repeat", prof.as_ref());
7266 let mut draft_ids = vec![ds.noise_token; block];
7267 draft_ids[0] = input_token;
7268 let e_rows = self.model.embed_rows(&draft_ids);
7269 let e_dev = upload_f32(&stream, &e_rows)?;
7270 let mut h = stream
7271 .alloc_zeros::<f32>(block * hc * hidden)
7272 .map_err(e("h0"))?;
7273 unsafe {
7274 ck(
7275 "repeat_hc dspark",
7276 k::memra_dsv4_repeat_hc(
7277 dpf!(e_dev, &stream),
7278 dpm!(h, &stream),
7279 block as i32,
7280 hc as i32,
7281 hidden as i32,
7282 sp(&stream),
7283 ),
7284 )?;
7285 }
7286 drop(_p_embed);
7287 let mut block_outs: Vec<Vec<f32>> = Vec::new();
7288 let _p_blocks = phase!("1d.drafter_blocks", prof.as_ref());
7289 let n_blocks = ds.blocks.len();
7290 for bi in 0..n_blocks {
7291 let mut ring = std::mem::replace(
7293 &mut state.rings[bi],
7294 stream.alloc_zeros::<f32>(0).map_err(e("swap"))?,
7295 );
7296 let out =
7297 self.dspark_block_forward(&self.dspark().blocks[bi], &mut ring, &h, block, pos);
7298 state.rings[bi] = ring;
7299 h = out?;
7300 if capture {
7301 block_outs.push(dtoh_f32(&stream, &h)?);
7302 }
7303 }
7304 drop(_p_blocks);
7305 let w = hc * hidden;
7308 let _p_mix = phase!("1e.exit_mix_dots", prof.as_ref());
7309 let mut mixes = stream.alloc_zeros::<f32>(block * hc).map_err(e("mx"))?;
7310 self.dspark_head_dots(
7314 st,
7315 h.device_ptr(&stream).0 as *const f32,
7316 ds.hc_head_fn.device_ptr(&stream).0 as *const c_void,
7317 0,
7318 block,
7319 w,
7320 hc,
7321 mixes.device_ptr_mut(&stream).0 as *mut f32,
7322 )?;
7323 unsafe {
7324 ck(
7325 "rowsq dspark head",
7326 k::memra_dsv4_rowsq_scale(
7327 dpf!(h, &stream),
7328 dpm!(mixes, &stream),
7329 block as i32,
7330 w as i32,
7331 hc as i32,
7332 eps,
7333 sp(&stream),
7334 ),
7335 )?;
7336 }
7337 drop(_p_mix);
7338 let _p_mixrt = phase!("1f.mix_D2H_host_H2D", prof.as_ref());
7339 let mut mixes_h = dtoh_f32(&stream, &mixes)?;
7340 for t in 0..block {
7341 for c in 0..hc {
7342 let m = mixes_h[t * hc + c];
7343 mixes_h[t * hc + c] =
7344 sigmoid_f32(m * ds.hc_head_scale[0] + ds.hc_head_base[c]) + d.hc_eps;
7345 }
7346 }
7347 let pre_d = upload_f32(&stream, &mixes_h)?;
7348 drop(_p_mixrt);
7349 let _p_cn = phase!("1g.collapse_norm", prof.as_ref());
7350 let mut xc = stream.alloc_zeros::<f32>(block * hidden).map_err(e("xc"))?;
7351 unsafe {
7352 ck(
7353 "hc_collapse dspark",
7354 k::memra_dsv4_hc_collapse(
7355 dpf!(h, &stream),
7356 dpf!(pre_d, &stream),
7357 dpm!(xc, &stream),
7358 block as i32,
7359 hc as i32,
7360 hidden as i32,
7361 sp(&stream),
7362 ),
7363 )?;
7364 }
7365 let mut normed = stream.alloc_zeros::<f32>(block * hidden).map_err(e("nr"))?;
7366 unsafe {
7367 ck(
7368 "rmsnorm dspark head",
7369 k::memra_dsv4_rmsnorm(
7370 dpf!(xc, &stream),
7371 dpf!(ds.norm, &stream),
7372 dpm!(normed, &stream),
7373 block as i32,
7374 hidden as i32,
7375 eps,
7376 sp(&stream),
7377 ),
7378 )?;
7379 }
7380 drop(_p_cn);
7381 let _p_head = phase!("1h.exit_head_dots", prof.as_ref());
7382 let mut logits = stream.alloc_zeros::<f32>(block * vocab).map_err(e("lg"))?;
7383 self.dspark_head_dots(
7387 st,
7388 normed.device_ptr(&stream).0 as *const f32,
7389 st.head.as_ref().expect("head").device_ptr(&stream).0 as *const c_void,
7390 1,
7391 block,
7392 hidden,
7393 vocab,
7394 logits.device_ptr_mut(&stream).0 as *mut f32,
7395 )?;
7396 drop(_p_head);
7397 let cap_logits_pre = if capture {
7400 Some(dtoh_f32(&stream, &logits)?)
7401 } else {
7402 None
7403 };
7404 let chain_device = dsv4_dspark_chain_device();
7417 let markov_rowblk = dsv4_dspark_markov_rowblk();
7418 let mut w1_row = stream.alloc_zeros::<f32>(rank).map_err(e("w1r"))?;
7419 let mut bias = stream.alloc_zeros::<f32>(vocab).map_err(e("bias"))?;
7420 let mut am_dev = stream.alloc_zeros::<i32>(block + 1).map_err(e("am"))?;
7423 {
7424 let mut dst = am_dev.slice_mut(0..1);
7425 stream
7426 .memcpy_htod(&[input_token as i32][..], &mut dst)
7427 .map_err(e("htod am0"))?;
7428 }
7429 let mut out_ids = vec![input_token];
7430 let mut margins = Vec::with_capacity(block);
7431 let mut top1_logits = Vec::with_capacity(block);
7432 let mut conf_in = stream.alloc_zeros::<f32>(hidden + rank).map_err(e("cin"))?;
7433 let mut conf_out = stream.alloc_zeros::<f32>(block).map_err(e("cout"))?;
7434 let mut confidence = Vec::with_capacity(block);
7435 let _p_mk = phase!("1i.markov_chain", prof.as_ref());
7436 for i in 0..block {
7437 {
7438 let _p = phase!("1i1.markov_w1_gather", prof.as_ref());
7439 if chain_device {
7440 unsafe {
7441 ck(
7442 "markov w1 gather dev",
7443 k::memra_dsv4_gather_row_by_idx(
7444 dpf!(ds.markov_w1, &stream),
7445 am_dev.device_ptr(&stream).0 as *const i32,
7446 i as i32,
7447 dpm!(w1_row, &stream),
7448 rank as i32,
7449 sp(&stream),
7450 ),
7451 )?;
7452 }
7453 } else {
7454 let prev = out_ids[i] as usize;
7455 let src = ds.markov_w1.slice(prev * rank..(prev + 1) * rank);
7456 stream.memcpy_dtod(&src, &mut w1_row).map_err(e("w1 cp"))?;
7457 }
7458 }
7459 {
7460 let _p = phase!("1i2.markov_bias_gemv", prof.as_ref());
7461 if markov_rowblk {
7462 unsafe {
7463 ck(
7464 "dots_f32 markov rowblk",
7465 k::memra_dsv4_dots_f32_rowblk(
7466 dpf!(w1_row, &stream),
7467 dp!(ds.markov_w2, &stream),
7468 0,
7469 dpm!(bias, &stream),
7470 1,
7471 rank as i32,
7472 vocab as i32,
7473 sp(&stream),
7474 ),
7475 )?;
7476 }
7477 } else {
7478 Self::dots(st, &w1_row, &ds.markov_w2, 1, rank, vocab, &mut bias)?;
7479 }
7480 }
7481 let _p_aa = phase!("1i3.markov_add_argmax", prof.as_ref());
7482 unsafe {
7483 ck(
7484 "markov add dspark",
7485 k::memra_dsv4_add_inplace(
7486 (logits.device_ptr_mut(&stream).0 as usize + i * vocab * 4) as *mut f32,
7487 dpf!(bias, &stream),
7488 vocab as i64,
7489 sp(&stream),
7490 ),
7491 )?;
7492 ck(
7493 "argmax dspark",
7494 k::memra_dsv4_argmax(
7495 (logits.device_ptr(&stream).0 as usize + i * vocab * 4) as *const f32,
7496 vocab as i64,
7497 (am_dev.device_ptr_mut(&stream).0 as usize + (i + 1) * 4) as *mut i32,
7498 sp(&stream),
7499 ),
7500 )?;
7501 }
7502 drop(_p_aa);
7503 if !chain_device {
7504 let _p_d2h = phase!("1i4.markov_argmax_D2H_SYNC", None);
7505 let mut am = [0i32; 1];
7506 let view = am_dev.slice(i + 1..i + 2);
7507 stream
7508 .memcpy_dtoh(&view, &mut am[..])
7509 .map_err(e("dtoh am"))?;
7510 stream.synchronize().map_err(e("sync am"))?;
7511 out_ids.push(am[0] as u32);
7512 }
7513 {
7515 let _p = phase!("1i5.conf_in_copies", prof.as_ref());
7516 let src = xc.slice(i * hidden..(i + 1) * hidden);
7517 let mut dst = conf_in.slice_mut(0..hidden);
7518 stream.memcpy_dtod(&src, &mut dst).map_err(e("cin x"))?;
7519 let src = w1_row.slice(0..rank);
7520 let mut dst = conf_in.slice_mut(hidden..hidden + rank);
7521 stream.memcpy_dtod(&src, &mut dst).map_err(e("cin m"))?;
7522 }
7523 {
7524 let _p = phase!("1i6.conf_dots", prof.as_ref());
7529 unsafe {
7530 ck(
7531 "dots_f32 conf dspark",
7532 k::memra_dsv4_dots_f32(
7533 dpf!(conf_in, &stream),
7534 dp!(ds.conf_w, &stream),
7535 0,
7536 (conf_out.device_ptr_mut(&stream).0 as usize + i * 4) as *mut f32,
7537 1,
7538 (hidden + rank) as i32,
7539 1,
7540 sp(&stream),
7541 ),
7542 )?;
7543 }
7544 }
7545 if !chain_device {
7546 let _p = phase!("1i7.conf_D2H_SYNC", None);
7547 let mut c = [0f32; 1];
7548 let view = conf_out.slice(i..i + 1);
7549 stream
7550 .memcpy_dtoh(&view, &mut c[..])
7551 .map_err(e("dtoh cf"))?;
7552 stream.synchronize().map_err(e("sync cf"))?;
7553 confidence.push(c[0]);
7554 }
7555 }
7556 if chain_device {
7557 let _p = phase!("1i8.chain_D2H_SYNC_once", None);
7559 let mut ids = vec![0i32; block];
7560 let view = am_dev.slice(1..block + 1);
7561 stream
7562 .memcpy_dtoh(&view, &mut ids[..])
7563 .map_err(e("dtoh chain ids"))?;
7564 let mut cf = vec![0f32; block];
7565 stream
7566 .memcpy_dtoh(&conf_out, &mut cf[..])
7567 .map_err(e("dtoh chain conf"))?;
7568 stream.synchronize().map_err(e("sync chain"))?;
7569 out_ids.extend(ids.iter().map(|&x| x as u32));
7570 confidence.extend_from_slice(&cf);
7571 }
7572 drop(_p_mk);
7573 let membeds: Vec<f32> = if capture {
7578 let mut m = Vec::with_capacity(block * rank);
7579 for i in 0..block {
7580 let prev = out_ids[i] as usize;
7581 m.extend_from_slice(&ds.markov_w1_host[prev * rank..(prev + 1) * rank]);
7582 }
7583 m
7584 } else {
7585 Vec::new()
7586 };
7587 let cap = if capture {
7588 let logits_post = dtoh_f32(&stream, &logits)?;
7589 for i in 0..block {
7590 let row = &logits_post[i * vocab..(i + 1) * vocab];
7591 let top = out_ids[i + 1];
7592 let mut second = f32::NEG_INFINITY;
7593 for (vv, &val) in row.iter().enumerate() {
7594 if vv as u32 != top && val > second {
7595 second = val;
7596 }
7597 }
7598 margins.push(row[top as usize] - second);
7599 top1_logits.push(row[top as usize]);
7600 }
7601 Some(DsparkCaptureOut {
7602 main_hidden: cap_main_hidden.unwrap(),
7603 main_x: cap_main_x.unwrap(),
7604 block_outs,
7605 x_collapsed: dtoh_f32(&stream, &xc)?,
7606 logits_pre: cap_logits_pre.unwrap(),
7607 logits_post,
7608 markov_embed: membeds,
7609 })
7610 } else {
7611 None
7612 };
7613 Ok(DsparkProposal {
7614 out_ids,
7615 confidence,
7616 margins,
7617 top1_logits,
7618 capture: cap,
7619 })
7620 }
7621
7622 pub fn decode_step_tap(
7625 &self,
7626 tok: u32,
7627 state: &mut DecodeState,
7628 dspark_state: &mut DsparkState,
7629 tap_row: usize,
7630 ) -> Res<Vec<f32>> {
7631 let DecodePath::Device { host_math } = self.decode_path else {
7632 return Err("decode_step_tap requires MEMRA_DSV4_DECODE_PATH=device".into());
7633 };
7634 let n_t = self.dspark().targets.len();
7635 let hidden = self.model.mc.n_embd as usize;
7636 let (logits, _) = self.decode_step_fast_tap(
7637 tok,
7638 state,
7639 true,
7640 host_math,
7641 Some((&mut dspark_state.taps, tap_row * n_t * hidden)),
7642 )?;
7643 Ok(logits.expect("want_logits"))
7644 }
7645
7646 pub fn decode_step_greedy_tap(
7648 &self,
7649 tok: u32,
7650 state: &mut DecodeState,
7651 dspark_state: &mut DsparkState,
7652 tap_row: usize,
7653 ) -> Res<u32> {
7654 let DecodePath::Device { host_math } = self.decode_path else {
7655 return Err("decode_step_greedy_tap requires MEMRA_DSV4_DECODE_PATH=device".into());
7656 };
7657 let ds = self.dspark();
7658 let hidden = self.model.mc.n_embd as usize;
7659 let n_t = ds.targets.len();
7660 let (_, tok_next) = self.decode_step_fast_tap(
7661 tok,
7662 state,
7663 false,
7664 host_math,
7665 Some((&mut dspark_state.taps, tap_row * n_t * hidden)),
7666 )?;
7667 Ok(tok_next)
7668 }
7669}
7670
7671pub struct VerifyWs {
7705 pub tmax: usize,
7706 h_a: CudaSlice<f32>,
7707 h_b: CudaSlice<f32>,
7708 h_rx: CudaSlice<f32>,
7709 emb: CudaSlice<f32>,
7710 mixes: CudaSlice<f32>,
7711 pre: CudaSlice<f32>,
7712 post: CudaSlice<f32>,
7713 comb: CudaSlice<f32>,
7714 y_hc: CudaSlice<f32>,
7715 x: CudaSlice<f32>,
7716 xf: CudaSlice<f32>,
7717 qr: CudaSlice<f32>,
7718 qr_b: CudaSlice<u8>,
7719 q: CudaSlice<f32>,
7720 kv: CudaSlice<f32>,
7721 qi: CudaSlice<f32>,
7722 wproj: CudaSlice<f32>,
7723 score: CudaSlice<f32>,
7724 idx: CudaSlice<i32>,
7725 idx_stride: usize,
7726 o: CudaSlice<f32>,
7727 o_b: CudaSlice<u8>,
7728 og: CudaSlice<f32>,
7729 attn_out: CudaSlice<f32>,
7730 gemm_xb: CudaSlice<u8>,
7731 raw: CudaSlice<f32>,
7732 sel: CudaSlice<i32>,
7733 selw: CudaSlice<f32>,
7734 order: CudaSlice<i32>,
7735 xq: CudaSlice<u8>,
7736 xs: CudaSlice<f32>,
7737 g1: CudaSlice<f32>,
7738 g3: CudaSlice<f32>,
7739 hbuf: CudaSlice<f32>,
7740 hq: CudaSlice<u8>,
7741 hs: CudaSlice<f32>,
7742 contrib: CudaSlice<f32>,
7743 y: CudaSlice<f32>,
7744 xb: CudaSlice<u8>,
7745 sg1: CudaSlice<f32>,
7746 sg3: CudaSlice<f32>,
7747 shbuf: CudaSlice<f32>,
7748 shb16: CudaSlice<u8>,
7749 sh_out: CudaSlice<f32>,
7750 cmp_emit: CudaSlice<f32>,
7751 cmp_shift: CudaSlice<f32>,
7752 sink_scores: CudaSlice<f32>,
7753 sink_evals: CudaSlice<f32>,
7754 sink_den: CudaSlice<f64>,
7755 head_mixes: CudaSlice<f32>,
7756 head_pre: CudaSlice<f32>,
7757 collapsed: CudaSlice<f32>,
7758 logits: CudaSlice<f32>,
7759 tok: CudaSlice<i32>,
7760 pos_dev: CudaSlice<i32>,
7761 argmax: CudaSlice<i32>,
7762 bounce: CudaSlice<f32>,
7766 slot_rows: CudaSlice<i32>,
7767 tap_tmp: CudaSlice<f32>,
7769}
7770
7771struct CmpCkptDev {
7776 kv_snap: CudaSlice<f32>,
7777 sc_snap: CudaSlice<f32>,
7778 rows_kv: CudaSlice<f32>,
7779 rows_sc: CudaSlice<f32>,
7780 latent: usize,
7781 ratio: usize,
7782 overlap: bool,
7783 n_blocks0: usize,
7784}
7785
7786struct LayerCkptDev {
7789 cmp: Option<CmpCkptDev>,
7790 idx: Option<CmpCkptDev>,
7791 trans_base: usize,
7793}
7794
7795pub struct VerifyState {
7797 ws: Vec<VerifyWs>,
7798 layers: Vec<LayerCkptDev>,
7799 pub tmax: usize,
7800 open: Option<(usize, usize)>,
7802 pub bytes: Vec<u64>,
7804}
7805
7806impl Dsv4Gpu {
7807 pub fn verify_tmax(&self) -> usize {
7810 self.dspark.as_ref().map(|d| d.block_size + 1).unwrap_or(0)
7811 }
7812
7813 pub fn alloc_verify_state(&self) -> Res<VerifyState> {
7816 let tmax = self.verify_tmax();
7817 if tmax == 0 {
7818 return Err("alloc_verify_state needs MEMRA_DSV4_DRAFTER=dspark".into());
7819 }
7820 if !matches!(self.decode_path, DecodePath::Device { .. }) {
7821 return Err(
7822 "batched verify is a device-path rung (MEMRA_DSV4_DECODE_PATH=device)".into(),
7823 );
7824 }
7825 let d = self.model.cfg();
7826 let mc = &self.model.mc;
7827 let moe = mc.moe.as_ref().expect("moe");
7828 let hc = d.hc_mult as usize;
7829 let hidden = mc.n_embd as usize;
7830 let heads = mc.n_head as usize;
7831 let hd = d.head_dim as usize;
7832 let q_lora = d.q_lora_rank as usize;
7833 let win = d.sliding_window as usize;
7834 let o_groups = d.o_groups as usize;
7835 let o_lora = d.o_lora_rank as usize;
7836 let iheads = d.index_n_heads as usize;
7837 let ihd = d.index_head_dim as usize;
7838 let topk = moe.expert_used_count as usize;
7839 let ne = moe.expert_count as usize;
7840 let inter = moe.expert_ff_length as usize;
7841 let itopk = d.index_topk as usize;
7842 let n_trunk = (mc.n_layer - mc.nextn_predict_layers) as usize;
7843 let vocab = {
7844 let (info, _) = self.model.st.raw("head.weight").expect("head");
7845 info.shape[0] as usize
7846 };
7847 let sh_inter = {
7848 let (info, _) = self
7849 .model
7850 .st
7851 .raw("layers.0.ffn.shared_experts.w1.weight")
7852 .expect("shared w1");
7853 info.shape[0] as usize
7854 };
7855 let mut max_d = 0usize;
7856 let mut max_shift = 0usize;
7857 let mut min_ratio = usize::MAX;
7858 for st in &self.stages {
7859 for l in &st.layers {
7860 for cmp in l.cmp.iter().chain(l.idx.as_ref().map(|ix| &ix.cmp)) {
7861 max_d = max_d.max(cmp.d);
7862 if cmp.overlap {
7863 max_shift = max_shift.max(cmp.ratio * cmp.latent);
7864 }
7865 min_ratio = min_ratio.min(cmp.ratio);
7866 }
7867 }
7868 }
7869 assert!(min_ratio != usize::MAX, "no compressor layers?");
7870 let score_cap = self.max_seq / min_ratio + 1;
7871 let idx_tail = itopk.max(self.max_seq / 128 + 1);
7872 let idx_stride = win + idx_tail;
7873 let max_gemm_k = (o_groups * o_lora).max(hidden).max(q_lora).max(sh_inter);
7874 let mut bytes = vec![0u64; self.stages.len()];
7875 let mut ws = Vec::with_capacity(self.stages.len());
7876 for st in &self.stages {
7877 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx vws"))?;
7878 let s = st.gpu.stream();
7879 let acc = std::cell::Cell::new(0u64);
7880 let f = |n: usize| {
7881 acc.set(acc.get() + (n * 4) as u64);
7882 s.alloc_zeros::<f32>(n).map_err(e("vws f32"))
7883 };
7884 let b = |n: usize| {
7885 acc.set(acc.get() + n as u64);
7886 s.alloc_zeros::<u8>(n).map_err(e("vws u8"))
7887 };
7888 let i = |n: usize| {
7889 acc.set(acc.get() + (n * 4) as u64);
7890 s.alloc_zeros::<i32>(n).map_err(e("vws i32"))
7891 };
7892 let w = VerifyWs {
7893 tmax,
7894 h_a: f(tmax * hc * hidden)?,
7895 h_b: f(tmax * hc * hidden)?,
7896 h_rx: f(tmax * hc * hidden)?,
7897 emb: f(tmax * hidden)?,
7898 mixes: f(tmax * (2 + hc) * hc)?,
7899 pre: f(tmax * hc)?,
7900 post: f(tmax * hc)?,
7901 comb: f(tmax * hc * hc)?,
7902 y_hc: f(tmax * hidden)?,
7903 x: f(tmax * hidden)?,
7904 xf: f(tmax * hidden)?,
7905 qr: f(tmax * q_lora)?,
7906 qr_b: b(tmax * q_lora * 2)?,
7907 q: f(tmax * heads * hd)?,
7908 kv: f(tmax * hd)?,
7909 qi: f(tmax * iheads * ihd)?,
7910 wproj: f(tmax * iheads)?,
7911 score: f(score_cap)?,
7912 idx: i(tmax * idx_stride)?,
7913 idx_stride,
7914 o: f(tmax * heads * hd)?,
7915 o_b: b(tmax * heads * hd * 2)?,
7916 og: f(tmax * o_groups * o_lora)?,
7917 attn_out: f(tmax * hidden)?,
7918 gemm_xb: b(tmax * max_gemm_k * 2)?,
7919 raw: f(tmax * ne)?,
7920 sel: i(tmax * topk)?,
7921 selw: f(tmax * topk)?,
7922 order: i(tmax * topk)?,
7923 xq: b(tmax * hidden)?,
7924 xs: f(tmax * hidden / 128)?,
7925 g1: f(tmax * topk * inter)?,
7926 g3: f(tmax * topk * inter)?,
7927 hbuf: f(tmax * topk * inter)?,
7928 hq: b(tmax * topk * inter)?,
7929 hs: f(tmax * topk * inter / 128)?,
7930 contrib: f(tmax * topk * hidden)?,
7931 y: f(tmax * hidden)?,
7932 xb: b(tmax * hidden * 2)?,
7933 sg1: f(tmax * sh_inter)?,
7934 sg3: f(tmax * sh_inter)?,
7935 shbuf: f(tmax * sh_inter)?,
7936 shb16: b(tmax * sh_inter * 2)?,
7937 sh_out: f(tmax * hidden)?,
7938 cmp_emit: f(2 * max_d)?,
7939 cmp_shift: f(max_shift.max(1))?,
7940 sink_scores: f(tmax * heads * idx_stride)?,
7941 sink_evals: f(tmax * heads * idx_stride)?,
7942 sink_den: {
7943 acc.set(acc.get() + (tmax * heads * 8) as u64);
7944 s.alloc_zeros::<f64>(tmax * heads).map_err(e("vws f64"))?
7945 },
7946 head_mixes: f(tmax * hc)?,
7947 head_pre: f(tmax * hc)?,
7948 collapsed: f(tmax * hidden)?,
7949 logits: f(tmax * vocab)?,
7950 tok: i(tmax)?,
7951 pos_dev: i(tmax)?,
7952 argmax: i(tmax)?,
7953 bounce: f(tmax * hd)?,
7954 slot_rows: i(tmax)?,
7955 tap_tmp: f(tmax * hidden)?,
7956 };
7957 bytes[st.dev] += acc.get();
7958 ws.push(w);
7959 }
7960 let mut layers = Vec::with_capacity(n_trunk);
7962 for il in 0..n_trunk {
7963 let stage_i = self.layer_stage[il];
7964 let st = &self.stages[stage_i];
7965 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx ckpt"))?;
7966 let stream = st.gpu.stream();
7967 let lidx = st
7968 .layers
7969 .iter()
7970 .position(|l| l.il == il as u32)
7971 .unwrap_or_else(|| panic!("layer {il} not on stage {stage_i}"));
7972 let layer = &st.layers[lidx];
7973 let cap_blocks = self.max_seq.checked_div(layer.ratio).unwrap_or(0);
7974 let mk = |cmp: &CmpDev| -> Res<CmpCkptDev> {
7975 let slots = if cmp.overlap {
7976 2 * cmp.ratio
7977 } else {
7978 cmp.ratio
7979 };
7980 Ok(CmpCkptDev {
7981 kv_snap: stream
7982 .alloc_zeros::<f32>(slots * cmp.latent)
7983 .map_err(e("ckpt kv snap"))?,
7984 sc_snap: stream
7985 .alloc_zeros::<f32>(slots * cmp.latent)
7986 .map_err(e("ckpt sc snap"))?,
7987 rows_kv: stream
7988 .alloc_zeros::<f32>(tmax * cmp.latent)
7989 .map_err(e("ckpt rows kv"))?,
7990 rows_sc: stream
7991 .alloc_zeros::<f32>(tmax * cmp.latent)
7992 .map_err(e("ckpt rows sc"))?,
7993 latent: cmp.latent,
7994 ratio: cmp.ratio,
7995 overlap: cmp.overlap,
7996 n_blocks0: 0,
7997 })
7998 };
7999 let cmp = match &layer.cmp {
8000 Some(c) => Some(mk(c)?),
8001 None => None,
8002 };
8003 let idxc = match &layer.idx {
8004 Some(ix) => Some(mk(&ix.cmp)?),
8005 None => None,
8006 };
8007 for c in cmp.iter().chain(idxc.iter()) {
8008 let slots = if c.overlap { 2 * c.ratio } else { c.ratio };
8009 bytes[st.dev] += ((2 * slots * c.latent + 2 * tmax * c.latent) * 4) as u64;
8010 }
8011 layers.push(LayerCkptDev {
8012 cmp,
8013 idx: idxc,
8014 trans_base: d.sliding_window as usize + cap_blocks,
8015 });
8016 }
8017 for st in &self.stages {
8018 st.gpu.stream().synchronize().map_err(e("vws sync"))?;
8019 }
8020 Ok(VerifyState {
8021 ws,
8022 layers,
8023 tmax,
8024 open: None,
8025 bytes,
8026 })
8027 }
8028
8029 #[allow(clippy::too_many_arguments)]
8033 fn gemv_m_dev(
8034 st: &Stage,
8035 w: DW,
8036 x_ptr: *const c_void,
8037 y_ptr: *mut f32,
8038 m: usize,
8039 n: usize,
8040 kdim: usize,
8041 xstride: usize,
8042 ystride: usize,
8043 ) -> Res<()> {
8044 let stream = st.gpu.stream();
8045 unsafe {
8046 match w {
8047 DW::Bf16(w_ptr) => ck(
8048 "gemv_bf16_m dev",
8049 k::memra_dsv4_gemv_bf16_m(
8050 w_ptr,
8051 x_ptr,
8052 y_ptr,
8053 m as i32,
8054 n as i32,
8055 kdim as i32,
8056 xstride as i32,
8057 ystride as i32,
8058 sp(&stream),
8059 ),
8060 ),
8061 DW::Fp8 {
8062 codes,
8063 scales,
8064 sc_cols,
8065 } => ck(
8066 "gemv_fp8_m dev",
8067 k::memra_dsv4_gemv_fp8_m(
8068 codes,
8069 scales,
8070 sc_cols,
8071 x_ptr,
8072 y_ptr,
8073 m as i32,
8074 n as i32,
8075 kdim as i32,
8076 xstride as i32,
8077 ystride as i32,
8078 sp(&stream),
8079 ),
8080 ),
8081 }
8082 }
8083 }
8084
8085 #[allow(clippy::too_many_arguments)]
8087 fn gemm_m_dev(
8088 st: &Stage,
8089 x_f32: *const f32,
8090 xb: &mut CudaSlice<u8>,
8091 w: DW,
8092 m: usize,
8093 n: usize,
8094 kdim: usize,
8095 y_ptr: *mut f32,
8096 ) -> Res<()> {
8097 let stream = st.gpu.stream();
8098 unsafe {
8099 ck(
8100 "cvt_bf16 m dev",
8101 k::memra_dsv4_cvt_bf16(
8102 x_f32,
8103 xb.device_ptr_mut(&stream).0 as *mut c_void,
8104 (m * kdim) as i64,
8105 sp(&stream),
8106 ),
8107 )?;
8108 }
8109 Self::gemv_m_dev(
8110 st,
8111 w,
8112 xb.device_ptr(&stream).0 as *const c_void,
8113 y_ptr,
8114 m,
8115 n,
8116 kdim,
8117 0,
8118 0,
8119 )
8120 }
8121
8122 #[allow(clippy::too_many_arguments)]
8124 fn dots_m_dev(
8125 &self,
8126 st: &Stage,
8127 x: *const f32,
8128 w_f32: *const c_void,
8129 w_is_bf16: i32,
8130 s: usize,
8131 kdim: usize,
8132 n: usize,
8133 y: *mut f32,
8134 ) -> Res<()> {
8135 let stream = st.gpu.stream();
8136 unsafe {
8137 if self.dots_f32 {
8138 ck(
8139 "dots_f32acc_mrow",
8140 k::memra_dsv4_dots_f32acc_mrow(
8141 x,
8142 w_f32,
8143 w_is_bf16,
8144 y,
8145 s as i32,
8146 kdim as i32,
8147 n as i32,
8148 sp(&stream),
8149 ),
8150 )
8151 } else {
8152 ck(
8153 "dots_f32_mrow",
8154 k::memra_dsv4_dots_f32_mrow(
8155 x,
8156 w_f32,
8157 w_is_bf16,
8158 y,
8159 s as i32,
8160 kdim as i32,
8161 n as i32,
8162 sp(&stream),
8163 ),
8164 )
8165 }
8166 }
8167 }
8168}
8169
8170impl Dsv4Gpu {
8171 #[allow(clippy::too_many_arguments)]
8175 fn hc_pre_batch_dev(
8176 &self,
8177 st: &Stage,
8178 h_ptr: *const f32,
8179 fn_w: &CudaSlice<f32>,
8180 base_host: &[f32],
8181 scale_host: &[f32],
8182 base_dev: &CudaSlice<f32>,
8183 scale_dev: &CudaSlice<f32>,
8184 vws: &mut VerifyWs,
8185 t: usize,
8186 hc: usize,
8187 hidden: usize,
8188 iters: u32,
8189 hc_eps: f32,
8190 host_math: bool,
8191 ) -> Res<()> {
8192 let stream = st.gpu.stream();
8193 let w = hc * hidden;
8194 let rows = (2 + hc) * hc;
8195 self.dots_m_dev(
8196 st,
8197 h_ptr,
8198 fn_w.device_ptr(&stream).0 as *const c_void,
8199 0,
8200 t,
8201 w,
8202 rows,
8203 vws.mixes.device_ptr_mut(&stream).0 as *mut f32,
8204 )?;
8205 unsafe {
8206 ck(
8207 "rowsq_scale batch",
8208 self.rowsq_scale_arm(
8209 h_ptr,
8210 dpm!(vws.mixes, &stream),
8211 t as i32,
8212 w as i32,
8213 rows as i32,
8214 hc_eps,
8215 sp(&stream),
8216 ),
8217 )?;
8218 }
8219 if host_math {
8220 let mut mixes_h = vec![0f32; t * rows];
8221 let view = vws.mixes.slice(0..t * rows);
8222 stream
8223 .memcpy_dtoh(&view, &mut mixes_h[..])
8224 .map_err(e("dtoh mixes batch"))?;
8225 stream.synchronize().map_err(e("sync mixes batch"))?;
8226 let (pre_h, post_h, comb_h) =
8227 hc_split_sinkhorn(&mixes_h, t, hc, scale_host, base_host, iters, hc_eps);
8228 let mut dp = vws.pre.slice_mut(0..t * hc);
8229 stream
8230 .memcpy_htod(&pre_h, &mut dp)
8231 .map_err(e("htod pre b"))?;
8232 let mut dp = vws.post.slice_mut(0..t * hc);
8233 stream
8234 .memcpy_htod(&post_h, &mut dp)
8235 .map_err(e("htod post b"))?;
8236 let mut dp = vws.comb.slice_mut(0..t * hc * hc);
8237 stream
8238 .memcpy_htod(&comb_h, &mut dp)
8239 .map_err(e("htod comb b"))?;
8240 } else {
8241 unsafe {
8242 ck(
8243 "hc_sinkhorn_m",
8244 k::memra_dsv4_hc_sinkhorn_m(
8245 dpf!(vws.mixes, &stream),
8246 dpf!(scale_dev, &stream),
8247 dpf!(base_dev, &stream),
8248 dpm!(vws.pre, &stream),
8249 dpm!(vws.post, &stream),
8250 dpm!(vws.comb, &stream),
8251 t as i32,
8252 hc as i32,
8253 iters as i32,
8254 hc_eps,
8255 sp(&stream),
8256 ),
8257 )?;
8258 }
8259 }
8260 unsafe {
8261 ck(
8262 "hc_collapse batch",
8263 k::memra_dsv4_hc_collapse(
8264 h_ptr,
8265 dpf!(vws.pre, &stream),
8266 dpm!(vws.y_hc, &stream),
8267 t as i32,
8268 hc as i32,
8269 hidden as i32,
8270 sp(&stream),
8271 ),
8272 )?;
8273 }
8274 Ok(())
8275 }
8276
8277 #[allow(clippy::too_many_arguments)]
8283 fn cmp_decode_batch_dev(
8284 &self,
8285 st: &Stage,
8286 cmp: &CmpDev,
8287 x_ptr: *const f32,
8288 t: usize,
8289 pos0: usize,
8290 hidden: usize,
8291 fc_dev: &CudaSlice<f32>,
8292 rd: usize,
8293 eps: f32,
8294 ck_dev: &mut CmpCkptDev,
8295 emit: &mut CudaSlice<f32>,
8296 shift: &mut CudaSlice<f32>,
8297 pend_kv: &mut CudaSlice<f32>,
8298 pend_score: &mut CudaSlice<f32>,
8299 store: &mut CudaSlice<f32>,
8300 row0: usize,
8301 blocks: &mut usize,
8302 ) -> Res<()> {
8303 let stream = st.gpu.stream();
8304 let (ratio, d, latent) = (cmp.ratio, cmp.d, cmp.latent);
8305 stream
8307 .memcpy_dtod(pend_kv, &mut ck_dev.kv_snap)
8308 .map_err(e("ckpt snap kv"))?;
8309 stream
8310 .memcpy_dtod(pend_score, &mut ck_dev.sc_snap)
8311 .map_err(e("ckpt snap sc"))?;
8312 ck_dev.n_blocks0 = *blocks;
8313 self.dots_m_dev(
8314 st,
8315 x_ptr,
8316 cmp.wkv.device_ptr(&stream).0 as *const c_void,
8317 0,
8318 t,
8319 hidden,
8320 latent,
8321 ck_dev.rows_kv.device_ptr_mut(&stream).0 as *mut f32,
8322 )?;
8323 self.dots_m_dev(
8324 st,
8325 x_ptr,
8326 cmp.wgate.device_ptr(&stream).0 as *const c_void,
8327 0,
8328 t,
8329 hidden,
8330 latent,
8331 ck_dev.rows_sc.device_ptr_mut(&stream).0 as *mut f32,
8332 )?;
8333 for i in 0..t {
8334 let pos = pos0 + i;
8335 let slot = if cmp.overlap {
8336 ratio + pos % ratio
8337 } else {
8338 pos % ratio
8339 };
8340 {
8341 let src = ck_dev.rows_kv.slice(i * latent..(i + 1) * latent);
8342 let mut dst = pend_kv.slice_mut(slot * latent..(slot + 1) * latent);
8343 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend kv b"))?;
8344 let src = ck_dev.rows_sc.slice(i * latent..(i + 1) * latent);
8345 let mut dst = pend_score.slice_mut(slot * latent..(slot + 1) * latent);
8346 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend sc b"))?;
8347 }
8348 if (pos + 1) % ratio != 0 {
8349 continue;
8350 }
8351 let j = pos / ratio;
8352 let nb_launch = if cmp.overlap { 2usize } else { 1 };
8353 let row_off = if cmp.overlap { d } else { 0 };
8354 unsafe {
8355 ck(
8356 "compressor_pool batch",
8357 k::memra_dsv4_compressor_pool(
8358 dpf!(*pend_kv, &stream),
8359 dpf!(*pend_score, &stream),
8360 dpf!(cmp.ape, &stream),
8361 dpm!(*emit, &stream),
8362 nb_launch as i32,
8363 ratio as i32,
8364 d as i32,
8365 latent as i32,
8366 cmp.overlap as i32,
8367 sp(&stream),
8368 ),
8369 )?;
8370 let row_c = (emit.device_ptr(&stream).0 as usize + row_off * 4) as *const f32;
8371 let row_m = (emit.device_ptr_mut(&stream).0 as usize + row_off * 4) as *mut f32;
8372 ck(
8373 "rmsnorm batch cmp",
8374 self.rmsnorm_arm(
8375 row_c,
8376 dpf!(cmp.norm, &stream),
8377 row_m,
8378 1,
8379 d as i32,
8380 eps,
8381 sp(&stream),
8382 ),
8383 )?;
8384 ck(
8385 "rope_at batch cmp",
8386 k::memra_dsv4_rope_at(
8387 row_m,
8388 1,
8389 d as i32,
8390 rd as i32,
8391 dpf!(fc_dev, &stream),
8392 (j * ratio) as i32,
8393 0,
8394 sp(&stream),
8395 ),
8396 )?;
8397 if cmp.rotate {
8398 let scale = (d as f32).powf(-0.5);
8399 ck(
8400 "hadamard batch cmp",
8401 k::memra_dsv4_hadamard(row_m, 1, d as i32, scale, sp(&stream)),
8402 )?;
8403 ck(
8404 "fp4 batch cmp",
8405 k::memra_dsv4_fp4_act_quant(row_m, 1, d as i64, d as i32, sp(&stream)),
8406 )?;
8407 } else {
8408 ck(
8409 "act_quant batch cmp",
8410 k::memra_dsv4_act_quant(
8411 row_m,
8412 1,
8413 d as i64,
8414 (d - rd) as i32,
8415 64,
8416 (self.variant == ActQuantVariant::ClampOnly) as i32,
8417 sp(&stream),
8418 ),
8419 )?;
8420 }
8421 }
8422 {
8423 let src = emit.slice(row_off..row_off + d);
8424 let mut dst = store.slice_mut((row0 + j) * d..(row0 + j + 1) * d);
8425 stream
8426 .memcpy_dtod(&src, &mut dst)
8427 .map_err(e("emit store b"))?;
8428 }
8429 if cmp.overlap {
8430 {
8431 let src = pend_kv.slice(ratio * latent..2 * ratio * latent);
8432 let mut dst = shift.slice_mut(0..ratio * latent);
8433 stream.memcpy_dtod(&src, &mut dst).map_err(e("bshift1"))?;
8434 }
8435 {
8436 let src = shift.slice(0..ratio * latent);
8437 let mut dst = pend_kv.slice_mut(0..ratio * latent);
8438 stream.memcpy_dtod(&src, &mut dst).map_err(e("bshift2"))?;
8439 }
8440 {
8441 let src = pend_score.slice(ratio * latent..2 * ratio * latent);
8442 let mut dst = shift.slice_mut(0..ratio * latent);
8443 stream.memcpy_dtod(&src, &mut dst).map_err(e("bshift3"))?;
8444 }
8445 {
8446 let src = shift.slice(0..ratio * latent);
8447 let mut dst = pend_score.slice_mut(0..ratio * latent);
8448 stream.memcpy_dtod(&src, &mut dst).map_err(e("bshift4"))?;
8449 }
8450 }
8451 *blocks = j + 1;
8452 }
8453 Ok(())
8454 }
8455
8456 #[allow(clippy::too_many_arguments)]
8462 fn cmp_rollback_replay_dev(
8463 &self,
8464 st: &Stage,
8465 ck_dev: &CmpCkptDev,
8466 n_commit: usize,
8467 t: usize,
8468 pos0: usize,
8469 shift: &mut CudaSlice<f32>,
8470 pend_kv: &mut CudaSlice<f32>,
8471 pend_score: &mut CudaSlice<f32>,
8472 blocks: &mut usize,
8473 ) -> Res<()> {
8474 if n_commit == t {
8475 return Ok(()); }
8477 let stream = st.gpu.stream();
8478 let (ratio, latent, overlap) = (ck_dev.ratio, ck_dev.latent, ck_dev.overlap);
8479 stream
8480 .memcpy_dtod(&ck_dev.kv_snap, pend_kv)
8481 .map_err(e("rb kv snap"))?;
8482 stream
8483 .memcpy_dtod(&ck_dev.sc_snap, pend_score)
8484 .map_err(e("rb sc snap"))?;
8485 *blocks = ck_dev.n_blocks0;
8486 for i in 0..n_commit {
8487 let pos = pos0 + i;
8488 let slot = if overlap {
8489 ratio + pos % ratio
8490 } else {
8491 pos % ratio
8492 };
8493 {
8494 let src = ck_dev.rows_kv.slice(i * latent..(i + 1) * latent);
8495 let mut dst = pend_kv.slice_mut(slot * latent..(slot + 1) * latent);
8496 stream.memcpy_dtod(&src, &mut dst).map_err(e("rb row kv"))?;
8497 let src = ck_dev.rows_sc.slice(i * latent..(i + 1) * latent);
8498 let mut dst = pend_score.slice_mut(slot * latent..(slot + 1) * latent);
8499 stream.memcpy_dtod(&src, &mut dst).map_err(e("rb row sc"))?;
8500 }
8501 if (pos + 1) % ratio != 0 {
8502 continue;
8503 }
8504 if overlap {
8505 {
8506 let src = pend_kv.slice(ratio * latent..2 * ratio * latent);
8507 let mut dst = shift.slice_mut(0..ratio * latent);
8508 stream.memcpy_dtod(&src, &mut dst).map_err(e("rbshift1"))?;
8509 }
8510 {
8511 let src = shift.slice(0..ratio * latent);
8512 let mut dst = pend_kv.slice_mut(0..ratio * latent);
8513 stream.memcpy_dtod(&src, &mut dst).map_err(e("rbshift2"))?;
8514 }
8515 {
8516 let src = pend_score.slice(ratio * latent..2 * ratio * latent);
8517 let mut dst = shift.slice_mut(0..ratio * latent);
8518 stream.memcpy_dtod(&src, &mut dst).map_err(e("rbshift3"))?;
8519 }
8520 {
8521 let src = shift.slice(0..ratio * latent);
8522 let mut dst = pend_score.slice_mut(0..ratio * latent);
8523 stream.memcpy_dtod(&src, &mut dst).map_err(e("rbshift4"))?;
8524 }
8525 }
8526 *blocks += 1;
8527 }
8528 Ok(())
8529 }
8530}
8531
8532impl Dsv4Gpu {
8533 #[allow(clippy::too_many_arguments)]
8539 fn block_verify_dev(
8540 &self,
8541 st: &Stage,
8542 layer: &LayerDev,
8543 cache: &mut LayerCache,
8544 lck: &mut LayerCkptDev,
8545 vws: &mut VerifyWs,
8546 input_rx: bool,
8547 pos0: usize,
8548 t: usize,
8549 toks: &[u32],
8550 host_math: bool,
8551 ) -> Res<()> {
8552 let d = self.model.cfg();
8553 let mc = &self.model.mc;
8554 let hc = d.hc_mult as usize;
8555 let hidden = mc.n_embd as usize;
8556 let heads = mc.n_head as usize;
8557 let hd = d.head_dim as usize;
8558 let rd = d.qk_rope_head_dim as usize;
8559 let q_lora = d.q_lora_rank as usize;
8560 let win = d.sliding_window as usize;
8561 let o_groups = d.o_groups as usize;
8562 let o_lora = d.o_lora_rank as usize;
8563 let eps = mc.rms_eps;
8564 let iters = d.hc_sinkhorn_iters;
8565 let hc_eps = d.hc_eps;
8566 let stream = st.gpu.stream();
8567 let fc_dev: *const f32 = if layer.ratio != 0 {
8568 st.fc_yarn.device_ptr(&stream).0 as *const f32
8569 } else {
8570 st.fc_plain.device_ptr(&stream).0 as *const f32
8571 };
8572 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
8573 let trans_base = lck.trans_base;
8574 let LayerCache {
8575 kvc,
8576 n_blocks,
8577 pend_kv,
8578 pend_score,
8579 ikvc,
8580 i_blocks,
8581 ipend_kv,
8582 ipend_score,
8583 } = cache;
8584
8585 let h_in_ptr: *const f32 = if input_rx {
8587 vws.h_rx.device_ptr(&stream).0 as *const f32
8588 } else {
8589 vws.h_a.device_ptr(&stream).0 as *const f32
8590 };
8591 self.hc_pre_batch_dev(
8592 st,
8593 h_in_ptr,
8594 &layer.hc_attn_fn,
8595 &layer.hc_attn_base,
8596 &layer.hc_attn_scale,
8597 &layer.hc_attn_base_dev,
8598 &layer.hc_attn_scale_dev,
8599 vws,
8600 t,
8601 hc,
8602 hidden,
8603 iters,
8604 hc_eps,
8605 host_math,
8606 )?;
8607 unsafe {
8608 ck(
8609 "rmsnorm attn batch",
8610 self.rmsnorm_arm(
8611 dpf!(vws.y_hc, &stream),
8612 dpf!(layer.attn_norm, &stream),
8613 dpm!(vws.x, &stream),
8614 t as i32,
8615 hidden as i32,
8616 eps,
8617 sp(&stream),
8618 ),
8619 )?;
8620 }
8621
8622 Self::gemm_m_dev(
8624 st,
8625 vws.x.device_ptr(&stream).0 as *const f32,
8626 &mut vws.gemm_xb,
8627 dwsel(self.dense_fp8, &stream, &layer.wq_a, &layer.wq_a_fp8),
8628 t,
8629 q_lora,
8630 hidden,
8631 vws.qr.device_ptr_mut(&stream).0 as *mut f32,
8632 )?;
8633 unsafe {
8634 ck(
8635 "rmsnorm q batch",
8636 self.rmsnorm_arm(
8637 dpf!(vws.qr, &stream),
8638 dpf!(layer.q_norm, &stream),
8639 dpm!(vws.qr, &stream),
8640 t as i32,
8641 q_lora as i32,
8642 eps,
8643 sp(&stream),
8644 ),
8645 )?;
8646 ck(
8647 "cvt qr batch",
8648 k::memra_dsv4_cvt_bf16(
8649 dpf!(vws.qr, &stream),
8650 vws.qr_b.device_ptr_mut(&stream).0 as *mut c_void,
8651 (t * q_lora) as i64,
8652 sp(&stream),
8653 ),
8654 )?;
8655 }
8656 Self::gemv_m_dev(
8657 st,
8658 dwsel(self.dense_fp8, &stream, &layer.wq_b, &layer.wq_b_fp8),
8659 vws.qr_b.device_ptr(&stream).0 as *const c_void,
8660 vws.q.device_ptr_mut(&stream).0 as *mut f32,
8661 t,
8662 heads * hd,
8663 q_lora,
8664 0,
8665 0,
8666 )?;
8667 unsafe {
8668 ck(
8669 "headrms batch",
8670 self.headrms_arm(
8671 dpm!(vws.q, &stream),
8672 (t * heads) as i32,
8673 hd as i32,
8674 eps,
8675 sp(&stream),
8676 ),
8677 )?;
8678 ck(
8679 "rope q batch",
8680 k::memra_dsv4_rope(
8681 dpm!(vws.q, &stream),
8682 t as i32,
8683 heads as i32,
8684 hd as i32,
8685 rd as i32,
8686 fc_dev,
8687 vws.pos_dev.device_ptr(&stream).0 as *const i32,
8688 0,
8689 sp(&stream),
8690 ),
8691 )?;
8692 }
8693
8694 Self::gemm_m_dev(
8696 st,
8697 vws.x.device_ptr(&stream).0 as *const f32,
8698 &mut vws.gemm_xb,
8699 dwsel(self.dense_fp8, &stream, &layer.wkv, &layer.wkv_fp8),
8700 t,
8701 hd,
8702 hidden,
8703 vws.kv.device_ptr_mut(&stream).0 as *mut f32,
8704 )?;
8705 unsafe {
8706 ck(
8707 "rmsnorm kv batch",
8708 self.rmsnorm_arm(
8709 dpf!(vws.kv, &stream),
8710 dpf!(layer.kv_norm, &stream),
8711 dpm!(vws.kv, &stream),
8712 t as i32,
8713 hd as i32,
8714 eps,
8715 sp(&stream),
8716 ),
8717 )?;
8718 ck(
8719 "rope kv batch",
8720 k::memra_dsv4_rope(
8721 dpm!(vws.kv, &stream),
8722 t as i32,
8723 1,
8724 hd as i32,
8725 rd as i32,
8726 fc_dev,
8727 vws.pos_dev.device_ptr(&stream).0 as *const i32,
8728 0,
8729 sp(&stream),
8730 ),
8731 )?;
8732 ck(
8733 "act_quant kv batch",
8734 k::memra_dsv4_act_quant(
8735 dpm!(vws.kv, &stream),
8736 t as i32,
8737 hd as i64,
8738 (hd - rd) as i32,
8739 64,
8740 clamp_only,
8741 sp(&stream),
8742 ),
8743 )?;
8744 }
8745 {
8746 let src = vws.kv.slice(0..t * hd);
8747 let mut dst = kvc.slice_mut(trans_base * hd..(trans_base + t) * hd);
8748 stream
8749 .memcpy_dtod(&src, &mut dst)
8750 .map_err(e("transient ring write"))?;
8751 }
8752
8753 let mut slots = win;
8755 if layer.ratio != 0 {
8756 let ratio = layer.ratio;
8757 let nbs: Vec<usize> = (0..t).map(|i| (pos0 + i + 1) / ratio).collect();
8760 if let Some(ix) = &layer.idx {
8761 Self::gemv_m_dev(
8763 st,
8764 dwsel(self.dense_fp8, &stream, &ix.wq_b, &ix.wq_b_fp8),
8765 vws.qr_b.device_ptr(&stream).0 as *const c_void,
8766 vws.qi.device_ptr_mut(&stream).0 as *mut f32,
8767 t,
8768 ix.heads * ix.hd,
8769 q_lora,
8770 0,
8771 0,
8772 )?;
8773 unsafe {
8774 ck(
8775 "rope qi batch",
8776 k::memra_dsv4_rope(
8777 dpm!(vws.qi, &stream),
8778 t as i32,
8779 ix.heads as i32,
8780 ix.hd as i32,
8781 rd as i32,
8782 fc_dev,
8783 vws.pos_dev.device_ptr(&stream).0 as *const i32,
8784 0,
8785 sp(&stream),
8786 ),
8787 )?;
8788 let scale = (ix.hd as f32).powf(-0.5);
8789 ck(
8790 "hadamard qi batch",
8791 k::memra_dsv4_hadamard(
8792 dpm!(vws.qi, &stream),
8793 (t * ix.heads) as i32,
8794 ix.hd as i32,
8795 scale,
8796 sp(&stream),
8797 ),
8798 )?;
8799 ck(
8800 "fp4 qi batch",
8801 k::memra_dsv4_fp4_act_quant(
8802 dpm!(vws.qi, &stream),
8803 (t * ix.heads) as i32,
8804 ix.hd as i64,
8805 ix.hd as i32,
8806 sp(&stream),
8807 ),
8808 )?;
8809 }
8810 Self::gemm_m_dev(
8812 st,
8813 vws.x.device_ptr(&stream).0 as *const f32,
8814 &mut vws.gemm_xb,
8815 dwsel(
8816 self.dense_fp8,
8817 &stream,
8818 &ix.weights_proj,
8819 &ix.weights_proj_fp8,
8820 ),
8821 t,
8822 ix.heads,
8823 hidden,
8824 vws.wproj.device_ptr_mut(&stream).0 as *mut f32,
8825 )?;
8826 {
8828 let VerifyWs {
8829 x,
8830 cmp_emit,
8831 cmp_shift,
8832 ..
8833 } = vws;
8834 self.cmp_decode_batch_dev(
8835 st,
8836 &ix.cmp,
8837 x.device_ptr(&stream).0 as *const f32,
8838 t,
8839 pos0,
8840 hidden,
8841 &st.fc_yarn,
8842 rd,
8843 eps,
8844 lck.idx.as_mut().expect("idx ckpt"),
8845 cmp_emit,
8846 cmp_shift,
8847 ipend_kv.as_mut().expect("ipend"),
8848 ipend_score.as_mut().expect("ipend"),
8849 ikvc.as_mut().expect("ikvc"),
8850 0,
8851 i_blocks,
8852 )?;
8853 }
8854 debug_assert_eq!(*i_blocks, nbs[t - 1], "indexer block count (batch)");
8855 let kks: Vec<usize> = nbs.iter().map(|&nb| ix.topk.min(nb)).collect();
8856 let tail_max = kks.iter().cloned().max().unwrap_or(0);
8857 slots = win + tail_max;
8858 for i in 0..t {
8859 let pos = pos0 + i;
8860 let idx_off = i * vws.idx_stride;
8861 unsafe {
8862 ck(
8863 "build_idx_redirect fine",
8864 k::memra_dsv4_build_idx_redirect(
8865 (vws.idx.device_ptr_mut(&stream).0 as usize + idx_off * 4)
8866 as *mut i32,
8867 pos as i32,
8868 win as i32,
8869 0, slots as i32,
8871 pos0 as i32,
8872 trans_base as i32,
8873 sp(&stream),
8874 ),
8875 )?;
8876 }
8877 let nb = nbs[i];
8878 if nb == 0 {
8879 continue;
8880 }
8881 let wscale = ((ix.hd as f64).powf(-0.5) * (ix.heads as f64).powf(-0.5)) as f32;
8882 unsafe {
8883 ck(
8884 "indexer_score batch",
8885 self.indexer_score_arm(
8886 (vws.qi.device_ptr(&stream).0 as usize + i * ix.heads * ix.hd * 4)
8887 as *const f32,
8888 dpf!(ikvc.as_ref().expect("ikvc"), &stream),
8889 (vws.wproj.device_ptr(&stream).0 as usize + i * ix.heads * 4)
8890 as *const f32,
8891 wscale,
8892 dpm!(vws.score, &stream),
8893 1,
8894 ix.heads as i32,
8895 ix.hd as i32,
8896 nb as i32,
8897 ratio as i32,
8898 nb as i32,
8899 sp(&stream),
8900 ),
8901 )?;
8902 }
8903 let kk = kks[i];
8904 if host_math {
8905 let score_h = {
8906 let view = vws.score.slice(0..nb);
8907 let mut v = vec![0f32; nb];
8908 stream
8909 .memcpy_dtoh(&view, &mut v[..])
8910 .map_err(e("dtoh sc b"))?;
8911 stream.synchronize().map_err(e("sync sc b"))?;
8912 v
8913 };
8914 let mut order: Vec<usize> = (0..nb).collect();
8915 order.sort_by(|&a, &b| {
8916 score_h[b]
8917 .partial_cmp(&score_h[a])
8918 .unwrap_or(std::cmp::Ordering::Equal)
8919 .then(a.cmp(&b))
8920 });
8921 let cidx: Vec<i32> = order
8922 .into_iter()
8923 .take(kk)
8924 .map(|j| (j + win) as i32)
8925 .collect();
8926 let mut dst = vws.idx.slice_mut(idx_off + win..idx_off + win + kk);
8927 stream
8928 .memcpy_htod(&cidx, &mut dst)
8929 .map_err(e("htod idx b"))?;
8930 } else {
8931 unsafe {
8932 let idx_tail_ptr = (vws.idx.device_ptr_mut(&stream).0 as usize
8933 + (idx_off + win) * 4)
8934 as *mut i32;
8935 ck(
8936 "topk_idx batch",
8937 k::memra_dsv4_topk_idx(
8938 dpf!(vws.score, &stream),
8939 nb as i32,
8940 kk as i32,
8941 win as i32,
8942 idx_tail_ptr,
8943 sp(&stream),
8944 ),
8945 )?;
8946 }
8947 }
8948 }
8949 } else {
8950 let tail_max = nbs.iter().cloned().max().unwrap_or(0);
8951 slots = win + tail_max;
8952 for (i, &nb_i) in nbs.iter().enumerate() {
8953 let pos = pos0 + i;
8954 let idx_off = i * vws.idx_stride;
8955 unsafe {
8956 ck(
8957 "build_idx_redirect coarse",
8958 k::memra_dsv4_build_idx_redirect(
8959 (vws.idx.device_ptr_mut(&stream).0 as usize + idx_off * 4)
8960 as *mut i32,
8961 pos as i32,
8962 win as i32,
8963 nb_i as i32,
8964 slots as i32,
8965 pos0 as i32,
8966 trans_base as i32,
8967 sp(&stream),
8968 ),
8969 )?;
8970 }
8971 }
8972 }
8973 {
8975 let VerifyWs {
8976 x,
8977 cmp_emit,
8978 cmp_shift,
8979 ..
8980 } = vws;
8981 self.cmp_decode_batch_dev(
8982 st,
8983 layer.cmp.as_ref().expect("ratio!=0 has compressor"),
8984 x.device_ptr(&stream).0 as *const f32,
8985 t,
8986 pos0,
8987 hidden,
8988 &st.fc_yarn,
8989 rd,
8990 eps,
8991 lck.cmp.as_mut().expect("cmp ckpt"),
8992 cmp_emit,
8993 cmp_shift,
8994 pend_kv.as_mut().expect("pend"),
8995 pend_score.as_mut().expect("pend"),
8996 kvc,
8997 win,
8998 n_blocks,
8999 )?;
9000 }
9001 debug_assert_eq!(*n_blocks, nbs[t - 1], "attn block count (batch)");
9002 } else {
9003 for i in 0..t {
9004 let pos = pos0 + i;
9005 let idx_off = i * vws.idx_stride;
9006 unsafe {
9007 ck(
9008 "build_idx_redirect window-only",
9009 k::memra_dsv4_build_idx_redirect(
9010 (vws.idx.device_ptr_mut(&stream).0 as usize + idx_off * 4) as *mut i32,
9011 pos as i32,
9012 win as i32,
9013 -1,
9014 win as i32,
9015 pos0 as i32,
9016 trans_base as i32,
9017 sp(&stream),
9018 ),
9019 )?;
9020 }
9021 }
9022 }
9023
9024 let scale = (hd as f64).powf(-0.5) as f32;
9027 unsafe {
9028 if self.chains_f32 {
9029 ck(
9030 "sink_attn_dec_mq_f32acc",
9031 k::memra_dsv4_sink_attn_dec_mq_f32acc(
9032 dpf!(vws.q, &stream),
9033 dpf!(kvc, &stream),
9034 vws.idx.device_ptr(&stream).0 as *const i32,
9035 dpf!(layer.sink, &stream),
9036 dpm!(vws.sink_scores, &stream),
9037 dpm!(vws.sink_evals, &stream),
9038 vws.sink_den.device_ptr_mut(&stream).0 as *mut f32,
9039 dpm!(vws.o, &stream),
9040 t as i32,
9041 heads as i32,
9042 hd as i32,
9043 slots as i32,
9044 vws.idx_stride as i32,
9045 scale,
9046 sp(&stream),
9047 ),
9048 )?;
9049 } else {
9050 ck(
9051 "sink_attn_dec_mq",
9052 k::memra_dsv4_sink_attn_dec_mq(
9053 dpf!(vws.q, &stream),
9054 dpf!(kvc, &stream),
9055 vws.idx.device_ptr(&stream).0 as *const i32,
9056 dpf!(layer.sink, &stream),
9057 dpm!(vws.sink_scores, &stream),
9058 dpm!(vws.sink_evals, &stream),
9059 vws.sink_den.device_ptr_mut(&stream).0 as *mut f64,
9060 dpm!(vws.o, &stream),
9061 t as i32,
9062 heads as i32,
9063 hd as i32,
9064 slots as i32,
9065 vws.idx_stride as i32,
9066 scale,
9067 sp(&stream),
9068 ),
9069 )?;
9070 }
9071 ck(
9072 "rope o inv batch",
9073 k::memra_dsv4_rope(
9074 dpm!(vws.o, &stream),
9075 t as i32,
9076 heads as i32,
9077 hd as i32,
9078 rd as i32,
9079 fc_dev,
9080 vws.pos_dev.device_ptr(&stream).0 as *const i32,
9081 1,
9082 sp(&stream),
9083 ),
9084 )?;
9085 }
9086
9087 let gw = heads / o_groups * hd;
9089 unsafe {
9090 ck(
9091 "cvt o batch",
9092 k::memra_dsv4_cvt_bf16(
9093 dpf!(vws.o, &stream),
9094 vws.o_b.device_ptr_mut(&stream).0 as *mut c_void,
9095 (t * heads * hd) as i64,
9096 sp(&stream),
9097 ),
9098 )?;
9099 }
9100 let wo_a_dw = dwsel(self.dense_fp8, &stream, &layer.wo_a, &layer.wo_a_fp8);
9101 for g in 0..o_groups {
9102 Self::gemv_m_dev(
9103 st,
9104 wo_a_dw.offset_rows(g * o_lora, gw),
9105 (vws.o_b.device_ptr(&stream).0 as usize + g * gw * 2) as *const c_void,
9106 (vws.og.device_ptr_mut(&stream).0 as usize + g * o_lora * 4) as *mut f32,
9107 t,
9108 o_lora,
9109 gw,
9110 heads * hd,
9111 o_groups * o_lora,
9112 )?;
9113 }
9114 Self::gemm_m_dev(
9115 st,
9116 vws.og.device_ptr(&stream).0 as *const f32,
9117 &mut vws.gemm_xb,
9118 dwsel(self.dense_fp8, &stream, &layer.wo_b, &layer.wo_b_fp8),
9119 t,
9120 hidden,
9121 o_groups * o_lora,
9122 vws.attn_out.device_ptr_mut(&stream).0 as *mut f32,
9123 )?;
9124
9125 unsafe {
9127 ck(
9128 "hc_post attn batch",
9129 k::memra_dsv4_hc_post(
9130 dpf!(vws.attn_out, &stream),
9131 h_in_ptr,
9132 dpf!(vws.post, &stream),
9133 dpf!(vws.comb, &stream),
9134 dpm!(vws.h_b, &stream),
9135 t as i32,
9136 hc as i32,
9137 hidden as i32,
9138 sp(&stream),
9139 ),
9140 )?;
9141 }
9142
9143 let h_b_ptr = vws.h_b.device_ptr(&stream).0 as *const f32;
9145 self.hc_pre_batch_dev(
9146 st,
9147 h_b_ptr,
9148 &layer.hc_ffn_fn,
9149 &layer.hc_ffn_base,
9150 &layer.hc_ffn_scale,
9151 &layer.hc_ffn_base_dev,
9152 &layer.hc_ffn_scale_dev,
9153 vws,
9154 t,
9155 hc,
9156 hidden,
9157 iters,
9158 hc_eps,
9159 host_math,
9160 )?;
9161 unsafe {
9162 ck(
9163 "rmsnorm ffn batch",
9164 self.rmsnorm_arm(
9165 dpf!(vws.y_hc, &stream),
9166 dpf!(layer.ffn_norm, &stream),
9167 dpm!(vws.xf, &stream),
9168 t as i32,
9169 hidden as i32,
9170 eps,
9171 sp(&stream),
9172 ),
9173 )?;
9174 }
9175 self.moe_verify_dev(st, layer, vws, t, toks, host_math)?;
9176 unsafe {
9177 ck(
9178 "hc_post ffn batch",
9179 k::memra_dsv4_hc_post(
9180 dpf!(vws.y, &stream),
9181 dpf!(vws.h_b, &stream),
9182 dpf!(vws.post, &stream),
9183 dpf!(vws.comb, &stream),
9184 dpm!(vws.h_a, &stream),
9185 t as i32,
9186 hc as i32,
9187 hidden as i32,
9188 sp(&stream),
9189 ),
9190 )?;
9191 }
9192 Ok(())
9193 }
9194
9195 fn moe_verify_dev(
9200 &self,
9201 st: &Stage,
9202 layer: &LayerDev,
9203 vws: &mut VerifyWs,
9204 t: usize,
9205 toks: &[u32],
9206 host_math: bool,
9207 ) -> Res<()> {
9208 let mc = &self.model.mc;
9209 let d = self.model.cfg();
9210 let moe = mc.moe.as_ref().expect("moe");
9211 let hidden = mc.n_embd as usize;
9212 let ne = moe.expert_count as usize;
9213 let topk = moe.expert_used_count as usize;
9214 let inter = moe.expert_ff_length as usize;
9215 let limit = d.swiglu_limit;
9216 let stream = st.gpu.stream();
9217 let kind = match layer.expert_kind {
9218 ExpertKind::Nvfp4 => 0i32,
9219 ExpertKind::Mxfp4 => 1i32,
9220 };
9221 let wstride = (inter * hidden / 2) as i64;
9222 let sstride = match layer.expert_kind {
9223 ExpertKind::Nvfp4 => (inter * hidden / 16) as i64,
9224 ExpertKind::Mxfp4 => (inter * hidden / 32) as i64,
9225 };
9226 let slots = t * topk;
9227
9228 self.dots_m_dev(
9229 st,
9230 vws.xf.device_ptr(&stream).0 as *const f32,
9231 layer.gate_w.device_ptr(&stream).0 as *const c_void,
9232 0,
9233 t,
9234 hidden,
9235 ne,
9236 vws.raw.device_ptr_mut(&stream).0 as *mut f32,
9237 )?;
9238 if host_math {
9239 let raw_h = {
9240 let view = vws.raw.slice(0..t * ne);
9241 let mut v = vec![0f32; t * ne];
9242 stream
9243 .memcpy_dtoh(&view, &mut v[..])
9244 .map_err(e("dtoh raw b"))?;
9245 stream.synchronize().map_err(e("sync raw b"))?;
9246 v
9247 };
9248 let (indices, weights) =
9249 Self::route_host(layer, &raw_h, toks, t, ne, topk, d.routed_scaling_factor);
9250 let sel: Vec<i32> = indices.iter().map(|&x| x as i32).collect();
9251 let mut order = vec![0i32; t * topk];
9252 for p in 0..t {
9253 let mut o: Vec<i32> = (0..topk as i32).collect();
9254 o.sort_by_key(|&s| indices[p * topk + s as usize]);
9255 order[p * topk..(p + 1) * topk].copy_from_slice(&o);
9256 }
9257 let mut dst = vws.sel.slice_mut(0..t * topk);
9258 stream
9259 .memcpy_htod(&sel, &mut dst)
9260 .map_err(e("htod sel b"))?;
9261 let mut dst = vws.selw.slice_mut(0..t * topk);
9262 stream
9263 .memcpy_htod(&weights, &mut dst)
9264 .map_err(e("htod selw b"))?;
9265 let mut dst = vws.order.slice_mut(0..t * topk);
9266 stream
9267 .memcpy_htod(&order, &mut dst)
9268 .map_err(e("htod order b"))?;
9269 } else {
9270 unsafe {
9271 ck(
9272 "route_m",
9273 k::memra_dsv4_route_m(
9274 dpf!(vws.raw, &stream),
9275 layer
9276 .gate_bias_dev
9277 .as_ref()
9278 .map(|b| b.device_ptr(&stream).0 as *const f32)
9279 .unwrap_or(std::ptr::null()),
9280 layer
9281 .tid2eid_dev
9282 .as_ref()
9283 .map(|x| x.device_ptr(&stream).0 as *const i32)
9284 .unwrap_or(std::ptr::null()),
9285 vws.tok.device_ptr(&stream).0 as *const i32,
9286 t as i32,
9287 ne as i32,
9288 topk as i32,
9289 d.routed_scaling_factor,
9290 vws.sel.device_ptr_mut(&stream).0 as *mut i32,
9291 vws.selw.device_ptr_mut(&stream).0 as *mut f32,
9292 vws.order.device_ptr_mut(&stream).0 as *mut i32,
9293 sp(&stream),
9294 ),
9295 )?;
9296 }
9297 }
9298
9299 unsafe {
9300 ck(
9301 "act_quant_fp8 x batch",
9302 k::memra_dsv4_act_quant_fp8(
9303 dpf!(vws.xf, &stream),
9304 vws.xq.device_ptr_mut(&stream).0 as *mut c_void,
9305 dpm!(vws.xs, &stream),
9306 t as i32,
9307 hidden as i32,
9308 sp(&stream),
9309 ),
9310 )?;
9311 for (proj, dst) in [(0i32, &mut vws.g1), (2i32, &mut vws.g3)] {
9312 ck(
9313 "fp4_gemm_sel_g w1/w3",
9314 k::memra_dsv4_fp4_gemm_sel_g(
9315 dp!(vws.xq, &stream),
9316 dpf!(vws.xs, &stream),
9317 dp!(layer.experts_w, &stream),
9318 dp!(layer.experts_sc, &stream),
9319 dpf!(layer.experts_s2_dev, &stream),
9320 vws.sel.device_ptr(&stream).0 as *const i32,
9321 proj,
9322 0,
9323 kind,
9324 dpm!(*dst, &stream),
9325 slots as i32,
9326 inter as i32,
9327 hidden as i32,
9328 wstride,
9329 sstride,
9330 topk as i32,
9331 sp(&stream),
9332 ),
9333 )?;
9334 }
9335 ck(
9336 "swiglu batch",
9337 k::memra_dsv4_swiglu(
9338 dpf!(vws.g1, &stream),
9339 dpf!(vws.g3, &stream),
9340 dpm!(vws.hbuf, &stream),
9341 slots as i32,
9342 inter as i32,
9343 limit,
9344 vws.selw.device_ptr(&stream).0 as *const f32,
9345 sp(&stream),
9346 ),
9347 )?;
9348 ck(
9349 "act_quant_fp8 h batch",
9350 k::memra_dsv4_act_quant_fp8(
9351 dpf!(vws.hbuf, &stream),
9352 vws.hq.device_ptr_mut(&stream).0 as *mut c_void,
9353 dpm!(vws.hs, &stream),
9354 slots as i32,
9355 inter as i32,
9356 sp(&stream),
9357 ),
9358 )?;
9359 ck(
9360 "fp4_gemm_sel_g w2",
9361 k::memra_dsv4_fp4_gemm_sel_g(
9362 dp!(vws.hq, &stream),
9363 dpf!(vws.hs, &stream),
9364 dp!(layer.experts_w, &stream),
9365 dp!(layer.experts_sc, &stream),
9366 dpf!(layer.experts_s2_dev, &stream),
9367 vws.sel.device_ptr(&stream).0 as *const i32,
9368 1,
9369 1,
9370 kind,
9371 dpm!(vws.contrib, &stream),
9372 slots as i32,
9373 hidden as i32,
9374 inter as i32,
9375 wstride,
9376 sstride,
9377 0,
9378 sp(&stream),
9379 ),
9380 )?;
9381 ck(
9382 "combine_rows_m",
9383 k::memra_dsv4_combine_rows_m(
9384 dpf!(vws.contrib, &stream),
9385 vws.order.device_ptr(&stream).0 as *const i32,
9386 topk as i32,
9387 dpm!(vws.y, &stream),
9388 hidden as i64,
9389 t as i32,
9390 sp(&stream),
9391 ),
9392 )?;
9393 ck(
9394 "cvt xb batch",
9395 k::memra_dsv4_cvt_bf16(
9396 dpf!(vws.xf, &stream),
9397 vws.xb.device_ptr_mut(&stream).0 as *mut c_void,
9398 (t * hidden) as i64,
9399 sp(&stream),
9400 ),
9401 )?;
9402 }
9403 let sh_inter = vws.sg1.len() / vws.tmax;
9404 Self::gemv_m_dev(
9405 st,
9406 dwsel(
9407 self.dense_fp8,
9408 &stream,
9409 &layer.shared_w[0],
9410 &layer.shared_fp8[0],
9411 ),
9412 vws.xb.device_ptr(&stream).0 as *const c_void,
9413 vws.sg1.device_ptr_mut(&stream).0 as *mut f32,
9414 t,
9415 sh_inter,
9416 hidden,
9417 0,
9418 0,
9419 )?;
9420 Self::gemv_m_dev(
9421 st,
9422 dwsel(
9423 self.dense_fp8,
9424 &stream,
9425 &layer.shared_w[2],
9426 &layer.shared_fp8[2],
9427 ),
9428 vws.xb.device_ptr(&stream).0 as *const c_void,
9429 vws.sg3.device_ptr_mut(&stream).0 as *mut f32,
9430 t,
9431 sh_inter,
9432 hidden,
9433 0,
9434 0,
9435 )?;
9436 unsafe {
9437 ck(
9438 "swiglu sh batch",
9439 k::memra_dsv4_swiglu(
9440 dpf!(vws.sg1, &stream),
9441 dpf!(vws.sg3, &stream),
9442 dpm!(vws.shbuf, &stream),
9443 t as i32,
9444 sh_inter as i32,
9445 limit,
9446 std::ptr::null(),
9447 sp(&stream),
9448 ),
9449 )?;
9450 ck(
9451 "cvt sh batch",
9452 k::memra_dsv4_cvt_bf16(
9453 dpf!(vws.shbuf, &stream),
9454 vws.shb16.device_ptr_mut(&stream).0 as *mut c_void,
9455 (t * sh_inter) as i64,
9456 sp(&stream),
9457 ),
9458 )?;
9459 }
9460 Self::gemv_m_dev(
9461 st,
9462 dwsel(
9463 self.dense_fp8,
9464 &stream,
9465 &layer.shared_w[1],
9466 &layer.shared_fp8[1],
9467 ),
9468 vws.shb16.device_ptr(&stream).0 as *const c_void,
9469 vws.sh_out.device_ptr_mut(&stream).0 as *mut f32,
9470 t,
9471 hidden,
9472 sh_inter,
9473 0,
9474 0,
9475 )?;
9476 unsafe {
9477 ck(
9478 "add shared batch",
9479 k::memra_dsv4_add_inplace(
9480 dpm!(vws.y, &stream),
9481 dpf!(vws.sh_out, &stream),
9482 (t * hidden) as i64,
9483 sp(&stream),
9484 ),
9485 )?;
9486 }
9487 Ok(())
9488 }
9489
9490 fn head_logits_batch_dev(&self, vws: &mut VerifyWs, t: usize, host_math: bool) -> Res<()> {
9494 let d = self.model.cfg();
9495 let mc = &self.model.mc;
9496 let hc = d.hc_mult as usize;
9497 let hidden = mc.n_embd as usize;
9498 let eps = mc.rms_eps;
9499 let last = self.stages.len() - 1;
9500 let st = &self.stages[last];
9501 let stream = st.gpu.stream();
9502 let w = hc * hidden;
9503 let fn_w = st.hc_head_fn.as_ref().expect("hc_head_fn");
9504 let norm = st.trunk_norm.as_ref().expect("trunk norm");
9505 let vocab = vws.logits.len() / vws.tmax;
9506 self.dots_m_dev(
9507 st,
9508 vws.h_a.device_ptr(&stream).0 as *const f32,
9509 fn_w.device_ptr(&stream).0 as *const c_void,
9510 0,
9511 t,
9512 w,
9513 hc,
9514 vws.head_mixes.device_ptr_mut(&stream).0 as *mut f32,
9515 )?;
9516 unsafe {
9517 ck(
9518 "rowsq head batch",
9519 self.rowsq_scale_arm(
9520 dpf!(vws.h_a, &stream),
9521 dpm!(vws.head_mixes, &stream),
9522 t as i32,
9523 w as i32,
9524 hc as i32,
9525 eps,
9526 sp(&stream),
9527 ),
9528 )?;
9529 }
9530 if host_math {
9531 let mut mixes_h = vec![0f32; t * hc];
9532 let view = vws.head_mixes.slice(0..t * hc);
9533 stream
9534 .memcpy_dtoh(&view, &mut mixes_h[..])
9535 .map_err(e("dtoh head mixes b"))?;
9536 stream.synchronize().map_err(e("sync head mixes b"))?;
9537 for p in 0..t {
9538 for c in 0..hc {
9539 let m = mixes_h[p * hc + c];
9540 mixes_h[p * hc + c] =
9541 sigmoid_f32(m * self.hc_head_scale[0] + self.hc_head_base[c]) + d.hc_eps;
9542 }
9543 }
9544 let mut dst = vws.head_pre.slice_mut(0..t * hc);
9545 stream
9546 .memcpy_htod(&mixes_h, &mut dst)
9547 .map_err(e("htod head pre b"))?;
9548 } else {
9549 unsafe {
9550 ck(
9551 "hc_head_pre_m",
9552 k::memra_dsv4_hc_head_pre_m(
9553 dpf!(vws.head_mixes, &stream),
9554 st.hc_head_scale_dev
9555 .as_ref()
9556 .expect("head scale dev")
9557 .device_ptr(&stream)
9558 .0 as *const f32,
9559 st.hc_head_base_dev
9560 .as_ref()
9561 .expect("head base dev")
9562 .device_ptr(&stream)
9563 .0 as *const f32,
9564 dpm!(vws.head_pre, &stream),
9565 t as i32,
9566 hc as i32,
9567 d.hc_eps,
9568 sp(&stream),
9569 ),
9570 )?;
9571 }
9572 }
9573 unsafe {
9574 ck(
9575 "hc_collapse head batch",
9576 k::memra_dsv4_hc_collapse(
9577 dpf!(vws.h_a, &stream),
9578 dpf!(vws.head_pre, &stream),
9579 dpm!(vws.collapsed, &stream),
9580 t as i32,
9581 hc as i32,
9582 hidden as i32,
9583 sp(&stream),
9584 ),
9585 )?;
9586 ck(
9587 "rmsnorm head batch",
9588 self.rmsnorm_arm(
9589 dpf!(vws.collapsed, &stream),
9590 dpf!(norm, &stream),
9591 dpm!(vws.collapsed, &stream),
9592 t as i32,
9593 hidden as i32,
9594 eps,
9595 sp(&stream),
9596 ),
9597 )?;
9598 }
9599 let head_ptr = st.head.as_ref().expect("head").device_ptr(&stream).0 as *const c_void;
9600 self.dots_m_dev(
9601 st,
9602 vws.collapsed.device_ptr(&stream).0 as *const f32,
9603 head_ptr,
9604 1,
9605 t,
9606 hidden,
9607 vocab,
9608 vws.logits.device_ptr_mut(&stream).0 as *mut f32,
9609 )?;
9610 Ok(())
9611 }
9612}
9613
9614pub struct SpecRoundGpu {
9616 pub start_pos: usize,
9617 pub drafts: Vec<u32>,
9618 pub accepts: usize,
9619 pub verified: usize,
9620 pub t_batch: usize,
9622 pub t_cap: usize,
9626 pub confidence: Vec<f32>,
9631 pub emitted: usize,
9633 pub round_us: u64,
9637}
9638
9639pub struct SpecRunGpu {
9640 pub tokens: Vec<u32>,
9641 pub rounds: Vec<SpecRoundGpu>,
9642}
9643
9644impl Dsv4Gpu {
9645 pub fn verify_batch_dev(
9654 &self,
9655 toks: &[u32],
9656 state: &mut DecodeState,
9657 vstate: &mut VerifyState,
9658 taps: Option<&mut CudaSlice<f32>>,
9659 want_logits: bool,
9660 ) -> Res<(Option<Vec<f32>>, Vec<u32>)> {
9661 let DecodePath::Device { host_math } = self.decode_path else {
9662 return Err("verify_batch_dev requires MEMRA_DSV4_DECODE_PATH=device".into());
9663 };
9664 let mc = &self.model.mc;
9665 let d = self.model.cfg();
9666 let t = toks.len();
9667 assert!(
9668 t >= 1 && t <= vstate.tmax,
9669 "round depth {t} > tmax {}",
9670 vstate.tmax
9671 );
9672 assert!(vstate.open.is_none(), "verify_batch_dev with an open round");
9673 let pos0 = state.pos;
9674 assert!(pos0 > 0, "batched verify needs prefill_with_cache first");
9675 assert!(
9676 pos0 + t <= self.max_seq,
9677 "round [{pos0}, {}) exceeds max_seq {}",
9678 pos0 + t,
9679 self.max_seq
9680 );
9681 let hidden = mc.n_embd as usize;
9682 let hc = d.hc_mult as usize;
9683 let n_trunk = (mc.n_layer - mc.nextn_predict_layers) as usize;
9684 let tok_i32: Vec<i32> = toks.iter().map(|&x| x as i32).collect();
9685 let pos_i32: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
9686
9687 for (si, st) in self.stages.iter().enumerate() {
9690 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx round"))?;
9691 let stream = st.gpu.stream();
9692 let vws = &mut vstate.ws[si];
9693 let mut dst = vws.tok.slice_mut(0..t);
9694 stream
9695 .memcpy_htod(&tok_i32, &mut dst)
9696 .map_err(e("htod tok round"))?;
9697 let mut dst = vws.pos_dev.slice_mut(0..t);
9698 stream
9699 .memcpy_htod(&pos_i32, &mut dst)
9700 .map_err(e("htod pos round"))?;
9701 }
9702
9703 {
9705 let st0 = &self.stages[0];
9706 st0.gpu.ctx.bind_to_thread().map_err(e("bind ctx0 round"))?;
9707 let stream0 = st0.gpu.stream();
9708 let vws0 = &mut vstate.ws[0];
9709 unsafe {
9710 ck(
9711 "embed_rows batch",
9712 k::memra_dsv4_embed_rows(
9713 st0.embed
9714 .as_ref()
9715 .expect("embed on stage 0")
9716 .device_ptr(&stream0)
9717 .0 as *const c_void,
9718 vws0.tok.device_ptr(&stream0).0 as *const i32,
9719 dpm!(vws0.emb, &stream0),
9720 t as i32,
9721 hidden as i32,
9722 sp(&stream0),
9723 ),
9724 )?;
9725 ck(
9726 "repeat_hc batch",
9727 k::memra_dsv4_repeat_hc(
9728 dpf!(vws0.emb, &stream0),
9729 dpm!(vws0.h_a, &stream0),
9730 t as i32,
9731 hc as i32,
9732 hidden as i32,
9733 sp(&stream0),
9734 ),
9735 )?;
9736 }
9737 }
9738
9739 let targets = self.dspark.as_ref().map(|ds| ds.targets.clone());
9740 let n_t = targets.as_ref().map(|x| x.len()).unwrap_or(0);
9741 let mut taps = taps;
9742 let mut cur_stage = 0usize;
9743 let mut input_rx = false;
9744 for il in 0..n_trunk {
9745 let stage = self.layer_stage[il];
9746 if stage != cur_stage {
9747 let bytes = t * hc * hidden * std::mem::size_of::<f32>();
9748 let src_stream = self.stages[cur_stage].gpu.stream();
9749 let dst_stream = self.stages[stage].gpu.stream();
9750 let (ws_src, ws_dst) = vstate.ws.split_at_mut(stage);
9751 let src_ws = &ws_src[cur_stage];
9752 let dst_ws = &mut ws_dst[0];
9753 self.stages[cur_stage]
9754 .gpu
9755 .ctx
9756 .bind_to_thread()
9757 .map_err(e("bind tx round"))?;
9758 let (sp_, _g0) = src_ws.h_a.device_ptr(&src_stream);
9759 let (dp_, _g1) = dst_ws.h_rx.device_ptr_mut(&src_stream);
9760 unsafe {
9761 cudarc::driver::result::memcpy_peer_async(
9762 self.stages[stage].gpu.ctx.cu_ctx(),
9763 dp_,
9764 self.stages[cur_stage].gpu.ctx.cu_ctx(),
9765 sp_,
9766 bytes,
9767 src_stream.cu_stream(),
9768 )
9769 .map_err(e("peer copy h round"))?;
9770 }
9771 let bnd = stage - 1;
9772 self.boundary_ev[bnd]
9773 .record(&src_stream)
9774 .map_err(e("ev record round"))?;
9775 dst_stream
9776 .wait(&self.boundary_ev[bnd])
9777 .map_err(e("ev wait round"))?;
9778 self.stages[stage]
9779 .gpu
9780 .ctx
9781 .bind_to_thread()
9782 .map_err(e("bind rx round"))?;
9783 cur_stage = stage;
9784 input_rx = true;
9785 }
9786 let st = &self.stages[stage];
9787 let lidx = st
9788 .layers
9789 .iter()
9790 .position(|l| l.il == il as u32)
9791 .unwrap_or_else(|| panic!("layer {il} not on stage {stage}"));
9792 self.block_verify_dev(
9793 st,
9794 &st.layers[lidx],
9795 &mut state.caches[il],
9796 &mut vstate.layers[il],
9797 &mut vstate.ws[stage],
9798 input_rx,
9799 pos0,
9800 t,
9801 toks,
9802 host_math,
9803 )?;
9804 input_rx = false;
9805 if let (Some(tp), Some(tg)) = (taps.as_mut(), targets.as_ref()) {
9807 if let Some(kk) = tg.iter().position(|&tl| tl == il) {
9808 let stream = self.stages[stage].gpu.stream();
9809 let vws = &mut vstate.ws[stage];
9810 unsafe {
9811 ck(
9812 "hc_mean tap batch",
9813 k::memra_dsv4_hc_mean(
9814 dpf!(vws.h_a, &stream),
9815 dpm!(vws.tap_tmp, &stream),
9816 t as i32,
9817 hc as i32,
9818 hidden as i32,
9819 sp(&stream),
9820 ),
9821 )?;
9822 ck(
9823 "place_cols tap batch",
9824 k::memra_dsv4_place_cols(
9825 dpf!(vws.tap_tmp, &stream),
9826 dpm!(**tp, &stream),
9827 t as i32,
9828 hidden as i32,
9829 (n_t * hidden) as i64,
9830 (kk * hidden) as i64,
9831 sp(&stream),
9832 ),
9833 )?;
9834 }
9835 }
9836 }
9837 }
9838
9839 let last = self.stages.len() - 1;
9840 assert_eq!(cur_stage, last, "device path expects the head stage last");
9841 self.head_logits_batch_dev(&mut vstate.ws[last], t, host_math)?;
9842 let stream_last = self.stages[last].gpu.stream();
9843 let vws = &mut vstate.ws[last];
9844 let vocab = vws.logits.len() / vws.tmax;
9845 let logits = if want_logits {
9846 let mut v = vec![0f32; t * vocab];
9847 let view = vws.logits.slice(0..t * vocab);
9848 stream_last
9849 .memcpy_dtoh(&view, &mut v[..])
9850 .map_err(e("dtoh logits batch"))?;
9851 stream_last.synchronize().map_err(e("sync logits batch"))?;
9852 Some(v)
9853 } else {
9854 None
9855 };
9856 let mut am = vec![0i32; t];
9857 if let Some(lg) = &logits {
9858 for (i, slot) in am.iter_mut().enumerate() {
9859 let row = &lg[i * vocab..(i + 1) * vocab];
9860 let mut best = 0usize;
9861 for j in 1..vocab {
9862 if row[j] > row[best] {
9863 best = j;
9864 }
9865 }
9866 *slot = best as i32;
9867 }
9868 } else {
9869 unsafe {
9870 for i in 0..t {
9871 ck(
9872 "argmax batch",
9873 k::memra_dsv4_argmax(
9874 (vws.logits.device_ptr(&stream_last).0 as usize + i * vocab * 4)
9875 as *const f32,
9876 vocab as i64,
9877 (vws.argmax.device_ptr_mut(&stream_last).0 as usize + i * 4)
9878 as *mut i32,
9879 sp(&stream_last),
9880 ),
9881 )?;
9882 }
9883 }
9884 let view = vws.argmax.slice(0..t);
9885 stream_last
9886 .memcpy_dtoh(&view, &mut am[..])
9887 .map_err(e("dtoh argmax batch"))?;
9888 stream_last.synchronize().map_err(e("sync argmax batch"))?;
9889 }
9890 vstate.open = Some((pos0, t));
9891 Ok((logits, am.into_iter().map(|x| x as u32).collect()))
9892 }
9893
9894 pub fn commit_verify_dev(
9899 &self,
9900 state: &mut DecodeState,
9901 vstate: &mut VerifyState,
9902 n_commit: usize,
9903 ) -> Res<()> {
9904 let (pos0, t) = vstate
9905 .open
9906 .take()
9907 .ok_or_else(|| "commit_verify_dev without an open round".to_string())?;
9908 assert!(
9909 n_commit >= 1 && n_commit <= t,
9910 "commit {n_commit} outside round width {t}"
9911 );
9912 let d = self.model.cfg();
9913 let mc = &self.model.mc;
9914 let hd = d.head_dim as usize;
9915 let win = d.sliding_window as usize;
9916 let n_trunk = (mc.n_layer - mc.nextn_predict_layers) as usize;
9917 let slot_rows: Vec<i32> = (0..n_commit).map(|j| ((pos0 + j) % win) as i32).collect();
9918 for il in 0..n_trunk {
9919 let stage = self.layer_stage[il];
9920 let st = &self.stages[stage];
9921 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx commit"))?;
9922 let stream = st.gpu.stream();
9923 let lck = &mut vstate.layers[il];
9924 let vws = &mut vstate.ws[stage];
9925 let cache = &mut state.caches[il];
9926 let trans_base = lck.trans_base;
9927 {
9930 let src = cache
9931 .kvc
9932 .slice(trans_base * hd..(trans_base + n_commit) * hd);
9933 let mut dst = vws.bounce.slice_mut(0..n_commit * hd);
9934 stream
9935 .memcpy_dtod(&src, &mut dst)
9936 .map_err(e("commit bounce"))?;
9937 }
9938 {
9939 let mut dst = vws.slot_rows.slice_mut(0..n_commit);
9940 stream
9941 .memcpy_htod(&slot_rows, &mut dst)
9942 .map_err(e("htod slot rows"))?;
9943 }
9944 unsafe {
9945 ck(
9946 "scatter_rows commit",
9947 k::memra_dsv4_scatter_rows(
9948 dpf!(vws.bounce, &stream),
9949 dpm!(cache.kvc, &stream),
9950 vws.slot_rows.device_ptr(&stream).0 as *const i32,
9951 n_commit as i32,
9952 hd as i32,
9953 sp(&stream),
9954 ),
9955 )?;
9956 }
9957 if let Some(ckd) = &lck.cmp {
9958 self.cmp_rollback_replay_dev(
9959 st,
9960 ckd,
9961 n_commit,
9962 t,
9963 pos0,
9964 &mut vws.cmp_shift,
9965 cache.pend_kv.as_mut().expect("pend kv"),
9966 cache.pend_score.as_mut().expect("pend sc"),
9967 &mut cache.n_blocks,
9968 )?;
9969 }
9970 if let Some(ckd) = &lck.idx {
9971 self.cmp_rollback_replay_dev(
9972 st,
9973 ckd,
9974 n_commit,
9975 t,
9976 pos0,
9977 &mut vws.cmp_shift,
9978 cache.ipend_kv.as_mut().expect("ipend kv"),
9979 cache.ipend_score.as_mut().expect("ipend sc"),
9980 &mut cache.i_blocks,
9981 )?;
9982 }
9983 }
9984 for st in &self.stages {
9985 st.gpu
9986 .ctx
9987 .bind_to_thread()
9988 .map_err(e("bind ctx commit sync"))?;
9989 st.gpu.stream().synchronize().map_err(e("commit sync"))?;
9990 }
9991 state.pos = pos0 + n_commit;
9992 Ok(())
9993 }
9994
9995 pub fn spec_greedy_batched_with(
10009 &self,
10010 prompt: &[u32],
10011 n_new: usize,
10012 state: &mut DecodeState,
10013 dstate: &mut DsparkState,
10014 vstate: &mut VerifyState,
10015 ) -> Res<SpecRunGpu> {
10016 let depth_cap = std::env::var("MEMRA_DSV4_SPEC_DEPTH")
10021 .ok()
10022 .and_then(|v| v.trim().parse::<usize>().ok())
10023 .filter(|t| *t > 0)
10024 .unwrap_or(usize::MAX)
10025 .max(1);
10026 if depth_cap != usize::MAX {
10027 println!("[spec] verify depth capped at T={depth_cap} (MEMRA_DSV4_SPEC_DEPTH)");
10028 }
10029 self.spec_greedy_batched_depth(prompt, n_new, state, dstate, vstate, depth_cap)
10030 }
10031
10032 pub fn spec_greedy_batched_depth(
10040 &self,
10041 prompt: &[u32],
10042 n_new: usize,
10043 state: &mut DecodeState,
10044 dstate: &mut DsparkState,
10045 vstate: &mut VerifyState,
10046 depth_cap: usize,
10047 ) -> Res<SpecRunGpu> {
10048 let p0 = prompt.len();
10049 assert!(n_new >= 1, "n_new must be positive");
10050 let pre = self.dspark_prefill_prime(prompt, state, dstate)?;
10051 let mut t_tok = {
10052 let lg = &pre.logits;
10053 let mut best = 0usize;
10054 for i in 1..lg.len() {
10055 if lg[i] > lg[best] {
10056 best = i;
10057 }
10058 }
10059 best as u32
10060 };
10061 let mut tokens: Vec<u32> = Vec::with_capacity(n_new);
10062 let mut rounds: Vec<SpecRoundGpu> = Vec::new();
10063 let mut mh_row = 0usize; let mut carry_pending = false;
10065 let profile_bracket = std::env::var("MEMRA_DSV4_BENCH_PROFILE").as_deref() == Ok("1");
10070 let depth_cap = depth_cap.max(1);
10071 while tokens.len() < n_new {
10072 if profile_bracket && rounds.len() == 4 {
10073 cudarc::driver::safe::profiler_start().map_err(e("profiler_start"))?;
10074 }
10075 if profile_bracket && rounds.len() == 12 {
10076 cudarc::driver::safe::profiler_stop().map_err(e("profiler_stop"))?;
10077 }
10078 if carry_pending {
10079 tokens.push(t_tok);
10080 break;
10081 }
10082 let round_t0 = std::time::Instant::now();
10083 let prof_stream = if dsv4_prof_on() {
10084 Some(self.stages[self.stages.len() - 1].gpu.stream())
10085 } else {
10086 None
10087 };
10088 let _p_round = phase!("round", prof_stream.as_ref());
10089 let m0 = p0 + tokens.len();
10090 let prop = {
10091 let _p = phase!("1.drafter_forward", prof_stream.as_ref());
10092 self.dspark_forward_spec(dstate, t_tok, mh_row, m0 - 1, false)?
10093 };
10094 let k_drafts = prop.out_ids.len() - 1;
10095 tokens.push(t_tok);
10096 if tokens.len() == n_new {
10097 rounds.push(SpecRoundGpu {
10098 start_pos: m0 - 1,
10099 drafts: prop.out_ids[1..].to_vec(),
10100 accepts: 0,
10101 verified: 0,
10102 t_batch: 0,
10103 t_cap: 0,
10104 confidence: prop.confidence.clone(),
10105 emitted: 1,
10106 round_us: round_t0.elapsed().as_micros() as u64,
10107 });
10108 break;
10109 }
10110 let forwards_left = n_new - tokens.len();
10111 let t_cap = (k_drafts + 1).min(depth_cap).min(vstate.tmax);
10115 let t_batch = t_cap.min(forwards_left);
10116 let kv = t_batch - 1;
10117 let mut batch_ids = Vec::with_capacity(t_batch);
10118 batch_ids.push(t_tok);
10119 batch_ids.extend_from_slice(&prop.out_ids[1..1 + kv]);
10120 let (_, am) = {
10121 let _p = phase!("2.verify_batch", prof_stream.as_ref());
10122 self.verify_batch_dev(&batch_ids, state, vstate, Some(&mut dstate.taps), false)?
10123 };
10124 let mut c_d = 0usize;
10126 let mut t_next = 0u32;
10127 for i in 0..t_batch {
10128 let a = am[i];
10129 if i < kv && a == batch_ids[i + 1] {
10130 c_d += 1;
10131 continue;
10132 }
10133 t_next = a;
10134 break;
10135 }
10136 let n_commit = c_d + 1;
10137 {
10138 let _p = phase!("3.commit_rollback", prof_stream.as_ref());
10139 self.commit_verify_dev(state, vstate, n_commit)?;
10140 }
10141 {
10143 let _p = phase!("4.ring_writes", prof_stream.as_ref());
10144 for i in 0..n_commit {
10145 self.dspark_write_rings(dstate, i, m0 + i)?;
10146 }
10147 }
10148 {
10149 let _p = phase!("5.round_close_sync", None);
10150 let last = self.stages.len() - 1;
10153 self.stages[last]
10154 .gpu
10155 .stream()
10156 .synchronize()
10157 .map_err(e("round close sync"))?;
10158 }
10159 mh_row = c_d;
10160 for i in 0..c_d {
10161 tokens.push(batch_ids[i + 1]);
10162 }
10163 carry_pending = c_d == kv && t_batch < t_cap;
10167 rounds.push(SpecRoundGpu {
10168 start_pos: m0 - 1,
10169 drafts: prop.out_ids[1..].to_vec(),
10170 accepts: c_d,
10171 verified: (c_d + 1).min(kv),
10172 t_batch,
10173 t_cap,
10174 confidence: prop.confidence.clone(),
10175 emitted: 1 + c_d,
10176 round_us: round_t0.elapsed().as_micros() as u64,
10177 });
10178 t_tok = t_next;
10179 }
10180 Ok(SpecRunGpu { tokens, rounds })
10181 }
10182
10183 pub fn spec_greedy_batched(&self, prompt: &[u32], n_new: usize) -> Res<SpecRunGpu> {
10185 let mut state = self.alloc_decode_state()?;
10186 let mut dstate = self.dspark_alloc_state()?;
10187 let mut vstate = self.alloc_verify_state()?;
10188 self.spec_greedy_batched_with(prompt, n_new, &mut state, &mut dstate, &mut vstate)
10189 }
10190}
10191
10192impl Dsv4Gpu {
10193 pub fn cache_classes(&self, state: &DecodeState) -> Res<Vec<(String, Vec<f32>)>> {
10199 let d = self.model.cfg();
10200 let hd = d.head_dim as usize;
10201 let win = d.sliding_window as usize;
10202 let mut out = Vec::new();
10203 for (il, cache) in state.caches.iter().enumerate() {
10204 let stage_i = self.layer_stage[il];
10205 let st = &self.stages[stage_i];
10206 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx classes"))?;
10207 let stream = st.gpu.stream();
10208 let lidx = st
10209 .layers
10210 .iter()
10211 .position(|l| l.il == il as u32)
10212 .unwrap_or_else(|| panic!("layer {il} not on stage {stage_i}"));
10213 let layer = &st.layers[lidx];
10214 let read = |sl: cudarc::driver::CudaView<'_, f32>| -> Res<Vec<f32>> {
10215 let mut v = vec![0f32; sl.len()];
10216 stream
10217 .memcpy_dtoh(&sl, &mut v[..])
10218 .map_err(e("dtoh class"))?;
10219 stream.synchronize().map_err(e("sync class"))?;
10220 Ok(v)
10221 };
10222 out.push((format!("l{il}.ring"), read(cache.kvc.slice(0..win * hd))?));
10223 if let Some(cmp) = &layer.cmp {
10224 out.push((
10225 format!("l{il}.cmp_store"),
10226 read(cache.kvc.slice(win * hd..(win + cache.n_blocks) * cmp.d))?,
10227 ));
10228 out.push((
10229 format!("l{il}.cmp_pend_kv"),
10230 read(cache.pend_kv.as_ref().expect("pend kv").slice(..))?,
10231 ));
10232 out.push((
10233 format!("l{il}.cmp_pend_score"),
10234 read(cache.pend_score.as_ref().expect("pend sc").slice(..))?,
10235 ));
10236 }
10237 if let Some(ix) = &layer.idx {
10238 let ikvc = cache.ikvc.as_ref().expect("ikvc");
10239 out.push((
10240 format!("l{il}.idx_store"),
10241 read(ikvc.slice(0..cache.i_blocks * ix.cmp.d))?,
10242 ));
10243 out.push((
10244 format!("l{il}.idx_pend_kv"),
10245 read(cache.ipend_kv.as_ref().expect("ipend kv").slice(..))?,
10246 ));
10247 out.push((
10248 format!("l{il}.idx_pend_score"),
10249 read(cache.ipend_score.as_ref().expect("ipend sc").slice(..))?,
10250 ));
10251 }
10252 }
10253 Ok(out)
10254 }
10255
10256 pub fn dspark_ring_classes(&self, dstate: &DsparkState) -> Res<Vec<(String, Vec<f32>)>> {
10260 let last = self.stages.len() - 1;
10261 let st = &self.stages[last];
10262 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx rings"))?;
10263 let stream = st.gpu.stream();
10264 let d = self.model.cfg();
10265 let hd = d.head_dim as usize;
10266 let win = d.sliding_window as usize;
10267 let mut out = Vec::new();
10268 for (bi, ring) in dstate.rings.iter().enumerate() {
10269 let view = ring.slice(0..win * hd);
10272 let mut v = vec![0f32; view.len()];
10273 stream
10274 .memcpy_dtoh(&view, &mut v[..])
10275 .map_err(e("dtoh ring class"))?;
10276 stream.synchronize().map_err(e("sync ring class"))?;
10277 out.push((format!("dspark.ring{bi}"), v));
10278 }
10279 Ok(out)
10280 }
10281}
10282
10283pub fn resolve_dense_arm(v: Option<&str>, on_device: bool) -> Result<bool, String> {
10289 match v {
10290 None | Some("") => Ok(on_device),
10291 Some("bf16") => Ok(false),
10292 Some("fp8") if !on_device => Err(
10293 "MEMRA_DSV4_DENSE_ARM=fp8 requires MEMRA_DSV4_DECODE_PATH=device (the \
10294 fp8 GEMV twins exist on the device decode/verify paths only; prefill \
10295 and the legacy path consume the bf16 slabs)"
10296 .to_string(),
10297 ),
10298 Some("fp8") => Ok(true),
10299 Some(other) => Err(format!(
10300 "MEMRA_DSV4_DENSE_ARM '{other}' unknown (bf16 | fp8)"
10301 )),
10302 }
10303}
10304
10305#[cfg(test)]
10306mod dense_arm_default_tests {
10307 use super::resolve_dense_arm;
10308
10309 #[test]
10312 fn ratified_default_dense_arm_is_fp8_on_device() {
10313 assert_eq!(
10314 resolve_dense_arm(None, true),
10315 Ok(true),
10316 "owner-ratified 2026-08-20: unset MEMRA_DSV4_DENSE_ARM defaults the DEVICE \
10317 decode path to fp8 (bit-identical on four boxes, x5 A/B 41.06->47.19, \
10318 item-3 residency green on box7)"
10319 );
10320 assert_eq!(resolve_dense_arm(Some(""), true), Ok(true));
10321 assert_eq!(resolve_dense_arm(None, false), Ok(false));
10323 assert_eq!(resolve_dense_arm(Some("bf16"), true), Ok(false));
10325 assert_eq!(resolve_dense_arm(Some("fp8"), true), Ok(true));
10326 assert!(
10327 resolve_dense_arm(Some("fp8"), false).is_err(),
10328 "legacy+fp8 stays a refusal"
10329 );
10330 assert!(
10331 resolve_dense_arm(Some("q8"), true).is_err(),
10332 "unknown values refuse"
10333 );
10334 }
10335}