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
620fn dsv4_peer_probe_pattern(
636 bytes: usize,
637 boundary: usize,
638 src_dev: usize,
639 dst_dev: usize,
640) -> Vec<u8> {
641 let mut state = 0xD1B5_4A32_D192_ED03u64
642 ^ (bytes as u64).rotate_left(7)
643 ^ (boundary as u64).rotate_left(19)
644 ^ (src_dev as u64).rotate_left(31)
645 ^ (dst_dev as u64).rotate_left(43);
646 (0..bytes)
647 .map(|_| {
648 state ^= state << 13;
649 state ^= state >> 7;
650 state ^= state << 17;
651 state as u8
652 })
653 .collect()
654}
655
656fn dsv4_peer_probe_mismatches(expected: &[u8], readback: &[u8]) -> usize {
657 expected
658 .iter()
659 .zip(readback)
660 .filter(|(a, b)| a != b)
661 .count()
662 + expected.len().abs_diff(readback.len())
663}
664
665fn dsv4_peer_probe_ladder(hidden: usize, hc: usize) -> Vec<usize> {
666 let hc_state = hidden * hc * std::mem::size_of::<f32>();
667 let mut ladder = vec![
668 16 << 10,
669 hc_state,
670 8 * hc_state,
671 1 << 20,
672 (4096usize * hidden * std::mem::size_of::<f32>()).min(64 << 20),
673 ];
674 ladder.sort_unstable();
675 ladder.dedup();
676 ladder
677}
678
679fn dsv4_peer_probe_copy(src: &Stage, dst: &Stage, boundary: usize, bytes: usize) -> Res<()> {
682 let expected = dsv4_peer_probe_pattern(bytes, boundary, src.dev, dst.dev);
683 src.gpu.ctx.bind_to_thread().map_err(e("probe bind src"))?;
684 let src_stream = src.gpu.stream();
685 let src_buf = upload_u8(&src_stream, &expected)?;
686 src_stream.synchronize().map_err(e("probe sync src htod"))?;
687
688 dst.gpu.ctx.bind_to_thread().map_err(e("probe bind dst"))?;
689 let dst_stream = dst.gpu.stream();
690 let poison: Vec<u8> = expected.iter().map(|b| !b).collect();
691 let mut dst_buf = upload_u8(&dst_stream, &poison)?;
692 dst_stream.synchronize().map_err(e("probe sync poison"))?;
693
694 src.gpu.ctx.bind_to_thread().map_err(e("probe bind tx"))?;
696 {
697 let (sp, _g0) = src_buf.device_ptr(&src_stream);
698 let (dp, _g1) = dst_buf.device_ptr_mut(&src_stream);
699 unsafe {
700 cudarc::driver::result::memcpy_peer_async(
701 dst.gpu.ctx.cu_ctx(),
702 dp,
703 src.gpu.ctx.cu_ctx(),
704 sp,
705 bytes,
706 src_stream.cu_stream(),
707 )
708 .map_err(e("probe peer copy"))?;
709 }
710 }
711 src_stream.synchronize().map_err(e("probe sync copy"))?;
712
713 dst.gpu.ctx.bind_to_thread().map_err(e("probe bind rx"))?;
714 let mut readback = vec![0u8; bytes];
715 dst_stream
716 .memcpy_dtoh(&dst_buf, &mut readback[..])
717 .map_err(e("probe readback"))?;
718 dst_stream.synchronize().map_err(e("probe sync readback"))?;
719 if std::env::var("MEMRA_DSV4_PEER_PROBE_POISON").as_deref() == Ok("1") && !readback.is_empty() {
723 readback[0] ^= 1;
724 }
725 let mismatches = dsv4_peer_probe_mismatches(&expected, &readback);
726 if mismatches == 0 {
727 Ok(())
728 } else {
729 Err(format!("{mismatches} mismatched byte(s) of {bytes}"))
730 }
731}
732
733#[derive(Default, Clone)]
758struct Dsv4PhaseAcc {
759 rows: Vec<(&'static str, u64, u64, u64)>,
761 stack: Vec<(usize, u64)>,
763}
764
765thread_local! {
766 static DSV4_PHASES: std::cell::RefCell<Dsv4PhaseAcc> =
767 std::cell::RefCell::new(Dsv4PhaseAcc::default());
768}
769
770fn dsv4_prof_sync() -> bool {
771 static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
772 *V.get_or_init(|| std::env::var("MEMRA_DSV4_ROUND_PROFILE").as_deref() == Ok("1"))
773}
774
775fn dsv4_prof_nvtx() -> bool {
776 static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
777 *V.get_or_init(|| std::env::var("MEMRA_DSV4_NVTX").as_deref() == Ok("1"))
778}
779
780pub fn dsv4_prof_on() -> bool {
783 dsv4_prof_sync() || dsv4_prof_nvtx()
784}
785
786pub struct Dsv4Phase<'a> {
789 stream: Option<&'a std::sync::Arc<CudaStream>>,
790 t0: std::time::Instant,
791 nvtx: bool,
792}
793
794impl<'a> Dsv4Phase<'a> {
795 pub fn new(name: &'static str, stream: Option<&'a std::sync::Arc<CudaStream>>) -> Option<Self> {
799 if !dsv4_prof_on() {
800 return None;
801 }
802 let nvtx = dsv4_prof_nvtx();
803 if nvtx {
804 unsafe {
805 k::memra_dsv4_nvtx_push(name.as_ptr() as *const std::os::raw::c_char);
806 }
807 }
808 let label = &name[..name.len() - 1];
809 DSV4_PHASES.with(|p| {
810 let mut p = p.borrow_mut();
811 let idx = match p.rows.iter().position(|r| r.0 == label) {
812 Some(i) => i,
813 None => {
814 p.rows.push((label, 0, 0, 0));
815 p.rows.len() - 1
816 }
817 };
818 p.stack.push((idx, 0));
819 });
820 Some(Dsv4Phase {
821 stream: if dsv4_prof_sync() { stream } else { None },
822 t0: std::time::Instant::now(),
823 nvtx,
824 })
825 }
826}
827
828impl Drop for Dsv4Phase<'_> {
829 fn drop(&mut self) {
830 if let Some(s) = self.stream {
833 let _ = s.synchronize();
834 }
835 let us = self.t0.elapsed().as_micros() as u64;
836 if self.nvtx {
837 unsafe {
838 k::memra_dsv4_nvtx_pop();
839 }
840 }
841 DSV4_PHASES.with(|p| {
842 let mut p = p.borrow_mut();
843 if let Some((idx, child)) = p.stack.pop() {
844 let r = &mut p.rows[idx];
845 r.1 += us;
846 r.2 += child;
847 r.3 += 1;
848 if let Some(top) = p.stack.last_mut() {
849 top.1 += us;
850 }
851 }
852 });
853 }
854}
855
856macro_rules! phase {
859 ($name:literal, $stream:expr) => {
860 crate::dsv4_gpu::Dsv4Phase::new(concat!($name, "\0"), $stream)
861 };
862}
863
864pub fn dsv4_phase_report(tag: &str, rounds: u64, plain_us: f64) {
867 DSV4_PHASES.with(|p| {
868 let p = p.borrow();
869 if p.rows.is_empty() {
870 return;
871 }
872 let mode = if dsv4_prof_sync() {
873 "sync-bracketed (PERTURBS: compare the round total against the unbracketed A/B)"
874 } else {
875 "nvtx-only (host wall; GPU-busy comes from nsys nvtx_gpu_proj_sum)"
876 };
877 println!("\n[phase] === F ITEMISATION: {tag} ===");
878 println!("[phase] rounds={rounds} plain step={plain_us:.1} us mode={mode}");
879 println!(
880 "[phase] {:<26} {:>11} {:>11} {:>9} {:>12} {:>12}",
881 "phase", "incl_us/rd", "self_us/rd", "calls/rd", "self_plainstp", "incl_plainstp"
882 );
883 let mut rows = p.rows.clone();
884 rows.sort_by_key(|r| std::cmp::Reverse(r.1.saturating_sub(r.2)));
885 let r = rounds.max(1) as f64;
886 let mut leaf_sum = 0f64;
887 for (name, incl, child, calls) in rows {
888 let selfus = incl.saturating_sub(child) as f64 / r;
889 let inclus = incl as f64 / r;
890 leaf_sum += selfus;
891 let (sp, ip) = if plain_us > 0.0 {
892 (selfus / plain_us, inclus / plain_us)
893 } else {
894 (0.0, 0.0)
895 };
896 println!(
897 "[phase] {name:<26} {inclus:>11.1} {selfus:>11.1} {:>9.2} {sp:>12.4} {ip:>12.4}",
898 calls as f64 / r
899 );
900 }
901 println!(
902 "[phase] {:<26} {:>11} {:>11.1} {:>9} {:>12.4}",
903 "SUM of self",
904 "",
905 leaf_sum,
906 "",
907 if plain_us > 0.0 {
908 leaf_sum / plain_us
909 } else {
910 0.0
911 }
912 );
913 });
914}
915
916fn dsv4_dspark_chain_device() -> bool {
921 static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
922 *V.get_or_init(|| {
923 let on = std::env::var("MEMRA_DSV4_DSPARK_CHAIN").as_deref() == Ok("device");
924 if on {
925 println!(
926 "[spec] DSpark markov chain RESIDENT ON DEVICE (MEMRA_DSV4_DSPARK_CHAIN=device): \
927 one D2H per round instead of 2 x block_size"
928 );
929 }
930 on
931 })
932}
933
934fn dsv4_dspark_markov_rowblk() -> bool {
940 static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
941 *V.get_or_init(|| {
942 let on = std::env::var("MEMRA_DSV4_DSPARK_MARKOV").as_deref() == Ok("rowblk");
943 if on {
944 println!(
945 "[spec] DSpark markov bias GEMV on the ROW-BLOCKED dots twin \
946 (MEMRA_DSV4_DSPARK_MARKOV=rowblk; bit-identical, geometry only)"
947 );
948 }
949 on
950 })
951}
952
953pub fn dsv4_phase_reset() {
956 DSV4_PHASES.with(|p| *p.borrow_mut() = Dsv4PhaseAcc::default());
957}
958
959macro_rules! dp {
960 ($slice:expr, $stream:expr) => {{ $slice.device_ptr($stream).0 as *const c_void }};
961}
962macro_rules! dpf {
963 ($slice:expr, $stream:expr) => {{ $slice.device_ptr($stream).0 as *const f32 }};
964}
965macro_rules! dpm {
966 ($slice:expr, $stream:expr) => {{ $slice.device_ptr_mut($stream).0 as *mut f32 }};
967}
968
969fn f32_to_bf16_exact(name: &str, v: &[f32]) -> Vec<u8> {
975 let mut out = Vec::with_capacity(v.len() * 2);
976 for (i, x) in v.iter().enumerate() {
977 let bits = x.to_bits();
978 assert!(
979 bits & 0xFFFF == 0,
980 "{name}: element {i} = {x} not exactly representable in bf16 — lane-4 rung \
981 exactness violated"
982 );
983 out.extend_from_slice(&((bits >> 16) as u16).to_le_bytes());
984 }
985 out
986}
987
988impl Dsv4Gpu {
989 fn tensor_bf16(&mut self, stage: usize, name: &str) -> Res<CudaSlice<u8>> {
992 let raw_name = format!("{name}.weight");
993 let is_bf16_raw = self
994 .model
995 .st
996 .raw(&raw_name)
997 .map(|(i, _)| i.dtype == "BF16")
998 .unwrap_or(false)
999 || self
1000 .model
1001 .st
1002 .raw(name)
1003 .map(|(i, _)| i.dtype == "BF16")
1004 .unwrap_or(false);
1005 let stream = self.stages[stage].gpu.stream();
1006 let bytes: u64;
1007 let out = if is_bf16_raw {
1008 let (_, raw) = self
1009 .model
1010 .st
1011 .raw(&raw_name)
1012 .or_else(|| self.model.st.raw(name))
1013 .unwrap();
1014 bytes = raw.len() as u64;
1015 upload_u8(&stream, raw)?
1016 } else {
1017 let (_, v) = self.model.tensor_f32(name);
1018 let b = f32_to_bf16_exact(name, &v);
1019 bytes = b.len() as u64;
1020 upload_u8(&stream, &b)?
1021 };
1022 self.stages[stage].loaded_bytes += bytes;
1023 Ok(out)
1024 }
1025
1026 fn tensor_dense(
1038 &mut self,
1039 stage: usize,
1040 name: &str,
1041 fp8_ok: bool,
1042 ) -> Res<(DenseBf16, Option<Fp8Dense>)> {
1043 let raw_name = format!("{name}.weight");
1044 let is_bf16_raw = self
1045 .model
1046 .st
1047 .raw(&raw_name)
1048 .map(|(i, _)| i.dtype == "BF16")
1049 .unwrap_or(false)
1050 || self
1051 .model
1052 .st
1053 .raw(name)
1054 .map(|(i, _)| i.dtype == "BF16")
1055 .unwrap_or(false);
1056 if is_bf16_raw || !fp8_ok || !self.dense_fp8 {
1057 return Ok((DenseBf16::Dev(self.tensor_bf16(stage, name)?), None));
1058 }
1059 let (wi, wraw, stem) = if let Some((i, r)) = self.model.st.raw(&raw_name) {
1061 (i.clone(), r.to_vec(), name.to_string())
1062 } else {
1063 let (i, r) = self
1064 .model
1065 .st
1066 .raw(name)
1067 .unwrap_or_else(|| panic!("missing dense tensor {name}"));
1068 let stem = name.strip_suffix(".weight").unwrap_or(name).to_string();
1069 (i.clone(), r.to_vec(), stem)
1070 };
1071 if wi.dtype != "F8_E4M3" {
1072 return Ok((DenseBf16::Dev(self.tensor_bf16(stage, name)?), None));
1074 }
1075 assert_eq!(wi.shape.len(), 2, "{name}: fp8 dense tensor must be 2-D");
1076 let rows = wi.shape[0] as usize;
1077 let cols = wi.shape[1] as usize;
1078 assert_eq!(cols % 8, 0, "{name}: fp8 dense cols {cols} % 8 != 0");
1079 assert_eq!(wraw.len(), rows * cols, "{name}: fp8 byte count");
1080 let scale_name = format!("{stem}.scale");
1081 let (si, sraw) = self
1082 .model
1083 .st
1084 .raw(&scale_name)
1085 .unwrap_or_else(|| panic!("{name}: F8_E4M3 weight without {scale_name}"));
1086 assert_eq!(si.dtype, "F8_E8M0", "{scale_name}: dtype");
1087 let sc_rows = rows.div_ceil(128);
1088 let sc_cols = cols.div_ceil(128);
1089 assert_eq!(
1090 (si.shape[0] as usize, si.shape[1] as usize),
1091 (sc_rows, sc_cols),
1092 "{scale_name}: scale grid shape vs [ceil({rows}/128), ceil({cols}/128)]"
1093 );
1094 let sc_f32: Vec<f32> = sraw
1095 .iter()
1096 .map(|&b| {
1097 assert_ne!(b, 0xFF, "{scale_name}: e8m0 NaN code");
1098 memra_gguf::dsv4::e8m0_to_f32(b)
1099 })
1100 .collect();
1101 let (_, v) = self.model.tensor_f32(name);
1103 assert_eq!(v.len(), rows * cols, "{name}: dequant len");
1104 let step = (v.len() / 1024).max(1);
1105 for idx in (0..v.len()).step_by(step) {
1106 let (r, c) = (idx / cols, idx % cols);
1107 let got = memra_gguf::nvfp4_repack::fp8_e4m3_to_f32(wraw[idx])
1108 * sc_f32[(r / 128) * sc_cols + c / 128];
1109 assert_eq!(
1110 got.to_bits(),
1111 v[idx].to_bits(),
1112 "{name}: fp8 arm layout check failed at [{r},{c}] ({got} vs {})",
1113 v[idx]
1114 );
1115 }
1116 let b = f32_to_bf16_exact(name, &v);
1117 let stream = self.stages[stage].gpu.stream();
1118 let codes = upload_u8(&stream, &wraw)?;
1122 let scales = upload_f32(&stream, &sc_f32)?;
1123 self.stages[stage].loaded_bytes += (wraw.len() + sc_f32.len() * 4) as u64;
1124 Ok((
1125 DenseBf16::Host(b),
1126 Some(Fp8Dense {
1127 codes,
1128 scales,
1129 sc_cols,
1130 rows,
1131 cols,
1132 }),
1133 ))
1134 }
1135
1136 fn tensor_f32_dev(&mut self, stage: usize, name: &str) -> Res<CudaSlice<f32>> {
1139 let (_, v) = self.model.tensor_f32(name);
1140 let stream = self.stages[stage].gpu.stream();
1141 self.stages[stage].loaded_bytes += (v.len() * 4) as u64;
1142 upload_f32(&stream, &v)
1143 }
1144
1145 fn load_cmp(
1146 &mut self,
1147 stage: usize,
1148 prefix: &str,
1149 ratio: usize,
1150 d: usize,
1151 rotate: bool,
1152 ) -> Res<CmpDev> {
1153 let (wkv_shape, _) = self.model.tensor_f32(&format!("{prefix}.wkv.weight"));
1154 let latent = wkv_shape[0];
1155 let overlap = ratio == 4;
1156 assert_eq!(latent, if overlap { 2 * d } else { d }, "{prefix} latent");
1157 Ok(CmpDev {
1158 ratio,
1159 d,
1160 latent,
1161 overlap,
1162 rotate,
1163 wkv: self.tensor_f32_dev(stage, &format!("{prefix}.wkv.weight"))?,
1164 wgate: self.tensor_f32_dev(stage, &format!("{prefix}.wgate.weight"))?,
1165 norm: self.tensor_f32_dev(stage, &format!("{prefix}.norm.weight"))?,
1166 ape: self.tensor_f32_dev(stage, &format!("{prefix}.ape"))?,
1167 })
1168 }
1169
1170 fn load_layer(&mut self, stage: usize, il: u32, prefix: &str) -> Res<LayerDev> {
1173 let d = self.model.cfg().clone();
1174 let moe = self.model.mc.moe.clone().expect("moe block");
1175 let ratio = d.compress_ratio(il) as usize;
1176 let hd = d.head_dim as usize;
1177 let p = prefix.to_string();
1178 let hash = d.is_hash_layer(il);
1179 let ne = moe.expert_count as usize;
1180 let inter = moe.expert_ff_length as usize;
1181 let hidden = self.model.mc.n_embd as usize;
1182
1183 let hc_load = |m: &Dsv4Model, fam: &str| -> (Vec<f32>, Vec<f32>, Vec<f32>) {
1185 let fn_w = m.tensor_f32(&format!("{p}.hc_{fam}_fn")).1;
1186 let base = m.tensor_f32(&format!("{p}.hc_{fam}_base")).1;
1187 let scale = m.tensor_f32(&format!("{p}.hc_{fam}_scale")).1;
1188 (fn_w, base, scale)
1189 };
1190 let (attn_fn, attn_base, attn_scale) = hc_load(&self.model, "attn");
1191 let (ffn_fn, ffn_base, ffn_scale) = hc_load(&self.model, "ffn");
1192 let stream = self.stages[stage].gpu.stream();
1193 let hc_attn_fn = upload_f32(&stream, &attn_fn)?;
1194 let hc_ffn_fn = upload_f32(&stream, &ffn_fn)?;
1195 self.stages[stage].loaded_bytes += ((attn_fn.len() + ffn_fn.len()) * 4) as u64;
1196
1197 let (wi0, _) = self
1201 .model
1202 .st
1203 .raw(&format!("{p}.ffn.experts.0.w1.weight"))
1204 .unwrap_or_else(|| panic!("missing {p}.ffn.experts.0.w1.weight"));
1205 let expert_kind = match wi0.dtype.as_str() {
1206 "U8" => ExpertKind::Nvfp4,
1207 "I8" => ExpertKind::Mxfp4,
1208 other => panic!("{p}: unexpected expert weight dtype {other}"),
1209 };
1210 let wbytes = inter * hidden / 2; let sbytes = match expert_kind {
1212 ExpertKind::Nvfp4 => inter * hidden / 16,
1213 ExpertKind::Mxfp4 => inter * hidden / 32,
1214 };
1215 let mut experts_w = stream
1216 .alloc_zeros::<u8>(ne * 3 * wbytes)
1217 .map_err(e("alloc expert slab"))?;
1218 let mut experts_sc = stream
1219 .alloc_zeros::<u8>(ne * 3 * sbytes)
1220 .map_err(e("alloc expert scale slab"))?;
1221 let mut experts_s2 = Vec::with_capacity(ne * 3);
1222 for ex in 0..ne {
1223 for (pi, pname) in ["w1", "w2", "w3"].iter().enumerate() {
1224 let base = format!("{p}.ffn.experts.{ex}.{pname}");
1225 let (wi, wb) = self
1226 .model
1227 .st
1228 .raw(&format!("{base}.weight"))
1229 .unwrap_or_else(|| panic!("missing {base}.weight"));
1230 assert_eq!(wb.len(), wbytes, "{base}: weight bytes");
1231 let sb = match expert_kind {
1232 ExpertKind::Nvfp4 => {
1233 assert_eq!(wi.dtype, "U8", "{base}: expected NVFP4 U8 weight");
1234 let (_, sb) = self
1235 .model
1236 .st
1237 .raw(&format!("{base}.weight_scale"))
1238 .unwrap_or_else(|| panic!("missing {base}.weight_scale"));
1239 let (_, s2b) = self
1240 .model
1241 .st
1242 .raw(&format!("{base}.weight_scale_2"))
1243 .unwrap_or_else(|| panic!("missing {base}.weight_scale_2"));
1244 let s2 = f32::from_le_bytes(s2b.try_into().expect("scale_2 4B"));
1245 assert!(
1248 s2 > 0.0 && s2.to_bits() & 0x007F_FFFF == 0,
1249 "{base}: scale_2 {s2} not a power of two — rung exactness violated"
1250 );
1251 experts_s2.push(s2);
1252 sb
1253 }
1254 ExpertKind::Mxfp4 => {
1255 assert_eq!(wi.dtype, "I8", "{base}: expected MXFP4 I8 weight");
1256 let (si, sb) = self
1257 .model
1258 .st
1259 .raw(&format!("{base}.scale"))
1260 .unwrap_or_else(|| panic!("missing {base}.scale"));
1261 assert_eq!(si.dtype, "F8_E8M0", "{base}: expected E8M0 scale");
1262 assert!(
1264 !sb.contains(&0xFFu8),
1265 "{base}: E8M0 NaN scale code — refusing"
1266 );
1267 experts_s2.push(1.0);
1268 sb
1269 }
1270 };
1271 assert_eq!(sb.len(), sbytes, "{base}: scale bytes");
1272 let off = (ex * 3 + pi) * wbytes;
1273 let mut view = experts_w.slice_mut(off..off + wbytes);
1274 stream
1275 .memcpy_htod(wb, &mut view)
1276 .map_err(e("htod expert w"))?;
1277 let soff = (ex * 3 + pi) * sbytes;
1278 let mut sview = experts_sc.slice_mut(soff..soff + sbytes);
1279 stream
1280 .memcpy_htod(sb, &mut sview)
1281 .map_err(e("htod expert sc"))?;
1282 }
1283 }
1284 self.stages[stage].loaded_bytes +=
1285 (ne * 3 * (wbytes + sbytes)) as u64 + (ne * 3 * 4) as u64;
1286
1287 let cmp = if ratio != 0 {
1288 Some(self.load_cmp(stage, &format!("{p}.attn.compressor"), ratio, hd, false)?)
1289 } else {
1290 None
1291 };
1292 let fp8_ok = p.starts_with("layers.");
1295 let idx = if d.has_indexer(il) {
1296 let heads = d.index_n_heads as usize;
1297 let ihd = d.index_head_dim as usize;
1298 let (iwq_b, iwq_b_fp8) =
1299 self.tensor_dense(stage, &format!("{p}.attn.indexer.wq_b"), fp8_ok)?;
1300 let (iwp, iwp_fp8) = self.tensor_dense(
1301 stage,
1302 &format!("{p}.attn.indexer.weights_proj.weight"),
1303 fp8_ok,
1304 )?;
1305 Some(IdxDev {
1306 wq_b: iwq_b,
1307 weights_proj: iwp,
1308 wq_b_fp8: iwq_b_fp8,
1309 weights_proj_fp8: iwp_fp8,
1310 cmp: self.load_cmp(
1311 stage,
1312 &format!("{p}.attn.indexer.compressor"),
1313 ratio,
1314 ihd,
1315 true,
1316 )?,
1317 heads,
1318 hd: ihd,
1319 topk: d.index_topk as usize,
1320 })
1321 } else {
1322 None
1323 };
1324
1325 let stream = self.stages[stage].gpu.stream();
1329 let hc_attn_base_dev = upload_f32(&stream, &attn_base)?;
1330 let hc_attn_scale_dev = upload_f32(&stream, &attn_scale)?;
1331 let hc_ffn_base_dev = upload_f32(&stream, &ffn_base)?;
1332 let hc_ffn_scale_dev = upload_f32(&stream, &ffn_scale)?;
1333 let gate_bias_host: Option<Vec<f32>> = if hash {
1334 None
1335 } else {
1336 Some(self.model.tensor_f32(&format!("{p}.ffn.gate.bias")).1)
1337 };
1338 let gate_bias_dev = match &gate_bias_host {
1339 Some(b) => Some(upload_f32(&stream, b)?),
1340 None => None,
1341 };
1342 let tid2eid_host: Option<Vec<i64>> = if hash {
1343 Some(self.model.tensor_i64(&format!("{p}.ffn.gate.tid2eid")).1)
1344 } else {
1345 None
1346 };
1347 let tid2eid_dev = match &tid2eid_host {
1348 Some(t) => {
1349 let topk = moe.expert_used_count as usize;
1350 assert_eq!(t.len() % topk, 0, "{p}: tid2eid rows");
1351 let mut t32 = Vec::with_capacity(t.len());
1352 for row in t.chunks(topk) {
1353 let mut seen = std::collections::BTreeSet::new();
1354 for &ex in row {
1355 assert!(
1356 (0..ne as i64).contains(&ex),
1357 "{p}: tid2eid out of range at load"
1358 );
1359 assert!(seen.insert(ex), "{p}: duplicate expert id in tid2eid row");
1360 t32.push(ex as i32);
1361 }
1362 }
1363 Some(upload_i32(&stream, &t32)?)
1364 }
1365 None => None,
1366 };
1367 let experts_s2_dev = upload_f32(&stream, &experts_s2)?;
1368
1369 let (wq_a, wq_a_fp8) = self.tensor_dense(stage, &format!("{p}.attn.wq_a"), fp8_ok)?;
1370 let (wq_b, wq_b_fp8) = self.tensor_dense(stage, &format!("{p}.attn.wq_b"), fp8_ok)?;
1371 let (wkv, wkv_fp8) = self.tensor_dense(stage, &format!("{p}.attn.wkv"), fp8_ok)?;
1372 let (wo_a, wo_a_fp8) = self.tensor_dense(stage, &format!("{p}.attn.wo_a"), fp8_ok)?;
1373 let (wo_b, wo_b_fp8) = self.tensor_dense(stage, &format!("{p}.attn.wo_b"), fp8_ok)?;
1374 let (sw1, sw1_fp8) =
1375 self.tensor_dense(stage, &format!("{p}.ffn.shared_experts.w1"), fp8_ok)?;
1376 let (sw2, sw2_fp8) =
1377 self.tensor_dense(stage, &format!("{p}.ffn.shared_experts.w2"), fp8_ok)?;
1378 let (sw3, sw3_fp8) =
1379 self.tensor_dense(stage, &format!("{p}.ffn.shared_experts.w3"), fp8_ok)?;
1380
1381 Ok(LayerDev {
1382 il,
1383 ratio,
1384 expert_kind,
1385 hc_attn_base_dev,
1386 hc_attn_scale_dev,
1387 hc_ffn_base_dev,
1388 hc_ffn_scale_dev,
1389 gate_bias_dev,
1390 tid2eid_dev,
1391 experts_s2_dev,
1392 wq_a,
1393 wq_b,
1394 wkv,
1395 wo_a,
1396 wo_b,
1397 wq_a_fp8,
1398 wq_b_fp8,
1399 wkv_fp8,
1400 wo_a_fp8,
1401 wo_b_fp8,
1402 q_norm: self.tensor_f32_dev(stage, &format!("{p}.attn.q_norm.weight"))?,
1403 kv_norm: self.tensor_f32_dev(stage, &format!("{p}.attn.kv_norm.weight"))?,
1404 attn_norm: self.tensor_f32_dev(stage, &format!("{p}.attn_norm.weight"))?,
1405 ffn_norm: self.tensor_f32_dev(stage, &format!("{p}.ffn_norm.weight"))?,
1406 sink: self.tensor_f32_dev(stage, &format!("{p}.attn.attn_sink"))?,
1407 cmp,
1408 idx,
1409 hc_attn_fn,
1410 hc_ffn_fn,
1411 hc_attn_base: attn_base,
1412 hc_attn_scale: attn_scale,
1413 hc_ffn_base: ffn_base,
1414 hc_ffn_scale: ffn_scale,
1415 gate_w: self.tensor_f32_dev(stage, &format!("{p}.ffn.gate.weight"))?,
1416 gate_bias: gate_bias_host,
1417 tid2eid: tid2eid_host,
1418 experts_w,
1419 experts_sc,
1420 experts_s2,
1421 shared_w: [sw1, sw2, sw3],
1422 shared_fp8: [sw1_fp8, sw2_fp8, sw3_fp8],
1423 })
1424 }
1425
1426 pub fn load(
1429 dir: &Path,
1430 devices: &[usize],
1431 variant: ActQuantVariant,
1432 max_seq: usize,
1433 ) -> Res<Self> {
1434 assert_eq!(devices.len(), 2, "lane 4 placement is a 2-card layer split");
1435 let model = Dsv4Model::open(dir)?;
1436 let d = model.cfg().clone();
1437 let mc = model.mc.clone();
1438 let n_trunk = mc.n_layer - mc.nextn_predict_layers;
1439 let rd = d.qk_rope_head_dim as usize;
1440
1441 let layer_bytes = |il: u32| -> u64 {
1444 let ratio = d.compress_ratio(il);
1445 let base = 3_875_000_000u64; match ratio {
1447 4 => base + 66_000_000,
1448 _ => base,
1449 }
1450 };
1451 let total: u64 = (0..n_trunk).map(layer_bytes).sum();
1452 let mut acc = 0u64;
1453 let mut split_at = n_trunk / 2;
1454 for il in 0..n_trunk {
1455 acc += layer_bytes(il);
1456 if acc * 2 >= total {
1457 split_at = il + 1;
1458 break;
1459 }
1460 }
1461
1462 let fc_yarn_host = precompute_freqs_cis(
1463 rd,
1464 max_seq,
1465 d.rope_yarn_orig_ctx,
1466 d.compress_rope_theta,
1467 d.rope_yarn_factor,
1468 d.rope_yarn_beta_fast,
1469 d.rope_yarn_beta_slow,
1470 );
1471 let fc_plain_host = precompute_freqs_cis(
1472 rd,
1473 max_seq,
1474 0,
1475 mc.rope_freq_base,
1476 d.rope_yarn_factor,
1477 d.rope_yarn_beta_fast,
1478 d.rope_yarn_beta_slow,
1479 );
1480 let flat =
1481 |fc: &FreqsCis| -> Vec<f32> { fc.cs.iter().flat_map(|&(c, s)| [c, s]).collect() };
1482
1483 let inter = mc.moe.as_ref().expect("moe").expert_ff_length as usize;
1484 let hidden = mc.n_embd as usize;
1485 let mut stages = Vec::new();
1486 for &dev in devices {
1487 let gpu = memra_runtime::Gpu::new(dev).map_err(e("Gpu::new"))?;
1488 unsafe { gpu.ctx.disable_event_tracking() };
1491 let stream = gpu.stream();
1492 let fc_yarn = upload_f32(&stream, &flat(&fc_yarn_host))?;
1493 let fc_plain = upload_f32(&stream, &flat(&fc_plain_host))?;
1494 let ws = stream.alloc_zeros::<u8>(64 << 20).map_err(e("ws alloc"))?;
1495 let deq = [
1496 stream
1497 .alloc_zeros::<u8>(inter * hidden * 2)
1498 .map_err(e("deq"))?,
1499 stream
1500 .alloc_zeros::<u8>(inter * hidden * 2)
1501 .map_err(e("deq"))?,
1502 stream
1503 .alloc_zeros::<u8>(inter * hidden * 2)
1504 .map_err(e("deq"))?,
1505 ];
1506 stages.push(Stage {
1507 dev,
1508 gpu,
1509 layers: Vec::new(),
1510 embed: None,
1511 head: None,
1512 trunk_norm: None,
1513 hc_head_fn: None,
1514 fc_yarn,
1515 fc_plain,
1516 ws,
1517 deq,
1518 loaded_bytes: 0,
1519 hc_head_base_dev: None,
1520 hc_head_scale_dev: None,
1521 });
1522 }
1523
1524 let decode_path = match std::env::var("MEMRA_DSV4_DECODE_PATH").as_deref() {
1526 Err(_) | Ok("") | Ok("legacy") => DecodePath::Legacy,
1527 Ok("device-hostmath") => DecodePath::Device { host_math: true },
1528 Ok("device") => DecodePath::Device { host_math: false },
1529 Ok(other) => {
1530 return Err(format!(
1531 "MEMRA_DSV4_DECODE_PATH '{other}' unknown (legacy | device | device-hostmath)"
1532 ));
1533 }
1534 };
1535 let on_device = matches!(decode_path, DecodePath::Device { .. });
1553 let (dots_f32, chains_f32) = match std::env::var("MEMRA_DSV4_DOTS_ARM").as_deref() {
1554 Err(_) | Ok("") => {
1555 if on_device {
1557 (true, true)
1558 } else {
1559 (false, false)
1560 }
1561 }
1562 Ok(explicit @ ("f32x" | "f32")) if !on_device => {
1563 return Err(format!(
1564 "MEMRA_DSV4_DOTS_ARM={explicit} requires MEMRA_DSV4_DECODE_PATH=device \
1565 (the f32 dots arms exist on the device decode path only)"
1566 ));
1567 }
1568 Ok("f32x") => (true, true),
1569 Ok("f64") => (false, false),
1570 Ok("f32") => (true, false),
1571 Ok(other) => {
1572 return Err(format!(
1573 "MEMRA_DSV4_DOTS_ARM '{other}' unknown (f64 | f32 | f32x)"
1574 ));
1575 }
1576 };
1577
1578 let dspark_head_f32 = match std::env::var("MEMRA_DSV4_DSPARK_HEAD_ARM").as_deref() {
1596 Err(_) | Ok("") | Ok("f32x") => true,
1597 Ok("f64") => false,
1598 Ok(other) => {
1599 return Err(format!(
1600 "MEMRA_DSV4_DSPARK_HEAD_ARM '{other}' unknown (f64 | f32x)"
1601 ));
1602 }
1603 };
1604
1605 let dense_fp8 = resolve_dense_arm(
1620 std::env::var("MEMRA_DSV4_DENSE_ARM").ok().as_deref(),
1621 on_device,
1622 )?;
1623
1624 let mut me = Dsv4Gpu {
1625 model,
1626 stages,
1627 layer_stage: (0..n_trunk).map(|il| usize::from(il >= split_at)).collect(),
1628 split_at,
1629 max_seq,
1630 variant,
1631 fc_yarn_host,
1632 fc_plain_host,
1633 mtp: None,
1634 expert_arm: if memra_gguf::dsv4_forward::expert_arm_native() {
1635 ExpertArm::Native
1636 } else {
1637 ExpertArm::Bf16Dequant
1638 },
1639 decode_path,
1640 dots_f32,
1641 chains_f32,
1642 dspark_head_f32,
1643 dense_fp8,
1644 dspark: None,
1645 boundary_ev: Vec::new(),
1646 hc_head_base: Vec::new(),
1647 hc_head_scale: Vec::new(),
1648 };
1649 eprintln!(
1650 "[load] expert arm: {:?} | decode path: {:?} | dots arm: {}",
1651 me.expert_arm,
1652 me.decode_path,
1653 if me.chains_f32 {
1654 "f32x (dots + sink/norm/indexer chains)"
1655 } else if me.dots_f32 {
1656 "f32"
1657 } else {
1658 "f64"
1659 }
1660 );
1661 eprintln!(
1662 "[load] dspark exit-head dots arm: {} (rung-4c fork; drafts only, never the \
1663 emitted stream)",
1664 if me.dspark_head_f32 { "f32x" } else { "f64" }
1665 );
1666 eprintln!(
1667 "[load] dense arm: {} (iteration-5; fp8 = FP8-blk linears as-stored on the \
1668 device decode/verify paths, bit-identical twins)",
1669 if me.dense_fp8 { "fp8" } else { "bf16" }
1670 );
1671 if matches!(me.decode_path, DecodePath::Device { .. }) && me.expert_arm != ExpertArm::Native
1672 {
1673 return Err(
1678 "MEMRA_DSV4_DECODE_PATH=device requires MEMRA_DSV4_EXPERT_ARM=native".to_string(),
1679 );
1680 }
1681
1682 if matches!(me.decode_path, DecodePath::Device { .. }) && me.stages.len() > 1 {
1686 use cudarc::driver::sys as cus;
1687 for a in 0..me.stages.len() {
1688 for b in 0..me.stages.len() {
1689 if a == b || me.stages[a].dev == me.stages[b].dev {
1690 continue;
1691 }
1692 me.stages[a]
1693 .gpu
1694 .ctx
1695 .bind_to_thread()
1696 .map_err(e("peer bind"))?;
1697 let rc =
1698 unsafe { cus::cuCtxEnablePeerAccess(me.stages[b].gpu.ctx.cu_ctx(), 0) };
1699 if rc != cus::cudaError_enum::CUDA_SUCCESS
1700 && rc != cus::cudaError_enum::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED
1701 {
1702 return Err(format!(
1703 "cuCtxEnablePeerAccess(dev{} -> dev{}) failed: {rc:?}",
1704 me.stages[a].dev, me.stages[b].dev
1705 ));
1706 }
1707 let dev = cudarc::driver::result::device::get(me.stages[a].dev as i32)
1708 .map_err(e("device get"))?;
1709 let mut pool: cus::CUmemoryPool = std::ptr::null_mut();
1710 unsafe {
1711 cus::cuDeviceGetDefaultMemPool(&mut pool, dev)
1712 .result()
1713 .map_err(e("default pool"))?;
1714 }
1715 let desc = cus::CUmemAccessDesc {
1716 location: cus::CUmemLocation {
1717 type_: cus::CUmemLocationType::CU_MEM_LOCATION_TYPE_DEVICE,
1718 id: me.stages[b].dev as i32,
1719 },
1720 flags: cus::CUmemAccess_flags::CU_MEM_ACCESS_FLAGS_PROT_READWRITE,
1721 };
1722 let rc = unsafe { cus::cuMemPoolSetAccess(pool, &desc, 1) };
1723 if rc != cus::cudaError_enum::CUDA_SUCCESS {
1724 return Err(format!(
1725 "cuMemPoolSetAccess(dev{} pool -> dev{}) failed: {rc:?}",
1726 me.stages[a].dev, me.stages[b].dev
1727 ));
1728 }
1729 }
1730 }
1731 for bnd in 0..me.stages.len() - 1 {
1732 let ev = me.stages[bnd]
1733 .gpu
1734 .ctx
1735 .new_event(None)
1736 .map_err(e("boundary event"))?;
1737 me.boundary_ev.push(ev);
1738 }
1739 {
1745 let hidden = me.model.mc.n_embd as usize;
1746 let hc = me.model.cfg().hc_mult as usize;
1747 let ladder = dsv4_peer_probe_ladder(hidden, hc);
1752 let probe_t0 = std::time::Instant::now();
1753 let mut copies = 0usize;
1754 for bnd in 0..me.stages.len() - 1 {
1755 if me.stages[bnd].dev == me.stages[bnd + 1].dev {
1756 continue;
1757 }
1758 for (s, d) in [(bnd, bnd + 1), (bnd + 1, bnd)] {
1759 for &bytes in &ladder {
1760 dsv4_peer_probe_copy(&me.stages[s], &me.stages[d], bnd, bytes)
1761 .map_err(|err| {
1762 format!(
1763 "dsv4 PP peer byte-integrity probe FAILED: \
1764 boundary={bnd} dev{}->dev{} bytes={bytes}: {err}; \
1765 refusing the device PP path (silent hidden-state \
1766 corruption class — fix the P2P fabric or serve a \
1767 non-device MEMRA_DSV4_DECODE_PATH)",
1768 me.stages[s].dev, me.stages[d].dev,
1769 )
1770 })?;
1771 copies += 1;
1772 }
1773 }
1774 }
1775 eprintln!(
1776 "[load] lane-8 peer byte-integrity probe PASS: {} boundaries, \
1777 {copies} copies, ladder {ladder:?} bytes, {:.1}ms",
1778 me.boundary_ev.len(),
1779 probe_t0.elapsed().as_secs_f64() * 1e3,
1780 );
1781 }
1782 eprintln!(
1783 "[load] lane-8 peer transport enabled ({} boundaries)",
1784 me.boundary_ev.len()
1785 );
1786 }
1787
1788 me.stages[0].embed = Some({
1790 let (_, raw) = me.model.st.raw("embed.weight").expect("embed.weight");
1791 let stream = me.stages[0].gpu.stream();
1792 me.stages[0].loaded_bytes += raw.len() as u64;
1793 upload_u8(&stream, raw)?
1794 });
1795 let last = me.stages.len() - 1;
1796 me.stages[last].head = Some({
1797 let (_, raw) = me.model.st.raw("head.weight").expect("head.weight");
1798 let stream = me.stages[last].gpu.stream();
1799 me.stages[last].loaded_bytes += raw.len() as u64;
1800 upload_u8(&stream, raw)?
1801 });
1802 me.stages[last].trunk_norm = Some(me.tensor_f32_dev(last, "norm.weight")?);
1803 me.stages[last].hc_head_fn = Some(me.tensor_f32_dev(last, "hc_head_fn")?);
1804 me.hc_head_base = me.model.tensor_f32("hc_head_base").1;
1805 me.hc_head_scale = me.model.tensor_f32("hc_head_scale").1;
1806 {
1807 let stream = me.stages[last].gpu.stream();
1808 let base_dev = upload_f32(&stream, &me.hc_head_base)?;
1809 let scale_dev = upload_f32(&stream, &me.hc_head_scale)?;
1810 me.stages[last].hc_head_base_dev = Some(base_dev);
1811 me.stages[last].hc_head_scale_dev = Some(scale_dev);
1812 }
1813
1814 let t0 = std::time::Instant::now();
1815 for il in 0..n_trunk {
1816 let stage = me.layer_stage[il as usize];
1817 let l = me.load_layer(stage, il, &format!("layers.{il}"))?;
1818 me.stages[stage].layers.push(l);
1819 if il % 4 == 3 || il + 1 == n_trunk {
1820 eprintln!(
1821 "[load] layer {il} -> dev{} done t={:.0}s",
1822 me.stages[stage].dev,
1823 t0.elapsed().as_secs_f64()
1824 );
1825 }
1826 }
1827 let nextn = me.model.mc.nextn_predict_layers;
1838 if nextn > 0 && me.model.has("mtp.0.e_proj.weight") {
1839 assert_eq!(
1840 nextn, 1,
1841 "multi-NextN chains not wired (single MTP layer expected)"
1842 );
1843 let p = "mtp.0";
1844 let layer = me.load_layer(last, n_trunk, p)?;
1845 assert_eq!(
1846 layer.expert_kind,
1847 ExpertKind::Mxfp4,
1848 "MTP experts must be MXFP4"
1849 );
1850 let mtp = MtpDev {
1851 layer,
1852 enorm: me.tensor_f32_dev(last, &format!("{p}.enorm.weight"))?,
1853 hnorm: me.tensor_f32_dev(last, &format!("{p}.hnorm.weight"))?,
1854 norm: me.tensor_f32_dev(last, &format!("{p}.norm.weight"))?,
1855 e_proj: me.tensor_bf16(last, &format!("{p}.e_proj"))?,
1856 h_proj: me.tensor_bf16(last, &format!("{p}.h_proj"))?,
1857 hc_head_fn: me.tensor_f32_dev(last, &format!("{p}.hc_head_fn"))?,
1858 hc_head_base: me.model.tensor_f32(&format!("{p}.hc_head_base")).1,
1859 hc_head_scale: me.model.tensor_f32(&format!("{p}.hc_head_scale")).1,
1860 };
1861 me.mtp = Some(mtp);
1862 } else if nextn > 0 {
1863 if std::env::var("MEMRA_DSV4_DRAFTER").as_deref() == Ok("dspark") {
1864 let cfg = memra_gguf::dsv4_dspark::DsparkConfig::load(dir, &me.model);
1869 let hidden = me.model.mc.n_embd as usize;
1870 let mut blocks = Vec::with_capacity(cfg.n_blocks);
1871 for k in 0..cfg.n_blocks {
1872 let layer = me.load_layer(last, n_trunk + k as u32, &format!("mtp.{k}"))?;
1873 assert_eq!(layer.ratio, 0, "dspark block mtp.{k} must be ratio 0");
1874 assert_eq!(
1875 layer.expert_kind,
1876 ExpertKind::Mxfp4,
1877 "dspark experts must be MXFP4"
1878 );
1879 blocks.push(layer);
1880 }
1881 let last_p = format!("mtp.{}", cfg.n_blocks - 1);
1882 let (mp_shape, _) = me.model.tensor_f32("mtp.0.main_proj");
1883 assert_eq!(
1884 mp_shape,
1885 vec![hidden, cfg.target_layer_ids.len() * hidden],
1886 "main_proj shape"
1887 );
1888 let (w1_shape, w1) = me
1889 .model
1890 .tensor_f32(&format!("{last_p}.markov_head.markov_w1.weight"));
1891 let (w2_shape, w2) = me
1892 .model
1893 .tensor_f32(&format!("{last_p}.markov_head.markov_w2.weight"));
1894 let vocab = w1_shape[0];
1895 assert_eq!(w1_shape[1], cfg.markov_rank, "markov_w1 rank");
1896 assert_eq!(w2_shape, vec![vocab, cfg.markov_rank], "markov_w2 shape");
1897 let (cf_shape, _) = me
1898 .model
1899 .tensor_f32(&format!("{last_p}.confidence_head.proj.weight"));
1900 assert_eq!(
1901 cf_shape,
1902 vec![1, hidden + cfg.markov_rank],
1903 "confidence proj shape"
1904 );
1905 let st_stream = me.stages[last].gpu.stream();
1906 let markov_w1 = upload_f32(&st_stream, &w1)?;
1907 let markov_w2 = upload_f32(&st_stream, &w2)?;
1908 let dspark = DsparkDev {
1909 blocks,
1910 main_proj: me.tensor_bf16(last, "mtp.0.main_proj")?,
1911 main_norm: me.tensor_f32_dev(last, "mtp.0.main_norm.weight")?,
1912 norm: me.tensor_f32_dev(last, &format!("{last_p}.norm.weight"))?,
1913 markov_w1,
1914 markov_w2,
1915 markov_w1_host: w1,
1916 conf_w: me
1917 .tensor_f32_dev(last, &format!("{last_p}.confidence_head.proj.weight"))?,
1918 hc_head_fn: me.tensor_f32_dev(last, &format!("{last_p}.hc_head_fn"))?,
1919 hc_head_base: me.model.tensor_f32(&format!("{last_p}.hc_head_base")).1,
1920 hc_head_scale: me.model.tensor_f32(&format!("{last_p}.hc_head_scale")).1,
1921 block_size: cfg.block_size,
1922 noise_token: cfg.noise_token_id,
1923 targets: cfg.target_layer_ids.clone(),
1924 rank: cfg.markov_rank,
1925 vocab,
1926 };
1927 eprintln!(
1928 "[load] drafter: DSpark ({} blocks, block_size {}, targets {:?}) \
1929 resident on stage {last}",
1930 cfg.n_blocks, cfg.block_size, cfg.target_layer_ids
1931 );
1932 me.dspark = Some(dspark);
1933 } else {
1934 eprintln!(
1935 "[load] drafter: {nextn} DSpark block(s) (mtp.0.e_proj absent) — GPU \
1936 drafter path off (set MEMRA_DSV4_DRAFTER=dspark); trunk-only"
1937 );
1938 }
1939 }
1940 for st in &me.stages {
1941 st.gpu.stream().synchronize().map_err(e("load sync"))?;
1942 }
1943 Ok(me)
1944 }
1945
1946 pub fn vram_report(&self) -> Res<Vec<(usize, u64, u64, u64)>> {
1948 let mut out = Vec::new();
1949 for st in &self.stages {
1950 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx"))?;
1951 let (free, total) = st.gpu.ctx.mem_get_info().map_err(e("mem_get_info"))?;
1952 out.push((st.dev, free as u64, total as u64, st.loaded_bytes));
1953 }
1954 Ok(out)
1955 }
1956
1957 #[allow(clippy::too_many_arguments)]
1962 fn gemm(
1963 st: &Stage,
1964 x_f32: &CudaSlice<f32>,
1965 w_bf16: &CudaSlice<u8>,
1966 w_off_elems: usize,
1967 m: usize,
1968 n: usize,
1969 kdim: usize,
1970 y: &mut CudaSlice<f32>,
1971 ) -> Res<()> {
1972 let stream = st.gpu.stream();
1973 let mut xb = stream
1974 .alloc_zeros::<u8>(m * kdim * 2)
1975 .map_err(e("alloc xb"))?;
1976 unsafe {
1977 ck(
1978 "cvt_bf16",
1979 k::memra_dsv4_cvt_bf16(
1980 dpf!(x_f32, &stream),
1981 xb.device_ptr_mut(&stream).0 as *mut c_void,
1982 (m * kdim) as i64,
1983 sp(&stream),
1984 ),
1985 )?;
1986 ck(
1987 "gemm_bf16",
1988 k::memra_dsv4_gemm_bf16(
1989 (w_bf16.device_ptr(&stream).0 as usize + w_off_elems * 2) as *const c_void,
1990 dp!(xb, &stream),
1991 dpm!(y, &stream),
1992 m as i32,
1993 n as i32,
1994 kdim as i32,
1995 st.dev as i32,
1996 st.ws.device_ptr(&stream).0 as *mut c_void,
1997 st.ws.len(),
1998 sp(&stream),
1999 ),
2000 )?;
2001 }
2002 Ok(())
2003 }
2004
2005 #[allow(clippy::too_many_arguments)]
2007 fn gemm_pre(
2008 st: &Stage,
2009 xb: &CudaSlice<u8>,
2010 w_bf16_ptr: *const c_void,
2011 m: usize,
2012 n: usize,
2013 kdim: usize,
2014 y: &mut CudaSlice<f32>,
2015 ) -> Res<()> {
2016 let stream = st.gpu.stream();
2017 unsafe {
2018 ck(
2019 "gemm_bf16",
2020 k::memra_dsv4_gemm_bf16(
2021 w_bf16_ptr,
2022 dp!(xb, &stream),
2023 dpm!(y, &stream),
2024 m as i32,
2025 n as i32,
2026 kdim as i32,
2027 st.dev as i32,
2028 st.ws.device_ptr(&stream).0 as *mut c_void,
2029 st.ws.len(),
2030 sp(&stream),
2031 ),
2032 )?;
2033 }
2034 Ok(())
2035 }
2036
2037 fn dots(
2039 st: &Stage,
2040 x: &CudaSlice<f32>,
2041 w_f32: &CudaSlice<f32>,
2042 s: usize,
2043 kdim: usize,
2044 n: usize,
2045 y: &mut CudaSlice<f32>,
2046 ) -> Res<()> {
2047 let stream = st.gpu.stream();
2048 unsafe {
2049 ck(
2050 "dots_f32",
2051 k::memra_dsv4_dots_f32(
2052 dpf!(x, &stream),
2053 dp!(w_f32, &stream),
2054 0,
2055 dpm!(y, &stream),
2056 s as i32,
2057 kdim as i32,
2058 n as i32,
2059 sp(&stream),
2060 ),
2061 )?;
2062 }
2063 Ok(())
2064 }
2065
2066 #[allow(clippy::too_many_arguments)] fn dots_dev(
2072 &self,
2073 st: &Stage,
2074 x: &CudaSlice<f32>,
2075 w_f32: &CudaSlice<f32>,
2076 s: usize,
2077 kdim: usize,
2078 n: usize,
2079 y: &mut CudaSlice<f32>,
2080 ) -> Res<()> {
2081 if !self.dots_f32 {
2082 return Self::dots(st, x, w_f32, s, kdim, n, y);
2083 }
2084 let stream = st.gpu.stream();
2085 unsafe {
2086 ck(
2087 "dots_f32acc",
2088 k::memra_dsv4_dots_f32acc(
2089 dpf!(x, &stream),
2090 dp!(w_f32, &stream),
2091 0,
2092 dpm!(y, &stream),
2093 s as i32,
2094 kdim as i32,
2095 n as i32,
2096 sp(&stream),
2097 ),
2098 )?;
2099 }
2100 Ok(())
2101 }
2102
2103 #[allow(clippy::too_many_arguments)]
2108 #[allow(clippy::type_complexity)] fn compressor(
2110 &self,
2111 st: &Stage,
2112 cmp: &CmpDev,
2113 x: &CudaSlice<f32>, s: usize,
2115 hidden: usize,
2116 fc_dev: &CudaSlice<f32>,
2117 rd: usize,
2118 eps: f32,
2119 ) -> Res<(
2120 Option<(CudaSlice<f32>, usize)>,
2121 CudaSlice<f32>,
2122 CudaSlice<f32>,
2123 )> {
2124 let stream = st.gpu.stream();
2125 let mut kv = stream
2126 .alloc_zeros::<f32>(s * cmp.latent)
2127 .map_err(e("cmp kv"))?;
2128 let mut score = stream
2129 .alloc_zeros::<f32>(s * cmp.latent)
2130 .map_err(e("cmp score"))?;
2131 Self::dots(st, x, &cmp.wkv, s, hidden, cmp.latent, &mut kv)?;
2132 Self::dots(st, x, &cmp.wgate, s, hidden, cmp.latent, &mut score)?;
2133 if s < cmp.ratio {
2134 return Ok((None, kv, score));
2135 }
2136 let cutoff = s - s % cmp.ratio;
2137 let nb = cutoff / cmp.ratio;
2138 let mut pooled = stream
2139 .alloc_zeros::<f32>(nb * cmp.d)
2140 .map_err(e("cmp out"))?;
2141 unsafe {
2142 ck(
2143 "compressor_pool",
2144 k::memra_dsv4_compressor_pool(
2145 dpf!(kv, &stream),
2146 dpf!(score, &stream),
2147 dpf!(cmp.ape, &stream),
2148 dpm!(pooled, &stream),
2149 nb as i32,
2150 cmp.ratio as i32,
2151 cmp.d as i32,
2152 cmp.latent as i32,
2153 cmp.overlap as i32,
2154 sp(&stream),
2155 ),
2156 )?;
2157 ck(
2158 "rmsnorm cmp",
2159 k::memra_dsv4_rmsnorm(
2160 dpf!(pooled, &stream),
2161 dpf!(cmp.norm, &stream),
2162 dpm!(pooled, &stream),
2163 nb as i32,
2164 cmp.d as i32,
2165 eps,
2166 sp(&stream),
2167 ),
2168 )?;
2169 let positions: Vec<i32> = (0..nb).map(|j| (j * cmp.ratio) as i32).collect();
2170 let pos_dev = upload_i32(&stream, &positions)?;
2171 ck(
2172 "rope cmp",
2173 k::memra_dsv4_rope(
2174 dpm!(pooled, &stream),
2175 nb as i32,
2176 1,
2177 cmp.d as i32,
2178 rd as i32,
2179 dpf!(fc_dev, &stream),
2180 pos_dev.device_ptr(&stream).0 as *const i32,
2181 0,
2182 sp(&stream),
2183 ),
2184 )?;
2185 if cmp.rotate {
2186 let scale = (cmp.d as f32).powf(-0.5);
2188 ck(
2189 "hadamard cmp",
2190 k::memra_dsv4_hadamard(
2191 dpm!(pooled, &stream),
2192 nb as i32,
2193 cmp.d as i32,
2194 scale,
2195 sp(&stream),
2196 ),
2197 )?;
2198 ck(
2199 "fp4 cmp",
2200 k::memra_dsv4_fp4_act_quant(
2201 dpm!(pooled, &stream),
2202 nb as i32,
2203 cmp.d as i64,
2204 cmp.d as i32,
2205 sp(&stream),
2206 ),
2207 )?;
2208 } else {
2209 ck(
2210 "act_quant cmp",
2211 k::memra_dsv4_act_quant(
2212 dpm!(pooled, &stream),
2213 nb as i32,
2214 cmp.d as i64,
2215 (cmp.d - rd) as i32,
2216 64,
2217 (self.variant == ActQuantVariant::ClampOnly) as i32,
2218 sp(&stream),
2219 ),
2220 )?;
2221 }
2222 }
2223 Ok((Some((pooled, nb)), kv, score))
2224 }
2225
2226 #[allow(clippy::too_many_arguments)]
2231 fn populate_cmp_cache(
2232 stream: &std::sync::Arc<CudaStream>,
2233 s: usize,
2234 cmp_ratio: usize,
2235 latent: usize,
2236 d: usize,
2237 pooled: &Option<(CudaSlice<f32>, usize)>,
2238 kv_raw: &CudaSlice<f32>,
2239 score_raw: &CudaSlice<f32>,
2240 store: &mut CudaSlice<f32>,
2241 row0: usize,
2242 blocks: &mut usize,
2243 pend_kv: &mut CudaSlice<f32>,
2244 pend_score: &mut CudaSlice<f32>,
2245 overlap: bool,
2246 ) -> Res<()> {
2247 *blocks = 0;
2248 if let Some((buf, nb)) = pooled {
2249 let src = buf.slice(0..nb * d);
2250 let mut dst = store.slice_mut(row0 * d..(row0 + nb) * d);
2251 stream.memcpy_dtod(&src, &mut dst).map_err(e("cmp store"))?;
2252 *blocks = *nb;
2253 }
2254 let cutoff = s - s % cmp_ratio;
2255 let rem = s - cutoff;
2256 if overlap {
2257 if cutoff >= cmp_ratio {
2258 let a = (cutoff - cmp_ratio) * latent;
2259 let b = cutoff * latent;
2260 let src = kv_raw.slice(a..b);
2261 let mut dst = pend_kv.slice_mut(0..cmp_ratio * latent);
2262 stream
2263 .memcpy_dtod(&src, &mut dst)
2264 .map_err(e("pend kv prev"))?;
2265 let src = score_raw.slice(a..b);
2266 let mut dst = pend_score.slice_mut(0..cmp_ratio * latent);
2267 stream
2268 .memcpy_dtod(&src, &mut dst)
2269 .map_err(e("pend sc prev"))?;
2270 }
2271 if rem > 0 {
2272 let a = cutoff * latent;
2273 let src = kv_raw.slice(a..s * latent);
2274 let mut dst = pend_kv.slice_mut(cmp_ratio * latent..(cmp_ratio + rem) * latent);
2275 stream
2276 .memcpy_dtod(&src, &mut dst)
2277 .map_err(e("pend kv cur"))?;
2278 let src = score_raw.slice(a..s * latent);
2279 let mut dst = pend_score.slice_mut(cmp_ratio * latent..(cmp_ratio + rem) * latent);
2280 stream
2281 .memcpy_dtod(&src, &mut dst)
2282 .map_err(e("pend sc cur"))?;
2283 }
2284 } else if rem > 0 {
2285 let a = cutoff * latent;
2286 let src = kv_raw.slice(a..s * latent);
2287 let mut dst = pend_kv.slice_mut(0..rem * latent);
2288 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend kv"))?;
2289 let src = score_raw.slice(a..s * latent);
2290 let mut dst = pend_score.slice_mut(0..rem * latent);
2291 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend sc"))?;
2292 }
2293 Ok(())
2294 }
2295
2296 #[allow(clippy::too_many_arguments)]
2299 fn hc_pre(
2300 st: &Stage,
2301 h: &CudaSlice<f32>, fn_w: &CudaSlice<f32>,
2303 base: &[f32],
2304 scale: &[f32],
2305 s: usize,
2306 hc: usize,
2307 hidden: usize,
2308 iters: u32,
2309 hc_eps: f32,
2310 ) -> Res<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)> {
2311 let stream = st.gpu.stream();
2312 let w = hc * hidden;
2313 let rows = (2 + hc) * hc;
2314 let mut mixes = stream.alloc_zeros::<f32>(s * rows).map_err(e("mixes"))?;
2315 Self::dots(st, h, fn_w, s, w, rows, &mut mixes)?;
2316 unsafe {
2317 ck(
2318 "rowsq_scale",
2319 k::memra_dsv4_rowsq_scale(
2320 dpf!(h, &stream),
2321 dpm!(mixes, &stream),
2322 s as i32,
2323 w as i32,
2324 rows as i32,
2325 hc_eps,
2326 sp(&stream),
2327 ),
2328 )?;
2329 }
2330 let mixes_h = dtoh_f32(&stream, &mixes)?;
2331 let (pre, post, comb) = hc_split_sinkhorn(&mixes_h, s, hc, scale, base, iters, hc_eps);
2332 let pre_d = upload_f32(&stream, &pre)?;
2333 let post_d = upload_f32(&stream, &post)?;
2334 let comb_d = upload_f32(&stream, &comb)?;
2335 let mut y = stream.alloc_zeros::<f32>(s * hidden).map_err(e("hc y"))?;
2336 unsafe {
2337 ck(
2338 "hc_collapse",
2339 k::memra_dsv4_hc_collapse(
2340 dpf!(h, &stream),
2341 dpf!(pre_d, &stream),
2342 dpm!(y, &stream),
2343 s as i32,
2344 hc as i32,
2345 hidden as i32,
2346 sp(&stream),
2347 ),
2348 )?;
2349 }
2350 Ok((y, post_d, comb_d))
2351 }
2352
2353 #[allow(clippy::too_many_arguments)]
2355 fn route_host(
2356 layer: &LayerDev,
2357 raw_scores: &[f32], ids: &[u32],
2359 s: usize,
2360 ne: usize,
2361 topk: usize,
2362 route_scale: f32,
2363 ) -> (Vec<usize>, Vec<f32>) {
2364 let mut scores = raw_scores.to_vec();
2365 for v in &mut scores {
2366 *v = softplus_f32(*v).sqrt();
2367 }
2368 let mut indices = vec![0usize; s * topk];
2369 if let Some(tid2eid) = &layer.tid2eid {
2370 for t in 0..s {
2371 let row = &tid2eid[ids[t] as usize * topk..(ids[t] as usize + 1) * topk];
2372 let mut seen = std::collections::BTreeSet::new();
2373 for (kk, &ex) in row.iter().enumerate() {
2374 assert!(
2375 (0..ne as i64).contains(&ex),
2376 "layer {}: tid2eid out of range",
2377 layer.il
2378 );
2379 assert!(
2380 seen.insert(ex),
2381 "layer {}: duplicate expert id in tid2eid row {}",
2382 layer.il,
2383 ids[t]
2384 );
2385 indices[t * topk + kk] = ex as usize;
2386 }
2387 }
2388 } else {
2389 let bias = layer.gate_bias.as_ref().expect("score layer needs bias");
2390 for t in 0..s {
2391 let biased: Vec<f32> = (0..ne).map(|ex| scores[t * ne + ex] + bias[ex]).collect();
2392 let mut order: Vec<usize> = (0..ne).collect();
2393 order.sort_by(|&a, &b| {
2394 biased[b]
2395 .partial_cmp(&biased[a])
2396 .unwrap_or(std::cmp::Ordering::Equal)
2397 .then(a.cmp(&b))
2398 });
2399 for kk in 0..topk {
2400 indices[t * topk + kk] = order[kk];
2401 }
2402 }
2403 }
2404 let mut weights = vec![0f32; s * topk];
2405 for t in 0..s {
2406 let mut sum = 0f32;
2407 for kk in 0..topk {
2408 let w = scores[t * ne + indices[t * topk + kk]];
2409 weights[t * topk + kk] = w;
2410 sum += w;
2411 }
2412 for kk in 0..topk {
2413 weights[t * topk + kk] = weights[t * topk + kk] / sum * route_scale;
2414 }
2415 }
2416 (indices, weights)
2417 }
2418
2419 #[allow(clippy::too_many_arguments)]
2422 fn block_forward(
2423 &self,
2424 st: &Stage,
2425 layer: &LayerDev,
2426 h: &CudaSlice<f32>,
2427 s: usize,
2428 ids: &[u32],
2429 mut capture: Option<&mut GpuCapture>,
2430 mut cache: Option<&mut LayerCache>,
2431 ) -> Res<CudaSlice<f32>> {
2432 let d = self.model.cfg();
2433 let mc = &self.model.mc;
2434 let hc = d.hc_mult as usize;
2435 let hidden = mc.n_embd as usize;
2436 let heads = mc.n_head as usize;
2437 let hd = d.head_dim as usize;
2438 let rd = d.qk_rope_head_dim as usize;
2439 let q_lora = d.q_lora_rank as usize;
2440 let win = d.sliding_window as usize;
2441 let o_groups = d.o_groups as usize;
2442 let o_lora = d.o_lora_rank as usize;
2443 let eps = mc.rms_eps;
2444 let iters = d.hc_sinkhorn_iters;
2445 let hc_eps = d.hc_eps;
2446 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx"))?;
2450 let stream = st.gpu.stream();
2451 let fc_dev = if layer.ratio != 0 {
2452 &st.fc_yarn
2453 } else {
2454 &st.fc_plain
2455 };
2456 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
2457
2458 let (y, post, comb) = Self::hc_pre(
2460 st,
2461 h,
2462 &layer.hc_attn_fn,
2463 &layer.hc_attn_base,
2464 &layer.hc_attn_scale,
2465 s,
2466 hc,
2467 hidden,
2468 iters,
2469 hc_eps,
2470 )?;
2471 let mut x = stream.alloc_zeros::<f32>(s * hidden).map_err(e("x"))?;
2472 unsafe {
2473 ck(
2474 "rmsnorm attn",
2475 k::memra_dsv4_rmsnorm(
2476 dpf!(y, &stream),
2477 dpf!(layer.attn_norm, &stream),
2478 dpm!(x, &stream),
2479 s as i32,
2480 hidden as i32,
2481 eps,
2482 sp(&stream),
2483 ),
2484 )?;
2485 }
2486
2487 let wq_a_v = layer.wq_a.staged(&stream)?;
2492 let mut qr = stream.alloc_zeros::<f32>(s * q_lora).map_err(e("qr"))?;
2493 Self::gemm(st, &x, wq_a_v.slab(), 0, s, q_lora, hidden, &mut qr)?;
2494 unsafe {
2495 ck(
2496 "rmsnorm q",
2497 k::memra_dsv4_rmsnorm(
2498 dpf!(qr, &stream),
2499 dpf!(layer.q_norm, &stream),
2500 dpm!(qr, &stream),
2501 s as i32,
2502 q_lora as i32,
2503 eps,
2504 sp(&stream),
2505 ),
2506 )?;
2507 }
2508 let mut qr_b = stream
2510 .alloc_zeros::<u8>(s * q_lora * 2)
2511 .map_err(e("qr_b"))?;
2512 unsafe {
2513 ck(
2514 "cvt qr",
2515 k::memra_dsv4_cvt_bf16(
2516 dpf!(qr, &stream),
2517 qr_b.device_ptr_mut(&stream).0 as *mut c_void,
2518 (s * q_lora) as i64,
2519 sp(&stream),
2520 ),
2521 )?;
2522 }
2523 let wq_b_v = layer.wq_b.staged(&stream)?;
2524 let mut q = stream.alloc_zeros::<f32>(s * heads * hd).map_err(e("q"))?;
2525 Self::gemm_pre(
2526 st,
2527 &qr_b,
2528 wq_b_v.slab().device_ptr(&stream).0 as *const c_void,
2529 s,
2530 heads * hd,
2531 q_lora,
2532 &mut q,
2533 )?;
2534 let positions: Vec<i32> = (0..s as i32).collect();
2535 let pos_dev = upload_i32(&stream, &positions)?;
2536 unsafe {
2537 ck(
2538 "headrms",
2539 k::memra_dsv4_headrms(
2540 dpm!(q, &stream),
2541 (s * heads) as i32,
2542 hd as i32,
2543 eps,
2544 sp(&stream),
2545 ),
2546 )?;
2547 ck(
2548 "rope q",
2549 k::memra_dsv4_rope(
2550 dpm!(q, &stream),
2551 s as i32,
2552 heads as i32,
2553 hd as i32,
2554 rd as i32,
2555 dpf!(fc_dev, &stream),
2556 pos_dev.device_ptr(&stream).0 as *const i32,
2557 0,
2558 sp(&stream),
2559 ),
2560 )?;
2561 }
2562
2563 let wkv_v = layer.wkv.staged(&stream)?;
2565 let mut kv = stream.alloc_zeros::<f32>(s * hd).map_err(e("kv"))?;
2566 Self::gemm(st, &x, wkv_v.slab(), 0, s, hd, hidden, &mut kv)?;
2567 unsafe {
2568 ck(
2569 "rmsnorm kv",
2570 k::memra_dsv4_rmsnorm(
2571 dpf!(kv, &stream),
2572 dpf!(layer.kv_norm, &stream),
2573 dpm!(kv, &stream),
2574 s as i32,
2575 hd as i32,
2576 eps,
2577 sp(&stream),
2578 ),
2579 )?;
2580 ck(
2581 "rope kv",
2582 k::memra_dsv4_rope(
2583 dpm!(kv, &stream),
2584 s as i32,
2585 1,
2586 hd as i32,
2587 rd as i32,
2588 dpf!(fc_dev, &stream),
2589 pos_dev.device_ptr(&stream).0 as *const i32,
2590 0,
2591 sp(&stream),
2592 ),
2593 )?;
2594 ck(
2595 "act_quant kv",
2596 k::memra_dsv4_act_quant(
2597 dpm!(kv, &stream),
2598 s as i32,
2599 hd as i64,
2600 (hd - rd) as i32,
2601 64,
2602 clamp_only,
2603 sp(&stream),
2604 ),
2605 )?;
2606 }
2607 if let Some(c) = cache.as_deref_mut() {
2611 for p in s.saturating_sub(win)..s {
2612 let slot = p % win;
2613 let src = kv.slice(p * hd..(p + 1) * hd);
2614 let mut dst = c.kvc.slice_mut(slot * hd..(slot + 1) * hd);
2615 stream.memcpy_dtod(&src, &mut dst).map_err(e("ring copy"))?;
2616 }
2617 }
2618
2619 let (widx, wslots) = window_topk_idxs(win, s);
2621 let mut idxs: Vec<i64> = widx;
2622 let mut slots = wslots;
2623 let mut n_kv = s;
2624 let mut kv_full = kv;
2625 let mut cap_cmp: Option<(Vec<f32>, usize)> = None;
2626 let mut cap_ikv: Option<(Vec<f32>, usize)> = None;
2627 let mut cap_isc: Option<(Vec<f32>, usize)> = None;
2628 let want_cap = capture
2629 .as_ref()
2630 .map(|c| c.want.contains(&layer.il))
2631 .unwrap_or(false);
2632 if layer.ratio != 0 {
2633 let offset = s;
2634 let (cidx, cslots) = if let Some(ix) = &layer.idx {
2635 let mut qi = stream
2637 .alloc_zeros::<f32>(s * ix.heads * ix.hd)
2638 .map_err(e("qi"))?;
2639 let iwq_b_v = ix.wq_b.staged(&stream)?;
2640 Self::gemm_pre(
2641 st,
2642 &qr_b,
2643 iwq_b_v.slab().device_ptr(&stream).0 as *const c_void,
2644 s,
2645 ix.heads * ix.hd,
2646 q_lora,
2647 &mut qi,
2648 )?;
2649 unsafe {
2650 ck(
2651 "rope qi",
2652 k::memra_dsv4_rope(
2653 dpm!(qi, &stream),
2654 s as i32,
2655 ix.heads as i32,
2656 ix.hd as i32,
2657 rd as i32,
2658 dpf!(fc_dev, &stream),
2659 pos_dev.device_ptr(&stream).0 as *const i32,
2660 0,
2661 sp(&stream),
2662 ),
2663 )?;
2664 let scale = (ix.hd as f32).powf(-0.5);
2665 ck(
2666 "hadamard qi",
2667 k::memra_dsv4_hadamard(
2668 dpm!(qi, &stream),
2669 (s * ix.heads) as i32,
2670 ix.hd as i32,
2671 scale,
2672 sp(&stream),
2673 ),
2674 )?;
2675 ck(
2676 "fp4 qi",
2677 k::memra_dsv4_fp4_act_quant(
2678 dpm!(qi, &stream),
2679 (s * ix.heads) as i32,
2680 ix.hd as i64,
2681 ix.hd as i32,
2682 sp(&stream),
2683 ),
2684 )?;
2685 }
2686 let (ckv_i, ikv_raw, isc_raw) =
2688 self.compressor(st, &ix.cmp, &x, s, hidden, fc_dev, rd, eps)?;
2689 if want_cap && let Some((buf, nb)) = &ckv_i {
2690 cap_ikv = Some((dtoh_f32(&stream, buf)?, *nb));
2691 }
2692 if let Some(c) = cache.as_deref_mut() {
2693 let mut i_blocks = c.i_blocks;
2694 Self::populate_cmp_cache(
2695 &stream,
2696 s,
2697 ix.cmp.ratio,
2698 ix.cmp.latent,
2699 ix.cmp.d,
2700 &ckv_i,
2701 &ikv_raw,
2702 &isc_raw,
2703 c.ikvc.as_mut().expect("fine layer has indexer store"),
2704 0,
2705 &mut i_blocks,
2706 c.ipend_kv.as_mut().expect("ipend"),
2707 c.ipend_score.as_mut().expect("ipend"),
2708 ix.cmp.overlap,
2709 )?;
2710 c.i_blocks = i_blocks;
2711 }
2712 let iwp_v = ix.weights_proj.staged(&stream)?;
2714 let mut wproj = stream.alloc_zeros::<f32>(s * ix.heads).map_err(e("wp"))?;
2715 Self::gemm(st, &x, iwp_v.slab(), 0, s, ix.heads, hidden, &mut wproj)?;
2716 if let Some((ckv, nb)) = &ckv_i {
2717 let wscale = ((ix.hd as f64).powf(-0.5) * (ix.heads as f64).powf(-0.5)) as f32;
2718 let mut score = stream.alloc_zeros::<f32>(s * nb).map_err(e("iscore"))?;
2719 unsafe {
2720 ck(
2721 "indexer_score",
2722 k::memra_dsv4_indexer_score(
2723 dpf!(qi, &stream),
2724 dpf!(ckv, &stream),
2725 dpf!(wproj, &stream),
2726 wscale,
2727 dpm!(score, &stream),
2728 s as i32,
2729 ix.heads as i32,
2730 ix.hd as i32,
2731 *nb as i32,
2732 layer.ratio as i32,
2733 -1, sp(&stream),
2735 ),
2736 )?;
2737 }
2738 let score_h = dtoh_f32(&stream, &score)?;
2739 if want_cap {
2740 cap_isc = Some((score_h.clone(), *nb));
2741 }
2742 let kk = ix.topk.min(*nb);
2744 let mut cidx = vec![-1i64; s * kk];
2745 for t in 0..s {
2746 let lim = (t + 1) / layer.ratio;
2747 let mut order: Vec<usize> = (0..*nb).collect();
2748 order.sort_by(|&a, &b| {
2749 score_h[t * nb + b]
2750 .partial_cmp(&score_h[t * nb + a])
2751 .unwrap_or(std::cmp::Ordering::Equal)
2752 .then(a.cmp(&b))
2753 });
2754 for (slot, &j) in order.iter().take(kk).enumerate() {
2755 cidx[t * kk + slot] = if j >= lim { -1 } else { (j + offset) as i64 };
2756 }
2757 }
2758 (cidx, kk)
2759 } else {
2760 (Vec::new(), 0)
2761 }
2762 } else {
2763 compress_topk_idxs(layer.ratio, s, offset)
2764 };
2765 if cslots > 0 {
2766 let mut merged = vec![-1i64; s * (slots + cslots)];
2767 for t in 0..s {
2768 merged[t * (slots + cslots)..t * (slots + cslots) + slots]
2769 .copy_from_slice(&idxs[t * slots..(t + 1) * slots]);
2770 merged[t * (slots + cslots) + slots..(t + 1) * (slots + cslots)]
2771 .copy_from_slice(&cidx[t * cslots..(t + 1) * cslots]);
2772 }
2773 idxs = merged;
2774 slots += cslots;
2775 }
2776 let acmp = layer.cmp.as_ref().expect("ratio!=0 has compressor");
2778 let (ckv, akv_raw, asc_raw) =
2779 self.compressor(st, acmp, &x, s, hidden, fc_dev, rd, eps)?;
2780 if want_cap && let Some((buf, nb)) = &ckv {
2781 cap_cmp = Some((dtoh_f32(&stream, buf)?, *nb));
2782 }
2783 if let Some(c) = cache {
2784 let mut n_blocks = c.n_blocks;
2785 Self::populate_cmp_cache(
2786 &stream,
2787 s,
2788 acmp.ratio,
2789 acmp.latent,
2790 acmp.d,
2791 &ckv,
2792 &akv_raw,
2793 &asc_raw,
2794 &mut c.kvc,
2795 win,
2796 &mut n_blocks,
2797 c.pend_kv.as_mut().expect("pend"),
2798 c.pend_score.as_mut().expect("pend"),
2799 acmp.overlap,
2800 )?;
2801 c.n_blocks = n_blocks;
2802 }
2803 if let Some((ckv_buf, nb)) = ckv {
2804 let mut merged_kv = stream
2805 .alloc_zeros::<f32>((s + nb) * hd)
2806 .map_err(e("kv_full"))?;
2807 {
2808 let mut head_view = merged_kv.slice_mut(0..s * hd);
2809 stream
2810 .memcpy_dtod(&kv_full.slice(0..s * hd), &mut head_view)
2811 .map_err(e("kv copy"))?;
2812 }
2813 {
2814 let mut tail = merged_kv.slice_mut(s * hd..(s + nb) * hd);
2815 stream
2816 .memcpy_dtod(&ckv_buf.slice(0..nb * hd), &mut tail)
2817 .map_err(e("ckv copy"))?;
2818 }
2819 kv_full = merged_kv;
2820 n_kv += nb;
2821 }
2822 }
2823 let _ = n_kv;
2824 let idxs_i32: Vec<i32> = idxs.iter().map(|&v| v as i32).collect();
2825 let idx_dev = upload_i32(&stream, &idxs_i32)?;
2826
2827 let mut o = stream.alloc_zeros::<f32>(s * heads * hd).map_err(e("o"))?;
2829 let scale = (hd as f64).powf(-0.5) as f32;
2830 unsafe {
2831 ck(
2832 "sink_attn",
2833 k::memra_dsv4_sink_attn(
2834 dpf!(q, &stream),
2835 dpf!(kv_full, &stream),
2836 idx_dev.device_ptr(&stream).0 as *const i32,
2837 dpf!(layer.sink, &stream),
2838 dpm!(o, &stream),
2839 s as i32,
2840 heads as i32,
2841 hd as i32,
2842 slots as i32,
2843 scale,
2844 sp(&stream),
2845 ),
2846 )?;
2847 ck(
2848 "rope o inv",
2849 k::memra_dsv4_rope(
2850 dpm!(o, &stream),
2851 s as i32,
2852 heads as i32,
2853 hd as i32,
2854 rd as i32,
2855 dpf!(fc_dev, &stream),
2856 pos_dev.device_ptr(&stream).0 as *const i32,
2857 1,
2858 sp(&stream),
2859 ),
2860 )?;
2861 }
2862
2863 let gw = heads / o_groups * hd;
2865 let mut og = stream
2866 .alloc_zeros::<f32>(s * o_groups * o_lora)
2867 .map_err(e("og"))?;
2868 let mut o_grp = stream.alloc_zeros::<f32>(s * gw).map_err(e("o_grp"))?;
2869 let mut y_grp = stream.alloc_zeros::<f32>(s * o_lora).map_err(e("y_grp"))?;
2870 let wo_a_v = layer.wo_a.staged(&stream)?; for g in 0..o_groups {
2872 unsafe {
2873 ck(
2874 "take_cols",
2875 k::memra_dsv4_take_cols(
2876 dpf!(o, &stream),
2877 dpm!(o_grp, &stream),
2878 s as i32,
2879 gw as i32,
2880 (heads * hd) as i64,
2881 (g * gw) as i64,
2882 sp(&stream),
2883 ),
2884 )?;
2885 }
2886 Self::gemm(
2887 st,
2888 &o_grp,
2889 wo_a_v.slab(),
2890 g * o_lora * gw,
2891 s,
2892 o_lora,
2893 gw,
2894 &mut y_grp,
2895 )?;
2896 unsafe {
2897 ck(
2898 "place_cols",
2899 k::memra_dsv4_place_cols(
2900 dpf!(y_grp, &stream),
2901 dpm!(og, &stream),
2902 s as i32,
2903 o_lora as i32,
2904 (o_groups * o_lora) as i64,
2905 (g * o_lora) as i64,
2906 sp(&stream),
2907 ),
2908 )?;
2909 }
2910 }
2911 let wo_b_v = layer.wo_b.staged(&stream)?;
2912 let mut attn_out = stream.alloc_zeros::<f32>(s * hidden).map_err(e("ao"))?;
2913 Self::gemm(
2914 st,
2915 &og,
2916 wo_b_v.slab(),
2917 0,
2918 s,
2919 hidden,
2920 o_groups * o_lora,
2921 &mut attn_out,
2922 )?;
2923
2924 let mut cap_attn: Option<Vec<f32>> = None;
2925 if want_cap {
2926 cap_attn = Some(dtoh_f32(&stream, &attn_out)?);
2927 }
2928
2929 let mut h2 = stream
2931 .alloc_zeros::<f32>(s * hc * hidden)
2932 .map_err(e("h2"))?;
2933 unsafe {
2934 ck(
2935 "hc_post attn",
2936 k::memra_dsv4_hc_post(
2937 dpf!(attn_out, &stream),
2938 dpf!(h, &stream),
2939 dpf!(post, &stream),
2940 dpf!(comb, &stream),
2941 dpm!(h2, &stream),
2942 s as i32,
2943 hc as i32,
2944 hidden as i32,
2945 sp(&stream),
2946 ),
2947 )?;
2948 }
2949
2950 let (y2, post2, comb2) = Self::hc_pre(
2952 st,
2953 &h2,
2954 &layer.hc_ffn_fn,
2955 &layer.hc_ffn_base,
2956 &layer.hc_ffn_scale,
2957 s,
2958 hc,
2959 hidden,
2960 iters,
2961 hc_eps,
2962 )?;
2963 let mut xf = stream.alloc_zeros::<f32>(s * hidden).map_err(e("xf"))?;
2964 unsafe {
2965 ck(
2966 "rmsnorm ffn",
2967 k::memra_dsv4_rmsnorm(
2968 dpf!(y2, &stream),
2969 dpf!(layer.ffn_norm, &stream),
2970 dpm!(xf, &stream),
2971 s as i32,
2972 hidden as i32,
2973 eps,
2974 sp(&stream),
2975 ),
2976 )?;
2977 }
2978 if let Some(c) = capture.as_deref_mut()
2979 && c.want.contains(&layer.il)
2980 {
2981 c.moe_x.insert(layer.il, dtoh_f32(&stream, &xf)?);
2982 }
2983 let moe_out = self.moe_forward(st, layer, &xf, s, ids)?;
2984 let mut h3 = stream
2985 .alloc_zeros::<f32>(s * hc * hidden)
2986 .map_err(e("h3"))?;
2987 unsafe {
2988 ck(
2989 "hc_post ffn",
2990 k::memra_dsv4_hc_post(
2991 dpf!(moe_out, &stream),
2992 dpf!(h2, &stream),
2993 dpf!(post2, &stream),
2994 dpf!(comb2, &stream),
2995 dpm!(h3, &stream),
2996 s as i32,
2997 hc as i32,
2998 hidden as i32,
2999 sp(&stream),
3000 ),
3001 )?;
3002 }
3003
3004 if let Some(c) = capture
3005 && c.want.contains(&layer.il)
3006 {
3007 c.layer_out.insert(layer.il, dtoh_f32(&stream, &h3)?);
3008 c.x_dbg.insert(layer.il, dtoh_f32(&stream, &x)?);
3009 c.q_dbg.insert(layer.il, dtoh_f32(&stream, &q)?);
3010 {
3011 let mut kvh = vec![0f32; s * hd];
3012 stream
3013 .memcpy_dtoh(&kv_full.slice(0..s * hd), &mut kvh[..])
3014 .map_err(e("dtoh kv_dbg"))?;
3015 stream.synchronize().map_err(e("sync kv_dbg"))?;
3016 c.kv_dbg.insert(layer.il, kvh);
3017 }
3018 c.o_dbg.insert(layer.il, dtoh_f32(&stream, &o)?);
3019 if let Some(a) = cap_attn {
3020 c.attn_out.insert(layer.il, a);
3021 }
3022 if let Some(v) = cap_cmp {
3023 c.compressor_kv.insert(layer.il, v);
3024 }
3025 if let Some(v) = cap_ikv {
3026 c.indexer_kv.insert(layer.il, v);
3027 }
3028 if let Some(v) = cap_isc {
3029 c.index_score.insert(layer.il, v);
3030 }
3031 }
3032 Ok(h3)
3033 }
3034
3035 fn moe_forward(
3039 &self,
3040 st: &Stage,
3041 layer: &LayerDev,
3042 x: &CudaSlice<f32>, s: usize,
3044 ids: &[u32],
3045 ) -> Res<CudaSlice<f32>> {
3046 let mc = &self.model.mc;
3047 let d = self.model.cfg();
3048 let moe = mc.moe.as_ref().expect("moe");
3049 let hidden = mc.n_embd as usize;
3050 let ne = moe.expert_count as usize;
3051 let topk = moe.expert_used_count as usize;
3052 let inter = moe.expert_ff_length as usize;
3053 let limit = d.swiglu_limit;
3054 let stream = st.gpu.stream();
3055
3056 let mut raw = stream.alloc_zeros::<f32>(s * ne).map_err(e("gate raw"))?;
3057 Self::dots(st, x, &layer.gate_w, s, hidden, ne, &mut raw)?;
3058 let raw_h = dtoh_f32(&stream, &raw)?;
3059 let (indices, weights) =
3060 Self::route_host(layer, &raw_h, ids, s, ne, topk, d.routed_scaling_factor);
3061
3062 let mut xb = stream
3064 .alloc_zeros::<u8>(s * hidden * 2)
3065 .map_err(e("xb moe"))?;
3066 unsafe {
3067 ck(
3068 "cvt moe x",
3069 k::memra_dsv4_cvt_bf16(
3070 dpf!(x, &stream),
3071 xb.device_ptr_mut(&stream).0 as *mut c_void,
3072 (s * hidden) as i64,
3073 sp(&stream),
3074 ),
3075 )?;
3076 }
3077 let mut y = stream.alloc_zeros::<f32>(s * hidden).map_err(e("moe y"))?;
3078
3079 let wbytes = inter * hidden / 2;
3080 let sbytes = match layer.expert_kind {
3081 ExpertKind::Nvfp4 => inter * hidden / 16,
3082 ExpertKind::Mxfp4 => inter * hidden / 32,
3083 };
3084 let mut uniq: Vec<usize> = indices.clone();
3085 uniq.sort_unstable();
3086 uniq.dedup();
3087 if self.expert_arm == ExpertArm::Native {
3088 let kind = match layer.expert_kind {
3093 ExpertKind::Nvfp4 => 0i32,
3094 ExpertKind::Mxfp4 => 1i32,
3095 };
3096 let kq_x = hidden / 128;
3097 let kq_h = inter / 128;
3098 let mut xq = stream.alloc_zeros::<u8>(s * hidden).map_err(e("xq"))?;
3099 let mut xs = stream.alloc_zeros::<f32>(s * kq_x).map_err(e("xs"))?;
3100 unsafe {
3101 ck(
3102 "act_quant_fp8 x",
3103 k::memra_dsv4_act_quant_fp8(
3104 dpf!(x, &stream),
3105 xq.device_ptr_mut(&stream).0 as *mut c_void,
3106 dpm!(xs, &stream),
3107 s as i32,
3108 hidden as i32,
3109 sp(&stream),
3110 ),
3111 )?;
3112 }
3113 let mut xgq = stream.alloc_zeros::<u8>(s * hidden).map_err(e("xgq"))?;
3114 let mut xgs = stream.alloc_zeros::<f32>(s * kq_x).map_err(e("xgs"))?;
3115 let mut g1 = stream.alloc_zeros::<f32>(s * inter).map_err(e("g1"))?;
3116 let mut g3 = stream.alloc_zeros::<f32>(s * inter).map_err(e("g3"))?;
3117 let mut hbuf = stream.alloc_zeros::<f32>(s * inter).map_err(e("hbuf"))?;
3118 let mut hq = stream.alloc_zeros::<u8>(s * inter).map_err(e("hq"))?;
3119 let mut hs = stream.alloc_zeros::<f32>(s * kq_h).map_err(e("hs"))?;
3120 let mut contrib = stream
3121 .alloc_zeros::<f32>(s * hidden)
3122 .map_err(e("contrib"))?;
3123 for &ex in &uniq {
3124 let toks: Vec<(usize, usize)> = (0..s * topk)
3125 .filter(|i| indices[*i] == ex)
3126 .map(|i| (i / topk, i % topk))
3127 .collect();
3128 let g = toks.len();
3129 let tok_rows: Vec<i32> = toks.iter().map(|&(t, _)| t as i32).collect();
3130 let wrow: Vec<f32> = toks.iter().map(|&(t, kk)| weights[t * topk + kk]).collect();
3131 let rows_dev = upload_i32(&stream, &tok_rows)?;
3132 let wrow_dev = upload_f32(&stream, &wrow)?;
3133 unsafe {
3134 ck(
3135 "gather xq",
3136 k::memra_dsv4_gather_rows_u8(
3137 dp!(xq, &stream),
3138 rows_dev.device_ptr(&stream).0 as *const i32,
3139 xgq.device_ptr_mut(&stream).0 as *mut c_void,
3140 g as i32,
3141 hidden as i64,
3142 sp(&stream),
3143 ),
3144 )?;
3145 ck(
3146 "gather xs",
3147 k::memra_dsv4_gather_rows_u8(
3148 xs.device_ptr(&stream).0 as *const c_void,
3149 rows_dev.device_ptr(&stream).0 as *const i32,
3150 xgs.device_ptr_mut(&stream).0 as *mut c_void,
3151 g as i32,
3152 (kq_x * 4) as i64,
3153 sp(&stream),
3154 ),
3155 )?;
3156 for (pi, dst) in [(0usize, &mut g1), (2usize, &mut g3)] {
3158 let woff = (ex * 3 + pi) * wbytes;
3159 let soff = (ex * 3 + pi) * sbytes;
3160 ck(
3161 "fp4_gemm w1/w3",
3162 k::memra_dsv4_fp4_gemm(
3163 dp!(xgq, &stream),
3164 dpf!(xgs, &stream),
3165 (layer.experts_w.device_ptr(&stream).0 as usize + woff)
3166 as *const c_void,
3167 (layer.experts_sc.device_ptr(&stream).0 as usize + soff)
3168 as *const c_void,
3169 layer.experts_s2[ex * 3 + pi],
3170 kind,
3171 dpm!(*dst, &stream),
3172 g as i32,
3173 inter as i32,
3174 hidden as i32,
3175 sp(&stream),
3176 ),
3177 )?;
3178 }
3179 ck(
3180 "swiglu",
3181 k::memra_dsv4_swiglu(
3182 dpf!(g1, &stream),
3183 dpf!(g3, &stream),
3184 dpm!(hbuf, &stream),
3185 g as i32,
3186 inter as i32,
3187 limit,
3188 wrow_dev.device_ptr(&stream).0 as *const f32,
3189 sp(&stream),
3190 ),
3191 )?;
3192 ck(
3193 "act_quant_fp8 h",
3194 k::memra_dsv4_act_quant_fp8(
3195 dpf!(hbuf, &stream),
3196 hq.device_ptr_mut(&stream).0 as *mut c_void,
3197 dpm!(hs, &stream),
3198 g as i32,
3199 inter as i32,
3200 sp(&stream),
3201 ),
3202 )?;
3203 let woff2 = (ex * 3 + 1) * wbytes;
3204 let soff2 = (ex * 3 + 1) * sbytes;
3205 ck(
3206 "fp4_gemm w2",
3207 k::memra_dsv4_fp4_gemm(
3208 dp!(hq, &stream),
3209 dpf!(hs, &stream),
3210 (layer.experts_w.device_ptr(&stream).0 as usize + woff2)
3211 as *const c_void,
3212 (layer.experts_sc.device_ptr(&stream).0 as usize + soff2)
3213 as *const c_void,
3214 layer.experts_s2[ex * 3 + 1],
3215 kind,
3216 dpm!(contrib, &stream),
3217 g as i32,
3218 hidden as i32,
3219 inter as i32,
3220 sp(&stream),
3221 ),
3222 )?;
3223 ck(
3224 "scatter",
3225 k::memra_dsv4_scatter_add(
3226 dpm!(y, &stream),
3227 dpf!(contrib, &stream),
3228 rows_dev.device_ptr(&stream).0 as *const i32,
3229 g as i32,
3230 hidden as i32,
3231 sp(&stream),
3232 ),
3233 )?;
3234 }
3235 }
3236 return self.moe_shared_and_finish(st, layer, &xb, s, y);
3237 }
3238 let mut xg = stream.alloc_zeros::<u8>(s * hidden * 2).map_err(e("xg"))?;
3240 let mut g1 = stream.alloc_zeros::<f32>(s * inter).map_err(e("g1"))?;
3241 let mut g3 = stream.alloc_zeros::<f32>(s * inter).map_err(e("g3"))?;
3242 let mut hbuf = stream.alloc_zeros::<f32>(s * inter).map_err(e("hbuf"))?;
3243 let mut hb = stream.alloc_zeros::<u8>(s * inter * 2).map_err(e("hb"))?;
3244 let mut contrib = stream
3245 .alloc_zeros::<f32>(s * hidden)
3246 .map_err(e("contrib"))?;
3247 for &ex in &uniq {
3248 let toks: Vec<(usize, usize)> = (0..s * topk)
3249 .filter(|i| indices[*i] == ex)
3250 .map(|i| (i / topk, i % topk))
3251 .collect();
3252 let g = toks.len();
3253 let tok_rows: Vec<i32> = toks.iter().map(|&(t, _)| t as i32).collect();
3254 let wrow: Vec<f32> = toks.iter().map(|&(t, kk)| weights[t * topk + kk]).collect();
3255 let rows_dev = upload_i32(&stream, &tok_rows)?;
3256 let wrow_dev = upload_f32(&stream, &wrow)?;
3257 unsafe {
3258 ck(
3259 "gather",
3260 k::memra_dsv4_gather_bf16(
3261 dp!(xb, &stream),
3262 rows_dev.device_ptr(&stream).0 as *const i32,
3263 xg.device_ptr_mut(&stream).0 as *mut c_void,
3264 g as i32,
3265 hidden as i32,
3266 sp(&stream),
3267 ),
3268 )?;
3269 for (pi, (rows, cols)) in [(inter, hidden), (hidden, inter), (inter, hidden)]
3271 .iter()
3272 .enumerate()
3273 {
3274 let woff = (ex * 3 + pi) * wbytes;
3275 let soff = (ex * 3 + pi) * sbytes;
3276 let wp =
3277 (layer.experts_w.device_ptr(&stream).0 as usize + woff) as *const c_void;
3278 let scp =
3279 (layer.experts_sc.device_ptr(&stream).0 as usize + soff) as *const c_void;
3280 let dst = st.deq[pi].device_ptr(&stream).0 as *mut c_void;
3281 match layer.expert_kind {
3282 ExpertKind::Nvfp4 => ck(
3283 "nvfp4 deq",
3284 k::memra_dsv4_nvfp4_deq_bf16(
3285 wp,
3286 scp,
3287 layer.experts_s2[ex * 3 + pi],
3288 *rows as i32,
3289 *cols as i32,
3290 dst,
3291 sp(&stream),
3292 ),
3293 )?,
3294 ExpertKind::Mxfp4 => ck(
3295 "mxfp4 deq",
3296 k::memra_dsv4_mxfp4_deq_bf16(
3297 wp,
3298 scp,
3299 *rows as i32,
3300 *cols as i32,
3301 dst,
3302 sp(&stream),
3303 ),
3304 )?,
3305 }
3306 }
3307 ck(
3308 "gemm w1",
3309 k::memra_dsv4_gemm_bf16(
3310 st.deq[0].device_ptr(&stream).0 as *const c_void,
3311 dp!(xg, &stream),
3312 dpm!(g1, &stream),
3313 g as i32,
3314 inter as i32,
3315 hidden as i32,
3316 st.dev as i32,
3317 st.ws.device_ptr(&stream).0 as *mut c_void,
3318 st.ws.len(),
3319 sp(&stream),
3320 ),
3321 )?;
3322 ck(
3323 "gemm w3",
3324 k::memra_dsv4_gemm_bf16(
3325 st.deq[2].device_ptr(&stream).0 as *const c_void,
3326 dp!(xg, &stream),
3327 dpm!(g3, &stream),
3328 g as i32,
3329 inter as i32,
3330 hidden as i32,
3331 st.dev as i32,
3332 st.ws.device_ptr(&stream).0 as *mut c_void,
3333 st.ws.len(),
3334 sp(&stream),
3335 ),
3336 )?;
3337 ck(
3338 "swiglu",
3339 k::memra_dsv4_swiglu(
3340 dpf!(g1, &stream),
3341 dpf!(g3, &stream),
3342 dpm!(hbuf, &stream),
3343 g as i32,
3344 inter as i32,
3345 limit,
3346 wrow_dev.device_ptr(&stream).0 as *const f32,
3347 sp(&stream),
3348 ),
3349 )?;
3350 ck(
3351 "cvt h",
3352 k::memra_dsv4_cvt_bf16(
3353 dpf!(hbuf, &stream),
3354 hb.device_ptr_mut(&stream).0 as *mut c_void,
3355 (g * inter) as i64,
3356 sp(&stream),
3357 ),
3358 )?;
3359 ck(
3360 "gemm w2",
3361 k::memra_dsv4_gemm_bf16(
3362 st.deq[1].device_ptr(&stream).0 as *const c_void,
3363 dp!(hb, &stream),
3364 dpm!(contrib, &stream),
3365 g as i32,
3366 hidden as i32,
3367 inter as i32,
3368 st.dev as i32,
3369 st.ws.device_ptr(&stream).0 as *mut c_void,
3370 st.ws.len(),
3371 sp(&stream),
3372 ),
3373 )?;
3374 ck(
3375 "scatter",
3376 k::memra_dsv4_scatter_add(
3377 dpm!(y, &stream),
3378 dpf!(contrib, &stream),
3379 rows_dev.device_ptr(&stream).0 as *const i32,
3380 g as i32,
3381 hidden as i32,
3382 sp(&stream),
3383 ),
3384 )?;
3385 }
3386 }
3387 self.moe_shared_and_finish(st, layer, &xb, s, y)
3388 }
3389
3390 fn moe_shared_and_finish(
3394 &self,
3395 st: &Stage,
3396 layer: &LayerDev,
3397 xb: &CudaSlice<u8>,
3398 s: usize,
3399 mut y: CudaSlice<f32>,
3400 ) -> Res<CudaSlice<f32>> {
3401 let d = self.model.cfg();
3402 let hidden = self.model.mc.n_embd as usize;
3403 let limit = d.swiglu_limit;
3404 let stream = st.gpu.stream();
3405 let sh_inter = {
3406 let (shape, _) = self
3408 .model
3409 .st
3410 .raw("layers.0.ffn.shared_experts.w1.weight")
3411 .map(|(i, _)| (i.shape.clone(), ()))
3412 .expect("shared w1");
3413 shape[0] as usize
3414 };
3415 let mut sg1 = stream.alloc_zeros::<f32>(s * sh_inter).map_err(e("sg1"))?;
3416 let mut sg3 = stream.alloc_zeros::<f32>(s * sh_inter).map_err(e("sg3"))?;
3417 let mut shbuf = stream.alloc_zeros::<f32>(s * sh_inter).map_err(e("shb"))?;
3418 let mut shb16 = stream
3419 .alloc_zeros::<u8>(s * sh_inter * 2)
3420 .map_err(e("shb16"))?;
3421 let mut sh_out = stream.alloc_zeros::<f32>(s * hidden).map_err(e("sh_out"))?;
3422 let sw = [
3424 layer.shared_w[0].staged(&stream)?,
3425 layer.shared_w[1].staged(&stream)?,
3426 layer.shared_w[2].staged(&stream)?,
3427 ];
3428 Self::gemm_pre(
3429 st,
3430 xb,
3431 sw[0].slab().device_ptr(&stream).0 as *const c_void,
3432 s,
3433 sh_inter,
3434 hidden,
3435 &mut sg1,
3436 )?;
3437 Self::gemm_pre(
3438 st,
3439 xb,
3440 sw[2].slab().device_ptr(&stream).0 as *const c_void,
3441 s,
3442 sh_inter,
3443 hidden,
3444 &mut sg3,
3445 )?;
3446 unsafe {
3447 ck(
3448 "swiglu sh",
3449 k::memra_dsv4_swiglu(
3450 dpf!(sg1, &stream),
3451 dpf!(sg3, &stream),
3452 dpm!(shbuf, &stream),
3453 s as i32,
3454 sh_inter as i32,
3455 limit,
3456 std::ptr::null(),
3457 sp(&stream),
3458 ),
3459 )?;
3460 ck(
3461 "cvt sh",
3462 k::memra_dsv4_cvt_bf16(
3463 dpf!(shbuf, &stream),
3464 shb16.device_ptr_mut(&stream).0 as *mut c_void,
3465 (s * sh_inter) as i64,
3466 sp(&stream),
3467 ),
3468 )?;
3469 }
3470 Self::gemm_pre(
3471 st,
3472 &shb16,
3473 sw[1].slab().device_ptr(&stream).0 as *const c_void,
3474 s,
3475 hidden,
3476 sh_inter,
3477 &mut sh_out,
3478 )?;
3479 unsafe {
3480 ck(
3481 "add shared",
3482 k::memra_dsv4_add_inplace(
3483 dpm!(y, &stream),
3484 dpf!(sh_out, &stream),
3485 (s * hidden) as i64,
3486 sp(&stream),
3487 ),
3488 )?;
3489 }
3490 Ok(y)
3491 }
3492
3493 pub fn forward(
3496 &self,
3497 ids: &[u32],
3498 capture: Option<&mut GpuCapture>,
3499 early_exit_after: Option<u32>,
3500 ) -> Res<Option<ForwardOut>> {
3501 self.forward_impl(ids, capture, early_exit_after, None)
3502 }
3503
3504 pub fn prefill_with_cache(&self, ids: &[u32], state: &mut DecodeState) -> Res<ForwardOut> {
3507 assert_eq!(state.pos, 0, "prefill_with_cache needs a fresh DecodeState");
3508 assert!(!ids.is_empty(), "empty prompt");
3509 let out = self
3510 .forward_impl(ids, None, None, Some(state))?
3511 .expect("prefill logits");
3512 state.pos = ids.len();
3513 Ok(out)
3514 }
3515
3516 fn forward_impl(
3517 &self,
3518 ids: &[u32],
3519 mut capture: Option<&mut GpuCapture>,
3520 early_exit_after: Option<u32>,
3521 mut state: Option<&mut DecodeState>,
3522 ) -> Res<Option<ForwardOut>> {
3523 let mc = &self.model.mc;
3524 let d = self.model.cfg();
3525 let s = ids.len();
3526 assert!(s <= self.max_seq, "seq {s} > max_seq {}", self.max_seq);
3527 let hidden = mc.n_embd as usize;
3528 let hc = d.hc_mult as usize;
3529 let n_trunk = mc.n_layer - mc.nextn_predict_layers;
3530
3531 let st0 = &self.stages[0];
3533 st0.gpu.ctx.bind_to_thread().map_err(e("bind ctx0"))?;
3534 let stream0 = st0.gpu.stream();
3535 let ids_i32: Vec<i32> = ids.iter().map(|&x| x as i32).collect();
3536 let ids_dev = upload_i32(&stream0, &ids_i32)?;
3537 let mut emb = stream0.alloc_zeros::<f32>(s * hidden).map_err(e("emb"))?;
3538 unsafe {
3539 ck(
3540 "embed_rows",
3541 k::memra_dsv4_embed_rows(
3542 st0.embed
3543 .as_ref()
3544 .expect("embed on stage 0")
3545 .device_ptr(&stream0)
3546 .0 as *const c_void,
3547 ids_dev.device_ptr(&stream0).0 as *const i32,
3548 dpm!(emb, &stream0),
3549 s as i32,
3550 hidden as i32,
3551 sp(&stream0),
3552 ),
3553 )?;
3554 }
3555 if let Some(c) = capture.as_deref_mut()
3556 && c.embed_out.is_none()
3557 {
3558 c.embed_out = Some(dtoh_f32(&stream0, &emb)?);
3559 }
3560 let mut h = stream0
3561 .alloc_zeros::<f32>(s * hc * hidden)
3562 .map_err(e("h0"))?;
3563 unsafe {
3564 ck(
3565 "repeat_hc",
3566 k::memra_dsv4_repeat_hc(
3567 dpf!(emb, &stream0),
3568 dpm!(h, &stream0),
3569 s as i32,
3570 hc as i32,
3571 hidden as i32,
3572 sp(&stream0),
3573 ),
3574 )?;
3575 }
3576
3577 let mut cur_stage = 0usize;
3579 for il in 0..n_trunk {
3580 let stage = self.layer_stage[il as usize];
3581 if stage != cur_stage {
3582 let src_stream = self.stages[cur_stage].gpu.stream();
3583 let host = dtoh_f32(&src_stream, &h)?;
3584 let dst_stream = self.stages[stage].gpu.stream();
3585 self.stages[stage]
3586 .gpu
3587 .ctx
3588 .bind_to_thread()
3589 .map_err(e("bind"))?;
3590 h = upload_f32(&dst_stream, &host)?;
3591 cur_stage = stage;
3592 }
3593 let st = &self.stages[stage];
3594 let lidx = st
3595 .layers
3596 .iter()
3597 .position(|l| l.il == il)
3598 .unwrap_or_else(|| panic!("layer {il} not on stage {stage}"));
3599 let layer_cache = state.as_deref_mut().map(|ds| &mut ds.caches[il as usize]);
3600 h = self.block_forward(
3601 st,
3602 &st.layers[lidx],
3603 &h,
3604 s,
3605 ids,
3606 capture.as_deref_mut(),
3607 layer_cache,
3608 )?;
3609 if early_exit_after == Some(il) {
3610 self.stages[cur_stage]
3611 .gpu
3612 .stream()
3613 .synchronize()
3614 .map_err(e("sync"))?;
3615 return Ok(None);
3616 }
3617 }
3618
3619 let last = self.stages.len() - 1;
3621 if cur_stage != last {
3622 let src_stream = self.stages[cur_stage].gpu.stream();
3623 let host = dtoh_f32(&src_stream, &h)?;
3624 let dst_stream = self.stages[last].gpu.stream();
3625 h = upload_f32(&dst_stream, &host)?;
3626 }
3627 let hc_head_fn = self.stages[last].hc_head_fn.as_ref().expect("hc_head_fn");
3628 let trunk_norm = self.stages[last].trunk_norm.as_ref().expect("trunk norm");
3629 let logits = self.head_logits_from(
3630 &h,
3631 s,
3632 hc_head_fn,
3633 &self.hc_head_base,
3634 &self.hc_head_scale,
3635 trunk_norm,
3636 )?;
3637 Ok(Some(ForwardOut { logits, h_last: h }))
3638 }
3639
3640 fn head_logits_from(
3644 &self,
3645 h: &CudaSlice<f32>,
3646 s: usize,
3647 fn_w: &CudaSlice<f32>,
3648 base: &[f32],
3649 scale: &[f32],
3650 norm: &CudaSlice<f32>,
3651 ) -> Res<Vec<f32>> {
3652 self.head_logits_row(h, s, s - 1, fn_w, base, scale, norm)
3653 }
3654
3655 #[allow(clippy::too_many_arguments)]
3659 fn head_logits_row(
3660 &self,
3661 h: &CudaSlice<f32>,
3662 s: usize,
3663 row: usize,
3664 fn_w: &CudaSlice<f32>,
3665 base: &[f32],
3666 scale: &[f32],
3667 norm: &CudaSlice<f32>,
3668 ) -> Res<Vec<f32>> {
3669 let d = self.model.cfg();
3670 let mc = &self.model.mc;
3671 let hc = d.hc_mult as usize;
3672 let hidden = mc.n_embd as usize;
3673 let eps = mc.rms_eps;
3674 let last = self.stages.len() - 1;
3675 let st = &self.stages[last];
3676 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx head"))?;
3677 let stream = st.gpu.stream();
3678 let w = hc * hidden;
3679 let mut mixes = stream.alloc_zeros::<f32>(s * hc).map_err(e("hm"))?;
3680 Self::dots(st, h, fn_w, s, w, hc, &mut mixes)?;
3681 unsafe {
3682 ck(
3683 "rowsq head",
3684 k::memra_dsv4_rowsq_scale(
3685 dpf!(h, &stream),
3686 dpm!(mixes, &stream),
3687 s as i32,
3688 w as i32,
3689 hc as i32,
3690 eps,
3691 sp(&stream),
3692 ),
3693 )?;
3694 }
3695 let mut mixes_h = dtoh_f32(&stream, &mixes)?;
3698 for t in 0..s {
3699 for c in 0..hc {
3700 let m = mixes_h[t * hc + c];
3701 mixes_h[t * hc + c] = sigmoid_f32(m * scale[0] + base[c]) + d.hc_eps;
3702 }
3703 }
3704 let pre_d = upload_f32(&stream, &mixes_h)?;
3705 let mut collapsed = stream.alloc_zeros::<f32>(s * hidden).map_err(e("col"))?;
3706 unsafe {
3707 ck(
3708 "hc_collapse head",
3709 k::memra_dsv4_hc_collapse(
3710 dpf!(h, &stream),
3711 dpf!(pre_d, &stream),
3712 dpm!(collapsed, &stream),
3713 s as i32,
3714 hc as i32,
3715 hidden as i32,
3716 sp(&stream),
3717 ),
3718 )?;
3719 ck(
3720 "rmsnorm head",
3721 k::memra_dsv4_rmsnorm(
3722 dpf!(collapsed, &stream),
3723 dpf!(norm, &stream),
3724 dpm!(collapsed, &stream),
3725 s as i32,
3726 hidden as i32,
3727 eps,
3728 sp(&stream),
3729 ),
3730 )?;
3731 }
3732 assert!(row < s, "logits row {row} out of range (s = {s})");
3734 let vocab = {
3735 let (info, _) = self.model.st.raw("head.weight").expect("head");
3736 info.shape[0] as usize
3737 };
3738 let last_row = collapsed.slice(row * hidden..(row + 1) * hidden);
3739 let mut logits = stream.alloc_zeros::<f32>(vocab).map_err(e("logits"))?;
3740 unsafe {
3741 ck(
3742 "head dots",
3743 k::memra_dsv4_dots_f32(
3744 last_row.device_ptr(&stream).0 as *const f32,
3745 st.head.as_ref().expect("head").device_ptr(&stream).0 as *const c_void,
3746 1,
3747 dpm!(logits, &stream),
3748 1,
3749 hidden as i32,
3750 vocab as i32,
3751 sp(&stream),
3752 ),
3753 )?;
3754 }
3755 dtoh_f32(&stream, &logits)
3756 }
3757
3758 pub fn mtp_logits_last(&self, h_trunk: &CudaSlice<f32>, ids: &[u32]) -> Res<Vec<f32>> {
3762 self.mtp_logits_last_cap(h_trunk, ids, None)
3763 }
3764
3765 pub fn mtp_logits_last_cap(
3768 &self,
3769 h_trunk: &CudaSlice<f32>,
3770 ids: &[u32],
3771 capture: Option<&mut GpuCapture>,
3772 ) -> Res<Vec<f32>> {
3773 let mtp = self.mtp.as_ref().expect("MTP not loaded");
3774 let d = self.model.cfg();
3775 let mc = &self.model.mc;
3776 let hc = d.hc_mult as usize;
3777 let hidden = mc.n_embd as usize;
3778 let eps = mc.rms_eps;
3779 let s = ids.len();
3780 let last = self.stages.len() - 1;
3781 let st = &self.stages[last];
3782 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx mtp"))?;
3783 let stream = st.gpu.stream();
3784
3785 let e_host = self.model.embed_rows(ids);
3788 let mut e_dev = upload_f32(&stream, &e_host)?;
3789 unsafe {
3790 ck(
3791 "rmsnorm enorm",
3792 k::memra_dsv4_rmsnorm(
3793 dpf!(e_dev, &stream),
3794 dpf!(mtp.enorm, &stream),
3795 dpm!(e_dev, &stream),
3796 s as i32,
3797 hidden as i32,
3798 eps,
3799 sp(&stream),
3800 ),
3801 )?;
3802 }
3803 let mut xh = stream
3805 .alloc_zeros::<f32>(s * hc * hidden)
3806 .map_err(e("mtp xh"))?;
3807 unsafe {
3808 ck(
3809 "rmsnorm hnorm",
3810 k::memra_dsv4_rmsnorm(
3811 dpf!(h_trunk, &stream),
3812 dpf!(mtp.hnorm, &stream),
3813 dpm!(xh, &stream),
3814 (s * hc) as i32,
3815 hidden as i32,
3816 eps,
3817 sp(&stream),
3818 ),
3819 )?;
3820 }
3821 let mut ep = stream.alloc_zeros::<f32>(s * hidden).map_err(e("mtp ep"))?;
3823 Self::gemm(st, &e_dev, &mtp.e_proj, 0, s, hidden, hidden, &mut ep)?;
3824 let mut hp = stream
3825 .alloc_zeros::<f32>(s * hc * hidden)
3826 .map_err(e("mtp hp"))?;
3827 Self::gemm(st, &xh, &mtp.h_proj, 0, s * hc, hidden, hidden, &mut hp)?;
3828 let mut xm = stream
3830 .alloc_zeros::<f32>(s * hc * hidden)
3831 .map_err(e("mtp xm"))?;
3832 unsafe {
3833 ck(
3834 "repeat ep",
3835 k::memra_dsv4_repeat_hc(
3836 dpf!(ep, &stream),
3837 dpm!(xm, &stream),
3838 s as i32,
3839 hc as i32,
3840 hidden as i32,
3841 sp(&stream),
3842 ),
3843 )?;
3844 ck(
3845 "add hp",
3846 k::memra_dsv4_add_inplace(
3847 dpm!(xm, &stream),
3848 dpf!(hp, &stream),
3849 (s * hc * hidden) as i64,
3850 sp(&stream),
3851 ),
3852 )?;
3853 }
3854 let xm = self.block_forward(st, &mtp.layer, &xm, s, ids, capture, None)?;
3855 self.head_logits_from(
3856 &xm,
3857 s,
3858 &mtp.hc_head_fn,
3859 &mtp.hc_head_base,
3860 &mtp.hc_head_scale,
3861 &mtp.norm,
3862 )
3863 }
3864
3865 pub fn trunk_logits_row(&self, h: &CudaSlice<f32>, s: usize, row: usize) -> Res<Vec<f32>> {
3867 let last = self.stages.len() - 1;
3868 let hc_head_fn = self.stages[last].hc_head_fn.as_ref().expect("hc_head_fn");
3869 let trunk_norm = self.stages[last].trunk_norm.as_ref().expect("trunk norm");
3870 self.head_logits_row(
3871 h,
3872 s,
3873 row,
3874 hc_head_fn,
3875 &self.hc_head_base,
3876 &self.hc_head_scale,
3877 trunk_norm,
3878 )
3879 }
3880
3881 pub fn alloc_decode_state(&self) -> Res<DecodeState> {
3887 let d = self.model.cfg();
3888 let mc = &self.model.mc;
3889 let win = d.sliding_window as usize;
3890 let hd = d.head_dim as usize;
3891 let n_trunk = mc.n_layer - mc.nextn_predict_layers;
3892 let mut caches = Vec::with_capacity(n_trunk as usize);
3893 let mut cache_bytes = vec![0u64; self.stages.len()];
3894 let trans_rows = self.verify_tmax();
3899 for il in 0..n_trunk {
3900 let stage_i = self.layer_stage[il as usize];
3901 let st = &self.stages[stage_i];
3902 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx cache"))?;
3903 let stream = st.gpu.stream();
3904 let lidx = st
3905 .layers
3906 .iter()
3907 .position(|l| l.il == il)
3908 .unwrap_or_else(|| panic!("layer {il} not on stage {stage_i}"));
3909 let layer = &st.layers[lidx];
3910 let ratio = layer.ratio;
3911 #[allow(clippy::manual_checked_ops)]
3912 let cap_blocks = if ratio != 0 { self.max_seq / ratio } else { 0 };
3914 let kvc_rows = win + cap_blocks + trans_rows;
3915 let mut bytes = (kvc_rows * hd * 4) as u64;
3916 let kvc = stream
3917 .alloc_zeros::<f32>(kvc_rows * hd)
3918 .map_err(e("kvc alloc"))?;
3919 let mk_pend = |latent: usize, slots: usize| -> Res<(CudaSlice<f32>, CudaSlice<f32>)> {
3921 let kv = stream
3922 .alloc_zeros::<f32>(slots * latent)
3923 .map_err(e("pend kv alloc"))?;
3924 let sc = upload_f32(&stream, &vec![f32::NEG_INFINITY; slots * latent])?;
3925 Ok((kv, sc))
3926 };
3927 let (pend_kv, pend_score) = if let Some(cmp) = &layer.cmp {
3928 let slots = if cmp.overlap {
3929 2 * cmp.ratio
3930 } else {
3931 cmp.ratio
3932 };
3933 bytes += (2 * slots * cmp.latent * 4) as u64;
3934 let (a, b) = mk_pend(cmp.latent, slots)?;
3935 (Some(a), Some(b))
3936 } else {
3937 (None, None)
3938 };
3939 let (ikvc, ipend_kv, ipend_score) = if let Some(ix) = &layer.idx {
3940 bytes += (cap_blocks * ix.cmp.d * 4) as u64;
3941 let store = stream
3942 .alloc_zeros::<f32>(cap_blocks * ix.cmp.d)
3943 .map_err(e("ikvc alloc"))?;
3944 let slots = if ix.cmp.overlap {
3945 2 * ix.cmp.ratio
3946 } else {
3947 ix.cmp.ratio
3948 };
3949 bytes += (2 * slots * ix.cmp.latent * 4) as u64;
3950 let (a, b) = mk_pend(ix.cmp.latent, slots)?;
3951 (Some(store), Some(a), Some(b))
3952 } else {
3953 (None, None, None)
3954 };
3955 cache_bytes[stage_i] += bytes;
3956 caches.push(LayerCache {
3957 kvc,
3958 n_blocks: 0,
3959 pend_kv,
3960 pend_score,
3961 ikvc,
3962 i_blocks: 0,
3963 ipend_kv,
3964 ipend_score,
3965 });
3966 }
3967 let ws = if matches!(self.decode_path, DecodePath::Device { .. }) {
3968 Some(self.alloc_step_ws()?)
3969 } else {
3970 None
3971 };
3972 for st in &self.stages {
3973 st.gpu.stream().synchronize().map_err(e("cache sync"))?;
3974 }
3975 Ok(DecodeState {
3976 caches,
3977 pos: 0,
3978 cache_bytes,
3979 ws,
3980 })
3981 }
3982
3983 fn alloc_step_ws(&self) -> Res<Vec<StepWs>> {
3985 let d = self.model.cfg();
3986 let mc = &self.model.mc;
3987 let moe = mc.moe.as_ref().expect("moe");
3988 let hc = d.hc_mult as usize;
3989 let hidden = mc.n_embd as usize;
3990 let heads = mc.n_head as usize;
3991 let hd = d.head_dim as usize;
3992 let q_lora = d.q_lora_rank as usize;
3993 let win = d.sliding_window as usize;
3994 let o_groups = d.o_groups as usize;
3995 let o_lora = d.o_lora_rank as usize;
3996 let iheads = d.index_n_heads as usize;
3997 let ihd = d.index_head_dim as usize;
3998 let topk = moe.expert_used_count as usize;
3999 let ne = moe.expert_count as usize;
4000 let inter = moe.expert_ff_length as usize;
4001 let itopk = d.index_topk as usize;
4002 let vocab = {
4003 let (info, _) = self.model.st.raw("head.weight").expect("head");
4004 info.shape[0] as usize
4005 };
4006 let sh_inter = {
4007 let (info, _) = self
4008 .model
4009 .st
4010 .raw("layers.0.ffn.shared_experts.w1.weight")
4011 .expect("shared w1");
4012 info.shape[0] as usize
4013 };
4014 let mut max_latent = 0usize;
4016 let mut max_d = 0usize;
4017 let mut max_shift = 0usize;
4018 let mut min_ratio = usize::MAX;
4019 for st in &self.stages {
4020 for l in &st.layers {
4021 for cmp in l.cmp.iter().chain(l.idx.as_ref().map(|ix| &ix.cmp)) {
4022 max_latent = max_latent.max(cmp.latent);
4023 max_d = max_d.max(cmp.d);
4024 if cmp.overlap {
4025 max_shift = max_shift.max(cmp.ratio * cmp.latent);
4026 }
4027 min_ratio = min_ratio.min(cmp.ratio);
4028 }
4029 }
4030 }
4031 assert!(min_ratio != usize::MAX, "no compressor layers?");
4032 let score_cap = self.max_seq / min_ratio + 1;
4033 let idx_tail = itopk.max(self.max_seq / 128 + 1);
4034 let max_gemm_k = (o_groups * o_lora).max(hidden).max(q_lora).max(sh_inter);
4037 let mut out = Vec::with_capacity(self.stages.len());
4038 for st in &self.stages {
4039 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx ws"))?;
4040 let s = st.gpu.stream();
4041 let f = |n: usize| s.alloc_zeros::<f32>(n).map_err(e("ws f32"));
4042 let b = |n: usize| s.alloc_zeros::<u8>(n).map_err(e("ws u8"));
4043 let i = |n: usize| s.alloc_zeros::<i32>(n).map_err(e("ws i32"));
4044 out.push(StepWs {
4045 h_a: f(hc * hidden)?,
4046 h_b: f(hc * hidden)?,
4047 h_rx: f(hc * hidden)?,
4048 emb: f(hidden)?,
4049 mixes: f((2 + hc) * hc)?,
4050 pre: f(hc)?,
4051 post: f(hc)?,
4052 comb: f(hc * hc)?,
4053 y_hc: f(hidden)?,
4054 x: f(hidden)?,
4055 xf: f(hidden)?,
4056 qr: f(q_lora)?,
4057 qr_b: b(q_lora * 2)?,
4058 q: f(heads * hd)?,
4059 kv: f(hd)?,
4060 qi: f(iheads * ihd)?,
4061 wproj: f(iheads)?,
4062 score: f(score_cap)?,
4063 idx: i(win + idx_tail)?,
4064 o: f(heads * hd)?,
4065 o_b: b(heads * hd * 2)?,
4066 og: f(o_groups * o_lora)?,
4067 attn_out: f(hidden)?,
4068 gemm_xb: b(max_gemm_k * 2)?,
4069 raw: f(ne)?,
4070 sel: i(topk)?,
4071 selw: f(topk)?,
4072 order: i(topk)?,
4073 xq: b(hidden)?,
4074 xs: f(hidden / 128)?,
4075 g1: f(topk * inter)?,
4076 g3: f(topk * inter)?,
4077 hbuf: f(topk * inter)?,
4078 hq: b(topk * inter)?,
4079 hs: f(topk * inter / 128)?,
4080 contrib: f(topk * hidden)?,
4081 y: f(hidden)?,
4082 xb: b(hidden * 2)?,
4083 sg1: f(sh_inter)?,
4084 sg3: f(sh_inter)?,
4085 shbuf: f(sh_inter)?,
4086 shb16: b(sh_inter * 2)?,
4087 sh_out: f(hidden)?,
4088 cmp_kv_row: f(max_latent)?,
4089 cmp_sc_row: f(max_latent)?,
4090 cmp_emit: f(2 * max_d)?,
4091 cmp_shift: f(max_shift.max(1))?,
4092 sink_scores: f(heads * (win + idx_tail))?,
4093 sink_evals: f(heads * (win + idx_tail))?,
4094 sink_den: s.alloc_zeros::<f64>(heads).map_err(e("ws f64"))?,
4095 head_mixes: f(hc)?,
4096 head_pre: f(hc)?,
4097 collapsed: f(hidden)?,
4098 logits: f(vocab)?,
4099 argmax: i(1)?,
4100 tok: i(1)?,
4101 });
4102 }
4103 Ok(out)
4104 }
4105
4106 #[allow(clippy::too_many_arguments)]
4114 #[allow(clippy::manual_is_multiple_of)] fn cmp_decode(
4116 &self,
4117 st: &Stage,
4118 cmp: &CmpDev,
4119 x: &CudaSlice<f32>, pos: usize,
4121 hidden: usize,
4122 fc_dev: &CudaSlice<f32>,
4123 rd: usize,
4124 eps: f32,
4125 pend_kv: &mut CudaSlice<f32>,
4126 pend_score: &mut CudaSlice<f32>,
4127 store: &mut CudaSlice<f32>,
4128 row0: usize,
4129 blocks: &mut usize,
4130 ) -> Res<()> {
4131 let stream = st.gpu.stream();
4132 let (ratio, d, latent) = (cmp.ratio, cmp.d, cmp.latent);
4133 let mut kv_row = stream.alloc_zeros::<f32>(latent).map_err(e("dkv"))?;
4134 let mut sc_row = stream.alloc_zeros::<f32>(latent).map_err(e("dsc"))?;
4135 Self::dots(st, x, &cmp.wkv, 1, hidden, latent, &mut kv_row)?;
4136 Self::dots(st, x, &cmp.wgate, 1, hidden, latent, &mut sc_row)?;
4137 let slot = if cmp.overlap {
4138 ratio + pos % ratio
4139 } else {
4140 pos % ratio
4141 };
4142 {
4143 let src = kv_row.slice(0..latent);
4144 let mut dst = pend_kv.slice_mut(slot * latent..(slot + 1) * latent);
4145 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend kv"))?;
4146 let src = sc_row.slice(0..latent);
4147 let mut dst = pend_score.slice_mut(slot * latent..(slot + 1) * latent);
4148 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend sc"))?;
4149 }
4150 if (pos + 1) % ratio != 0 {
4151 return Ok(());
4152 }
4153 let j = pos / ratio;
4154 let nb_launch = if cmp.overlap { 2usize } else { 1 };
4155 let row_off = if cmp.overlap { d } else { 0 };
4156 let mut out = stream
4157 .alloc_zeros::<f32>(nb_launch * d)
4158 .map_err(e("emit"))?;
4159 unsafe {
4160 ck(
4161 "compressor_pool dec",
4162 k::memra_dsv4_compressor_pool(
4163 dpf!(pend_kv, &stream),
4164 dpf!(pend_score, &stream),
4165 dpf!(cmp.ape, &stream),
4166 dpm!(out, &stream),
4167 nb_launch as i32,
4168 ratio as i32,
4169 d as i32,
4170 latent as i32,
4171 cmp.overlap as i32,
4172 sp(&stream),
4173 ),
4174 )?;
4175 let row_c = (out.device_ptr(&stream).0 as usize + row_off * 4) as *const f32;
4177 let row_m = (out.device_ptr_mut(&stream).0 as usize + row_off * 4) as *mut f32;
4178 ck(
4179 "rmsnorm dec cmp",
4180 k::memra_dsv4_rmsnorm(
4181 row_c,
4182 dpf!(cmp.norm, &stream),
4183 row_m,
4184 1,
4185 d as i32,
4186 eps,
4187 sp(&stream),
4188 ),
4189 )?;
4190 let pos_dev = upload_i32(&stream, &[(j * ratio) as i32])?;
4191 ck(
4192 "rope dec cmp",
4193 k::memra_dsv4_rope(
4194 row_m,
4195 1,
4196 1,
4197 d as i32,
4198 rd as i32,
4199 dpf!(fc_dev, &stream),
4200 pos_dev.device_ptr(&stream).0 as *const i32,
4201 0,
4202 sp(&stream),
4203 ),
4204 )?;
4205 if cmp.rotate {
4206 let scale = (d as f32).powf(-0.5);
4207 ck(
4208 "hadamard dec cmp",
4209 k::memra_dsv4_hadamard(row_m, 1, d as i32, scale, sp(&stream)),
4210 )?;
4211 ck(
4212 "fp4 dec cmp",
4213 k::memra_dsv4_fp4_act_quant(row_m, 1, d as i64, d as i32, sp(&stream)),
4214 )?;
4215 } else {
4216 ck(
4217 "act_quant dec cmp",
4218 k::memra_dsv4_act_quant(
4219 row_m,
4220 1,
4221 d as i64,
4222 (d - rd) as i32,
4223 64,
4224 (self.variant == ActQuantVariant::ClampOnly) as i32,
4225 sp(&stream),
4226 ),
4227 )?;
4228 }
4229 }
4230 {
4231 let src = out.slice(row_off..row_off + d);
4232 let mut dst = store.slice_mut((row0 + j) * d..(row0 + j + 1) * d);
4233 stream
4234 .memcpy_dtod(&src, &mut dst)
4235 .map_err(e("emit store"))?;
4236 }
4237 if cmp.overlap {
4238 let mut tmp = stream
4240 .alloc_zeros::<f32>(ratio * latent)
4241 .map_err(e("shift tmp"))?;
4242 {
4243 let src = pend_kv.slice(ratio * latent..2 * ratio * latent);
4244 stream.memcpy_dtod(&src, &mut tmp).map_err(e("shift1"))?;
4245 }
4246 {
4247 let mut dst = pend_kv.slice_mut(0..ratio * latent);
4248 stream
4249 .memcpy_dtod(&tmp.slice(0..ratio * latent), &mut dst)
4250 .map_err(e("shift2"))?;
4251 }
4252 {
4253 let src = pend_score.slice(ratio * latent..2 * ratio * latent);
4254 stream.memcpy_dtod(&src, &mut tmp).map_err(e("shift3"))?;
4255 }
4256 {
4257 let mut dst = pend_score.slice_mut(0..ratio * latent);
4258 stream
4259 .memcpy_dtod(&tmp.slice(0..ratio * latent), &mut dst)
4260 .map_err(e("shift4"))?;
4261 }
4262 }
4263 *blocks = j + 1;
4264 Ok(())
4265 }
4266
4267 #[allow(clippy::too_many_arguments)]
4273 #[allow(clippy::manual_checked_ops)] fn block_decode(
4275 &self,
4276 st: &Stage,
4277 layer: &LayerDev,
4278 cache: &mut LayerCache,
4279 h: &CudaSlice<f32>,
4280 pos: usize,
4281 tok: u32,
4282 mut dump: Option<&mut Vec<(String, Vec<f32>)>>,
4283 ) -> Res<CudaSlice<f32>> {
4284 let d = self.model.cfg();
4285 let mc = &self.model.mc;
4286 let hc = d.hc_mult as usize;
4287 let hidden = mc.n_embd as usize;
4288 let heads = mc.n_head as usize;
4289 let hd = d.head_dim as usize;
4290 let rd = d.qk_rope_head_dim as usize;
4291 let q_lora = d.q_lora_rank as usize;
4292 let win = d.sliding_window as usize;
4293 let o_groups = d.o_groups as usize;
4294 let o_lora = d.o_lora_rank as usize;
4295 let eps = mc.rms_eps;
4296 let iters = d.hc_sinkhorn_iters;
4297 let hc_eps = d.hc_eps;
4298 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx"))?;
4299 let stream = st.gpu.stream();
4300 let fc_dev = if layer.ratio != 0 {
4301 &st.fc_yarn
4302 } else {
4303 &st.fc_plain
4304 };
4305 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
4306 let LayerCache {
4307 kvc,
4308 n_blocks,
4309 pend_kv,
4310 pend_score,
4311 ikvc,
4312 i_blocks,
4313 ipend_kv,
4314 ipend_score,
4315 } = cache;
4316
4317 let (y, post, comb) = Self::hc_pre(
4319 st,
4320 h,
4321 &layer.hc_attn_fn,
4322 &layer.hc_attn_base,
4323 &layer.hc_attn_scale,
4324 1,
4325 hc,
4326 hidden,
4327 iters,
4328 hc_eps,
4329 )?;
4330 let mut x = stream.alloc_zeros::<f32>(hidden).map_err(e("x"))?;
4331 unsafe {
4332 ck(
4333 "rmsnorm attn",
4334 k::memra_dsv4_rmsnorm(
4335 dpf!(y, &stream),
4336 dpf!(layer.attn_norm, &stream),
4337 dpm!(x, &stream),
4338 1,
4339 hidden as i32,
4340 eps,
4341 sp(&stream),
4342 ),
4343 )?;
4344 }
4345 if let Some(dm) = dump.as_deref_mut() {
4346 dm.push((format!("layer{}.x", layer.il), dtoh_f32(&stream, &x)?));
4347 }
4348
4349 let mut qr = stream.alloc_zeros::<f32>(q_lora).map_err(e("qr"))?;
4352 Self::gemm(st, &x, layer.wq_a.dev(), 0, 1, q_lora, hidden, &mut qr)?;
4353 unsafe {
4354 ck(
4355 "rmsnorm q",
4356 k::memra_dsv4_rmsnorm(
4357 dpf!(qr, &stream),
4358 dpf!(layer.q_norm, &stream),
4359 dpm!(qr, &stream),
4360 1,
4361 q_lora as i32,
4362 eps,
4363 sp(&stream),
4364 ),
4365 )?;
4366 }
4367 let mut qr_b = stream.alloc_zeros::<u8>(q_lora * 2).map_err(e("qr_b"))?;
4368 unsafe {
4369 ck(
4370 "cvt qr",
4371 k::memra_dsv4_cvt_bf16(
4372 dpf!(qr, &stream),
4373 qr_b.device_ptr_mut(&stream).0 as *mut c_void,
4374 q_lora as i64,
4375 sp(&stream),
4376 ),
4377 )?;
4378 }
4379 let mut q = stream.alloc_zeros::<f32>(heads * hd).map_err(e("q"))?;
4380 Self::gemm_pre(
4381 st,
4382 &qr_b,
4383 layer.wq_b.dev().device_ptr(&stream).0 as *const c_void,
4384 1,
4385 heads * hd,
4386 q_lora,
4387 &mut q,
4388 )?;
4389 let pos_dev = upload_i32(&stream, &[pos as i32])?;
4390 unsafe {
4391 ck(
4392 "headrms",
4393 k::memra_dsv4_headrms(dpm!(q, &stream), heads as i32, hd as i32, eps, sp(&stream)),
4394 )?;
4395 ck(
4396 "rope q",
4397 k::memra_dsv4_rope(
4398 dpm!(q, &stream),
4399 1,
4400 heads as i32,
4401 hd as i32,
4402 rd as i32,
4403 dpf!(fc_dev, &stream),
4404 pos_dev.device_ptr(&stream).0 as *const i32,
4405 0,
4406 sp(&stream),
4407 ),
4408 )?;
4409 }
4410
4411 if let Some(dm) = dump.as_deref_mut() {
4412 dm.push((format!("layer{}.q", layer.il), dtoh_f32(&stream, &q)?));
4413 }
4414 let mut kv = stream.alloc_zeros::<f32>(hd).map_err(e("kv"))?;
4416 Self::gemm(st, &x, layer.wkv.dev(), 0, 1, hd, hidden, &mut kv)?;
4417 unsafe {
4418 ck(
4419 "rmsnorm kv",
4420 k::memra_dsv4_rmsnorm(
4421 dpf!(kv, &stream),
4422 dpf!(layer.kv_norm, &stream),
4423 dpm!(kv, &stream),
4424 1,
4425 hd as i32,
4426 eps,
4427 sp(&stream),
4428 ),
4429 )?;
4430 ck(
4431 "rope kv",
4432 k::memra_dsv4_rope(
4433 dpm!(kv, &stream),
4434 1,
4435 1,
4436 hd as i32,
4437 rd as i32,
4438 dpf!(fc_dev, &stream),
4439 pos_dev.device_ptr(&stream).0 as *const i32,
4440 0,
4441 sp(&stream),
4442 ),
4443 )?;
4444 ck(
4445 "act_quant kv",
4446 k::memra_dsv4_act_quant(
4447 dpm!(kv, &stream),
4448 1,
4449 hd as i64,
4450 (hd - rd) as i32,
4451 64,
4452 clamp_only,
4453 sp(&stream),
4454 ),
4455 )?;
4456 }
4457 {
4458 let slot = pos % win;
4459 let src = kv.slice(0..hd);
4460 let mut dst = kvc.slice_mut(slot * hd..(slot + 1) * hd);
4461 stream
4462 .memcpy_dtod(&src, &mut dst)
4463 .map_err(e("ring write"))?;
4464 }
4465 if let Some(dm) = dump.as_deref_mut() {
4466 dm.push((format!("layer{}.kv", layer.il), dtoh_f32(&stream, &kv)?));
4467 }
4468
4469 let mut idxs: Vec<i64> = vec![-1; win];
4471 if pos >= win - 1 {
4472 let sp_ = pos % win;
4473 let mut k_ = 0usize;
4474 for s_ in (sp_ + 1)..win {
4475 idxs[k_] = s_ as i64;
4476 k_ += 1;
4477 }
4478 for s_ in 0..=sp_ {
4479 idxs[k_] = s_ as i64;
4480 k_ += 1;
4481 }
4482 } else {
4483 for (p, v) in idxs.iter_mut().enumerate().take(pos + 1) {
4484 *v = p as i64;
4485 }
4486 }
4487
4488 if layer.ratio != 0 {
4489 let cidx: Vec<i64> = if let Some(ix) = &layer.idx {
4490 let mut qi = stream
4492 .alloc_zeros::<f32>(ix.heads * ix.hd)
4493 .map_err(e("qi"))?;
4494 Self::gemm_pre(
4495 st,
4496 &qr_b,
4497 ix.wq_b.dev().device_ptr(&stream).0 as *const c_void,
4498 1,
4499 ix.heads * ix.hd,
4500 q_lora,
4501 &mut qi,
4502 )?;
4503 unsafe {
4504 ck(
4505 "rope qi",
4506 k::memra_dsv4_rope(
4507 dpm!(qi, &stream),
4508 1,
4509 ix.heads as i32,
4510 ix.hd as i32,
4511 rd as i32,
4512 dpf!(fc_dev, &stream),
4513 pos_dev.device_ptr(&stream).0 as *const i32,
4514 0,
4515 sp(&stream),
4516 ),
4517 )?;
4518 let scale = (ix.hd as f32).powf(-0.5);
4519 ck(
4520 "hadamard qi",
4521 k::memra_dsv4_hadamard(
4522 dpm!(qi, &stream),
4523 ix.heads as i32,
4524 ix.hd as i32,
4525 scale,
4526 sp(&stream),
4527 ),
4528 )?;
4529 ck(
4530 "fp4 qi",
4531 k::memra_dsv4_fp4_act_quant(
4532 dpm!(qi, &stream),
4533 ix.heads as i32,
4534 ix.hd as i64,
4535 ix.hd as i32,
4536 sp(&stream),
4537 ),
4538 )?;
4539 }
4540 self.cmp_decode(
4542 st,
4543 &ix.cmp,
4544 &x,
4545 pos,
4546 hidden,
4547 fc_dev,
4548 rd,
4549 eps,
4550 ipend_kv.as_mut().expect("ipend"),
4551 ipend_score.as_mut().expect("ipend"),
4552 ikvc.as_mut().expect("ikvc"),
4553 0,
4554 i_blocks,
4555 )?;
4556 let nb = *i_blocks;
4557 debug_assert_eq!(nb, (pos + 1) / layer.ratio, "indexer block count");
4558 if nb > 0 {
4559 let mut wproj = stream.alloc_zeros::<f32>(ix.heads).map_err(e("wp"))?;
4560 Self::gemm(
4561 st,
4562 &x,
4563 ix.weights_proj.dev(),
4564 0,
4565 1,
4566 ix.heads,
4567 hidden,
4568 &mut wproj,
4569 )?;
4570 let wscale = ((ix.hd as f64).powf(-0.5) * (ix.heads as f64).powf(-0.5)) as f32;
4571 let mut score = stream.alloc_zeros::<f32>(nb).map_err(e("iscore"))?;
4572 unsafe {
4573 ck(
4574 "indexer_score dec",
4575 k::memra_dsv4_indexer_score(
4576 dpf!(qi, &stream),
4577 dpf!(ikvc.as_ref().expect("ikvc"), &stream),
4578 dpf!(wproj, &stream),
4579 wscale,
4580 dpm!(score, &stream),
4581 1,
4582 ix.heads as i32,
4583 ix.hd as i32,
4584 nb as i32,
4585 layer.ratio as i32,
4586 nb as i32, sp(&stream),
4588 ),
4589 )?;
4590 }
4591 let score_h = dtoh_f32(&stream, &score)?;
4592 let kk = ix.topk.min(nb);
4594 let mut order: Vec<usize> = (0..nb).collect();
4595 order.sort_by(|&a, &b| {
4596 score_h[b]
4597 .partial_cmp(&score_h[a])
4598 .unwrap_or(std::cmp::Ordering::Equal)
4599 .then(a.cmp(&b))
4600 });
4601 order
4602 .into_iter()
4603 .take(kk)
4604 .map(|j| (j + win) as i64)
4605 .collect()
4606 } else {
4607 Vec::new()
4608 }
4609 } else {
4610 let nb = (pos + 1) / layer.ratio;
4612 (0..nb).map(|j| (j + win) as i64).collect()
4613 };
4614 self.cmp_decode(
4616 st,
4617 layer.cmp.as_ref().expect("ratio!=0 has compressor"),
4618 &x,
4619 pos,
4620 hidden,
4621 fc_dev,
4622 rd,
4623 eps,
4624 pend_kv.as_mut().expect("pend"),
4625 pend_score.as_mut().expect("pend"),
4626 kvc,
4627 win,
4628 n_blocks,
4629 )?;
4630 debug_assert_eq!(*n_blocks, (pos + 1) / layer.ratio, "attn block count");
4631 idxs.extend_from_slice(&cidx);
4632 }
4633 let slots = idxs.len();
4634 let idxs_i32: Vec<i32> = idxs.iter().map(|&v| v as i32).collect();
4635 let idx_dev = upload_i32(&stream, &idxs_i32)?;
4636
4637 let mut o = stream.alloc_zeros::<f32>(heads * hd).map_err(e("o"))?;
4639 let scale = (hd as f64).powf(-0.5) as f32;
4640 unsafe {
4641 ck(
4642 "sink_attn dec",
4643 k::memra_dsv4_sink_attn(
4644 dpf!(q, &stream),
4645 dpf!(kvc, &stream),
4646 idx_dev.device_ptr(&stream).0 as *const i32,
4647 dpf!(layer.sink, &stream),
4648 dpm!(o, &stream),
4649 1,
4650 heads as i32,
4651 hd as i32,
4652 slots as i32,
4653 scale,
4654 sp(&stream),
4655 ),
4656 )?;
4657 ck(
4658 "rope o inv",
4659 k::memra_dsv4_rope(
4660 dpm!(o, &stream),
4661 1,
4662 heads as i32,
4663 hd as i32,
4664 rd as i32,
4665 dpf!(fc_dev, &stream),
4666 pos_dev.device_ptr(&stream).0 as *const i32,
4667 1,
4668 sp(&stream),
4669 ),
4670 )?;
4671 }
4672
4673 if let Some(dm) = dump.as_deref_mut() {
4674 dm.push((format!("layer{}.o", layer.il), dtoh_f32(&stream, &o)?));
4675 }
4676 let gw = heads / o_groups * hd;
4678 let mut og = stream
4679 .alloc_zeros::<f32>(o_groups * o_lora)
4680 .map_err(e("og"))?;
4681 let mut o_grp = stream.alloc_zeros::<f32>(gw).map_err(e("o_grp"))?;
4682 let mut y_grp = stream.alloc_zeros::<f32>(o_lora).map_err(e("y_grp"))?;
4683 for g in 0..o_groups {
4684 unsafe {
4685 ck(
4686 "take_cols",
4687 k::memra_dsv4_take_cols(
4688 dpf!(o, &stream),
4689 dpm!(o_grp, &stream),
4690 1,
4691 gw as i32,
4692 (heads * hd) as i64,
4693 (g * gw) as i64,
4694 sp(&stream),
4695 ),
4696 )?;
4697 }
4698 Self::gemm(
4699 st,
4700 &o_grp,
4701 layer.wo_a.dev(),
4702 g * o_lora * gw,
4703 1,
4704 o_lora,
4705 gw,
4706 &mut y_grp,
4707 )?;
4708 unsafe {
4709 ck(
4710 "place_cols",
4711 k::memra_dsv4_place_cols(
4712 dpf!(y_grp, &stream),
4713 dpm!(og, &stream),
4714 1,
4715 o_lora as i32,
4716 (o_groups * o_lora) as i64,
4717 (g * o_lora) as i64,
4718 sp(&stream),
4719 ),
4720 )?;
4721 }
4722 }
4723 let mut attn_out = stream.alloc_zeros::<f32>(hidden).map_err(e("ao"))?;
4724 Self::gemm(
4725 st,
4726 &og,
4727 layer.wo_b.dev(),
4728 0,
4729 1,
4730 hidden,
4731 o_groups * o_lora,
4732 &mut attn_out,
4733 )?;
4734
4735 if let Some(dm) = dump.as_deref_mut() {
4736 dm.push((
4737 format!("layer{}.attn_out", layer.il),
4738 dtoh_f32(&stream, &attn_out)?,
4739 ));
4740 }
4741 let mut h2 = stream.alloc_zeros::<f32>(hc * hidden).map_err(e("h2"))?;
4743 unsafe {
4744 ck(
4745 "hc_post attn",
4746 k::memra_dsv4_hc_post(
4747 dpf!(attn_out, &stream),
4748 dpf!(h, &stream),
4749 dpf!(post, &stream),
4750 dpf!(comb, &stream),
4751 dpm!(h2, &stream),
4752 1,
4753 hc as i32,
4754 hidden as i32,
4755 sp(&stream),
4756 ),
4757 )?;
4758 }
4759
4760 let (y2, post2, comb2) = Self::hc_pre(
4762 st,
4763 &h2,
4764 &layer.hc_ffn_fn,
4765 &layer.hc_ffn_base,
4766 &layer.hc_ffn_scale,
4767 1,
4768 hc,
4769 hidden,
4770 iters,
4771 hc_eps,
4772 )?;
4773 let mut xf = stream.alloc_zeros::<f32>(hidden).map_err(e("xf"))?;
4774 unsafe {
4775 ck(
4776 "rmsnorm ffn",
4777 k::memra_dsv4_rmsnorm(
4778 dpf!(y2, &stream),
4779 dpf!(layer.ffn_norm, &stream),
4780 dpm!(xf, &stream),
4781 1,
4782 hidden as i32,
4783 eps,
4784 sp(&stream),
4785 ),
4786 )?;
4787 }
4788 let moe_out = self.moe_forward(st, layer, &xf, 1, &[tok])?;
4789 if let Some(dm) = dump.as_deref_mut() {
4790 dm.push((
4791 format!("layer{}.moe_out", layer.il),
4792 dtoh_f32(&stream, &moe_out)?,
4793 ));
4794 }
4795 let mut h3 = stream.alloc_zeros::<f32>(hc * hidden).map_err(e("h3"))?;
4796 unsafe {
4797 ck(
4798 "hc_post ffn",
4799 k::memra_dsv4_hc_post(
4800 dpf!(moe_out, &stream),
4801 dpf!(h2, &stream),
4802 dpf!(post2, &stream),
4803 dpf!(comb2, &stream),
4804 dpm!(h3, &stream),
4805 1,
4806 hc as i32,
4807 hidden as i32,
4808 sp(&stream),
4809 ),
4810 )?;
4811 }
4812 if let Some(dm) = dump {
4813 dm.push((format!("layer{}.h3", layer.il), dtoh_f32(&stream, &h3)?));
4814 }
4815 Ok(h3)
4816 }
4817
4818 pub fn decode_step(&self, tok: u32, state: &mut DecodeState) -> Res<Vec<f32>> {
4823 self.decode_step_impl(tok, state, None)
4824 }
4825
4826 #[allow(clippy::type_complexity)] pub fn decode_step_probe(
4829 &self,
4830 tok: u32,
4831 state: &mut DecodeState,
4832 ) -> Res<(Vec<f32>, Vec<(String, Vec<f32>)>)> {
4833 let mut dump = Vec::new();
4834 let logits = self.decode_step_impl(tok, state, Some(&mut dump))?;
4835 Ok((logits, dump))
4836 }
4837
4838 #[allow(clippy::too_many_arguments)]
4844 fn gemm_dev(
4845 st: &Stage,
4846 x_f32: *const f32,
4847 xb: &mut CudaSlice<u8>,
4848 w: DW,
4849 m: usize,
4850 n: usize,
4851 kdim: usize,
4852 y_ptr: *mut f32,
4853 ) -> Res<()> {
4854 assert_eq!(m, 1, "gemm_dev is the m=1 decode path");
4855 let stream = st.gpu.stream();
4856 unsafe {
4857 ck(
4858 "cvt_bf16 dev",
4859 k::memra_dsv4_cvt_bf16(
4860 x_f32,
4861 xb.device_ptr_mut(&stream).0 as *mut c_void,
4862 kdim as i64,
4863 sp(&stream),
4864 ),
4865 )?;
4866 }
4867 let xb_ptr = xb.device_ptr(&stream).0 as *const c_void;
4868 Self::gemv_pre_dev(st, xb_ptr, w, n, kdim, y_ptr)
4869 }
4870
4871 fn gemv_pre_dev(
4875 st: &Stage,
4876 xb_ptr: *const c_void,
4877 w: DW,
4878 n: usize,
4879 kdim: usize,
4880 y_ptr: *mut f32,
4881 ) -> Res<()> {
4882 let stream = st.gpu.stream();
4883 unsafe {
4884 match w {
4885 DW::Bf16(w_ptr) => ck(
4886 "gemv_bf16 pre dev",
4887 k::memra_dsv4_gemv_bf16(
4888 w_ptr,
4889 xb_ptr,
4890 y_ptr,
4891 n as i32,
4892 kdim as i32,
4893 sp(&stream),
4894 ),
4895 )?,
4896 DW::Fp8 {
4897 codes,
4898 scales,
4899 sc_cols,
4900 } => ck(
4901 "gemv_fp8 pre dev",
4902 k::memra_dsv4_gemv_fp8(
4903 codes,
4904 scales,
4905 sc_cols,
4906 xb_ptr,
4907 y_ptr,
4908 n as i32,
4909 kdim as i32,
4910 sp(&stream),
4911 ),
4912 )?,
4913 }
4914 }
4915 Ok(())
4916 }
4917
4918 #[allow(clippy::too_many_arguments)]
4923 #[allow(clippy::too_many_arguments)]
4928 unsafe fn rmsnorm_arm(
4929 &self,
4930 x: *const f32,
4931 w: *const f32,
4932 dst: *mut f32,
4933 rows: i32,
4934 ncols: i32,
4935 eps: f32,
4936 sv: *mut c_void,
4937 ) -> i32 {
4938 unsafe {
4939 if self.chains_f32 {
4940 k::memra_dsv4_rmsnorm_f32acc(x, w, dst, rows, ncols, eps, sv)
4941 } else {
4942 k::memra_dsv4_rmsnorm(x, w, dst, rows, ncols, eps, sv)
4943 }
4944 }
4945 }
4946
4947 unsafe fn headrms_arm(&self, x: *mut f32, rows: i32, d: i32, eps: f32, sv: *mut c_void) -> i32 {
4948 unsafe {
4949 if self.chains_f32 {
4950 k::memra_dsv4_headrms_f32acc(x, rows, d, eps, sv)
4951 } else {
4952 k::memra_dsv4_headrms(x, rows, d, eps, sv)
4953 }
4954 }
4955 }
4956
4957 #[allow(clippy::too_many_arguments)]
4958 unsafe fn rowsq_scale_arm(
4959 &self,
4960 x: *const f32,
4961 mixes: *mut f32,
4962 s: i32,
4963 w: i32,
4964 rows: i32,
4965 eps: f32,
4966 sv: *mut c_void,
4967 ) -> i32 {
4968 unsafe {
4969 if self.chains_f32 {
4970 k::memra_dsv4_rowsq_scale_f32acc(x, mixes, s, w, rows, eps, sv)
4971 } else {
4972 k::memra_dsv4_rowsq_scale(x, mixes, s, w, rows, eps, sv)
4973 }
4974 }
4975 }
4976
4977 #[allow(clippy::too_many_arguments)]
4978 unsafe fn indexer_score_arm(
4979 &self,
4980 q: *const f32,
4981 ckv: *const f32,
4982 w: *const f32,
4983 wscale: f32,
4984 score: *mut f32,
4985 s: i32,
4986 heads: i32,
4987 hd: i32,
4988 nb: i32,
4989 ratio: i32,
4990 lim0: i32,
4991 sv: *mut c_void,
4992 ) -> i32 {
4993 unsafe {
4994 if self.chains_f32 {
4995 k::memra_dsv4_indexer_score_f32acc(
4996 q, ckv, w, wscale, score, s, heads, hd, nb, ratio, lim0, sv,
4997 )
4998 } else {
4999 k::memra_dsv4_indexer_score(
5000 q, ckv, w, wscale, score, s, heads, hd, nb, ratio, lim0, sv,
5001 )
5002 }
5003 }
5004 }
5005
5006 #[allow(clippy::too_many_arguments)]
5009 unsafe fn sink_attn_dec_arm(
5010 &self,
5011 q: *const f32,
5012 kv: *const f32,
5013 idxs: *const i32,
5014 sink: *const f32,
5015 scores: *mut f32,
5016 evals: *mut f32,
5017 den: *mut f64,
5018 o: *mut f32,
5019 heads: i32,
5020 hd: i32,
5021 slots: i32,
5022 scale: f32,
5023 sv: *mut c_void,
5024 ) -> i32 {
5025 unsafe {
5026 if self.chains_f32 {
5027 k::memra_dsv4_sink_attn_dec_f32acc(
5028 q,
5029 kv,
5030 idxs,
5031 sink,
5032 scores,
5033 evals,
5034 den as *mut f32,
5035 o,
5036 heads,
5037 hd,
5038 slots,
5039 scale,
5040 sv,
5041 )
5042 } else {
5043 k::memra_dsv4_sink_attn_dec(
5044 q, kv, idxs, sink, scores, evals, den, o, heads, hd, slots, scale, sv,
5045 )
5046 }
5047 }
5048 }
5049
5050 #[allow(clippy::too_many_arguments)] fn hc_pre_dev(
5052 &self,
5053 st: &Stage,
5054 h: &CudaSlice<f32>,
5055 fn_w: &CudaSlice<f32>,
5056 base_host: &[f32],
5057 scale_host: &[f32],
5058 base_dev: &CudaSlice<f32>,
5059 scale_dev: &CudaSlice<f32>,
5060 mixes: &mut CudaSlice<f32>,
5061 pre: &mut CudaSlice<f32>,
5062 post: &mut CudaSlice<f32>,
5063 comb: &mut CudaSlice<f32>,
5064 y_hc: &mut CudaSlice<f32>,
5065 hc: usize,
5066 hidden: usize,
5067 iters: u32,
5068 hc_eps: f32,
5069 host_math: bool,
5070 ) -> Res<()> {
5071 let stream = st.gpu.stream();
5072 let w = hc * hidden;
5073 let rows = (2 + hc) * hc;
5074 self.dots_dev(st, h, fn_w, 1, w, rows, mixes)?;
5075 unsafe {
5076 ck(
5077 "rowsq_scale dev",
5078 self.rowsq_scale_arm(
5079 dpf!(h, &stream),
5080 dpm!(*mixes, &stream),
5081 1,
5082 w as i32,
5083 rows as i32,
5084 hc_eps,
5085 sp(&stream),
5086 ),
5087 )?;
5088 }
5089 if host_math {
5090 let mixes_h = dtoh_f32(&stream, mixes)?;
5091 let (pre_h, post_h, comb_h) =
5092 hc_split_sinkhorn(&mixes_h, 1, hc, scale_host, base_host, iters, hc_eps);
5093 stream.memcpy_htod(&pre_h, pre).map_err(e("htod pre"))?;
5094 stream.memcpy_htod(&post_h, post).map_err(e("htod post"))?;
5095 stream.memcpy_htod(&comb_h, comb).map_err(e("htod comb"))?;
5096 } else {
5097 unsafe {
5098 ck(
5099 "hc_sinkhorn",
5100 k::memra_dsv4_hc_sinkhorn(
5101 dpf!(*mixes, &stream),
5102 dpf!(scale_dev, &stream),
5103 dpf!(base_dev, &stream),
5104 dpm!(*pre, &stream),
5105 dpm!(*post, &stream),
5106 dpm!(*comb, &stream),
5107 hc as i32,
5108 iters as i32,
5109 hc_eps,
5110 sp(&stream),
5111 ),
5112 )?;
5113 }
5114 }
5115 unsafe {
5116 ck(
5117 "hc_collapse dev",
5118 k::memra_dsv4_hc_collapse(
5119 dpf!(h, &stream),
5120 dpf!(*pre, &stream),
5121 dpm!(*y_hc, &stream),
5122 1,
5123 hc as i32,
5124 hidden as i32,
5125 sp(&stream),
5126 ),
5127 )?;
5128 }
5129 Ok(())
5130 }
5131
5132 #[allow(clippy::too_many_arguments)]
5136 #[allow(clippy::manual_is_multiple_of)] fn cmp_decode_dev(
5138 &self,
5139 st: &Stage,
5140 cmp: &CmpDev,
5141 x: &CudaSlice<f32>,
5142 pos: usize,
5143 hidden: usize,
5144 fc_dev: &CudaSlice<f32>,
5145 rd: usize,
5146 eps: f32,
5147 kv_row: &mut CudaSlice<f32>,
5148 sc_row: &mut CudaSlice<f32>,
5149 emit: &mut CudaSlice<f32>,
5150 shift: &mut CudaSlice<f32>,
5151 pend_kv: &mut CudaSlice<f32>,
5152 pend_score: &mut CudaSlice<f32>,
5153 store: &mut CudaSlice<f32>,
5154 row0: usize,
5155 blocks: &mut usize,
5156 ) -> Res<()> {
5157 let stream = st.gpu.stream();
5158 let (ratio, d, latent) = (cmp.ratio, cmp.d, cmp.latent);
5159 self.dots_dev(st, x, &cmp.wkv, 1, hidden, latent, kv_row)?;
5160 self.dots_dev(st, x, &cmp.wgate, 1, hidden, latent, sc_row)?;
5161 let slot = if cmp.overlap {
5162 ratio + pos % ratio
5163 } else {
5164 pos % ratio
5165 };
5166 {
5167 let src = kv_row.slice(0..latent);
5168 let mut dst = pend_kv.slice_mut(slot * latent..(slot + 1) * latent);
5169 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend kv"))?;
5170 let src = sc_row.slice(0..latent);
5171 let mut dst = pend_score.slice_mut(slot * latent..(slot + 1) * latent);
5172 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend sc"))?;
5173 }
5174 if (pos + 1) % ratio != 0 {
5175 return Ok(());
5176 }
5177 let j = pos / ratio;
5178 let nb_launch = if cmp.overlap { 2usize } else { 1 };
5179 let row_off = if cmp.overlap { d } else { 0 };
5180 unsafe {
5181 ck(
5182 "compressor_pool dec",
5183 k::memra_dsv4_compressor_pool(
5184 dpf!(*pend_kv, &stream),
5185 dpf!(*pend_score, &stream),
5186 dpf!(cmp.ape, &stream),
5187 dpm!(*emit, &stream),
5188 nb_launch as i32,
5189 ratio as i32,
5190 d as i32,
5191 latent as i32,
5192 cmp.overlap as i32,
5193 sp(&stream),
5194 ),
5195 )?;
5196 let row_c = (emit.device_ptr(&stream).0 as usize + row_off * 4) as *const f32;
5197 let row_m = (emit.device_ptr_mut(&stream).0 as usize + row_off * 4) as *mut f32;
5198 ck(
5199 "rmsnorm dec cmp",
5200 self.rmsnorm_arm(
5201 row_c,
5202 dpf!(cmp.norm, &stream),
5203 row_m,
5204 1,
5205 d as i32,
5206 eps,
5207 sp(&stream),
5208 ),
5209 )?;
5210 ck(
5211 "rope_at dec cmp",
5212 k::memra_dsv4_rope_at(
5213 row_m,
5214 1,
5215 d as i32,
5216 rd as i32,
5217 dpf!(fc_dev, &stream),
5218 (j * ratio) as i32,
5219 0,
5220 sp(&stream),
5221 ),
5222 )?;
5223 if cmp.rotate {
5224 let scale = (d as f32).powf(-0.5);
5225 ck(
5226 "hadamard dec cmp",
5227 k::memra_dsv4_hadamard(row_m, 1, d as i32, scale, sp(&stream)),
5228 )?;
5229 ck(
5230 "fp4 dec cmp",
5231 k::memra_dsv4_fp4_act_quant(row_m, 1, d as i64, d as i32, sp(&stream)),
5232 )?;
5233 } else {
5234 ck(
5235 "act_quant dec cmp",
5236 k::memra_dsv4_act_quant(
5237 row_m,
5238 1,
5239 d as i64,
5240 (d - rd) as i32,
5241 64,
5242 (self.variant == ActQuantVariant::ClampOnly) as i32,
5243 sp(&stream),
5244 ),
5245 )?;
5246 }
5247 }
5248 {
5249 let src = emit.slice(row_off..row_off + d);
5250 let mut dst = store.slice_mut((row0 + j) * d..(row0 + j + 1) * d);
5251 stream
5252 .memcpy_dtod(&src, &mut dst)
5253 .map_err(e("emit store"))?;
5254 }
5255 if cmp.overlap {
5256 {
5257 let src = pend_kv.slice(ratio * latent..2 * ratio * latent);
5258 let mut dst = shift.slice_mut(0..ratio * latent);
5259 stream.memcpy_dtod(&src, &mut dst).map_err(e("shift1"))?;
5260 }
5261 {
5262 let src = shift.slice(0..ratio * latent);
5263 let mut dst = pend_kv.slice_mut(0..ratio * latent);
5264 stream.memcpy_dtod(&src, &mut dst).map_err(e("shift2"))?;
5265 }
5266 {
5267 let src = pend_score.slice(ratio * latent..2 * ratio * latent);
5268 let mut dst = shift.slice_mut(0..ratio * latent);
5269 stream.memcpy_dtod(&src, &mut dst).map_err(e("shift3"))?;
5270 }
5271 {
5272 let src = shift.slice(0..ratio * latent);
5273 let mut dst = pend_score.slice_mut(0..ratio * latent);
5274 stream.memcpy_dtod(&src, &mut dst).map_err(e("shift4"))?;
5275 }
5276 }
5277 *blocks = j + 1;
5278 Ok(())
5279 }
5280
5281 #[allow(clippy::too_many_arguments)]
5286 fn block_decode_dev(
5287 &self,
5288 st: &Stage,
5289 layer: &LayerDev,
5290 cache: &mut LayerCache,
5291 ws: &mut StepWs,
5292 input_rx: bool,
5293 pos: usize,
5294 tok: u32,
5295 host_math: bool,
5296 ) -> Res<()> {
5297 let d = self.model.cfg();
5298 let mc = &self.model.mc;
5299 let hc = d.hc_mult as usize;
5300 let hidden = mc.n_embd as usize;
5301 let heads = mc.n_head as usize;
5302 let hd = d.head_dim as usize;
5303 let rd = d.qk_rope_head_dim as usize;
5304 let q_lora = d.q_lora_rank as usize;
5305 let win = d.sliding_window as usize;
5306 let o_groups = d.o_groups as usize;
5307 let o_lora = d.o_lora_rank as usize;
5308 let eps = mc.rms_eps;
5309 let iters = d.hc_sinkhorn_iters;
5310 let hc_eps = d.hc_eps;
5311 let stream = st.gpu.stream();
5312 let fc_dev: *const f32 = if layer.ratio != 0 {
5313 st.fc_yarn.device_ptr(&stream).0 as *const f32
5314 } else {
5315 st.fc_plain.device_ptr(&stream).0 as *const f32
5316 };
5317 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
5318 let LayerCache {
5319 kvc,
5320 n_blocks,
5321 pend_kv,
5322 pend_score,
5323 ikvc,
5324 i_blocks,
5325 ipend_kv,
5326 ipend_score,
5327 } = cache;
5328
5329 {
5331 let StepWs {
5333 h_a,
5334 h_rx,
5335 mixes,
5336 pre,
5337 post,
5338 comb,
5339 y_hc,
5340 ..
5341 } = ws;
5342 let h_in: &CudaSlice<f32> = if input_rx { h_rx } else { h_a };
5343 self.hc_pre_dev(
5344 st,
5345 h_in,
5346 &layer.hc_attn_fn,
5347 &layer.hc_attn_base,
5348 &layer.hc_attn_scale,
5349 &layer.hc_attn_base_dev,
5350 &layer.hc_attn_scale_dev,
5351 mixes,
5352 pre,
5353 post,
5354 comb,
5355 y_hc,
5356 hc,
5357 hidden,
5358 iters,
5359 hc_eps,
5360 host_math,
5361 )?;
5362 }
5363 unsafe {
5364 ck(
5365 "rmsnorm attn dev",
5366 self.rmsnorm_arm(
5367 dpf!(ws.y_hc, &stream),
5368 dpf!(layer.attn_norm, &stream),
5369 dpm!(ws.x, &stream),
5370 1,
5371 hidden as i32,
5372 eps,
5373 sp(&stream),
5374 ),
5375 )?;
5376 }
5377
5378 Self::gemm_dev(
5380 st,
5381 ws.x.device_ptr(&stream).0 as *const f32,
5382 &mut ws.gemm_xb,
5383 dwsel(self.dense_fp8, &stream, &layer.wq_a, &layer.wq_a_fp8),
5384 1,
5385 q_lora,
5386 hidden,
5387 ws.qr.device_ptr_mut(&stream).0 as *mut f32,
5388 )?;
5389 unsafe {
5390 ck(
5391 "rmsnorm q dev",
5392 self.rmsnorm_arm(
5393 dpf!(ws.qr, &stream),
5394 dpf!(layer.q_norm, &stream),
5395 dpm!(ws.qr, &stream),
5396 1,
5397 q_lora as i32,
5398 eps,
5399 sp(&stream),
5400 ),
5401 )?;
5402 ck(
5403 "cvt qr dev",
5404 k::memra_dsv4_cvt_bf16(
5405 dpf!(ws.qr, &stream),
5406 ws.qr_b.device_ptr_mut(&stream).0 as *mut c_void,
5407 q_lora as i64,
5408 sp(&stream),
5409 ),
5410 )?;
5411 }
5412 Self::gemv_pre_dev(
5413 st,
5414 ws.qr_b.device_ptr(&stream).0 as *const c_void,
5415 dwsel(self.dense_fp8, &stream, &layer.wq_b, &layer.wq_b_fp8),
5416 heads * hd,
5417 q_lora,
5418 ws.q.device_ptr_mut(&stream).0 as *mut f32,
5419 )?;
5420 unsafe {
5421 ck(
5422 "headrms dev",
5423 self.headrms_arm(
5424 dpm!(ws.q, &stream),
5425 heads as i32,
5426 hd as i32,
5427 eps,
5428 sp(&stream),
5429 ),
5430 )?;
5431 ck(
5432 "rope_at q dev",
5433 k::memra_dsv4_rope_at(
5434 dpm!(ws.q, &stream),
5435 heads as i32,
5436 hd as i32,
5437 rd as i32,
5438 fc_dev,
5439 pos as i32,
5440 0,
5441 sp(&stream),
5442 ),
5443 )?;
5444 }
5445
5446 Self::gemm_dev(
5448 st,
5449 ws.x.device_ptr(&stream).0 as *const f32,
5450 &mut ws.gemm_xb,
5451 dwsel(self.dense_fp8, &stream, &layer.wkv, &layer.wkv_fp8),
5452 1,
5453 hd,
5454 hidden,
5455 ws.kv.device_ptr_mut(&stream).0 as *mut f32,
5456 )?;
5457 unsafe {
5458 ck(
5459 "rmsnorm kv dev",
5460 self.rmsnorm_arm(
5461 dpf!(ws.kv, &stream),
5462 dpf!(layer.kv_norm, &stream),
5463 dpm!(ws.kv, &stream),
5464 1,
5465 hd as i32,
5466 eps,
5467 sp(&stream),
5468 ),
5469 )?;
5470 ck(
5471 "rope_at kv dev",
5472 k::memra_dsv4_rope_at(
5473 dpm!(ws.kv, &stream),
5474 1,
5475 hd as i32,
5476 rd as i32,
5477 fc_dev,
5478 pos as i32,
5479 0,
5480 sp(&stream),
5481 ),
5482 )?;
5483 ck(
5484 "act_quant kv dev",
5485 k::memra_dsv4_act_quant(
5486 dpm!(ws.kv, &stream),
5487 1,
5488 hd as i64,
5489 (hd - rd) as i32,
5490 64,
5491 clamp_only,
5492 sp(&stream),
5493 ),
5494 )?;
5495 }
5496 {
5497 let slot = pos % win;
5498 let src = ws.kv.slice(0..hd);
5499 let mut dst = kvc.slice_mut(slot * hd..(slot + 1) * hd);
5500 stream
5501 .memcpy_dtod(&src, &mut dst)
5502 .map_err(e("ring write"))?;
5503 }
5504
5505 let mut slots = win;
5507 if layer.ratio != 0 {
5508 if let Some(ix) = &layer.idx {
5509 Self::gemv_pre_dev(
5511 st,
5512 ws.qr_b.device_ptr(&stream).0 as *const c_void,
5513 dwsel(self.dense_fp8, &stream, &ix.wq_b, &ix.wq_b_fp8),
5514 ix.heads * ix.hd,
5515 q_lora,
5516 ws.qi.device_ptr_mut(&stream).0 as *mut f32,
5517 )?;
5518 unsafe {
5519 ck(
5520 "rope_at qi dev",
5521 k::memra_dsv4_rope_at(
5522 dpm!(ws.qi, &stream),
5523 ix.heads as i32,
5524 ix.hd as i32,
5525 rd as i32,
5526 fc_dev,
5527 pos as i32,
5528 0,
5529 sp(&stream),
5530 ),
5531 )?;
5532 let scale = (ix.hd as f32).powf(-0.5);
5533 ck(
5534 "hadamard qi dev",
5535 k::memra_dsv4_hadamard(
5536 dpm!(ws.qi, &stream),
5537 ix.heads as i32,
5538 ix.hd as i32,
5539 scale,
5540 sp(&stream),
5541 ),
5542 )?;
5543 ck(
5544 "fp4 qi dev",
5545 k::memra_dsv4_fp4_act_quant(
5546 dpm!(ws.qi, &stream),
5547 ix.heads as i32,
5548 ix.hd as i64,
5549 ix.hd as i32,
5550 sp(&stream),
5551 ),
5552 )?;
5553 }
5554 {
5556 let StepWs {
5557 x,
5558 cmp_kv_row,
5559 cmp_sc_row,
5560 cmp_emit,
5561 cmp_shift,
5562 ..
5563 } = ws;
5564 self.cmp_decode_dev(
5565 st,
5566 &ix.cmp,
5567 x,
5568 pos,
5569 hidden,
5570 if layer.ratio != 0 {
5571 &st.fc_yarn
5572 } else {
5573 &st.fc_plain
5574 },
5575 rd,
5576 eps,
5577 cmp_kv_row,
5578 cmp_sc_row,
5579 cmp_emit,
5580 cmp_shift,
5581 ipend_kv.as_mut().expect("ipend"),
5582 ipend_score.as_mut().expect("ipend"),
5583 ikvc.as_mut().expect("ikvc"),
5584 0,
5585 i_blocks,
5586 )?;
5587 }
5588 let nb = *i_blocks;
5589 debug_assert_eq!(nb, (pos + 1) / layer.ratio, "indexer block count");
5590 unsafe {
5592 ck(
5593 "build_idx win",
5594 k::memra_dsv4_build_idx(
5595 ws.idx.device_ptr_mut(&stream).0 as *mut i32,
5596 pos as i32,
5597 win as i32,
5598 -1,
5599 win as i32,
5600 sp(&stream),
5601 ),
5602 )?;
5603 }
5604 if nb > 0 {
5605 Self::gemm_dev(
5606 st,
5607 ws.x.device_ptr(&stream).0 as *const f32,
5608 &mut ws.gemm_xb,
5609 dwsel(
5610 self.dense_fp8,
5611 &stream,
5612 &ix.weights_proj,
5613 &ix.weights_proj_fp8,
5614 ),
5615 1,
5616 ix.heads,
5617 hidden,
5618 ws.wproj.device_ptr_mut(&stream).0 as *mut f32,
5619 )?;
5620 let wscale = ((ix.hd as f64).powf(-0.5) * (ix.heads as f64).powf(-0.5)) as f32;
5621 unsafe {
5622 ck(
5623 "indexer_score dev",
5624 self.indexer_score_arm(
5625 dpf!(ws.qi, &stream),
5626 dpf!(ikvc.as_ref().expect("ikvc"), &stream),
5627 dpf!(ws.wproj, &stream),
5628 wscale,
5629 dpm!(ws.score, &stream),
5630 1,
5631 ix.heads as i32,
5632 ix.hd as i32,
5633 nb as i32,
5634 layer.ratio as i32,
5635 nb as i32,
5636 sp(&stream),
5637 ),
5638 )?;
5639 }
5640 let kk = ix.topk.min(nb);
5641 if host_math {
5642 let score_h = {
5645 let view = ws.score.slice(0..nb);
5646 let mut v = vec![0f32; nb];
5647 stream
5648 .memcpy_dtoh(&view, &mut v[..])
5649 .map_err(e("dtoh sc"))?;
5650 stream.synchronize().map_err(e("sync sc"))?;
5651 v
5652 };
5653 let mut order: Vec<usize> = (0..nb).collect();
5654 order.sort_by(|&a, &b| {
5655 score_h[b]
5656 .partial_cmp(&score_h[a])
5657 .unwrap_or(std::cmp::Ordering::Equal)
5658 .then(a.cmp(&b))
5659 });
5660 let cidx: Vec<i32> = order
5661 .into_iter()
5662 .take(kk)
5663 .map(|j| (j + win) as i32)
5664 .collect();
5665 let mut dst = ws.idx.slice_mut(win..win + kk);
5666 stream.memcpy_htod(&cidx, &mut dst).map_err(e("htod idx"))?;
5667 } else {
5668 unsafe {
5669 let idx_tail =
5670 (ws.idx.device_ptr_mut(&stream).0 as usize + win * 4) as *mut i32;
5671 ck(
5672 "topk_idx dev",
5673 k::memra_dsv4_topk_idx(
5674 dpf!(ws.score, &stream),
5675 nb as i32,
5676 kk as i32,
5677 win as i32,
5678 idx_tail,
5679 sp(&stream),
5680 ),
5681 )?;
5682 }
5683 }
5684 slots = win + kk;
5685 }
5686 } else {
5687 let nb = (pos + 1) / layer.ratio;
5690 unsafe {
5691 ck(
5692 "build_idx coarse",
5693 k::memra_dsv4_build_idx(
5694 ws.idx.device_ptr_mut(&stream).0 as *mut i32,
5695 pos as i32,
5696 win as i32,
5697 nb as i32,
5698 (win + nb) as i32,
5699 sp(&stream),
5700 ),
5701 )?;
5702 }
5703 slots = win + nb;
5704 }
5705 {
5707 let StepWs {
5708 x,
5709 cmp_kv_row,
5710 cmp_sc_row,
5711 cmp_emit,
5712 cmp_shift,
5713 ..
5714 } = ws;
5715 self.cmp_decode_dev(
5716 st,
5717 layer.cmp.as_ref().expect("ratio!=0 has compressor"),
5718 x,
5719 pos,
5720 hidden,
5721 &st.fc_yarn,
5722 rd,
5723 eps,
5724 cmp_kv_row,
5725 cmp_sc_row,
5726 cmp_emit,
5727 cmp_shift,
5728 pend_kv.as_mut().expect("pend"),
5729 pend_score.as_mut().expect("pend"),
5730 kvc,
5731 win,
5732 n_blocks,
5733 )?;
5734 }
5735 debug_assert_eq!(*n_blocks, (pos + 1) / layer.ratio, "attn block count");
5736 } else {
5737 unsafe {
5739 ck(
5740 "build_idx window-only",
5741 k::memra_dsv4_build_idx(
5742 ws.idx.device_ptr_mut(&stream).0 as *mut i32,
5743 pos as i32,
5744 win as i32,
5745 -1,
5746 win as i32,
5747 sp(&stream),
5748 ),
5749 )?;
5750 }
5751 }
5752
5753 let scale = (hd as f64).powf(-0.5) as f32;
5756 unsafe {
5757 ck(
5758 "sink_attn_dec dev",
5759 self.sink_attn_dec_arm(
5760 dpf!(ws.q, &stream),
5761 dpf!(kvc, &stream),
5762 ws.idx.device_ptr(&stream).0 as *const i32,
5763 dpf!(layer.sink, &stream),
5764 dpm!(ws.sink_scores, &stream),
5765 dpm!(ws.sink_evals, &stream),
5766 ws.sink_den.device_ptr_mut(&stream).0 as *mut f64,
5767 dpm!(ws.o, &stream),
5768 heads as i32,
5769 hd as i32,
5770 slots as i32,
5771 scale,
5772 sp(&stream),
5773 ),
5774 )?;
5775 ck(
5776 "rope_at o inv dev",
5777 k::memra_dsv4_rope_at(
5778 dpm!(ws.o, &stream),
5779 heads as i32,
5780 hd as i32,
5781 rd as i32,
5782 fc_dev,
5783 pos as i32,
5784 1,
5785 sp(&stream),
5786 ),
5787 )?;
5788 }
5789
5790 let gw = heads / o_groups * hd;
5794 unsafe {
5795 ck(
5796 "cvt o dev",
5797 k::memra_dsv4_cvt_bf16(
5798 dpf!(ws.o, &stream),
5799 ws.o_b.device_ptr_mut(&stream).0 as *mut c_void,
5800 (heads * hd) as i64,
5801 sp(&stream),
5802 ),
5803 )?;
5804 }
5805 let wo_a_dw = dwsel(self.dense_fp8, &stream, &layer.wo_a, &layer.wo_a_fp8);
5806 for g in 0..o_groups {
5807 Self::gemv_pre_dev(
5808 st,
5809 (ws.o_b.device_ptr(&stream).0 as usize + g * gw * 2) as *const c_void,
5810 wo_a_dw.offset_rows(g * o_lora, gw),
5811 o_lora,
5812 gw,
5813 (ws.og.device_ptr_mut(&stream).0 as usize + g * o_lora * 4) as *mut f32,
5814 )?;
5815 }
5816 Self::gemm_dev(
5817 st,
5818 ws.og.device_ptr(&stream).0 as *const f32,
5819 &mut ws.gemm_xb,
5820 dwsel(self.dense_fp8, &stream, &layer.wo_b, &layer.wo_b_fp8),
5821 1,
5822 hidden,
5823 o_groups * o_lora,
5824 ws.attn_out.device_ptr_mut(&stream).0 as *mut f32,
5825 )?;
5826
5827 {
5829 let StepWs {
5830 h_a,
5831 h_b,
5832 h_rx,
5833 attn_out,
5834 post,
5835 comb,
5836 ..
5837 } = ws;
5838 let h_in: &CudaSlice<f32> = if input_rx { h_rx } else { h_a };
5839 unsafe {
5840 ck(
5841 "hc_post attn dev",
5842 k::memra_dsv4_hc_post(
5843 dpf!(attn_out, &stream),
5844 dpf!(h_in, &stream),
5845 dpf!(post, &stream),
5846 dpf!(comb, &stream),
5847 dpm!(*h_b, &stream),
5848 1,
5849 hc as i32,
5850 hidden as i32,
5851 sp(&stream),
5852 ),
5853 )?;
5854 }
5855 }
5856
5857 {
5859 let StepWs {
5860 h_b,
5861 mixes,
5862 pre,
5863 post,
5864 comb,
5865 y_hc,
5866 ..
5867 } = ws;
5868 self.hc_pre_dev(
5869 st,
5870 h_b,
5871 &layer.hc_ffn_fn,
5872 &layer.hc_ffn_base,
5873 &layer.hc_ffn_scale,
5874 &layer.hc_ffn_base_dev,
5875 &layer.hc_ffn_scale_dev,
5876 mixes,
5877 pre,
5878 post,
5879 comb,
5880 y_hc,
5881 hc,
5882 hidden,
5883 iters,
5884 hc_eps,
5885 host_math,
5886 )?;
5887 }
5888 unsafe {
5889 ck(
5890 "rmsnorm ffn dev",
5891 self.rmsnorm_arm(
5892 dpf!(ws.y_hc, &stream),
5893 dpf!(layer.ffn_norm, &stream),
5894 dpm!(ws.xf, &stream),
5895 1,
5896 hidden as i32,
5897 eps,
5898 sp(&stream),
5899 ),
5900 )?;
5901 }
5902 self.moe_forward_dev(st, layer, ws, tok, host_math)?;
5903 {
5904 let StepWs {
5905 h_a,
5906 h_b,
5907 y,
5908 post,
5909 comb,
5910 ..
5911 } = ws;
5912 unsafe {
5913 ck(
5914 "hc_post ffn dev",
5915 k::memra_dsv4_hc_post(
5916 dpf!(y, &stream),
5917 dpf!(h_b, &stream),
5918 dpf!(post, &stream),
5919 dpf!(comb, &stream),
5920 dpm!(*h_a, &stream),
5921 1,
5922 hc as i32,
5923 hidden as i32,
5924 sp(&stream),
5925 ),
5926 )?;
5927 }
5928 }
5929 Ok(())
5930 }
5931
5932 fn moe_forward_dev(
5938 &self,
5939 st: &Stage,
5940 layer: &LayerDev,
5941 ws: &mut StepWs,
5942 tok: u32,
5943 host_math: bool,
5944 ) -> Res<()> {
5945 let mc = &self.model.mc;
5946 let d = self.model.cfg();
5947 let moe = mc.moe.as_ref().expect("moe");
5948 let hidden = mc.n_embd as usize;
5949 let ne = moe.expert_count as usize;
5950 let topk = moe.expert_used_count as usize;
5951 let inter = moe.expert_ff_length as usize;
5952 let limit = d.swiglu_limit;
5953 let stream = st.gpu.stream();
5954 let kind = match layer.expert_kind {
5955 ExpertKind::Nvfp4 => 0i32,
5956 ExpertKind::Mxfp4 => 1i32,
5957 };
5958 let wstride = (inter * hidden / 2) as i64;
5959 let sstride = match layer.expert_kind {
5960 ExpertKind::Nvfp4 => (inter * hidden / 16) as i64,
5961 ExpertKind::Mxfp4 => (inter * hidden / 32) as i64,
5962 };
5963
5964 self.dots_dev(st, &ws.xf, &layer.gate_w, 1, hidden, ne, &mut ws.raw)?;
5965 if host_math {
5966 let raw_h = dtoh_f32(&stream, &ws.raw)?;
5967 let (indices, weights) =
5968 Self::route_host(layer, &raw_h, &[tok], 1, ne, topk, d.routed_scaling_factor);
5969 let sel: Vec<i32> = indices.iter().map(|&x| x as i32).collect();
5970 let mut order: Vec<i32> = (0..topk as i32).collect();
5971 order.sort_by_key(|&s| indices[s as usize]);
5972 stream
5973 .memcpy_htod(&sel, &mut ws.sel)
5974 .map_err(e("htod sel"))?;
5975 stream
5976 .memcpy_htod(&weights, &mut ws.selw)
5977 .map_err(e("htod selw"))?;
5978 stream
5979 .memcpy_htod(&order, &mut ws.order)
5980 .map_err(e("htod order"))?;
5981 } else {
5982 unsafe {
5983 ck(
5984 "route dev",
5985 k::memra_dsv4_route(
5986 dpf!(ws.raw, &stream),
5987 layer
5988 .gate_bias_dev
5989 .as_ref()
5990 .map(|b| b.device_ptr(&stream).0 as *const f32)
5991 .unwrap_or(std::ptr::null()),
5992 layer
5993 .tid2eid_dev
5994 .as_ref()
5995 .map(|t| t.device_ptr(&stream).0 as *const i32)
5996 .unwrap_or(std::ptr::null()),
5997 ws.tok.device_ptr(&stream).0 as *const i32,
5998 ne as i32,
5999 topk as i32,
6000 d.routed_scaling_factor,
6001 ws.sel.device_ptr_mut(&stream).0 as *mut i32,
6002 ws.selw.device_ptr_mut(&stream).0 as *mut f32,
6003 ws.order.device_ptr_mut(&stream).0 as *mut i32,
6004 sp(&stream),
6005 ),
6006 )?;
6007 }
6008 }
6009
6010 unsafe {
6011 ck(
6012 "act_quant_fp8 x dev",
6013 k::memra_dsv4_act_quant_fp8(
6014 dpf!(ws.xf, &stream),
6015 ws.xq.device_ptr_mut(&stream).0 as *mut c_void,
6016 dpm!(ws.xs, &stream),
6017 1,
6018 hidden as i32,
6019 sp(&stream),
6020 ),
6021 )?;
6022 for (proj, dst) in [(0i32, &mut ws.g1), (2i32, &mut ws.g3)] {
6023 ck(
6024 "fp4_gemm_sel w1/w3",
6025 k::memra_dsv4_fp4_gemm_sel(
6026 dp!(ws.xq, &stream),
6027 dpf!(ws.xs, &stream),
6028 dp!(layer.experts_w, &stream),
6029 dp!(layer.experts_sc, &stream),
6030 dpf!(layer.experts_s2_dev, &stream),
6031 ws.sel.device_ptr(&stream).0 as *const i32,
6032 proj,
6033 0,
6034 kind,
6035 dpm!(*dst, &stream),
6036 topk as i32,
6037 inter as i32,
6038 hidden as i32,
6039 wstride,
6040 sstride,
6041 sp(&stream),
6042 ),
6043 )?;
6044 }
6045 ck(
6046 "swiglu dev",
6047 k::memra_dsv4_swiglu(
6048 dpf!(ws.g1, &stream),
6049 dpf!(ws.g3, &stream),
6050 dpm!(ws.hbuf, &stream),
6051 topk as i32,
6052 inter as i32,
6053 limit,
6054 ws.selw.device_ptr(&stream).0 as *const f32,
6055 sp(&stream),
6056 ),
6057 )?;
6058 ck(
6059 "act_quant_fp8 h dev",
6060 k::memra_dsv4_act_quant_fp8(
6061 dpf!(ws.hbuf, &stream),
6062 ws.hq.device_ptr_mut(&stream).0 as *mut c_void,
6063 dpm!(ws.hs, &stream),
6064 topk as i32,
6065 inter as i32,
6066 sp(&stream),
6067 ),
6068 )?;
6069 ck(
6070 "fp4_gemm_sel w2",
6071 k::memra_dsv4_fp4_gemm_sel(
6072 dp!(ws.hq, &stream),
6073 dpf!(ws.hs, &stream),
6074 dp!(layer.experts_w, &stream),
6075 dp!(layer.experts_sc, &stream),
6076 dpf!(layer.experts_s2_dev, &stream),
6077 ws.sel.device_ptr(&stream).0 as *const i32,
6078 1,
6079 1,
6080 kind,
6081 dpm!(ws.contrib, &stream),
6082 topk as i32,
6083 hidden as i32,
6084 inter as i32,
6085 wstride,
6086 sstride,
6087 sp(&stream),
6088 ),
6089 )?;
6090 ck(
6091 "combine dev",
6092 k::memra_dsv4_combine_rows(
6093 dpf!(ws.contrib, &stream),
6094 ws.order.device_ptr(&stream).0 as *const i32,
6095 topk as i32,
6096 dpm!(ws.y, &stream),
6097 hidden as i64,
6098 sp(&stream),
6099 ),
6100 )?;
6101 ck(
6103 "cvt xb dev",
6104 k::memra_dsv4_cvt_bf16(
6105 dpf!(ws.xf, &stream),
6106 ws.xb.device_ptr_mut(&stream).0 as *mut c_void,
6107 hidden as i64,
6108 sp(&stream),
6109 ),
6110 )?;
6111 }
6112 let sh_inter = ws.sg1.len();
6113 Self::gemv_pre_dev(
6114 st,
6115 ws.xb.device_ptr(&stream).0 as *const c_void,
6116 dwsel(
6117 self.dense_fp8,
6118 &stream,
6119 &layer.shared_w[0],
6120 &layer.shared_fp8[0],
6121 ),
6122 sh_inter,
6123 hidden,
6124 ws.sg1.device_ptr_mut(&stream).0 as *mut f32,
6125 )?;
6126 Self::gemv_pre_dev(
6127 st,
6128 ws.xb.device_ptr(&stream).0 as *const c_void,
6129 dwsel(
6130 self.dense_fp8,
6131 &stream,
6132 &layer.shared_w[2],
6133 &layer.shared_fp8[2],
6134 ),
6135 sh_inter,
6136 hidden,
6137 ws.sg3.device_ptr_mut(&stream).0 as *mut f32,
6138 )?;
6139 unsafe {
6140 ck(
6141 "swiglu sh dev",
6142 k::memra_dsv4_swiglu(
6143 dpf!(ws.sg1, &stream),
6144 dpf!(ws.sg3, &stream),
6145 dpm!(ws.shbuf, &stream),
6146 1,
6147 sh_inter as i32,
6148 limit,
6149 std::ptr::null(),
6150 sp(&stream),
6151 ),
6152 )?;
6153 ck(
6154 "cvt sh dev",
6155 k::memra_dsv4_cvt_bf16(
6156 dpf!(ws.shbuf, &stream),
6157 ws.shb16.device_ptr_mut(&stream).0 as *mut c_void,
6158 sh_inter as i64,
6159 sp(&stream),
6160 ),
6161 )?;
6162 }
6163 Self::gemv_pre_dev(
6164 st,
6165 ws.shb16.device_ptr(&stream).0 as *const c_void,
6166 dwsel(
6167 self.dense_fp8,
6168 &stream,
6169 &layer.shared_w[1],
6170 &layer.shared_fp8[1],
6171 ),
6172 hidden,
6173 sh_inter,
6174 ws.sh_out.device_ptr_mut(&stream).0 as *mut f32,
6175 )?;
6176 unsafe {
6177 ck(
6178 "add shared dev",
6179 k::memra_dsv4_add_inplace(
6180 dpm!(ws.y, &stream),
6181 dpf!(ws.sh_out, &stream),
6182 hidden as i64,
6183 sp(&stream),
6184 ),
6185 )?;
6186 }
6187 Ok(())
6188 }
6189
6190 fn head_logits_dev(&self, ws: &mut StepWs, host_math: bool) -> Res<()> {
6194 let d = self.model.cfg();
6195 let mc = &self.model.mc;
6196 let hc = d.hc_mult as usize;
6197 let hidden = mc.n_embd as usize;
6198 let eps = mc.rms_eps;
6199 let last = self.stages.len() - 1;
6200 let st = &self.stages[last];
6201 let stream = st.gpu.stream();
6202 let w = hc * hidden;
6203 let fn_w = st.hc_head_fn.as_ref().expect("hc_head_fn");
6204 let norm = st.trunk_norm.as_ref().expect("trunk norm");
6205 self.dots_dev(st, &ws.h_a, fn_w, 1, w, hc, &mut ws.head_mixes)?;
6206 unsafe {
6207 ck(
6208 "rowsq head dev",
6209 self.rowsq_scale_arm(
6210 dpf!(ws.h_a, &stream),
6211 dpm!(ws.head_mixes, &stream),
6212 1,
6213 w as i32,
6214 hc as i32,
6215 eps,
6216 sp(&stream),
6217 ),
6218 )?;
6219 }
6220 if host_math {
6221 let mut mixes_h = dtoh_f32(&stream, &ws.head_mixes)?;
6222 #[allow(clippy::needless_range_loop)]
6223 for c in 0..hc {
6225 let m = mixes_h[c];
6226 mixes_h[c] =
6227 sigmoid_f32(m * self.hc_head_scale[0] + self.hc_head_base[c]) + d.hc_eps;
6228 }
6229 stream
6230 .memcpy_htod(&mixes_h, &mut ws.head_pre)
6231 .map_err(e("htod head pre"))?;
6232 } else {
6233 unsafe {
6234 ck(
6235 "hc_head_pre dev",
6236 k::memra_dsv4_hc_head_pre(
6237 dpf!(ws.head_mixes, &stream),
6238 st.hc_head_scale_dev
6239 .as_ref()
6240 .expect("head scale dev")
6241 .device_ptr(&stream)
6242 .0 as *const f32,
6243 st.hc_head_base_dev
6244 .as_ref()
6245 .expect("head base dev")
6246 .device_ptr(&stream)
6247 .0 as *const f32,
6248 dpm!(ws.head_pre, &stream),
6249 hc as i32,
6250 d.hc_eps,
6251 sp(&stream),
6252 ),
6253 )?;
6254 }
6255 }
6256 unsafe {
6257 ck(
6258 "hc_collapse head dev",
6259 k::memra_dsv4_hc_collapse(
6260 dpf!(ws.h_a, &stream),
6261 dpf!(ws.head_pre, &stream),
6262 dpm!(ws.collapsed, &stream),
6263 1,
6264 hc as i32,
6265 hidden as i32,
6266 sp(&stream),
6267 ),
6268 )?;
6269 ck(
6270 "rmsnorm head dev",
6271 self.rmsnorm_arm(
6272 dpf!(ws.collapsed, &stream),
6273 dpf!(norm, &stream),
6274 dpm!(ws.collapsed, &stream),
6275 1,
6276 hidden as i32,
6277 eps,
6278 sp(&stream),
6279 ),
6280 )?;
6281 let head_ptr = st.head.as_ref().expect("head").device_ptr(&stream).0 as *const c_void;
6282 if self.dots_f32 {
6283 ck(
6284 "head dots f32acc dev",
6285 k::memra_dsv4_dots_f32acc(
6286 dpf!(ws.collapsed, &stream),
6287 head_ptr,
6288 1,
6289 dpm!(ws.logits, &stream),
6290 1,
6291 hidden as i32,
6292 ws.logits.len() as i32,
6293 sp(&stream),
6294 ),
6295 )?;
6296 } else {
6297 ck(
6298 "head dots dev",
6299 k::memra_dsv4_dots_f32(
6300 dpf!(ws.collapsed, &stream),
6301 head_ptr,
6302 1,
6303 dpm!(ws.logits, &stream),
6304 1,
6305 hidden as i32,
6306 ws.logits.len() as i32,
6307 sp(&stream),
6308 ),
6309 )?;
6310 }
6311 }
6312 Ok(())
6313 }
6314
6315 fn decode_step_fast(
6319 &self,
6320 tok: u32,
6321 state: &mut DecodeState,
6322 want_logits: bool,
6323 host_math: bool,
6324 ) -> Res<(Option<Vec<f32>>, u32)> {
6325 self.decode_step_fast_tap(tok, state, want_logits, host_math, None)
6326 }
6327
6328 fn decode_step_fast_tap(
6334 &self,
6335 tok: u32,
6336 state: &mut DecodeState,
6337 want_logits: bool,
6338 host_math: bool,
6339 mut taps: Option<(&mut CudaSlice<f32>, usize)>,
6340 ) -> Res<(Option<Vec<f32>>, u32)> {
6341 let mc = &self.model.mc;
6342 let d = self.model.cfg();
6343 let pos = state.pos;
6344 assert!(pos > 0, "decode_step needs prefill_with_cache first");
6345 assert!(pos < self.max_seq, "pos {pos} >= max_seq {}", self.max_seq);
6346 let hidden = mc.n_embd as usize;
6347 let hc = d.hc_mult as usize;
6348 let n_trunk = mc.n_layer - mc.nextn_predict_layers;
6349 let ws_all = state.ws.as_mut().expect("device path needs StepWs");
6350
6351 let st0 = &self.stages[0];
6353 st0.gpu.ctx.bind_to_thread().map_err(e("bind ctx0"))?;
6354 let stream0 = st0.gpu.stream();
6355 {
6356 let ws0 = &mut ws_all[0];
6357 stream0
6358 .memcpy_htod(&[tok as i32], &mut ws0.tok)
6359 .map_err(e("htod tok"))?;
6360 unsafe {
6361 ck(
6362 "embed_rows dev",
6363 k::memra_dsv4_embed_rows(
6364 st0.embed
6365 .as_ref()
6366 .expect("embed on stage 0")
6367 .device_ptr(&stream0)
6368 .0 as *const c_void,
6369 ws0.tok.device_ptr(&stream0).0 as *const i32,
6370 dpm!(ws0.emb, &stream0),
6371 1,
6372 hidden as i32,
6373 sp(&stream0),
6374 ),
6375 )?;
6376 ck(
6377 "repeat_hc dev",
6378 k::memra_dsv4_repeat_hc(
6379 dpf!(ws0.emb, &stream0),
6380 dpm!(ws0.h_a, &stream0),
6381 1,
6382 hc as i32,
6383 hidden as i32,
6384 sp(&stream0),
6385 ),
6386 )?;
6387 }
6388 }
6389
6390 let mut cur_stage = 0usize;
6391 let mut input_rx = false;
6392 for il in 0..n_trunk {
6393 let stage = self.layer_stage[il as usize];
6394 if stage != cur_stage {
6395 let bytes = hc * hidden * std::mem::size_of::<f32>();
6398 let src_stream = self.stages[cur_stage].gpu.stream();
6399 let dst_stream = self.stages[stage].gpu.stream();
6400 let (ws_src, ws_dst) = ws_all.split_at_mut(stage);
6401 let src_ws = &ws_src[cur_stage];
6402 let dst_ws = &mut ws_dst[0];
6403 self.stages[cur_stage]
6404 .gpu
6405 .ctx
6406 .bind_to_thread()
6407 .map_err(e("bind tx"))?;
6408 let (sp_, _g0) = src_ws.h_a.device_ptr(&src_stream);
6409 let (dp_, _g1) = dst_ws.h_rx.device_ptr_mut(&src_stream);
6410 unsafe {
6411 cudarc::driver::result::memcpy_peer_async(
6412 self.stages[stage].gpu.ctx.cu_ctx(),
6413 dp_,
6414 self.stages[cur_stage].gpu.ctx.cu_ctx(),
6415 sp_,
6416 bytes,
6417 src_stream.cu_stream(),
6418 )
6419 .map_err(e("peer copy h"))?;
6420 }
6421 let bnd = stage - 1;
6422 self.boundary_ev[bnd]
6423 .record(&src_stream)
6424 .map_err(e("ev record"))?;
6425 dst_stream
6426 .wait(&self.boundary_ev[bnd])
6427 .map_err(e("ev wait"))?;
6428 self.stages[stage]
6429 .gpu
6430 .ctx
6431 .bind_to_thread()
6432 .map_err(e("bind rx"))?;
6433 cur_stage = stage;
6434 input_rx = true;
6435 }
6436 let st = &self.stages[stage];
6437 let lidx = st
6438 .layers
6439 .iter()
6440 .position(|l| l.il == il)
6441 .unwrap_or_else(|| panic!("layer {il} not on stage {stage}"));
6442 self.block_decode_dev(
6443 st,
6444 &st.layers[lidx],
6445 &mut state.caches[il as usize],
6446 &mut ws_all[stage],
6447 input_rx,
6448 pos,
6449 tok,
6450 host_math,
6451 )?;
6452 input_rx = false;
6453 if let Some((t, base)) = taps.as_mut()
6456 && let Some(ds) = &self.dspark
6457 && let Some(k) = ds.targets.iter().position(|&tl| tl == il as usize)
6458 {
6459 let stream = self.stages[stage].gpu.stream();
6460 let hidden_i = hidden as i32;
6461 unsafe {
6462 ck(
6463 "hc_mean tap dev",
6464 k::memra_dsv4_hc_mean(
6465 dpf!(ws_all[stage].h_a, &stream),
6466 (t.device_ptr_mut(&stream).0 as usize + (*base + k * hidden) * 4)
6467 as *mut f32,
6468 1,
6469 hc as i32,
6470 hidden_i,
6471 sp(&stream),
6472 ),
6473 )?;
6474 }
6475 }
6476 }
6477
6478 let last = self.stages.len() - 1;
6479 assert_eq!(cur_stage, last, "device path expects the head stage last");
6480 self.head_logits_dev(&mut ws_all[last], host_math)?;
6481 let stream_last = self.stages[last].gpu.stream();
6482 state.pos += 1;
6483 if want_logits {
6484 let logits = dtoh_f32(&stream_last, &ws_all[last].logits)?;
6485 let mut best = 0usize;
6486 for i in 1..logits.len() {
6487 if logits[i] > logits[best] {
6488 best = i;
6489 }
6490 }
6491 Ok((Some(logits), best as u32))
6492 } else {
6493 unsafe {
6494 ck(
6495 "argmax dev",
6496 k::memra_dsv4_argmax(
6497 dpf!(ws_all[last].logits, &stream_last),
6498 ws_all[last].logits.len() as i64,
6499 ws_all[last].argmax.device_ptr_mut(&stream_last).0 as *mut i32,
6500 sp(&stream_last),
6501 ),
6502 )?;
6503 }
6504 let mut out = [0i32; 1];
6505 stream_last
6506 .memcpy_dtoh(&ws_all[last].argmax, &mut out[..])
6507 .map_err(e("dtoh argmax"))?;
6508 stream_last.synchronize().map_err(e("sync argmax"))?;
6509 Ok((None, out[0] as u32))
6510 }
6511 }
6512
6513 pub fn decode_step_greedy(&self, tok: u32, state: &mut DecodeState) -> Res<u32> {
6518 match self.decode_path {
6519 DecodePath::Legacy => {
6520 let logits = self.decode_step_impl(tok, state, None)?;
6521 let mut best = 0usize;
6522 for i in 1..logits.len() {
6523 if logits[i] > logits[best] {
6524 best = i;
6525 }
6526 }
6527 Ok(best as u32)
6528 }
6529 DecodePath::Device { host_math } => {
6530 Ok(self.decode_step_fast(tok, state, false, host_math)?.1)
6531 }
6532 }
6533 }
6534
6535 fn decode_step_impl(
6536 &self,
6537 tok: u32,
6538 state: &mut DecodeState,
6539 mut dump: Option<&mut Vec<(String, Vec<f32>)>>,
6540 ) -> Res<Vec<f32>> {
6541 if let DecodePath::Device { host_math } = self.decode_path {
6542 assert!(
6543 dump.is_none(),
6544 "decode_step_probe is a legacy-path diagnostic (set MEMRA_DSV4_DECODE_PATH=legacy)"
6545 );
6546 let (logits, _) = self.decode_step_fast(tok, state, true, host_math)?;
6547 return Ok(logits.expect("want_logits"));
6548 }
6549 let mc = &self.model.mc;
6550 let d = self.model.cfg();
6551 let pos = state.pos;
6552 assert!(pos > 0, "decode_step needs prefill_with_cache first");
6553 assert!(pos < self.max_seq, "pos {pos} >= max_seq {}", self.max_seq);
6554 let hidden = mc.n_embd as usize;
6555 let hc = d.hc_mult as usize;
6556 let n_trunk = mc.n_layer - mc.nextn_predict_layers;
6557
6558 let st0 = &self.stages[0];
6560 st0.gpu.ctx.bind_to_thread().map_err(e("bind ctx0"))?;
6561 let stream0 = st0.gpu.stream();
6562 let ids_dev = upload_i32(&stream0, &[tok as i32])?;
6563 let mut emb = stream0.alloc_zeros::<f32>(hidden).map_err(e("emb"))?;
6564 unsafe {
6565 ck(
6566 "embed_rows",
6567 k::memra_dsv4_embed_rows(
6568 st0.embed
6569 .as_ref()
6570 .expect("embed on stage 0")
6571 .device_ptr(&stream0)
6572 .0 as *const c_void,
6573 ids_dev.device_ptr(&stream0).0 as *const i32,
6574 dpm!(emb, &stream0),
6575 1,
6576 hidden as i32,
6577 sp(&stream0),
6578 ),
6579 )?;
6580 }
6581 let mut h = stream0.alloc_zeros::<f32>(hc * hidden).map_err(e("h0"))?;
6582 unsafe {
6583 ck(
6584 "repeat_hc",
6585 k::memra_dsv4_repeat_hc(
6586 dpf!(emb, &stream0),
6587 dpm!(h, &stream0),
6588 1,
6589 hc as i32,
6590 hidden as i32,
6591 sp(&stream0),
6592 ),
6593 )?;
6594 }
6595
6596 let mut cur_stage = 0usize;
6597 for il in 0..n_trunk {
6598 let stage = self.layer_stage[il as usize];
6599 if stage != cur_stage {
6600 let src_stream = self.stages[cur_stage].gpu.stream();
6601 let host = dtoh_f32(&src_stream, &h)?;
6602 let dst_stream = self.stages[stage].gpu.stream();
6603 self.stages[stage]
6604 .gpu
6605 .ctx
6606 .bind_to_thread()
6607 .map_err(e("bind"))?;
6608 h = upload_f32(&dst_stream, &host)?;
6609 cur_stage = stage;
6610 }
6611 let st = &self.stages[stage];
6612 let lidx = st
6613 .layers
6614 .iter()
6615 .position(|l| l.il == il)
6616 .unwrap_or_else(|| panic!("layer {il} not on stage {stage}"));
6617 h = self.block_decode(
6618 st,
6619 &st.layers[lidx],
6620 &mut state.caches[il as usize],
6621 &h,
6622 pos,
6623 tok,
6624 dump.as_deref_mut(),
6625 )?;
6626 }
6627
6628 let last = self.stages.len() - 1;
6629 if cur_stage != last {
6630 let src_stream = self.stages[cur_stage].gpu.stream();
6631 let host = dtoh_f32(&src_stream, &h)?;
6632 let dst_stream = self.stages[last].gpu.stream();
6633 h = upload_f32(&dst_stream, &host)?;
6634 }
6635 let hc_head_fn = self.stages[last].hc_head_fn.as_ref().expect("hc_head_fn");
6636 let trunk_norm = self.stages[last].trunk_norm.as_ref().expect("trunk norm");
6637 let logits = self.head_logits_from(
6638 &h,
6639 1,
6640 hc_head_fn,
6641 &self.hc_head_base,
6642 &self.hc_head_scale,
6643 trunk_norm,
6644 )?;
6645 state.pos += 1;
6646 Ok(logits)
6647 }
6648}
6649
6650impl Dsv4Gpu {
6659 fn dspark(&self) -> &DsparkDev {
6660 self.dspark
6661 .as_ref()
6662 .expect("MEMRA_DSV4_DRAFTER=dspark not loaded")
6663 }
6664
6665 #[allow(clippy::too_many_arguments)]
6673 fn dspark_head_dots(
6674 &self,
6675 st: &Stage,
6676 x: *const f32,
6677 w: *const c_void,
6678 w_is_bf16: i32,
6679 s: usize,
6680 kdim: usize,
6681 n: usize,
6682 y: *mut f32,
6683 ) -> Res<()> {
6684 let stream = st.gpu.stream();
6685 unsafe {
6686 if self.dspark_head_f32 {
6687 ck(
6688 "dspark head dots f32acc_mrow",
6689 k::memra_dsv4_dots_f32acc_mrow(
6690 x,
6691 w,
6692 w_is_bf16,
6693 y,
6694 s as i32,
6695 kdim as i32,
6696 n as i32,
6697 sp(&stream),
6698 ),
6699 )
6700 } else {
6701 ck(
6702 "dspark head dots f32_mrow",
6703 k::memra_dsv4_dots_f32_mrow(
6704 x,
6705 w,
6706 w_is_bf16,
6707 y,
6708 s as i32,
6709 kdim as i32,
6710 n as i32,
6711 sp(&stream),
6712 ),
6713 )
6714 }
6715 }
6716 }
6717
6718 pub fn dspark_alloc_state(&self) -> Res<DsparkState> {
6721 let ds = self.dspark();
6722 let d = self.model.cfg();
6723 let hd = d.head_dim as usize;
6724 let win = d.sliding_window as usize;
6725 let hidden = self.model.mc.n_embd as usize;
6726 let last = self.stages.len() - 1;
6727 let stream = self.stages[last].gpu.stream();
6728 let mut rings = Vec::with_capacity(ds.blocks.len());
6729 for _ in 0..ds.blocks.len() {
6730 rings.push(
6731 stream
6732 .alloc_zeros::<f32>((win + ds.block_size) * hd)
6733 .map_err(e("dspark ring"))?,
6734 );
6735 }
6736 let taps = stream
6737 .alloc_zeros::<f32>((ds.block_size + 1) * ds.targets.len() * hidden)
6738 .map_err(e("dspark taps"))?;
6739 Ok(DsparkState { rings, taps })
6740 }
6741
6742 fn dspark_main_x(&self, main_hidden: &CudaSlice<f32>, s: usize) -> Res<CudaSlice<f32>> {
6744 let ds = self.dspark();
6745 let hidden = self.model.mc.n_embd as usize;
6746 let k = ds.targets.len() * hidden;
6747 let last = self.stages.len() - 1;
6748 let st = &self.stages[last];
6749 let stream = st.gpu.stream();
6750 let mut mx = stream.alloc_zeros::<f32>(s * hidden).map_err(e("main_x"))?;
6751 Self::gemm(st, main_hidden, &ds.main_proj, 0, s, hidden, k, &mut mx)?;
6752 unsafe {
6753 ck(
6754 "rmsnorm main_x",
6755 k::memra_dsv4_rmsnorm(
6756 dpf!(mx, &stream),
6757 dpf!(ds.main_norm, &stream),
6758 dpm!(mx, &stream),
6759 s as i32,
6760 hidden as i32,
6761 self.model.mc.rms_eps,
6762 sp(&stream),
6763 ),
6764 )?;
6765 }
6766 Ok(mx)
6767 }
6768
6769 fn dspark_main_kv(
6772 &self,
6773 blk: &LayerDev,
6774 main_x: &CudaSlice<f32>,
6775 s: usize,
6776 positions: &[i32],
6777 ) -> Res<CudaSlice<f32>> {
6778 let d = self.model.cfg();
6779 let hd = d.head_dim as usize;
6780 let rd = d.qk_rope_head_dim as usize;
6781 let hidden = self.model.mc.n_embd as usize;
6782 let eps = self.model.mc.rms_eps;
6783 let last = self.stages.len() - 1;
6784 let st = &self.stages[last];
6785 let stream = st.gpu.stream();
6786 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
6787 let mut kv = stream.alloc_zeros::<f32>(s * hd).map_err(e("dspark kv"))?;
6791 Self::gemm(st, main_x, blk.wkv.dev(), 0, s, hd, hidden, &mut kv)?;
6792 let pos_dev = upload_i32(&stream, positions)?;
6793 unsafe {
6794 ck(
6795 "rmsnorm dspark kv",
6796 k::memra_dsv4_rmsnorm(
6797 dpf!(kv, &stream),
6798 dpf!(blk.kv_norm, &stream),
6799 dpm!(kv, &stream),
6800 s as i32,
6801 hd as i32,
6802 eps,
6803 sp(&stream),
6804 ),
6805 )?;
6806 ck(
6807 "rope dspark kv",
6808 k::memra_dsv4_rope(
6809 dpm!(kv, &stream),
6810 s as i32,
6811 1,
6812 hd as i32,
6813 rd as i32,
6814 dpf!(st.fc_plain, &stream),
6815 pos_dev.device_ptr(&stream).0 as *const i32,
6816 0,
6817 sp(&stream),
6818 ),
6819 )?;
6820 ck(
6821 "act_quant dspark kv",
6822 k::memra_dsv4_act_quant(
6823 dpm!(kv, &stream),
6824 s as i32,
6825 hd as i64,
6826 (hd - rd) as i32,
6827 64,
6828 clamp_only,
6829 sp(&stream),
6830 ),
6831 )?;
6832 }
6833 Ok(kv)
6834 }
6835
6836 pub fn dspark_prime_prefill(
6839 &self,
6840 state: &mut DsparkState,
6841 main_hidden: &CudaSlice<f32>,
6842 s: usize,
6843 ) -> Res<()> {
6844 let d = self.model.cfg();
6845 let hd = d.head_dim as usize;
6846 let win = d.sliding_window as usize;
6847 let last = self.stages.len() - 1;
6848 let stream = self.stages[last].gpu.stream();
6849 let mx = self.dspark_main_x(main_hidden, s)?;
6850 let positions: Vec<i32> = (0..s as i32).collect();
6851 let n_blocks = self.dspark().blocks.len();
6852 for bi in 0..n_blocks {
6853 let blk = &self.dspark().blocks[bi];
6854 let kv = self.dspark_main_kv(blk, &mx, s, &positions)?;
6855 for p in s.saturating_sub(win)..s {
6856 let slot = p % win;
6857 let src = kv.slice(p * hd..(p + 1) * hd);
6858 let mut dst = state.rings[bi].slice_mut(slot * hd..(slot + 1) * hd);
6859 stream
6860 .memcpy_dtod(&src, &mut dst)
6861 .map_err(e("prime ring"))?;
6862 }
6863 }
6864 Ok(())
6865 }
6866
6867 pub fn dspark_prefill_prime(
6878 &self,
6879 ids: &[u32],
6880 state: &mut DecodeState,
6881 dstate: &mut DsparkState,
6882 ) -> Res<ForwardOut> {
6883 assert_eq!(
6884 state.pos, 0,
6885 "dspark_prefill_prime needs a fresh DecodeState"
6886 );
6887 assert!(!ids.is_empty(), "empty prompt");
6888 let hidden = self.model.mc.n_embd as usize;
6889 let hc = self.model.cfg().hc_mult as usize;
6890 let s = ids.len();
6891 let targets = self.dspark().targets.clone();
6892 let n_t = targets.len();
6893 let mut cap = GpuCapture {
6894 want: targets.iter().map(|&t| t as u32).collect(),
6895 ..Default::default()
6896 };
6897 let out = self
6898 .forward_impl(ids, Some(&mut cap), None, Some(state))?
6899 .expect("prefill logits");
6900 state.pos = s;
6901
6902 let last = self.stages.len() - 1;
6903 let stream = self.stages[last].gpu.stream();
6904 self.stages[last]
6905 .gpu
6906 .ctx
6907 .bind_to_thread()
6908 .map_err(e("bind ctx prime"))?;
6909 let mut main_hidden = stream
6910 .alloc_zeros::<f32>(s * n_t * hidden)
6911 .map_err(e("prefill main_hidden"))?;
6912 let mut tmp = stream
6913 .alloc_zeros::<f32>(s * hidden)
6914 .map_err(e("tap tmp"))?;
6915 for (k, &il) in targets.iter().enumerate() {
6916 let h = cap
6917 .layer_out
6918 .get(&(il as u32))
6919 .unwrap_or_else(|| panic!("prefill capture missing target layer {il}"));
6920 assert_eq!(
6921 h.len(),
6922 s * hc * hidden,
6923 "target layer {il} capture is not [s, hc, hidden]"
6924 );
6925 let h_dev = upload_f32(&stream, h)?;
6926 unsafe {
6927 ck(
6928 "hc_mean prefill tap",
6929 k::memra_dsv4_hc_mean(
6930 dpf!(h_dev, &stream),
6931 dpm!(tmp, &stream),
6932 s as i32,
6933 hc as i32,
6934 hidden as i32,
6935 sp(&stream),
6936 ),
6937 )?;
6938 ck(
6939 "place_cols prefill tap",
6940 k::memra_dsv4_place_cols(
6941 dpf!(tmp, &stream),
6942 dpm!(main_hidden, &stream),
6943 s as i32,
6944 hidden as i32,
6945 (n_t * hidden) as i64,
6946 (k * hidden) as i64,
6947 sp(&stream),
6948 ),
6949 )?;
6950 }
6951 }
6952 self.dspark_prime_prefill(dstate, &main_hidden, s)?;
6953 {
6958 let src = main_hidden.slice((s - 1) * n_t * hidden..s * n_t * hidden);
6959 let mut dst = dstate.taps.slice_mut(0..n_t * hidden);
6960 stream
6961 .memcpy_dtod(&src, &mut dst)
6962 .map_err(e("seed tap row"))?;
6963 }
6964 stream.synchronize().map_err(e("prime sync"))?;
6965 Ok(out)
6966 }
6967
6968 pub fn dspark_write_rings(
6972 &self,
6973 state: &mut DsparkState,
6974 tap_row: usize,
6975 pos: usize,
6976 ) -> Res<()> {
6977 let d = self.model.cfg();
6978 let hd = d.head_dim as usize;
6979 let win = d.sliding_window as usize;
6980 let hidden = self.model.mc.n_embd as usize;
6981 let n_t = self.dspark().targets.len();
6982 let last = self.stages.len() - 1;
6983 let stream = self.stages[last].gpu.stream();
6984 let tap = {
6985 let mut row = stream
6987 .alloc_zeros::<f32>(n_t * hidden)
6988 .map_err(e("tap row"))?;
6989 let src = state
6990 .taps
6991 .slice(tap_row * n_t * hidden..(tap_row + 1) * n_t * hidden);
6992 stream.memcpy_dtod(&src, &mut row).map_err(e("tap copy"))?;
6993 row
6994 };
6995 let mx = self.dspark_main_x(&tap, 1)?;
6996 let n_blocks = self.dspark().blocks.len();
6997 for bi in 0..n_blocks {
6998 let blk = &self.dspark().blocks[bi];
6999 let kv = self.dspark_main_kv(blk, &mx, 1, &[pos as i32])?;
7000 let slot = pos % win;
7001 let src = kv.slice(0..hd);
7002 let mut dst = state.rings[bi].slice_mut(slot * hd..(slot + 1) * hd);
7003 stream
7004 .memcpy_dtod(&src, &mut dst)
7005 .map_err(e("ring write"))?;
7006 }
7007 Ok(())
7008 }
7009
7010 #[allow(clippy::too_many_arguments)]
7014 fn dspark_block_forward(
7015 &self,
7016 blk: &LayerDev,
7017 ring: &mut CudaSlice<f32>,
7018 h: &CudaSlice<f32>,
7019 block: usize,
7020 pos: usize,
7021 ) -> Res<CudaSlice<f32>> {
7022 let d = self.model.cfg();
7023 let mc = &self.model.mc;
7024 let hc = d.hc_mult as usize;
7025 let hidden = mc.n_embd as usize;
7026 let heads = mc.n_head as usize;
7027 let hd = d.head_dim as usize;
7028 let rd = d.qk_rope_head_dim as usize;
7029 let q_lora = d.q_lora_rank as usize;
7030 let win = d.sliding_window as usize;
7031 let o_groups = d.o_groups as usize;
7032 let o_lora = d.o_lora_rank as usize;
7033 let eps = mc.rms_eps;
7034 let iters = d.hc_sinkhorn_iters;
7035 let hc_eps = d.hc_eps;
7036 let last = self.stages.len() - 1;
7037 let st = &self.stages[last];
7038 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx dspark"))?;
7039 let stream = st.gpu.stream();
7040 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
7041 let positions: Vec<i32> = (1..=block as i32).map(|j| pos as i32 + j).collect();
7043 let pos_dev = upload_i32(&stream, &positions)?;
7044
7045 let (y, post, comb) = Self::hc_pre(
7047 st,
7048 h,
7049 &blk.hc_attn_fn,
7050 &blk.hc_attn_base,
7051 &blk.hc_attn_scale,
7052 block,
7053 hc,
7054 hidden,
7055 iters,
7056 hc_eps,
7057 )?;
7058 let mut x = stream.alloc_zeros::<f32>(block * hidden).map_err(e("x"))?;
7059 unsafe {
7060 ck(
7061 "rmsnorm dspark attn",
7062 k::memra_dsv4_rmsnorm(
7063 dpf!(y, &stream),
7064 dpf!(blk.attn_norm, &stream),
7065 dpm!(x, &stream),
7066 block as i32,
7067 hidden as i32,
7068 eps,
7069 sp(&stream),
7070 ),
7071 )?;
7072 }
7073 let mut qr = stream.alloc_zeros::<f32>(block * q_lora).map_err(e("qr"))?;
7075 Self::gemm(st, &x, blk.wq_a.dev(), 0, block, q_lora, hidden, &mut qr)?;
7076 unsafe {
7077 ck(
7078 "rmsnorm dspark q",
7079 k::memra_dsv4_rmsnorm(
7080 dpf!(qr, &stream),
7081 dpf!(blk.q_norm, &stream),
7082 dpm!(qr, &stream),
7083 block as i32,
7084 q_lora as i32,
7085 eps,
7086 sp(&stream),
7087 ),
7088 )?;
7089 }
7090 let mut qr_b = stream
7091 .alloc_zeros::<u8>(block * q_lora * 2)
7092 .map_err(e("qr_b"))?;
7093 unsafe {
7094 ck(
7095 "cvt dspark qr",
7096 k::memra_dsv4_cvt_bf16(
7097 dpf!(qr, &stream),
7098 qr_b.device_ptr_mut(&stream).0 as *mut c_void,
7099 (block * q_lora) as i64,
7100 sp(&stream),
7101 ),
7102 )?;
7103 }
7104 let mut q = stream
7105 .alloc_zeros::<f32>(block * heads * hd)
7106 .map_err(e("q"))?;
7107 Self::gemm_pre(
7108 st,
7109 &qr_b,
7110 blk.wq_b.dev().device_ptr(&stream).0 as *const c_void,
7111 block,
7112 heads * hd,
7113 q_lora,
7114 &mut q,
7115 )?;
7116 unsafe {
7117 ck(
7118 "headrms dspark",
7119 k::memra_dsv4_headrms(
7120 dpm!(q, &stream),
7121 (block * heads) as i32,
7122 hd as i32,
7123 eps,
7124 sp(&stream),
7125 ),
7126 )?;
7127 ck(
7128 "rope dspark q",
7129 k::memra_dsv4_rope(
7130 dpm!(q, &stream),
7131 block as i32,
7132 heads as i32,
7133 hd as i32,
7134 rd as i32,
7135 dpf!(st.fc_plain, &stream),
7136 pos_dev.device_ptr(&stream).0 as *const i32,
7137 0,
7138 sp(&stream),
7139 ),
7140 )?;
7141 }
7142 {
7144 let mut kv = stream.alloc_zeros::<f32>(block * hd).map_err(e("dkv"))?;
7145 Self::gemm(st, &x, blk.wkv.dev(), 0, block, hd, hidden, &mut kv)?;
7146 unsafe {
7147 ck(
7148 "rmsnorm dspark dkv",
7149 k::memra_dsv4_rmsnorm(
7150 dpf!(kv, &stream),
7151 dpf!(blk.kv_norm, &stream),
7152 dpm!(kv, &stream),
7153 block as i32,
7154 hd as i32,
7155 eps,
7156 sp(&stream),
7157 ),
7158 )?;
7159 ck(
7160 "rope dspark dkv",
7161 k::memra_dsv4_rope(
7162 dpm!(kv, &stream),
7163 block as i32,
7164 1,
7165 hd as i32,
7166 rd as i32,
7167 dpf!(st.fc_plain, &stream),
7168 pos_dev.device_ptr(&stream).0 as *const i32,
7169 0,
7170 sp(&stream),
7171 ),
7172 )?;
7173 ck(
7174 "act_quant dspark dkv",
7175 k::memra_dsv4_act_quant(
7176 dpm!(kv, &stream),
7177 block as i32,
7178 hd as i64,
7179 (hd - rd) as i32,
7180 64,
7181 clamp_only,
7182 sp(&stream),
7183 ),
7184 )?;
7185 }
7186 let src = kv.slice(0..block * hd);
7187 let mut dst = ring.slice_mut(win * hd..(win + block) * hd);
7188 stream.memcpy_dtod(&src, &mut dst).map_err(e("draft kv"))?;
7189 }
7190 let n_ring = win.min(pos + 1);
7194 let mut idx_row: Vec<i32> = (0..n_ring as i32).collect();
7195 idx_row.extend((0..block as i32).map(|j| win as i32 + j));
7196 let slots = idx_row.len();
7197 let mut idxs = Vec::with_capacity(block * slots);
7198 for _ in 0..block {
7199 idxs.extend_from_slice(&idx_row);
7200 }
7201 let idx_dev = upload_i32(&stream, &idxs)?;
7202 let mut o = stream
7203 .alloc_zeros::<f32>(block * heads * hd)
7204 .map_err(e("o"))?;
7205 let scale = (hd as f64).powf(-0.5) as f32;
7206 unsafe {
7207 ck(
7208 "sink_attn dspark",
7209 k::memra_dsv4_sink_attn(
7210 dpf!(q, &stream),
7211 dpf!(ring, &stream),
7212 idx_dev.device_ptr(&stream).0 as *const i32,
7213 dpf!(blk.sink, &stream),
7214 dpm!(o, &stream),
7215 block as i32,
7216 heads as i32,
7217 hd as i32,
7218 slots as i32,
7219 scale,
7220 sp(&stream),
7221 ),
7222 )?;
7223 ck(
7224 "rope dspark o inv",
7225 k::memra_dsv4_rope(
7226 dpm!(o, &stream),
7227 block as i32,
7228 heads as i32,
7229 hd as i32,
7230 rd as i32,
7231 dpf!(st.fc_plain, &stream),
7232 pos_dev.device_ptr(&stream).0 as *const i32,
7233 1,
7234 sp(&stream),
7235 ),
7236 )?;
7237 }
7238 let gw = heads / o_groups * hd;
7240 let mut og = stream
7241 .alloc_zeros::<f32>(block * o_groups * o_lora)
7242 .map_err(e("og"))?;
7243 let mut o_grp = stream.alloc_zeros::<f32>(block * gw).map_err(e("o_grp"))?;
7244 let mut y_grp = stream
7245 .alloc_zeros::<f32>(block * o_lora)
7246 .map_err(e("y_grp"))?;
7247 for g in 0..o_groups {
7248 unsafe {
7249 ck(
7250 "take_cols dspark",
7251 k::memra_dsv4_take_cols(
7252 dpf!(o, &stream),
7253 dpm!(o_grp, &stream),
7254 block as i32,
7255 gw as i32,
7256 (heads * hd) as i64,
7257 (g * gw) as i64,
7258 sp(&stream),
7259 ),
7260 )?;
7261 }
7262 Self::gemm(
7263 st,
7264 &o_grp,
7265 blk.wo_a.dev(),
7266 g * o_lora * gw,
7267 block,
7268 o_lora,
7269 gw,
7270 &mut y_grp,
7271 )?;
7272 unsafe {
7273 ck(
7274 "place_cols dspark",
7275 k::memra_dsv4_place_cols(
7276 dpf!(y_grp, &stream),
7277 dpm!(og, &stream),
7278 block as i32,
7279 o_lora as i32,
7280 (o_groups * o_lora) as i64,
7281 (g * o_lora) as i64,
7282 sp(&stream),
7283 ),
7284 )?;
7285 }
7286 }
7287 let mut attn_out = stream.alloc_zeros::<f32>(block * hidden).map_err(e("ao"))?;
7288 Self::gemm(
7289 st,
7290 &og,
7291 blk.wo_b.dev(),
7292 0,
7293 block,
7294 hidden,
7295 o_groups * o_lora,
7296 &mut attn_out,
7297 )?;
7298 let mut h2 = stream
7299 .alloc_zeros::<f32>(block * hc * hidden)
7300 .map_err(e("h2"))?;
7301 unsafe {
7302 ck(
7303 "hc_post dspark attn",
7304 k::memra_dsv4_hc_post(
7305 dpf!(attn_out, &stream),
7306 dpf!(h, &stream),
7307 dpf!(post, &stream),
7308 dpf!(comb, &stream),
7309 dpm!(h2, &stream),
7310 block as i32,
7311 hc as i32,
7312 hidden as i32,
7313 sp(&stream),
7314 ),
7315 )?;
7316 }
7317 let (y2, post2, comb2) = Self::hc_pre(
7319 st,
7320 &h2,
7321 &blk.hc_ffn_fn,
7322 &blk.hc_ffn_base,
7323 &blk.hc_ffn_scale,
7324 block,
7325 hc,
7326 hidden,
7327 iters,
7328 hc_eps,
7329 )?;
7330 let mut xf = stream.alloc_zeros::<f32>(block * hidden).map_err(e("xf"))?;
7331 unsafe {
7332 ck(
7333 "rmsnorm dspark ffn",
7334 k::memra_dsv4_rmsnorm(
7335 dpf!(y2, &stream),
7336 dpf!(blk.ffn_norm, &stream),
7337 dpm!(xf, &stream),
7338 block as i32,
7339 hidden as i32,
7340 eps,
7341 sp(&stream),
7342 ),
7343 )?;
7344 }
7345 let ids = vec![0u32; block];
7346 let moe_out = self.moe_forward(st, blk, &xf, block, &ids)?;
7347 let mut h3 = stream
7348 .alloc_zeros::<f32>(block * hc * hidden)
7349 .map_err(e("h3"))?;
7350 unsafe {
7351 ck(
7352 "hc_post dspark ffn",
7353 k::memra_dsv4_hc_post(
7354 dpf!(moe_out, &stream),
7355 dpf!(h2, &stream),
7356 dpf!(post2, &stream),
7357 dpf!(comb2, &stream),
7358 dpm!(h3, &stream),
7359 block as i32,
7360 hc as i32,
7361 hidden as i32,
7362 sp(&stream),
7363 ),
7364 )?;
7365 }
7366 Ok(h3)
7367 }
7368
7369 pub fn dspark_forward_spec(
7375 &self,
7376 state: &mut DsparkState,
7377 input_token: u32,
7378 tap_row: usize,
7379 pos: usize,
7380 capture: bool,
7381 ) -> Res<DsparkProposal> {
7382 let ds = self.dspark();
7383 let mc = &self.model.mc;
7384 let d = self.model.cfg();
7385 let hc = d.hc_mult as usize;
7386 let hidden = mc.n_embd as usize;
7387 let eps = mc.rms_eps;
7388 let block = ds.block_size;
7389 let rank = ds.rank;
7390 let vocab = ds.vocab;
7391 let n_t = ds.targets.len();
7392 let last = self.stages.len() - 1;
7393 let st = &self.stages[last];
7394 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx spec"))?;
7395 let stream = st.gpu.stream();
7396
7397 let prof = if dsv4_prof_on() {
7398 Some(stream.clone())
7399 } else {
7400 None
7401 };
7402 let tap = {
7404 let _p = phase!("1a.tap_copy", prof.as_ref());
7405 let mut row = stream.alloc_zeros::<f32>(n_t * hidden).map_err(e("tapr"))?;
7406 let src = state
7407 .taps
7408 .slice(tap_row * n_t * hidden..(tap_row + 1) * n_t * hidden);
7409 stream.memcpy_dtod(&src, &mut row).map_err(e("tap cp"))?;
7410 row
7411 };
7412 let mx = {
7413 let _p = phase!("1b.main_x", prof.as_ref());
7414 self.dspark_main_x(&tap, 1)?
7415 };
7416 let (cap_main_hidden, cap_main_x) = if capture {
7417 (
7418 Some(dtoh_f32(&stream, &tap)?),
7419 Some(dtoh_f32(&stream, &mx)?),
7420 )
7421 } else {
7422 (None, None)
7423 };
7424 let _p_embed = phase!("1c.embed_h2d_repeat", prof.as_ref());
7427 let mut draft_ids = vec![ds.noise_token; block];
7428 draft_ids[0] = input_token;
7429 let e_rows = self.model.embed_rows(&draft_ids);
7430 let e_dev = upload_f32(&stream, &e_rows)?;
7431 let mut h = stream
7432 .alloc_zeros::<f32>(block * hc * hidden)
7433 .map_err(e("h0"))?;
7434 unsafe {
7435 ck(
7436 "repeat_hc dspark",
7437 k::memra_dsv4_repeat_hc(
7438 dpf!(e_dev, &stream),
7439 dpm!(h, &stream),
7440 block as i32,
7441 hc as i32,
7442 hidden as i32,
7443 sp(&stream),
7444 ),
7445 )?;
7446 }
7447 drop(_p_embed);
7448 let mut block_outs: Vec<Vec<f32>> = Vec::new();
7449 let _p_blocks = phase!("1d.drafter_blocks", prof.as_ref());
7450 let n_blocks = ds.blocks.len();
7451 for bi in 0..n_blocks {
7452 let mut ring = std::mem::replace(
7454 &mut state.rings[bi],
7455 stream.alloc_zeros::<f32>(0).map_err(e("swap"))?,
7456 );
7457 let out =
7458 self.dspark_block_forward(&self.dspark().blocks[bi], &mut ring, &h, block, pos);
7459 state.rings[bi] = ring;
7460 h = out?;
7461 if capture {
7462 block_outs.push(dtoh_f32(&stream, &h)?);
7463 }
7464 }
7465 drop(_p_blocks);
7466 let w = hc * hidden;
7469 let _p_mix = phase!("1e.exit_mix_dots", prof.as_ref());
7470 let mut mixes = stream.alloc_zeros::<f32>(block * hc).map_err(e("mx"))?;
7471 self.dspark_head_dots(
7475 st,
7476 h.device_ptr(&stream).0 as *const f32,
7477 ds.hc_head_fn.device_ptr(&stream).0 as *const c_void,
7478 0,
7479 block,
7480 w,
7481 hc,
7482 mixes.device_ptr_mut(&stream).0 as *mut f32,
7483 )?;
7484 unsafe {
7485 ck(
7486 "rowsq dspark head",
7487 k::memra_dsv4_rowsq_scale(
7488 dpf!(h, &stream),
7489 dpm!(mixes, &stream),
7490 block as i32,
7491 w as i32,
7492 hc as i32,
7493 eps,
7494 sp(&stream),
7495 ),
7496 )?;
7497 }
7498 drop(_p_mix);
7499 let _p_mixrt = phase!("1f.mix_D2H_host_H2D", prof.as_ref());
7500 let mut mixes_h = dtoh_f32(&stream, &mixes)?;
7501 for t in 0..block {
7502 for c in 0..hc {
7503 let m = mixes_h[t * hc + c];
7504 mixes_h[t * hc + c] =
7505 sigmoid_f32(m * ds.hc_head_scale[0] + ds.hc_head_base[c]) + d.hc_eps;
7506 }
7507 }
7508 let pre_d = upload_f32(&stream, &mixes_h)?;
7509 drop(_p_mixrt);
7510 let _p_cn = phase!("1g.collapse_norm", prof.as_ref());
7511 let mut xc = stream.alloc_zeros::<f32>(block * hidden).map_err(e("xc"))?;
7512 unsafe {
7513 ck(
7514 "hc_collapse dspark",
7515 k::memra_dsv4_hc_collapse(
7516 dpf!(h, &stream),
7517 dpf!(pre_d, &stream),
7518 dpm!(xc, &stream),
7519 block as i32,
7520 hc as i32,
7521 hidden as i32,
7522 sp(&stream),
7523 ),
7524 )?;
7525 }
7526 let mut normed = stream.alloc_zeros::<f32>(block * hidden).map_err(e("nr"))?;
7527 unsafe {
7528 ck(
7529 "rmsnorm dspark head",
7530 k::memra_dsv4_rmsnorm(
7531 dpf!(xc, &stream),
7532 dpf!(ds.norm, &stream),
7533 dpm!(normed, &stream),
7534 block as i32,
7535 hidden as i32,
7536 eps,
7537 sp(&stream),
7538 ),
7539 )?;
7540 }
7541 drop(_p_cn);
7542 let _p_head = phase!("1h.exit_head_dots", prof.as_ref());
7543 let mut logits = stream.alloc_zeros::<f32>(block * vocab).map_err(e("lg"))?;
7544 self.dspark_head_dots(
7548 st,
7549 normed.device_ptr(&stream).0 as *const f32,
7550 st.head.as_ref().expect("head").device_ptr(&stream).0 as *const c_void,
7551 1,
7552 block,
7553 hidden,
7554 vocab,
7555 logits.device_ptr_mut(&stream).0 as *mut f32,
7556 )?;
7557 drop(_p_head);
7558 let cap_logits_pre = if capture {
7561 Some(dtoh_f32(&stream, &logits)?)
7562 } else {
7563 None
7564 };
7565 let chain_device = dsv4_dspark_chain_device();
7578 let markov_rowblk = dsv4_dspark_markov_rowblk();
7579 let mut w1_row = stream.alloc_zeros::<f32>(rank).map_err(e("w1r"))?;
7580 let mut bias = stream.alloc_zeros::<f32>(vocab).map_err(e("bias"))?;
7581 let mut am_dev = stream.alloc_zeros::<i32>(block + 1).map_err(e("am"))?;
7584 {
7585 let mut dst = am_dev.slice_mut(0..1);
7586 stream
7587 .memcpy_htod(&[input_token as i32][..], &mut dst)
7588 .map_err(e("htod am0"))?;
7589 }
7590 let mut out_ids = vec![input_token];
7591 let mut margins = Vec::with_capacity(block);
7592 let mut top1_logits = Vec::with_capacity(block);
7593 let mut conf_in = stream.alloc_zeros::<f32>(hidden + rank).map_err(e("cin"))?;
7594 let mut conf_out = stream.alloc_zeros::<f32>(block).map_err(e("cout"))?;
7595 let mut confidence = Vec::with_capacity(block);
7596 let _p_mk = phase!("1i.markov_chain", prof.as_ref());
7597 for i in 0..block {
7598 {
7599 let _p = phase!("1i1.markov_w1_gather", prof.as_ref());
7600 if chain_device {
7601 unsafe {
7602 ck(
7603 "markov w1 gather dev",
7604 k::memra_dsv4_gather_row_by_idx(
7605 dpf!(ds.markov_w1, &stream),
7606 am_dev.device_ptr(&stream).0 as *const i32,
7607 i as i32,
7608 dpm!(w1_row, &stream),
7609 rank as i32,
7610 sp(&stream),
7611 ),
7612 )?;
7613 }
7614 } else {
7615 let prev = out_ids[i] as usize;
7616 let src = ds.markov_w1.slice(prev * rank..(prev + 1) * rank);
7617 stream.memcpy_dtod(&src, &mut w1_row).map_err(e("w1 cp"))?;
7618 }
7619 }
7620 {
7621 let _p = phase!("1i2.markov_bias_gemv", prof.as_ref());
7622 if markov_rowblk {
7623 unsafe {
7624 ck(
7625 "dots_f32 markov rowblk",
7626 k::memra_dsv4_dots_f32_rowblk(
7627 dpf!(w1_row, &stream),
7628 dp!(ds.markov_w2, &stream),
7629 0,
7630 dpm!(bias, &stream),
7631 1,
7632 rank as i32,
7633 vocab as i32,
7634 sp(&stream),
7635 ),
7636 )?;
7637 }
7638 } else {
7639 Self::dots(st, &w1_row, &ds.markov_w2, 1, rank, vocab, &mut bias)?;
7640 }
7641 }
7642 let _p_aa = phase!("1i3.markov_add_argmax", prof.as_ref());
7643 unsafe {
7644 ck(
7645 "markov add dspark",
7646 k::memra_dsv4_add_inplace(
7647 (logits.device_ptr_mut(&stream).0 as usize + i * vocab * 4) as *mut f32,
7648 dpf!(bias, &stream),
7649 vocab as i64,
7650 sp(&stream),
7651 ),
7652 )?;
7653 ck(
7654 "argmax dspark",
7655 k::memra_dsv4_argmax(
7656 (logits.device_ptr(&stream).0 as usize + i * vocab * 4) as *const f32,
7657 vocab as i64,
7658 (am_dev.device_ptr_mut(&stream).0 as usize + (i + 1) * 4) as *mut i32,
7659 sp(&stream),
7660 ),
7661 )?;
7662 }
7663 drop(_p_aa);
7664 if !chain_device {
7665 let _p_d2h = phase!("1i4.markov_argmax_D2H_SYNC", None);
7666 let mut am = [0i32; 1];
7667 let view = am_dev.slice(i + 1..i + 2);
7668 stream
7669 .memcpy_dtoh(&view, &mut am[..])
7670 .map_err(e("dtoh am"))?;
7671 stream.synchronize().map_err(e("sync am"))?;
7672 out_ids.push(am[0] as u32);
7673 }
7674 {
7676 let _p = phase!("1i5.conf_in_copies", prof.as_ref());
7677 let src = xc.slice(i * hidden..(i + 1) * hidden);
7678 let mut dst = conf_in.slice_mut(0..hidden);
7679 stream.memcpy_dtod(&src, &mut dst).map_err(e("cin x"))?;
7680 let src = w1_row.slice(0..rank);
7681 let mut dst = conf_in.slice_mut(hidden..hidden + rank);
7682 stream.memcpy_dtod(&src, &mut dst).map_err(e("cin m"))?;
7683 }
7684 {
7685 let _p = phase!("1i6.conf_dots", prof.as_ref());
7690 unsafe {
7691 ck(
7692 "dots_f32 conf dspark",
7693 k::memra_dsv4_dots_f32(
7694 dpf!(conf_in, &stream),
7695 dp!(ds.conf_w, &stream),
7696 0,
7697 (conf_out.device_ptr_mut(&stream).0 as usize + i * 4) as *mut f32,
7698 1,
7699 (hidden + rank) as i32,
7700 1,
7701 sp(&stream),
7702 ),
7703 )?;
7704 }
7705 }
7706 if !chain_device {
7707 let _p = phase!("1i7.conf_D2H_SYNC", None);
7708 let mut c = [0f32; 1];
7709 let view = conf_out.slice(i..i + 1);
7710 stream
7711 .memcpy_dtoh(&view, &mut c[..])
7712 .map_err(e("dtoh cf"))?;
7713 stream.synchronize().map_err(e("sync cf"))?;
7714 confidence.push(c[0]);
7715 }
7716 }
7717 if chain_device {
7718 let _p = phase!("1i8.chain_D2H_SYNC_once", None);
7720 let mut ids = vec![0i32; block];
7721 let view = am_dev.slice(1..block + 1);
7722 stream
7723 .memcpy_dtoh(&view, &mut ids[..])
7724 .map_err(e("dtoh chain ids"))?;
7725 let mut cf = vec![0f32; block];
7726 stream
7727 .memcpy_dtoh(&conf_out, &mut cf[..])
7728 .map_err(e("dtoh chain conf"))?;
7729 stream.synchronize().map_err(e("sync chain"))?;
7730 out_ids.extend(ids.iter().map(|&x| x as u32));
7731 confidence.extend_from_slice(&cf);
7732 }
7733 drop(_p_mk);
7734 let membeds: Vec<f32> = if capture {
7739 let mut m = Vec::with_capacity(block * rank);
7740 #[allow(clippy::needless_range_loop)]
7741 for i in 0..block {
7743 let prev = out_ids[i] as usize;
7744 m.extend_from_slice(&ds.markov_w1_host[prev * rank..(prev + 1) * rank]);
7745 }
7746 m
7747 } else {
7748 Vec::new()
7749 };
7750 let cap = if capture {
7751 let logits_post = dtoh_f32(&stream, &logits)?;
7752 for i in 0..block {
7753 let row = &logits_post[i * vocab..(i + 1) * vocab];
7754 let top = out_ids[i + 1];
7755 let mut second = f32::NEG_INFINITY;
7756 for (vv, &val) in row.iter().enumerate() {
7757 if vv as u32 != top && val > second {
7758 second = val;
7759 }
7760 }
7761 margins.push(row[top as usize] - second);
7762 top1_logits.push(row[top as usize]);
7763 }
7764 Some(DsparkCaptureOut {
7765 main_hidden: cap_main_hidden.unwrap(),
7766 main_x: cap_main_x.unwrap(),
7767 block_outs,
7768 x_collapsed: dtoh_f32(&stream, &xc)?,
7769 logits_pre: cap_logits_pre.unwrap(),
7770 logits_post,
7771 markov_embed: membeds,
7772 })
7773 } else {
7774 None
7775 };
7776 Ok(DsparkProposal {
7777 out_ids,
7778 confidence,
7779 margins,
7780 top1_logits,
7781 capture: cap,
7782 })
7783 }
7784
7785 pub fn decode_step_tap(
7788 &self,
7789 tok: u32,
7790 state: &mut DecodeState,
7791 dspark_state: &mut DsparkState,
7792 tap_row: usize,
7793 ) -> Res<Vec<f32>> {
7794 let DecodePath::Device { host_math } = self.decode_path else {
7795 return Err("decode_step_tap requires MEMRA_DSV4_DECODE_PATH=device".into());
7796 };
7797 let n_t = self.dspark().targets.len();
7798 let hidden = self.model.mc.n_embd as usize;
7799 let (logits, _) = self.decode_step_fast_tap(
7800 tok,
7801 state,
7802 true,
7803 host_math,
7804 Some((&mut dspark_state.taps, tap_row * n_t * hidden)),
7805 )?;
7806 Ok(logits.expect("want_logits"))
7807 }
7808
7809 pub fn decode_step_greedy_tap(
7811 &self,
7812 tok: u32,
7813 state: &mut DecodeState,
7814 dspark_state: &mut DsparkState,
7815 tap_row: usize,
7816 ) -> Res<u32> {
7817 let DecodePath::Device { host_math } = self.decode_path else {
7818 return Err("decode_step_greedy_tap requires MEMRA_DSV4_DECODE_PATH=device".into());
7819 };
7820 let ds = self.dspark();
7821 let hidden = self.model.mc.n_embd as usize;
7822 let n_t = ds.targets.len();
7823 let (_, tok_next) = self.decode_step_fast_tap(
7824 tok,
7825 state,
7826 false,
7827 host_math,
7828 Some((&mut dspark_state.taps, tap_row * n_t * hidden)),
7829 )?;
7830 Ok(tok_next)
7831 }
7832}
7833
7834pub struct VerifyWs {
7868 pub tmax: usize,
7869 h_a: CudaSlice<f32>,
7870 h_b: CudaSlice<f32>,
7871 h_rx: CudaSlice<f32>,
7872 emb: CudaSlice<f32>,
7873 mixes: CudaSlice<f32>,
7874 pre: CudaSlice<f32>,
7875 post: CudaSlice<f32>,
7876 comb: CudaSlice<f32>,
7877 y_hc: CudaSlice<f32>,
7878 x: CudaSlice<f32>,
7879 xf: CudaSlice<f32>,
7880 qr: CudaSlice<f32>,
7881 qr_b: CudaSlice<u8>,
7882 q: CudaSlice<f32>,
7883 kv: CudaSlice<f32>,
7884 qi: CudaSlice<f32>,
7885 wproj: CudaSlice<f32>,
7886 score: CudaSlice<f32>,
7887 idx: CudaSlice<i32>,
7888 idx_stride: usize,
7889 o: CudaSlice<f32>,
7890 o_b: CudaSlice<u8>,
7891 og: CudaSlice<f32>,
7892 attn_out: CudaSlice<f32>,
7893 gemm_xb: CudaSlice<u8>,
7894 raw: CudaSlice<f32>,
7895 sel: CudaSlice<i32>,
7896 selw: CudaSlice<f32>,
7897 order: CudaSlice<i32>,
7898 xq: CudaSlice<u8>,
7899 xs: CudaSlice<f32>,
7900 g1: CudaSlice<f32>,
7901 g3: CudaSlice<f32>,
7902 hbuf: CudaSlice<f32>,
7903 hq: CudaSlice<u8>,
7904 hs: CudaSlice<f32>,
7905 contrib: CudaSlice<f32>,
7906 y: CudaSlice<f32>,
7907 xb: CudaSlice<u8>,
7908 sg1: CudaSlice<f32>,
7909 sg3: CudaSlice<f32>,
7910 shbuf: CudaSlice<f32>,
7911 shb16: CudaSlice<u8>,
7912 sh_out: CudaSlice<f32>,
7913 cmp_emit: CudaSlice<f32>,
7914 cmp_shift: CudaSlice<f32>,
7915 sink_scores: CudaSlice<f32>,
7916 sink_evals: CudaSlice<f32>,
7917 sink_den: CudaSlice<f64>,
7918 head_mixes: CudaSlice<f32>,
7919 head_pre: CudaSlice<f32>,
7920 collapsed: CudaSlice<f32>,
7921 logits: CudaSlice<f32>,
7922 tok: CudaSlice<i32>,
7923 pos_dev: CudaSlice<i32>,
7924 argmax: CudaSlice<i32>,
7925 bounce: CudaSlice<f32>,
7929 slot_rows: CudaSlice<i32>,
7930 tap_tmp: CudaSlice<f32>,
7932}
7933
7934struct CmpCkptDev {
7939 kv_snap: CudaSlice<f32>,
7940 sc_snap: CudaSlice<f32>,
7941 rows_kv: CudaSlice<f32>,
7942 rows_sc: CudaSlice<f32>,
7943 latent: usize,
7944 ratio: usize,
7945 overlap: bool,
7946 n_blocks0: usize,
7947}
7948
7949struct LayerCkptDev {
7952 cmp: Option<CmpCkptDev>,
7953 idx: Option<CmpCkptDev>,
7954 trans_base: usize,
7956}
7957
7958pub struct VerifyState {
7960 ws: Vec<VerifyWs>,
7961 layers: Vec<LayerCkptDev>,
7962 pub tmax: usize,
7963 open: Option<(usize, usize)>,
7965 pub bytes: Vec<u64>,
7967}
7968
7969impl Dsv4Gpu {
7970 pub fn verify_tmax(&self) -> usize {
7973 self.dspark.as_ref().map(|d| d.block_size + 1).unwrap_or(0)
7974 }
7975
7976 pub fn alloc_verify_state(&self) -> Res<VerifyState> {
7979 let tmax = self.verify_tmax();
7980 if tmax == 0 {
7981 return Err("alloc_verify_state needs MEMRA_DSV4_DRAFTER=dspark".into());
7982 }
7983 if !matches!(self.decode_path, DecodePath::Device { .. }) {
7984 return Err(
7985 "batched verify is a device-path rung (MEMRA_DSV4_DECODE_PATH=device)".into(),
7986 );
7987 }
7988 let d = self.model.cfg();
7989 let mc = &self.model.mc;
7990 let moe = mc.moe.as_ref().expect("moe");
7991 let hc = d.hc_mult as usize;
7992 let hidden = mc.n_embd as usize;
7993 let heads = mc.n_head as usize;
7994 let hd = d.head_dim as usize;
7995 let q_lora = d.q_lora_rank as usize;
7996 let win = d.sliding_window as usize;
7997 let o_groups = d.o_groups as usize;
7998 let o_lora = d.o_lora_rank as usize;
7999 let iheads = d.index_n_heads as usize;
8000 let ihd = d.index_head_dim as usize;
8001 let topk = moe.expert_used_count as usize;
8002 let ne = moe.expert_count as usize;
8003 let inter = moe.expert_ff_length as usize;
8004 let itopk = d.index_topk as usize;
8005 let n_trunk = (mc.n_layer - mc.nextn_predict_layers) as usize;
8006 let vocab = {
8007 let (info, _) = self.model.st.raw("head.weight").expect("head");
8008 info.shape[0] as usize
8009 };
8010 let sh_inter = {
8011 let (info, _) = self
8012 .model
8013 .st
8014 .raw("layers.0.ffn.shared_experts.w1.weight")
8015 .expect("shared w1");
8016 info.shape[0] as usize
8017 };
8018 let mut max_d = 0usize;
8019 let mut max_shift = 0usize;
8020 let mut min_ratio = usize::MAX;
8021 for st in &self.stages {
8022 for l in &st.layers {
8023 for cmp in l.cmp.iter().chain(l.idx.as_ref().map(|ix| &ix.cmp)) {
8024 max_d = max_d.max(cmp.d);
8025 if cmp.overlap {
8026 max_shift = max_shift.max(cmp.ratio * cmp.latent);
8027 }
8028 min_ratio = min_ratio.min(cmp.ratio);
8029 }
8030 }
8031 }
8032 assert!(min_ratio != usize::MAX, "no compressor layers?");
8033 let score_cap = self.max_seq / min_ratio + 1;
8034 let idx_tail = itopk.max(self.max_seq / 128 + 1);
8035 let idx_stride = win + idx_tail;
8036 let max_gemm_k = (o_groups * o_lora).max(hidden).max(q_lora).max(sh_inter);
8037 let mut bytes = vec![0u64; self.stages.len()];
8038 let mut ws = Vec::with_capacity(self.stages.len());
8039 for st in &self.stages {
8040 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx vws"))?;
8041 let s = st.gpu.stream();
8042 let acc = std::cell::Cell::new(0u64);
8043 let f = |n: usize| {
8044 acc.set(acc.get() + (n * 4) as u64);
8045 s.alloc_zeros::<f32>(n).map_err(e("vws f32"))
8046 };
8047 let b = |n: usize| {
8048 acc.set(acc.get() + n as u64);
8049 s.alloc_zeros::<u8>(n).map_err(e("vws u8"))
8050 };
8051 let i = |n: usize| {
8052 acc.set(acc.get() + (n * 4) as u64);
8053 s.alloc_zeros::<i32>(n).map_err(e("vws i32"))
8054 };
8055 let w = VerifyWs {
8056 tmax,
8057 h_a: f(tmax * hc * hidden)?,
8058 h_b: f(tmax * hc * hidden)?,
8059 h_rx: f(tmax * hc * hidden)?,
8060 emb: f(tmax * hidden)?,
8061 mixes: f(tmax * (2 + hc) * hc)?,
8062 pre: f(tmax * hc)?,
8063 post: f(tmax * hc)?,
8064 comb: f(tmax * hc * hc)?,
8065 y_hc: f(tmax * hidden)?,
8066 x: f(tmax * hidden)?,
8067 xf: f(tmax * hidden)?,
8068 qr: f(tmax * q_lora)?,
8069 qr_b: b(tmax * q_lora * 2)?,
8070 q: f(tmax * heads * hd)?,
8071 kv: f(tmax * hd)?,
8072 qi: f(tmax * iheads * ihd)?,
8073 wproj: f(tmax * iheads)?,
8074 score: f(score_cap)?,
8075 idx: i(tmax * idx_stride)?,
8076 idx_stride,
8077 o: f(tmax * heads * hd)?,
8078 o_b: b(tmax * heads * hd * 2)?,
8079 og: f(tmax * o_groups * o_lora)?,
8080 attn_out: f(tmax * hidden)?,
8081 gemm_xb: b(tmax * max_gemm_k * 2)?,
8082 raw: f(tmax * ne)?,
8083 sel: i(tmax * topk)?,
8084 selw: f(tmax * topk)?,
8085 order: i(tmax * topk)?,
8086 xq: b(tmax * hidden)?,
8087 xs: f(tmax * hidden / 128)?,
8088 g1: f(tmax * topk * inter)?,
8089 g3: f(tmax * topk * inter)?,
8090 hbuf: f(tmax * topk * inter)?,
8091 hq: b(tmax * topk * inter)?,
8092 hs: f(tmax * topk * inter / 128)?,
8093 contrib: f(tmax * topk * hidden)?,
8094 y: f(tmax * hidden)?,
8095 xb: b(tmax * hidden * 2)?,
8096 sg1: f(tmax * sh_inter)?,
8097 sg3: f(tmax * sh_inter)?,
8098 shbuf: f(tmax * sh_inter)?,
8099 shb16: b(tmax * sh_inter * 2)?,
8100 sh_out: f(tmax * hidden)?,
8101 cmp_emit: f(2 * max_d)?,
8102 cmp_shift: f(max_shift.max(1))?,
8103 sink_scores: f(tmax * heads * idx_stride)?,
8104 sink_evals: f(tmax * heads * idx_stride)?,
8105 sink_den: {
8106 acc.set(acc.get() + (tmax * heads * 8) as u64);
8107 s.alloc_zeros::<f64>(tmax * heads).map_err(e("vws f64"))?
8108 },
8109 head_mixes: f(tmax * hc)?,
8110 head_pre: f(tmax * hc)?,
8111 collapsed: f(tmax * hidden)?,
8112 logits: f(tmax * vocab)?,
8113 tok: i(tmax)?,
8114 pos_dev: i(tmax)?,
8115 argmax: i(tmax)?,
8116 bounce: f(tmax * hd)?,
8117 slot_rows: i(tmax)?,
8118 tap_tmp: f(tmax * hidden)?,
8119 };
8120 bytes[st.dev] += acc.get();
8121 ws.push(w);
8122 }
8123 let mut layers = Vec::with_capacity(n_trunk);
8125 for il in 0..n_trunk {
8126 let stage_i = self.layer_stage[il];
8127 let st = &self.stages[stage_i];
8128 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx ckpt"))?;
8129 let stream = st.gpu.stream();
8130 let lidx = st
8131 .layers
8132 .iter()
8133 .position(|l| l.il == il as u32)
8134 .unwrap_or_else(|| panic!("layer {il} not on stage {stage_i}"));
8135 let layer = &st.layers[lidx];
8136 let cap_blocks = self.max_seq.checked_div(layer.ratio).unwrap_or(0);
8137 let mk = |cmp: &CmpDev| -> Res<CmpCkptDev> {
8138 let slots = if cmp.overlap {
8139 2 * cmp.ratio
8140 } else {
8141 cmp.ratio
8142 };
8143 Ok(CmpCkptDev {
8144 kv_snap: stream
8145 .alloc_zeros::<f32>(slots * cmp.latent)
8146 .map_err(e("ckpt kv snap"))?,
8147 sc_snap: stream
8148 .alloc_zeros::<f32>(slots * cmp.latent)
8149 .map_err(e("ckpt sc snap"))?,
8150 rows_kv: stream
8151 .alloc_zeros::<f32>(tmax * cmp.latent)
8152 .map_err(e("ckpt rows kv"))?,
8153 rows_sc: stream
8154 .alloc_zeros::<f32>(tmax * cmp.latent)
8155 .map_err(e("ckpt rows sc"))?,
8156 latent: cmp.latent,
8157 ratio: cmp.ratio,
8158 overlap: cmp.overlap,
8159 n_blocks0: 0,
8160 })
8161 };
8162 let cmp = match &layer.cmp {
8163 Some(c) => Some(mk(c)?),
8164 None => None,
8165 };
8166 let idxc = match &layer.idx {
8167 Some(ix) => Some(mk(&ix.cmp)?),
8168 None => None,
8169 };
8170 for c in cmp.iter().chain(idxc.iter()) {
8171 let slots = if c.overlap { 2 * c.ratio } else { c.ratio };
8172 bytes[st.dev] += ((2 * slots * c.latent + 2 * tmax * c.latent) * 4) as u64;
8173 }
8174 layers.push(LayerCkptDev {
8175 cmp,
8176 idx: idxc,
8177 trans_base: d.sliding_window as usize + cap_blocks,
8178 });
8179 }
8180 for st in &self.stages {
8181 st.gpu.stream().synchronize().map_err(e("vws sync"))?;
8182 }
8183 Ok(VerifyState {
8184 ws,
8185 layers,
8186 tmax,
8187 open: None,
8188 bytes,
8189 })
8190 }
8191
8192 #[allow(clippy::too_many_arguments)]
8196 fn gemv_m_dev(
8197 st: &Stage,
8198 w: DW,
8199 x_ptr: *const c_void,
8200 y_ptr: *mut f32,
8201 m: usize,
8202 n: usize,
8203 kdim: usize,
8204 xstride: usize,
8205 ystride: usize,
8206 ) -> Res<()> {
8207 let stream = st.gpu.stream();
8208 unsafe {
8209 match w {
8210 DW::Bf16(w_ptr) => ck(
8211 "gemv_bf16_m dev",
8212 k::memra_dsv4_gemv_bf16_m(
8213 w_ptr,
8214 x_ptr,
8215 y_ptr,
8216 m as i32,
8217 n as i32,
8218 kdim as i32,
8219 xstride as i32,
8220 ystride as i32,
8221 sp(&stream),
8222 ),
8223 ),
8224 DW::Fp8 {
8225 codes,
8226 scales,
8227 sc_cols,
8228 } => ck(
8229 "gemv_fp8_m dev",
8230 k::memra_dsv4_gemv_fp8_m(
8231 codes,
8232 scales,
8233 sc_cols,
8234 x_ptr,
8235 y_ptr,
8236 m as i32,
8237 n as i32,
8238 kdim as i32,
8239 xstride as i32,
8240 ystride as i32,
8241 sp(&stream),
8242 ),
8243 ),
8244 }
8245 }
8246 }
8247
8248 #[allow(clippy::too_many_arguments)]
8250 fn gemm_m_dev(
8251 st: &Stage,
8252 x_f32: *const f32,
8253 xb: &mut CudaSlice<u8>,
8254 w: DW,
8255 m: usize,
8256 n: usize,
8257 kdim: usize,
8258 y_ptr: *mut f32,
8259 ) -> Res<()> {
8260 let stream = st.gpu.stream();
8261 unsafe {
8262 ck(
8263 "cvt_bf16 m dev",
8264 k::memra_dsv4_cvt_bf16(
8265 x_f32,
8266 xb.device_ptr_mut(&stream).0 as *mut c_void,
8267 (m * kdim) as i64,
8268 sp(&stream),
8269 ),
8270 )?;
8271 }
8272 Self::gemv_m_dev(
8273 st,
8274 w,
8275 xb.device_ptr(&stream).0 as *const c_void,
8276 y_ptr,
8277 m,
8278 n,
8279 kdim,
8280 0,
8281 0,
8282 )
8283 }
8284
8285 #[allow(clippy::too_many_arguments)]
8287 fn dots_m_dev(
8288 &self,
8289 st: &Stage,
8290 x: *const f32,
8291 w_f32: *const c_void,
8292 w_is_bf16: i32,
8293 s: usize,
8294 kdim: usize,
8295 n: usize,
8296 y: *mut f32,
8297 ) -> Res<()> {
8298 let stream = st.gpu.stream();
8299 unsafe {
8300 if self.dots_f32 {
8301 ck(
8302 "dots_f32acc_mrow",
8303 k::memra_dsv4_dots_f32acc_mrow(
8304 x,
8305 w_f32,
8306 w_is_bf16,
8307 y,
8308 s as i32,
8309 kdim as i32,
8310 n as i32,
8311 sp(&stream),
8312 ),
8313 )
8314 } else {
8315 ck(
8316 "dots_f32_mrow",
8317 k::memra_dsv4_dots_f32_mrow(
8318 x,
8319 w_f32,
8320 w_is_bf16,
8321 y,
8322 s as i32,
8323 kdim as i32,
8324 n as i32,
8325 sp(&stream),
8326 ),
8327 )
8328 }
8329 }
8330 }
8331}
8332
8333impl Dsv4Gpu {
8334 #[allow(clippy::too_many_arguments)]
8338 fn hc_pre_batch_dev(
8339 &self,
8340 st: &Stage,
8341 h_ptr: *const f32,
8342 fn_w: &CudaSlice<f32>,
8343 base_host: &[f32],
8344 scale_host: &[f32],
8345 base_dev: &CudaSlice<f32>,
8346 scale_dev: &CudaSlice<f32>,
8347 vws: &mut VerifyWs,
8348 t: usize,
8349 hc: usize,
8350 hidden: usize,
8351 iters: u32,
8352 hc_eps: f32,
8353 host_math: bool,
8354 ) -> Res<()> {
8355 let stream = st.gpu.stream();
8356 let w = hc * hidden;
8357 let rows = (2 + hc) * hc;
8358 self.dots_m_dev(
8359 st,
8360 h_ptr,
8361 fn_w.device_ptr(&stream).0 as *const c_void,
8362 0,
8363 t,
8364 w,
8365 rows,
8366 vws.mixes.device_ptr_mut(&stream).0 as *mut f32,
8367 )?;
8368 unsafe {
8369 ck(
8370 "rowsq_scale batch",
8371 self.rowsq_scale_arm(
8372 h_ptr,
8373 dpm!(vws.mixes, &stream),
8374 t as i32,
8375 w as i32,
8376 rows as i32,
8377 hc_eps,
8378 sp(&stream),
8379 ),
8380 )?;
8381 }
8382 if host_math {
8383 let mut mixes_h = vec![0f32; t * rows];
8384 let view = vws.mixes.slice(0..t * rows);
8385 stream
8386 .memcpy_dtoh(&view, &mut mixes_h[..])
8387 .map_err(e("dtoh mixes batch"))?;
8388 stream.synchronize().map_err(e("sync mixes batch"))?;
8389 let (pre_h, post_h, comb_h) =
8390 hc_split_sinkhorn(&mixes_h, t, hc, scale_host, base_host, iters, hc_eps);
8391 let mut dp = vws.pre.slice_mut(0..t * hc);
8392 stream
8393 .memcpy_htod(&pre_h, &mut dp)
8394 .map_err(e("htod pre b"))?;
8395 let mut dp = vws.post.slice_mut(0..t * hc);
8396 stream
8397 .memcpy_htod(&post_h, &mut dp)
8398 .map_err(e("htod post b"))?;
8399 let mut dp = vws.comb.slice_mut(0..t * hc * hc);
8400 stream
8401 .memcpy_htod(&comb_h, &mut dp)
8402 .map_err(e("htod comb b"))?;
8403 } else {
8404 unsafe {
8405 ck(
8406 "hc_sinkhorn_m",
8407 k::memra_dsv4_hc_sinkhorn_m(
8408 dpf!(vws.mixes, &stream),
8409 dpf!(scale_dev, &stream),
8410 dpf!(base_dev, &stream),
8411 dpm!(vws.pre, &stream),
8412 dpm!(vws.post, &stream),
8413 dpm!(vws.comb, &stream),
8414 t as i32,
8415 hc as i32,
8416 iters as i32,
8417 hc_eps,
8418 sp(&stream),
8419 ),
8420 )?;
8421 }
8422 }
8423 unsafe {
8424 ck(
8425 "hc_collapse batch",
8426 k::memra_dsv4_hc_collapse(
8427 h_ptr,
8428 dpf!(vws.pre, &stream),
8429 dpm!(vws.y_hc, &stream),
8430 t as i32,
8431 hc as i32,
8432 hidden as i32,
8433 sp(&stream),
8434 ),
8435 )?;
8436 }
8437 Ok(())
8438 }
8439
8440 #[allow(clippy::too_many_arguments)]
8446 #[allow(clippy::manual_is_multiple_of)] fn cmp_decode_batch_dev(
8448 &self,
8449 st: &Stage,
8450 cmp: &CmpDev,
8451 x_ptr: *const f32,
8452 t: usize,
8453 pos0: usize,
8454 hidden: usize,
8455 fc_dev: &CudaSlice<f32>,
8456 rd: usize,
8457 eps: f32,
8458 ck_dev: &mut CmpCkptDev,
8459 emit: &mut CudaSlice<f32>,
8460 shift: &mut CudaSlice<f32>,
8461 pend_kv: &mut CudaSlice<f32>,
8462 pend_score: &mut CudaSlice<f32>,
8463 store: &mut CudaSlice<f32>,
8464 row0: usize,
8465 blocks: &mut usize,
8466 ) -> Res<()> {
8467 let stream = st.gpu.stream();
8468 let (ratio, d, latent) = (cmp.ratio, cmp.d, cmp.latent);
8469 stream
8471 .memcpy_dtod(pend_kv, &mut ck_dev.kv_snap)
8472 .map_err(e("ckpt snap kv"))?;
8473 stream
8474 .memcpy_dtod(pend_score, &mut ck_dev.sc_snap)
8475 .map_err(e("ckpt snap sc"))?;
8476 ck_dev.n_blocks0 = *blocks;
8477 self.dots_m_dev(
8478 st,
8479 x_ptr,
8480 cmp.wkv.device_ptr(&stream).0 as *const c_void,
8481 0,
8482 t,
8483 hidden,
8484 latent,
8485 ck_dev.rows_kv.device_ptr_mut(&stream).0 as *mut f32,
8486 )?;
8487 self.dots_m_dev(
8488 st,
8489 x_ptr,
8490 cmp.wgate.device_ptr(&stream).0 as *const c_void,
8491 0,
8492 t,
8493 hidden,
8494 latent,
8495 ck_dev.rows_sc.device_ptr_mut(&stream).0 as *mut f32,
8496 )?;
8497 for i in 0..t {
8498 let pos = pos0 + i;
8499 let slot = if cmp.overlap {
8500 ratio + pos % ratio
8501 } else {
8502 pos % ratio
8503 };
8504 {
8505 let src = ck_dev.rows_kv.slice(i * latent..(i + 1) * latent);
8506 let mut dst = pend_kv.slice_mut(slot * latent..(slot + 1) * latent);
8507 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend kv b"))?;
8508 let src = ck_dev.rows_sc.slice(i * latent..(i + 1) * latent);
8509 let mut dst = pend_score.slice_mut(slot * latent..(slot + 1) * latent);
8510 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend sc b"))?;
8511 }
8512 if (pos + 1) % ratio != 0 {
8513 continue;
8514 }
8515 let j = pos / ratio;
8516 let nb_launch = if cmp.overlap { 2usize } else { 1 };
8517 let row_off = if cmp.overlap { d } else { 0 };
8518 unsafe {
8519 ck(
8520 "compressor_pool batch",
8521 k::memra_dsv4_compressor_pool(
8522 dpf!(*pend_kv, &stream),
8523 dpf!(*pend_score, &stream),
8524 dpf!(cmp.ape, &stream),
8525 dpm!(*emit, &stream),
8526 nb_launch as i32,
8527 ratio as i32,
8528 d as i32,
8529 latent as i32,
8530 cmp.overlap as i32,
8531 sp(&stream),
8532 ),
8533 )?;
8534 let row_c = (emit.device_ptr(&stream).0 as usize + row_off * 4) as *const f32;
8535 let row_m = (emit.device_ptr_mut(&stream).0 as usize + row_off * 4) as *mut f32;
8536 ck(
8537 "rmsnorm batch cmp",
8538 self.rmsnorm_arm(
8539 row_c,
8540 dpf!(cmp.norm, &stream),
8541 row_m,
8542 1,
8543 d as i32,
8544 eps,
8545 sp(&stream),
8546 ),
8547 )?;
8548 ck(
8549 "rope_at batch cmp",
8550 k::memra_dsv4_rope_at(
8551 row_m,
8552 1,
8553 d as i32,
8554 rd as i32,
8555 dpf!(fc_dev, &stream),
8556 (j * ratio) as i32,
8557 0,
8558 sp(&stream),
8559 ),
8560 )?;
8561 if cmp.rotate {
8562 let scale = (d as f32).powf(-0.5);
8563 ck(
8564 "hadamard batch cmp",
8565 k::memra_dsv4_hadamard(row_m, 1, d as i32, scale, sp(&stream)),
8566 )?;
8567 ck(
8568 "fp4 batch cmp",
8569 k::memra_dsv4_fp4_act_quant(row_m, 1, d as i64, d as i32, sp(&stream)),
8570 )?;
8571 } else {
8572 ck(
8573 "act_quant batch cmp",
8574 k::memra_dsv4_act_quant(
8575 row_m,
8576 1,
8577 d as i64,
8578 (d - rd) as i32,
8579 64,
8580 (self.variant == ActQuantVariant::ClampOnly) as i32,
8581 sp(&stream),
8582 ),
8583 )?;
8584 }
8585 }
8586 {
8587 let src = emit.slice(row_off..row_off + d);
8588 let mut dst = store.slice_mut((row0 + j) * d..(row0 + j + 1) * d);
8589 stream
8590 .memcpy_dtod(&src, &mut dst)
8591 .map_err(e("emit store b"))?;
8592 }
8593 if cmp.overlap {
8594 {
8595 let src = pend_kv.slice(ratio * latent..2 * ratio * latent);
8596 let mut dst = shift.slice_mut(0..ratio * latent);
8597 stream.memcpy_dtod(&src, &mut dst).map_err(e("bshift1"))?;
8598 }
8599 {
8600 let src = shift.slice(0..ratio * latent);
8601 let mut dst = pend_kv.slice_mut(0..ratio * latent);
8602 stream.memcpy_dtod(&src, &mut dst).map_err(e("bshift2"))?;
8603 }
8604 {
8605 let src = pend_score.slice(ratio * latent..2 * ratio * latent);
8606 let mut dst = shift.slice_mut(0..ratio * latent);
8607 stream.memcpy_dtod(&src, &mut dst).map_err(e("bshift3"))?;
8608 }
8609 {
8610 let src = shift.slice(0..ratio * latent);
8611 let mut dst = pend_score.slice_mut(0..ratio * latent);
8612 stream.memcpy_dtod(&src, &mut dst).map_err(e("bshift4"))?;
8613 }
8614 }
8615 *blocks = j + 1;
8616 }
8617 Ok(())
8618 }
8619
8620 #[allow(clippy::too_many_arguments)]
8626 #[allow(clippy::manual_is_multiple_of)] fn cmp_rollback_replay_dev(
8628 &self,
8629 st: &Stage,
8630 ck_dev: &CmpCkptDev,
8631 n_commit: usize,
8632 t: usize,
8633 pos0: usize,
8634 shift: &mut CudaSlice<f32>,
8635 pend_kv: &mut CudaSlice<f32>,
8636 pend_score: &mut CudaSlice<f32>,
8637 blocks: &mut usize,
8638 ) -> Res<()> {
8639 if n_commit == t {
8640 return Ok(()); }
8642 let stream = st.gpu.stream();
8643 let (ratio, latent, overlap) = (ck_dev.ratio, ck_dev.latent, ck_dev.overlap);
8644 stream
8645 .memcpy_dtod(&ck_dev.kv_snap, pend_kv)
8646 .map_err(e("rb kv snap"))?;
8647 stream
8648 .memcpy_dtod(&ck_dev.sc_snap, pend_score)
8649 .map_err(e("rb sc snap"))?;
8650 *blocks = ck_dev.n_blocks0;
8651 for i in 0..n_commit {
8652 let pos = pos0 + i;
8653 let slot = if overlap {
8654 ratio + pos % ratio
8655 } else {
8656 pos % ratio
8657 };
8658 {
8659 let src = ck_dev.rows_kv.slice(i * latent..(i + 1) * latent);
8660 let mut dst = pend_kv.slice_mut(slot * latent..(slot + 1) * latent);
8661 stream.memcpy_dtod(&src, &mut dst).map_err(e("rb row kv"))?;
8662 let src = ck_dev.rows_sc.slice(i * latent..(i + 1) * latent);
8663 let mut dst = pend_score.slice_mut(slot * latent..(slot + 1) * latent);
8664 stream.memcpy_dtod(&src, &mut dst).map_err(e("rb row sc"))?;
8665 }
8666 if (pos + 1) % ratio != 0 {
8667 continue;
8668 }
8669 if overlap {
8670 {
8671 let src = pend_kv.slice(ratio * latent..2 * ratio * latent);
8672 let mut dst = shift.slice_mut(0..ratio * latent);
8673 stream.memcpy_dtod(&src, &mut dst).map_err(e("rbshift1"))?;
8674 }
8675 {
8676 let src = shift.slice(0..ratio * latent);
8677 let mut dst = pend_kv.slice_mut(0..ratio * latent);
8678 stream.memcpy_dtod(&src, &mut dst).map_err(e("rbshift2"))?;
8679 }
8680 {
8681 let src = pend_score.slice(ratio * latent..2 * ratio * latent);
8682 let mut dst = shift.slice_mut(0..ratio * latent);
8683 stream.memcpy_dtod(&src, &mut dst).map_err(e("rbshift3"))?;
8684 }
8685 {
8686 let src = shift.slice(0..ratio * latent);
8687 let mut dst = pend_score.slice_mut(0..ratio * latent);
8688 stream.memcpy_dtod(&src, &mut dst).map_err(e("rbshift4"))?;
8689 }
8690 }
8691 *blocks += 1;
8692 }
8693 Ok(())
8694 }
8695}
8696
8697impl Dsv4Gpu {
8698 #[allow(clippy::too_many_arguments)]
8704 fn block_verify_dev(
8705 &self,
8706 st: &Stage,
8707 layer: &LayerDev,
8708 cache: &mut LayerCache,
8709 lck: &mut LayerCkptDev,
8710 vws: &mut VerifyWs,
8711 input_rx: bool,
8712 pos0: usize,
8713 t: usize,
8714 toks: &[u32],
8715 host_math: bool,
8716 ) -> Res<()> {
8717 let d = self.model.cfg();
8718 let mc = &self.model.mc;
8719 let hc = d.hc_mult as usize;
8720 let hidden = mc.n_embd as usize;
8721 let heads = mc.n_head as usize;
8722 let hd = d.head_dim as usize;
8723 let rd = d.qk_rope_head_dim as usize;
8724 let q_lora = d.q_lora_rank as usize;
8725 let win = d.sliding_window as usize;
8726 let o_groups = d.o_groups as usize;
8727 let o_lora = d.o_lora_rank as usize;
8728 let eps = mc.rms_eps;
8729 let iters = d.hc_sinkhorn_iters;
8730 let hc_eps = d.hc_eps;
8731 let stream = st.gpu.stream();
8732 let fc_dev: *const f32 = if layer.ratio != 0 {
8733 st.fc_yarn.device_ptr(&stream).0 as *const f32
8734 } else {
8735 st.fc_plain.device_ptr(&stream).0 as *const f32
8736 };
8737 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
8738 let trans_base = lck.trans_base;
8739 let LayerCache {
8740 kvc,
8741 n_blocks,
8742 pend_kv,
8743 pend_score,
8744 ikvc,
8745 i_blocks,
8746 ipend_kv,
8747 ipend_score,
8748 } = cache;
8749
8750 let h_in_ptr: *const f32 = if input_rx {
8752 vws.h_rx.device_ptr(&stream).0 as *const f32
8753 } else {
8754 vws.h_a.device_ptr(&stream).0 as *const f32
8755 };
8756 self.hc_pre_batch_dev(
8757 st,
8758 h_in_ptr,
8759 &layer.hc_attn_fn,
8760 &layer.hc_attn_base,
8761 &layer.hc_attn_scale,
8762 &layer.hc_attn_base_dev,
8763 &layer.hc_attn_scale_dev,
8764 vws,
8765 t,
8766 hc,
8767 hidden,
8768 iters,
8769 hc_eps,
8770 host_math,
8771 )?;
8772 unsafe {
8773 ck(
8774 "rmsnorm attn batch",
8775 self.rmsnorm_arm(
8776 dpf!(vws.y_hc, &stream),
8777 dpf!(layer.attn_norm, &stream),
8778 dpm!(vws.x, &stream),
8779 t as i32,
8780 hidden as i32,
8781 eps,
8782 sp(&stream),
8783 ),
8784 )?;
8785 }
8786
8787 Self::gemm_m_dev(
8789 st,
8790 vws.x.device_ptr(&stream).0 as *const f32,
8791 &mut vws.gemm_xb,
8792 dwsel(self.dense_fp8, &stream, &layer.wq_a, &layer.wq_a_fp8),
8793 t,
8794 q_lora,
8795 hidden,
8796 vws.qr.device_ptr_mut(&stream).0 as *mut f32,
8797 )?;
8798 unsafe {
8799 ck(
8800 "rmsnorm q batch",
8801 self.rmsnorm_arm(
8802 dpf!(vws.qr, &stream),
8803 dpf!(layer.q_norm, &stream),
8804 dpm!(vws.qr, &stream),
8805 t as i32,
8806 q_lora as i32,
8807 eps,
8808 sp(&stream),
8809 ),
8810 )?;
8811 ck(
8812 "cvt qr batch",
8813 k::memra_dsv4_cvt_bf16(
8814 dpf!(vws.qr, &stream),
8815 vws.qr_b.device_ptr_mut(&stream).0 as *mut c_void,
8816 (t * q_lora) as i64,
8817 sp(&stream),
8818 ),
8819 )?;
8820 }
8821 Self::gemv_m_dev(
8822 st,
8823 dwsel(self.dense_fp8, &stream, &layer.wq_b, &layer.wq_b_fp8),
8824 vws.qr_b.device_ptr(&stream).0 as *const c_void,
8825 vws.q.device_ptr_mut(&stream).0 as *mut f32,
8826 t,
8827 heads * hd,
8828 q_lora,
8829 0,
8830 0,
8831 )?;
8832 unsafe {
8833 ck(
8834 "headrms batch",
8835 self.headrms_arm(
8836 dpm!(vws.q, &stream),
8837 (t * heads) as i32,
8838 hd as i32,
8839 eps,
8840 sp(&stream),
8841 ),
8842 )?;
8843 ck(
8844 "rope q batch",
8845 k::memra_dsv4_rope(
8846 dpm!(vws.q, &stream),
8847 t as i32,
8848 heads as i32,
8849 hd as i32,
8850 rd as i32,
8851 fc_dev,
8852 vws.pos_dev.device_ptr(&stream).0 as *const i32,
8853 0,
8854 sp(&stream),
8855 ),
8856 )?;
8857 }
8858
8859 Self::gemm_m_dev(
8861 st,
8862 vws.x.device_ptr(&stream).0 as *const f32,
8863 &mut vws.gemm_xb,
8864 dwsel(self.dense_fp8, &stream, &layer.wkv, &layer.wkv_fp8),
8865 t,
8866 hd,
8867 hidden,
8868 vws.kv.device_ptr_mut(&stream).0 as *mut f32,
8869 )?;
8870 unsafe {
8871 ck(
8872 "rmsnorm kv batch",
8873 self.rmsnorm_arm(
8874 dpf!(vws.kv, &stream),
8875 dpf!(layer.kv_norm, &stream),
8876 dpm!(vws.kv, &stream),
8877 t as i32,
8878 hd as i32,
8879 eps,
8880 sp(&stream),
8881 ),
8882 )?;
8883 ck(
8884 "rope kv batch",
8885 k::memra_dsv4_rope(
8886 dpm!(vws.kv, &stream),
8887 t as i32,
8888 1,
8889 hd as i32,
8890 rd as i32,
8891 fc_dev,
8892 vws.pos_dev.device_ptr(&stream).0 as *const i32,
8893 0,
8894 sp(&stream),
8895 ),
8896 )?;
8897 ck(
8898 "act_quant kv batch",
8899 k::memra_dsv4_act_quant(
8900 dpm!(vws.kv, &stream),
8901 t as i32,
8902 hd as i64,
8903 (hd - rd) as i32,
8904 64,
8905 clamp_only,
8906 sp(&stream),
8907 ),
8908 )?;
8909 }
8910 {
8911 let src = vws.kv.slice(0..t * hd);
8912 let mut dst = kvc.slice_mut(trans_base * hd..(trans_base + t) * hd);
8913 stream
8914 .memcpy_dtod(&src, &mut dst)
8915 .map_err(e("transient ring write"))?;
8916 }
8917
8918 let mut slots = win;
8920 if layer.ratio != 0 {
8921 let ratio = layer.ratio;
8922 let nbs: Vec<usize> = (0..t).map(|i| (pos0 + i + 1) / ratio).collect();
8925 if let Some(ix) = &layer.idx {
8926 Self::gemv_m_dev(
8928 st,
8929 dwsel(self.dense_fp8, &stream, &ix.wq_b, &ix.wq_b_fp8),
8930 vws.qr_b.device_ptr(&stream).0 as *const c_void,
8931 vws.qi.device_ptr_mut(&stream).0 as *mut f32,
8932 t,
8933 ix.heads * ix.hd,
8934 q_lora,
8935 0,
8936 0,
8937 )?;
8938 unsafe {
8939 ck(
8940 "rope qi batch",
8941 k::memra_dsv4_rope(
8942 dpm!(vws.qi, &stream),
8943 t as i32,
8944 ix.heads as i32,
8945 ix.hd as i32,
8946 rd as i32,
8947 fc_dev,
8948 vws.pos_dev.device_ptr(&stream).0 as *const i32,
8949 0,
8950 sp(&stream),
8951 ),
8952 )?;
8953 let scale = (ix.hd as f32).powf(-0.5);
8954 ck(
8955 "hadamard qi batch",
8956 k::memra_dsv4_hadamard(
8957 dpm!(vws.qi, &stream),
8958 (t * ix.heads) as i32,
8959 ix.hd as i32,
8960 scale,
8961 sp(&stream),
8962 ),
8963 )?;
8964 ck(
8965 "fp4 qi batch",
8966 k::memra_dsv4_fp4_act_quant(
8967 dpm!(vws.qi, &stream),
8968 (t * ix.heads) as i32,
8969 ix.hd as i64,
8970 ix.hd as i32,
8971 sp(&stream),
8972 ),
8973 )?;
8974 }
8975 Self::gemm_m_dev(
8977 st,
8978 vws.x.device_ptr(&stream).0 as *const f32,
8979 &mut vws.gemm_xb,
8980 dwsel(
8981 self.dense_fp8,
8982 &stream,
8983 &ix.weights_proj,
8984 &ix.weights_proj_fp8,
8985 ),
8986 t,
8987 ix.heads,
8988 hidden,
8989 vws.wproj.device_ptr_mut(&stream).0 as *mut f32,
8990 )?;
8991 {
8993 let VerifyWs {
8994 x,
8995 cmp_emit,
8996 cmp_shift,
8997 ..
8998 } = vws;
8999 self.cmp_decode_batch_dev(
9000 st,
9001 &ix.cmp,
9002 x.device_ptr(&stream).0 as *const f32,
9003 t,
9004 pos0,
9005 hidden,
9006 &st.fc_yarn,
9007 rd,
9008 eps,
9009 lck.idx.as_mut().expect("idx ckpt"),
9010 cmp_emit,
9011 cmp_shift,
9012 ipend_kv.as_mut().expect("ipend"),
9013 ipend_score.as_mut().expect("ipend"),
9014 ikvc.as_mut().expect("ikvc"),
9015 0,
9016 i_blocks,
9017 )?;
9018 }
9019 debug_assert_eq!(*i_blocks, nbs[t - 1], "indexer block count (batch)");
9020 let kks: Vec<usize> = nbs.iter().map(|&nb| ix.topk.min(nb)).collect();
9021 let tail_max = kks.iter().cloned().max().unwrap_or(0);
9022 slots = win + tail_max;
9023 for i in 0..t {
9024 let pos = pos0 + i;
9025 let idx_off = i * vws.idx_stride;
9026 unsafe {
9027 ck(
9028 "build_idx_redirect fine",
9029 k::memra_dsv4_build_idx_redirect(
9030 (vws.idx.device_ptr_mut(&stream).0 as usize + idx_off * 4)
9031 as *mut i32,
9032 pos as i32,
9033 win as i32,
9034 0, slots as i32,
9036 pos0 as i32,
9037 trans_base as i32,
9038 sp(&stream),
9039 ),
9040 )?;
9041 }
9042 let nb = nbs[i];
9043 if nb == 0 {
9044 continue;
9045 }
9046 let wscale = ((ix.hd as f64).powf(-0.5) * (ix.heads as f64).powf(-0.5)) as f32;
9047 unsafe {
9048 ck(
9049 "indexer_score batch",
9050 self.indexer_score_arm(
9051 (vws.qi.device_ptr(&stream).0 as usize + i * ix.heads * ix.hd * 4)
9052 as *const f32,
9053 dpf!(ikvc.as_ref().expect("ikvc"), &stream),
9054 (vws.wproj.device_ptr(&stream).0 as usize + i * ix.heads * 4)
9055 as *const f32,
9056 wscale,
9057 dpm!(vws.score, &stream),
9058 1,
9059 ix.heads as i32,
9060 ix.hd as i32,
9061 nb as i32,
9062 ratio as i32,
9063 nb as i32,
9064 sp(&stream),
9065 ),
9066 )?;
9067 }
9068 let kk = kks[i];
9069 if host_math {
9070 let score_h = {
9071 let view = vws.score.slice(0..nb);
9072 let mut v = vec![0f32; nb];
9073 stream
9074 .memcpy_dtoh(&view, &mut v[..])
9075 .map_err(e("dtoh sc b"))?;
9076 stream.synchronize().map_err(e("sync sc b"))?;
9077 v
9078 };
9079 let mut order: Vec<usize> = (0..nb).collect();
9080 order.sort_by(|&a, &b| {
9081 score_h[b]
9082 .partial_cmp(&score_h[a])
9083 .unwrap_or(std::cmp::Ordering::Equal)
9084 .then(a.cmp(&b))
9085 });
9086 let cidx: Vec<i32> = order
9087 .into_iter()
9088 .take(kk)
9089 .map(|j| (j + win) as i32)
9090 .collect();
9091 let mut dst = vws.idx.slice_mut(idx_off + win..idx_off + win + kk);
9092 stream
9093 .memcpy_htod(&cidx, &mut dst)
9094 .map_err(e("htod idx b"))?;
9095 } else {
9096 unsafe {
9097 let idx_tail_ptr = (vws.idx.device_ptr_mut(&stream).0 as usize
9098 + (idx_off + win) * 4)
9099 as *mut i32;
9100 ck(
9101 "topk_idx batch",
9102 k::memra_dsv4_topk_idx(
9103 dpf!(vws.score, &stream),
9104 nb as i32,
9105 kk as i32,
9106 win as i32,
9107 idx_tail_ptr,
9108 sp(&stream),
9109 ),
9110 )?;
9111 }
9112 }
9113 }
9114 } else {
9115 let tail_max = nbs.iter().cloned().max().unwrap_or(0);
9116 slots = win + tail_max;
9117 for (i, &nb_i) in nbs.iter().enumerate() {
9118 let pos = pos0 + i;
9119 let idx_off = i * vws.idx_stride;
9120 unsafe {
9121 ck(
9122 "build_idx_redirect coarse",
9123 k::memra_dsv4_build_idx_redirect(
9124 (vws.idx.device_ptr_mut(&stream).0 as usize + idx_off * 4)
9125 as *mut i32,
9126 pos as i32,
9127 win as i32,
9128 nb_i as i32,
9129 slots as i32,
9130 pos0 as i32,
9131 trans_base as i32,
9132 sp(&stream),
9133 ),
9134 )?;
9135 }
9136 }
9137 }
9138 {
9140 let VerifyWs {
9141 x,
9142 cmp_emit,
9143 cmp_shift,
9144 ..
9145 } = vws;
9146 self.cmp_decode_batch_dev(
9147 st,
9148 layer.cmp.as_ref().expect("ratio!=0 has compressor"),
9149 x.device_ptr(&stream).0 as *const f32,
9150 t,
9151 pos0,
9152 hidden,
9153 &st.fc_yarn,
9154 rd,
9155 eps,
9156 lck.cmp.as_mut().expect("cmp ckpt"),
9157 cmp_emit,
9158 cmp_shift,
9159 pend_kv.as_mut().expect("pend"),
9160 pend_score.as_mut().expect("pend"),
9161 kvc,
9162 win,
9163 n_blocks,
9164 )?;
9165 }
9166 debug_assert_eq!(*n_blocks, nbs[t - 1], "attn block count (batch)");
9167 } else {
9168 for i in 0..t {
9169 let pos = pos0 + i;
9170 let idx_off = i * vws.idx_stride;
9171 unsafe {
9172 ck(
9173 "build_idx_redirect window-only",
9174 k::memra_dsv4_build_idx_redirect(
9175 (vws.idx.device_ptr_mut(&stream).0 as usize + idx_off * 4) as *mut i32,
9176 pos as i32,
9177 win as i32,
9178 -1,
9179 win as i32,
9180 pos0 as i32,
9181 trans_base as i32,
9182 sp(&stream),
9183 ),
9184 )?;
9185 }
9186 }
9187 }
9188
9189 let scale = (hd as f64).powf(-0.5) as f32;
9192 unsafe {
9193 if self.chains_f32 {
9194 ck(
9195 "sink_attn_dec_mq_f32acc",
9196 k::memra_dsv4_sink_attn_dec_mq_f32acc(
9197 dpf!(vws.q, &stream),
9198 dpf!(kvc, &stream),
9199 vws.idx.device_ptr(&stream).0 as *const i32,
9200 dpf!(layer.sink, &stream),
9201 dpm!(vws.sink_scores, &stream),
9202 dpm!(vws.sink_evals, &stream),
9203 vws.sink_den.device_ptr_mut(&stream).0 as *mut f32,
9204 dpm!(vws.o, &stream),
9205 t as i32,
9206 heads as i32,
9207 hd as i32,
9208 slots as i32,
9209 vws.idx_stride as i32,
9210 scale,
9211 sp(&stream),
9212 ),
9213 )?;
9214 } else {
9215 ck(
9216 "sink_attn_dec_mq",
9217 k::memra_dsv4_sink_attn_dec_mq(
9218 dpf!(vws.q, &stream),
9219 dpf!(kvc, &stream),
9220 vws.idx.device_ptr(&stream).0 as *const i32,
9221 dpf!(layer.sink, &stream),
9222 dpm!(vws.sink_scores, &stream),
9223 dpm!(vws.sink_evals, &stream),
9224 vws.sink_den.device_ptr_mut(&stream).0 as *mut f64,
9225 dpm!(vws.o, &stream),
9226 t as i32,
9227 heads as i32,
9228 hd as i32,
9229 slots as i32,
9230 vws.idx_stride as i32,
9231 scale,
9232 sp(&stream),
9233 ),
9234 )?;
9235 }
9236 ck(
9237 "rope o inv batch",
9238 k::memra_dsv4_rope(
9239 dpm!(vws.o, &stream),
9240 t as i32,
9241 heads as i32,
9242 hd as i32,
9243 rd as i32,
9244 fc_dev,
9245 vws.pos_dev.device_ptr(&stream).0 as *const i32,
9246 1,
9247 sp(&stream),
9248 ),
9249 )?;
9250 }
9251
9252 let gw = heads / o_groups * hd;
9254 unsafe {
9255 ck(
9256 "cvt o batch",
9257 k::memra_dsv4_cvt_bf16(
9258 dpf!(vws.o, &stream),
9259 vws.o_b.device_ptr_mut(&stream).0 as *mut c_void,
9260 (t * heads * hd) as i64,
9261 sp(&stream),
9262 ),
9263 )?;
9264 }
9265 let wo_a_dw = dwsel(self.dense_fp8, &stream, &layer.wo_a, &layer.wo_a_fp8);
9266 for g in 0..o_groups {
9267 Self::gemv_m_dev(
9268 st,
9269 wo_a_dw.offset_rows(g * o_lora, gw),
9270 (vws.o_b.device_ptr(&stream).0 as usize + g * gw * 2) as *const c_void,
9271 (vws.og.device_ptr_mut(&stream).0 as usize + g * o_lora * 4) as *mut f32,
9272 t,
9273 o_lora,
9274 gw,
9275 heads * hd,
9276 o_groups * o_lora,
9277 )?;
9278 }
9279 Self::gemm_m_dev(
9280 st,
9281 vws.og.device_ptr(&stream).0 as *const f32,
9282 &mut vws.gemm_xb,
9283 dwsel(self.dense_fp8, &stream, &layer.wo_b, &layer.wo_b_fp8),
9284 t,
9285 hidden,
9286 o_groups * o_lora,
9287 vws.attn_out.device_ptr_mut(&stream).0 as *mut f32,
9288 )?;
9289
9290 unsafe {
9292 ck(
9293 "hc_post attn batch",
9294 k::memra_dsv4_hc_post(
9295 dpf!(vws.attn_out, &stream),
9296 h_in_ptr,
9297 dpf!(vws.post, &stream),
9298 dpf!(vws.comb, &stream),
9299 dpm!(vws.h_b, &stream),
9300 t as i32,
9301 hc as i32,
9302 hidden as i32,
9303 sp(&stream),
9304 ),
9305 )?;
9306 }
9307
9308 let h_b_ptr = vws.h_b.device_ptr(&stream).0 as *const f32;
9310 self.hc_pre_batch_dev(
9311 st,
9312 h_b_ptr,
9313 &layer.hc_ffn_fn,
9314 &layer.hc_ffn_base,
9315 &layer.hc_ffn_scale,
9316 &layer.hc_ffn_base_dev,
9317 &layer.hc_ffn_scale_dev,
9318 vws,
9319 t,
9320 hc,
9321 hidden,
9322 iters,
9323 hc_eps,
9324 host_math,
9325 )?;
9326 unsafe {
9327 ck(
9328 "rmsnorm ffn batch",
9329 self.rmsnorm_arm(
9330 dpf!(vws.y_hc, &stream),
9331 dpf!(layer.ffn_norm, &stream),
9332 dpm!(vws.xf, &stream),
9333 t as i32,
9334 hidden as i32,
9335 eps,
9336 sp(&stream),
9337 ),
9338 )?;
9339 }
9340 self.moe_verify_dev(st, layer, vws, t, toks, host_math)?;
9341 unsafe {
9342 ck(
9343 "hc_post ffn batch",
9344 k::memra_dsv4_hc_post(
9345 dpf!(vws.y, &stream),
9346 dpf!(vws.h_b, &stream),
9347 dpf!(vws.post, &stream),
9348 dpf!(vws.comb, &stream),
9349 dpm!(vws.h_a, &stream),
9350 t as i32,
9351 hc as i32,
9352 hidden as i32,
9353 sp(&stream),
9354 ),
9355 )?;
9356 }
9357 Ok(())
9358 }
9359
9360 fn moe_verify_dev(
9365 &self,
9366 st: &Stage,
9367 layer: &LayerDev,
9368 vws: &mut VerifyWs,
9369 t: usize,
9370 toks: &[u32],
9371 host_math: bool,
9372 ) -> Res<()> {
9373 let mc = &self.model.mc;
9374 let d = self.model.cfg();
9375 let moe = mc.moe.as_ref().expect("moe");
9376 let hidden = mc.n_embd as usize;
9377 let ne = moe.expert_count as usize;
9378 let topk = moe.expert_used_count as usize;
9379 let inter = moe.expert_ff_length as usize;
9380 let limit = d.swiglu_limit;
9381 let stream = st.gpu.stream();
9382 let kind = match layer.expert_kind {
9383 ExpertKind::Nvfp4 => 0i32,
9384 ExpertKind::Mxfp4 => 1i32,
9385 };
9386 let wstride = (inter * hidden / 2) as i64;
9387 let sstride = match layer.expert_kind {
9388 ExpertKind::Nvfp4 => (inter * hidden / 16) as i64,
9389 ExpertKind::Mxfp4 => (inter * hidden / 32) as i64,
9390 };
9391 let slots = t * topk;
9392
9393 self.dots_m_dev(
9394 st,
9395 vws.xf.device_ptr(&stream).0 as *const f32,
9396 layer.gate_w.device_ptr(&stream).0 as *const c_void,
9397 0,
9398 t,
9399 hidden,
9400 ne,
9401 vws.raw.device_ptr_mut(&stream).0 as *mut f32,
9402 )?;
9403 if host_math {
9404 let raw_h = {
9405 let view = vws.raw.slice(0..t * ne);
9406 let mut v = vec![0f32; t * ne];
9407 stream
9408 .memcpy_dtoh(&view, &mut v[..])
9409 .map_err(e("dtoh raw b"))?;
9410 stream.synchronize().map_err(e("sync raw b"))?;
9411 v
9412 };
9413 let (indices, weights) =
9414 Self::route_host(layer, &raw_h, toks, t, ne, topk, d.routed_scaling_factor);
9415 let sel: Vec<i32> = indices.iter().map(|&x| x as i32).collect();
9416 let mut order = vec![0i32; t * topk];
9417 for p in 0..t {
9418 let mut o: Vec<i32> = (0..topk as i32).collect();
9419 o.sort_by_key(|&s| indices[p * topk + s as usize]);
9420 order[p * topk..(p + 1) * topk].copy_from_slice(&o);
9421 }
9422 let mut dst = vws.sel.slice_mut(0..t * topk);
9423 stream
9424 .memcpy_htod(&sel, &mut dst)
9425 .map_err(e("htod sel b"))?;
9426 let mut dst = vws.selw.slice_mut(0..t * topk);
9427 stream
9428 .memcpy_htod(&weights, &mut dst)
9429 .map_err(e("htod selw b"))?;
9430 let mut dst = vws.order.slice_mut(0..t * topk);
9431 stream
9432 .memcpy_htod(&order, &mut dst)
9433 .map_err(e("htod order b"))?;
9434 } else {
9435 unsafe {
9436 ck(
9437 "route_m",
9438 k::memra_dsv4_route_m(
9439 dpf!(vws.raw, &stream),
9440 layer
9441 .gate_bias_dev
9442 .as_ref()
9443 .map(|b| b.device_ptr(&stream).0 as *const f32)
9444 .unwrap_or(std::ptr::null()),
9445 layer
9446 .tid2eid_dev
9447 .as_ref()
9448 .map(|x| x.device_ptr(&stream).0 as *const i32)
9449 .unwrap_or(std::ptr::null()),
9450 vws.tok.device_ptr(&stream).0 as *const i32,
9451 t as i32,
9452 ne as i32,
9453 topk as i32,
9454 d.routed_scaling_factor,
9455 vws.sel.device_ptr_mut(&stream).0 as *mut i32,
9456 vws.selw.device_ptr_mut(&stream).0 as *mut f32,
9457 vws.order.device_ptr_mut(&stream).0 as *mut i32,
9458 sp(&stream),
9459 ),
9460 )?;
9461 }
9462 }
9463
9464 unsafe {
9465 ck(
9466 "act_quant_fp8 x batch",
9467 k::memra_dsv4_act_quant_fp8(
9468 dpf!(vws.xf, &stream),
9469 vws.xq.device_ptr_mut(&stream).0 as *mut c_void,
9470 dpm!(vws.xs, &stream),
9471 t as i32,
9472 hidden as i32,
9473 sp(&stream),
9474 ),
9475 )?;
9476 for (proj, dst) in [(0i32, &mut vws.g1), (2i32, &mut vws.g3)] {
9477 ck(
9478 "fp4_gemm_sel_g w1/w3",
9479 k::memra_dsv4_fp4_gemm_sel_g(
9480 dp!(vws.xq, &stream),
9481 dpf!(vws.xs, &stream),
9482 dp!(layer.experts_w, &stream),
9483 dp!(layer.experts_sc, &stream),
9484 dpf!(layer.experts_s2_dev, &stream),
9485 vws.sel.device_ptr(&stream).0 as *const i32,
9486 proj,
9487 0,
9488 kind,
9489 dpm!(*dst, &stream),
9490 slots as i32,
9491 inter as i32,
9492 hidden as i32,
9493 wstride,
9494 sstride,
9495 topk as i32,
9496 sp(&stream),
9497 ),
9498 )?;
9499 }
9500 ck(
9501 "swiglu batch",
9502 k::memra_dsv4_swiglu(
9503 dpf!(vws.g1, &stream),
9504 dpf!(vws.g3, &stream),
9505 dpm!(vws.hbuf, &stream),
9506 slots as i32,
9507 inter as i32,
9508 limit,
9509 vws.selw.device_ptr(&stream).0 as *const f32,
9510 sp(&stream),
9511 ),
9512 )?;
9513 ck(
9514 "act_quant_fp8 h batch",
9515 k::memra_dsv4_act_quant_fp8(
9516 dpf!(vws.hbuf, &stream),
9517 vws.hq.device_ptr_mut(&stream).0 as *mut c_void,
9518 dpm!(vws.hs, &stream),
9519 slots as i32,
9520 inter as i32,
9521 sp(&stream),
9522 ),
9523 )?;
9524 ck(
9525 "fp4_gemm_sel_g w2",
9526 k::memra_dsv4_fp4_gemm_sel_g(
9527 dp!(vws.hq, &stream),
9528 dpf!(vws.hs, &stream),
9529 dp!(layer.experts_w, &stream),
9530 dp!(layer.experts_sc, &stream),
9531 dpf!(layer.experts_s2_dev, &stream),
9532 vws.sel.device_ptr(&stream).0 as *const i32,
9533 1,
9534 1,
9535 kind,
9536 dpm!(vws.contrib, &stream),
9537 slots as i32,
9538 hidden as i32,
9539 inter as i32,
9540 wstride,
9541 sstride,
9542 0,
9543 sp(&stream),
9544 ),
9545 )?;
9546 ck(
9547 "combine_rows_m",
9548 k::memra_dsv4_combine_rows_m(
9549 dpf!(vws.contrib, &stream),
9550 vws.order.device_ptr(&stream).0 as *const i32,
9551 topk as i32,
9552 dpm!(vws.y, &stream),
9553 hidden as i64,
9554 t as i32,
9555 sp(&stream),
9556 ),
9557 )?;
9558 ck(
9559 "cvt xb batch",
9560 k::memra_dsv4_cvt_bf16(
9561 dpf!(vws.xf, &stream),
9562 vws.xb.device_ptr_mut(&stream).0 as *mut c_void,
9563 (t * hidden) as i64,
9564 sp(&stream),
9565 ),
9566 )?;
9567 }
9568 let sh_inter = vws.sg1.len() / vws.tmax;
9569 Self::gemv_m_dev(
9570 st,
9571 dwsel(
9572 self.dense_fp8,
9573 &stream,
9574 &layer.shared_w[0],
9575 &layer.shared_fp8[0],
9576 ),
9577 vws.xb.device_ptr(&stream).0 as *const c_void,
9578 vws.sg1.device_ptr_mut(&stream).0 as *mut f32,
9579 t,
9580 sh_inter,
9581 hidden,
9582 0,
9583 0,
9584 )?;
9585 Self::gemv_m_dev(
9586 st,
9587 dwsel(
9588 self.dense_fp8,
9589 &stream,
9590 &layer.shared_w[2],
9591 &layer.shared_fp8[2],
9592 ),
9593 vws.xb.device_ptr(&stream).0 as *const c_void,
9594 vws.sg3.device_ptr_mut(&stream).0 as *mut f32,
9595 t,
9596 sh_inter,
9597 hidden,
9598 0,
9599 0,
9600 )?;
9601 unsafe {
9602 ck(
9603 "swiglu sh batch",
9604 k::memra_dsv4_swiglu(
9605 dpf!(vws.sg1, &stream),
9606 dpf!(vws.sg3, &stream),
9607 dpm!(vws.shbuf, &stream),
9608 t as i32,
9609 sh_inter as i32,
9610 limit,
9611 std::ptr::null(),
9612 sp(&stream),
9613 ),
9614 )?;
9615 ck(
9616 "cvt sh batch",
9617 k::memra_dsv4_cvt_bf16(
9618 dpf!(vws.shbuf, &stream),
9619 vws.shb16.device_ptr_mut(&stream).0 as *mut c_void,
9620 (t * sh_inter) as i64,
9621 sp(&stream),
9622 ),
9623 )?;
9624 }
9625 Self::gemv_m_dev(
9626 st,
9627 dwsel(
9628 self.dense_fp8,
9629 &stream,
9630 &layer.shared_w[1],
9631 &layer.shared_fp8[1],
9632 ),
9633 vws.shb16.device_ptr(&stream).0 as *const c_void,
9634 vws.sh_out.device_ptr_mut(&stream).0 as *mut f32,
9635 t,
9636 hidden,
9637 sh_inter,
9638 0,
9639 0,
9640 )?;
9641 unsafe {
9642 ck(
9643 "add shared batch",
9644 k::memra_dsv4_add_inplace(
9645 dpm!(vws.y, &stream),
9646 dpf!(vws.sh_out, &stream),
9647 (t * hidden) as i64,
9648 sp(&stream),
9649 ),
9650 )?;
9651 }
9652 Ok(())
9653 }
9654
9655 fn head_logits_batch_dev(&self, vws: &mut VerifyWs, t: usize, host_math: bool) -> Res<()> {
9659 let d = self.model.cfg();
9660 let mc = &self.model.mc;
9661 let hc = d.hc_mult as usize;
9662 let hidden = mc.n_embd as usize;
9663 let eps = mc.rms_eps;
9664 let last = self.stages.len() - 1;
9665 let st = &self.stages[last];
9666 let stream = st.gpu.stream();
9667 let w = hc * hidden;
9668 let fn_w = st.hc_head_fn.as_ref().expect("hc_head_fn");
9669 let norm = st.trunk_norm.as_ref().expect("trunk norm");
9670 let vocab = vws.logits.len() / vws.tmax;
9671 self.dots_m_dev(
9672 st,
9673 vws.h_a.device_ptr(&stream).0 as *const f32,
9674 fn_w.device_ptr(&stream).0 as *const c_void,
9675 0,
9676 t,
9677 w,
9678 hc,
9679 vws.head_mixes.device_ptr_mut(&stream).0 as *mut f32,
9680 )?;
9681 unsafe {
9682 ck(
9683 "rowsq head batch",
9684 self.rowsq_scale_arm(
9685 dpf!(vws.h_a, &stream),
9686 dpm!(vws.head_mixes, &stream),
9687 t as i32,
9688 w as i32,
9689 hc as i32,
9690 eps,
9691 sp(&stream),
9692 ),
9693 )?;
9694 }
9695 if host_math {
9696 let mut mixes_h = vec![0f32; t * hc];
9697 let view = vws.head_mixes.slice(0..t * hc);
9698 stream
9699 .memcpy_dtoh(&view, &mut mixes_h[..])
9700 .map_err(e("dtoh head mixes b"))?;
9701 stream.synchronize().map_err(e("sync head mixes b"))?;
9702 for p in 0..t {
9703 for c in 0..hc {
9704 let m = mixes_h[p * hc + c];
9705 mixes_h[p * hc + c] =
9706 sigmoid_f32(m * self.hc_head_scale[0] + self.hc_head_base[c]) + d.hc_eps;
9707 }
9708 }
9709 let mut dst = vws.head_pre.slice_mut(0..t * hc);
9710 stream
9711 .memcpy_htod(&mixes_h, &mut dst)
9712 .map_err(e("htod head pre b"))?;
9713 } else {
9714 unsafe {
9715 ck(
9716 "hc_head_pre_m",
9717 k::memra_dsv4_hc_head_pre_m(
9718 dpf!(vws.head_mixes, &stream),
9719 st.hc_head_scale_dev
9720 .as_ref()
9721 .expect("head scale dev")
9722 .device_ptr(&stream)
9723 .0 as *const f32,
9724 st.hc_head_base_dev
9725 .as_ref()
9726 .expect("head base dev")
9727 .device_ptr(&stream)
9728 .0 as *const f32,
9729 dpm!(vws.head_pre, &stream),
9730 t as i32,
9731 hc as i32,
9732 d.hc_eps,
9733 sp(&stream),
9734 ),
9735 )?;
9736 }
9737 }
9738 unsafe {
9739 ck(
9740 "hc_collapse head batch",
9741 k::memra_dsv4_hc_collapse(
9742 dpf!(vws.h_a, &stream),
9743 dpf!(vws.head_pre, &stream),
9744 dpm!(vws.collapsed, &stream),
9745 t as i32,
9746 hc as i32,
9747 hidden as i32,
9748 sp(&stream),
9749 ),
9750 )?;
9751 ck(
9752 "rmsnorm head batch",
9753 self.rmsnorm_arm(
9754 dpf!(vws.collapsed, &stream),
9755 dpf!(norm, &stream),
9756 dpm!(vws.collapsed, &stream),
9757 t as i32,
9758 hidden as i32,
9759 eps,
9760 sp(&stream),
9761 ),
9762 )?;
9763 }
9764 let head_ptr = st.head.as_ref().expect("head").device_ptr(&stream).0 as *const c_void;
9765 self.dots_m_dev(
9766 st,
9767 vws.collapsed.device_ptr(&stream).0 as *const f32,
9768 head_ptr,
9769 1,
9770 t,
9771 hidden,
9772 vocab,
9773 vws.logits.device_ptr_mut(&stream).0 as *mut f32,
9774 )?;
9775 Ok(())
9776 }
9777}
9778
9779pub struct SpecRoundGpu {
9781 pub start_pos: usize,
9782 pub drafts: Vec<u32>,
9783 pub accepts: usize,
9784 pub verified: usize,
9785 pub t_batch: usize,
9787 pub t_cap: usize,
9791 pub confidence: Vec<f32>,
9796 pub emitted: usize,
9798 pub round_us: u64,
9802}
9803
9804pub struct SpecRunGpu {
9805 pub tokens: Vec<u32>,
9806 pub rounds: Vec<SpecRoundGpu>,
9807}
9808
9809impl Dsv4Gpu {
9810 pub fn verify_batch_dev(
9819 &self,
9820 toks: &[u32],
9821 state: &mut DecodeState,
9822 vstate: &mut VerifyState,
9823 taps: Option<&mut CudaSlice<f32>>,
9824 want_logits: bool,
9825 ) -> Res<(Option<Vec<f32>>, Vec<u32>)> {
9826 let DecodePath::Device { host_math } = self.decode_path else {
9827 return Err("verify_batch_dev requires MEMRA_DSV4_DECODE_PATH=device".into());
9828 };
9829 let mc = &self.model.mc;
9830 let d = self.model.cfg();
9831 let t = toks.len();
9832 assert!(
9833 t >= 1 && t <= vstate.tmax,
9834 "round depth {t} > tmax {}",
9835 vstate.tmax
9836 );
9837 assert!(vstate.open.is_none(), "verify_batch_dev with an open round");
9838 let pos0 = state.pos;
9839 assert!(pos0 > 0, "batched verify needs prefill_with_cache first");
9840 assert!(
9841 pos0 + t <= self.max_seq,
9842 "round [{pos0}, {}) exceeds max_seq {}",
9843 pos0 + t,
9844 self.max_seq
9845 );
9846 let hidden = mc.n_embd as usize;
9847 let hc = d.hc_mult as usize;
9848 let n_trunk = (mc.n_layer - mc.nextn_predict_layers) as usize;
9849 let tok_i32: Vec<i32> = toks.iter().map(|&x| x as i32).collect();
9850 let pos_i32: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
9851
9852 for (si, st) in self.stages.iter().enumerate() {
9855 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx round"))?;
9856 let stream = st.gpu.stream();
9857 let vws = &mut vstate.ws[si];
9858 let mut dst = vws.tok.slice_mut(0..t);
9859 stream
9860 .memcpy_htod(&tok_i32, &mut dst)
9861 .map_err(e("htod tok round"))?;
9862 let mut dst = vws.pos_dev.slice_mut(0..t);
9863 stream
9864 .memcpy_htod(&pos_i32, &mut dst)
9865 .map_err(e("htod pos round"))?;
9866 }
9867
9868 {
9870 let st0 = &self.stages[0];
9871 st0.gpu.ctx.bind_to_thread().map_err(e("bind ctx0 round"))?;
9872 let stream0 = st0.gpu.stream();
9873 let vws0 = &mut vstate.ws[0];
9874 unsafe {
9875 ck(
9876 "embed_rows batch",
9877 k::memra_dsv4_embed_rows(
9878 st0.embed
9879 .as_ref()
9880 .expect("embed on stage 0")
9881 .device_ptr(&stream0)
9882 .0 as *const c_void,
9883 vws0.tok.device_ptr(&stream0).0 as *const i32,
9884 dpm!(vws0.emb, &stream0),
9885 t as i32,
9886 hidden as i32,
9887 sp(&stream0),
9888 ),
9889 )?;
9890 ck(
9891 "repeat_hc batch",
9892 k::memra_dsv4_repeat_hc(
9893 dpf!(vws0.emb, &stream0),
9894 dpm!(vws0.h_a, &stream0),
9895 t as i32,
9896 hc as i32,
9897 hidden as i32,
9898 sp(&stream0),
9899 ),
9900 )?;
9901 }
9902 }
9903
9904 let targets = self.dspark.as_ref().map(|ds| ds.targets.clone());
9905 let n_t = targets.as_ref().map(|x| x.len()).unwrap_or(0);
9906 let mut taps = taps;
9907 let mut cur_stage = 0usize;
9908 let mut input_rx = false;
9909 for il in 0..n_trunk {
9910 let stage = self.layer_stage[il];
9911 if stage != cur_stage {
9912 let bytes = t * hc * hidden * std::mem::size_of::<f32>();
9913 let src_stream = self.stages[cur_stage].gpu.stream();
9914 let dst_stream = self.stages[stage].gpu.stream();
9915 let (ws_src, ws_dst) = vstate.ws.split_at_mut(stage);
9916 let src_ws = &ws_src[cur_stage];
9917 let dst_ws = &mut ws_dst[0];
9918 self.stages[cur_stage]
9919 .gpu
9920 .ctx
9921 .bind_to_thread()
9922 .map_err(e("bind tx round"))?;
9923 let (sp_, _g0) = src_ws.h_a.device_ptr(&src_stream);
9924 let (dp_, _g1) = dst_ws.h_rx.device_ptr_mut(&src_stream);
9925 unsafe {
9926 cudarc::driver::result::memcpy_peer_async(
9927 self.stages[stage].gpu.ctx.cu_ctx(),
9928 dp_,
9929 self.stages[cur_stage].gpu.ctx.cu_ctx(),
9930 sp_,
9931 bytes,
9932 src_stream.cu_stream(),
9933 )
9934 .map_err(e("peer copy h round"))?;
9935 }
9936 let bnd = stage - 1;
9937 self.boundary_ev[bnd]
9938 .record(&src_stream)
9939 .map_err(e("ev record round"))?;
9940 dst_stream
9941 .wait(&self.boundary_ev[bnd])
9942 .map_err(e("ev wait round"))?;
9943 self.stages[stage]
9944 .gpu
9945 .ctx
9946 .bind_to_thread()
9947 .map_err(e("bind rx round"))?;
9948 cur_stage = stage;
9949 input_rx = true;
9950 }
9951 let st = &self.stages[stage];
9952 let lidx = st
9953 .layers
9954 .iter()
9955 .position(|l| l.il == il as u32)
9956 .unwrap_or_else(|| panic!("layer {il} not on stage {stage}"));
9957 self.block_verify_dev(
9958 st,
9959 &st.layers[lidx],
9960 &mut state.caches[il],
9961 &mut vstate.layers[il],
9962 &mut vstate.ws[stage],
9963 input_rx,
9964 pos0,
9965 t,
9966 toks,
9967 host_math,
9968 )?;
9969 input_rx = false;
9970 if let (Some(tp), Some(tg)) = (taps.as_mut(), targets.as_ref())
9972 && let Some(kk) = tg.iter().position(|&tl| tl == il)
9973 {
9974 let stream = self.stages[stage].gpu.stream();
9975 let vws = &mut vstate.ws[stage];
9976 unsafe {
9977 ck(
9978 "hc_mean tap batch",
9979 k::memra_dsv4_hc_mean(
9980 dpf!(vws.h_a, &stream),
9981 dpm!(vws.tap_tmp, &stream),
9982 t as i32,
9983 hc as i32,
9984 hidden as i32,
9985 sp(&stream),
9986 ),
9987 )?;
9988 ck(
9989 "place_cols tap batch",
9990 k::memra_dsv4_place_cols(
9991 dpf!(vws.tap_tmp, &stream),
9992 dpm!(**tp, &stream),
9993 t as i32,
9994 hidden as i32,
9995 (n_t * hidden) as i64,
9996 (kk * hidden) as i64,
9997 sp(&stream),
9998 ),
9999 )?;
10000 }
10001 }
10002 }
10003
10004 let last = self.stages.len() - 1;
10005 assert_eq!(cur_stage, last, "device path expects the head stage last");
10006 self.head_logits_batch_dev(&mut vstate.ws[last], t, host_math)?;
10007 let stream_last = self.stages[last].gpu.stream();
10008 let vws = &mut vstate.ws[last];
10009 let vocab = vws.logits.len() / vws.tmax;
10010 let logits = if want_logits {
10011 let mut v = vec![0f32; t * vocab];
10012 let view = vws.logits.slice(0..t * vocab);
10013 stream_last
10014 .memcpy_dtoh(&view, &mut v[..])
10015 .map_err(e("dtoh logits batch"))?;
10016 stream_last.synchronize().map_err(e("sync logits batch"))?;
10017 Some(v)
10018 } else {
10019 None
10020 };
10021 let mut am = vec![0i32; t];
10022 if let Some(lg) = &logits {
10023 for (i, slot) in am.iter_mut().enumerate() {
10024 let row = &lg[i * vocab..(i + 1) * vocab];
10025 let mut best = 0usize;
10026 for j in 1..vocab {
10027 if row[j] > row[best] {
10028 best = j;
10029 }
10030 }
10031 *slot = best as i32;
10032 }
10033 } else {
10034 unsafe {
10035 for i in 0..t {
10036 ck(
10037 "argmax batch",
10038 k::memra_dsv4_argmax(
10039 (vws.logits.device_ptr(&stream_last).0 as usize + i * vocab * 4)
10040 as *const f32,
10041 vocab as i64,
10042 (vws.argmax.device_ptr_mut(&stream_last).0 as usize + i * 4)
10043 as *mut i32,
10044 sp(&stream_last),
10045 ),
10046 )?;
10047 }
10048 }
10049 let view = vws.argmax.slice(0..t);
10050 stream_last
10051 .memcpy_dtoh(&view, &mut am[..])
10052 .map_err(e("dtoh argmax batch"))?;
10053 stream_last.synchronize().map_err(e("sync argmax batch"))?;
10054 }
10055 vstate.open = Some((pos0, t));
10056 Ok((logits, am.into_iter().map(|x| x as u32).collect()))
10057 }
10058
10059 pub fn commit_verify_dev(
10064 &self,
10065 state: &mut DecodeState,
10066 vstate: &mut VerifyState,
10067 n_commit: usize,
10068 ) -> Res<()> {
10069 let (pos0, t) = vstate
10070 .open
10071 .take()
10072 .ok_or_else(|| "commit_verify_dev without an open round".to_string())?;
10073 assert!(
10074 n_commit >= 1 && n_commit <= t,
10075 "commit {n_commit} outside round width {t}"
10076 );
10077 let d = self.model.cfg();
10078 let mc = &self.model.mc;
10079 let hd = d.head_dim as usize;
10080 let win = d.sliding_window as usize;
10081 let n_trunk = (mc.n_layer - mc.nextn_predict_layers) as usize;
10082 let slot_rows: Vec<i32> = (0..n_commit).map(|j| ((pos0 + j) % win) as i32).collect();
10083 for il in 0..n_trunk {
10084 let stage = self.layer_stage[il];
10085 let st = &self.stages[stage];
10086 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx commit"))?;
10087 let stream = st.gpu.stream();
10088 let lck = &mut vstate.layers[il];
10089 let vws = &mut vstate.ws[stage];
10090 let cache = &mut state.caches[il];
10091 let trans_base = lck.trans_base;
10092 {
10095 let src = cache
10096 .kvc
10097 .slice(trans_base * hd..(trans_base + n_commit) * hd);
10098 let mut dst = vws.bounce.slice_mut(0..n_commit * hd);
10099 stream
10100 .memcpy_dtod(&src, &mut dst)
10101 .map_err(e("commit bounce"))?;
10102 }
10103 {
10104 let mut dst = vws.slot_rows.slice_mut(0..n_commit);
10105 stream
10106 .memcpy_htod(&slot_rows, &mut dst)
10107 .map_err(e("htod slot rows"))?;
10108 }
10109 unsafe {
10110 ck(
10111 "scatter_rows commit",
10112 k::memra_dsv4_scatter_rows(
10113 dpf!(vws.bounce, &stream),
10114 dpm!(cache.kvc, &stream),
10115 vws.slot_rows.device_ptr(&stream).0 as *const i32,
10116 n_commit as i32,
10117 hd as i32,
10118 sp(&stream),
10119 ),
10120 )?;
10121 }
10122 if let Some(ckd) = &lck.cmp {
10123 self.cmp_rollback_replay_dev(
10124 st,
10125 ckd,
10126 n_commit,
10127 t,
10128 pos0,
10129 &mut vws.cmp_shift,
10130 cache.pend_kv.as_mut().expect("pend kv"),
10131 cache.pend_score.as_mut().expect("pend sc"),
10132 &mut cache.n_blocks,
10133 )?;
10134 }
10135 if let Some(ckd) = &lck.idx {
10136 self.cmp_rollback_replay_dev(
10137 st,
10138 ckd,
10139 n_commit,
10140 t,
10141 pos0,
10142 &mut vws.cmp_shift,
10143 cache.ipend_kv.as_mut().expect("ipend kv"),
10144 cache.ipend_score.as_mut().expect("ipend sc"),
10145 &mut cache.i_blocks,
10146 )?;
10147 }
10148 }
10149 for st in &self.stages {
10150 st.gpu
10151 .ctx
10152 .bind_to_thread()
10153 .map_err(e("bind ctx commit sync"))?;
10154 st.gpu.stream().synchronize().map_err(e("commit sync"))?;
10155 }
10156 state.pos = pos0 + n_commit;
10157 Ok(())
10158 }
10159
10160 pub fn spec_greedy_batched_with(
10174 &self,
10175 prompt: &[u32],
10176 n_new: usize,
10177 state: &mut DecodeState,
10178 dstate: &mut DsparkState,
10179 vstate: &mut VerifyState,
10180 ) -> Res<SpecRunGpu> {
10181 let depth_cap = std::env::var("MEMRA_DSV4_SPEC_DEPTH")
10186 .ok()
10187 .and_then(|v| v.trim().parse::<usize>().ok())
10188 .filter(|t| *t > 0)
10189 .unwrap_or(usize::MAX)
10190 .max(1);
10191 if depth_cap != usize::MAX {
10192 println!("[spec] verify depth capped at T={depth_cap} (MEMRA_DSV4_SPEC_DEPTH)");
10193 }
10194 self.spec_greedy_batched_depth(prompt, n_new, state, dstate, vstate, depth_cap)
10195 }
10196
10197 pub fn spec_greedy_batched_depth(
10205 &self,
10206 prompt: &[u32],
10207 n_new: usize,
10208 state: &mut DecodeState,
10209 dstate: &mut DsparkState,
10210 vstate: &mut VerifyState,
10211 depth_cap: usize,
10212 ) -> Res<SpecRunGpu> {
10213 let vt = resolve_vt(
10216 std::env::var("MEMRA_DSV4_VT").ok().as_deref(),
10217 std::env::var("MEMRA_DSV4_VT_TAU").ok().as_deref(),
10218 std::env::var("MEMRA_DSV4_VT_FLOOR").ok().as_deref(),
10219 )?;
10220 self.spec_greedy_batched_policy(prompt, n_new, state, dstate, vstate, depth_cap, vt)
10221 }
10222
10223 #[allow(clippy::too_many_arguments)]
10228 #[allow(clippy::too_many_arguments)]
10229 pub fn spec_greedy_batched_policy(
10230 &self,
10231 prompt: &[u32],
10232 n_new: usize,
10233 state: &mut DecodeState,
10234 dstate: &mut DsparkState,
10235 vstate: &mut VerifyState,
10236 depth_cap: usize,
10237 vt: Dsv4Vt,
10238 ) -> Res<SpecRunGpu> {
10239 self.spec_greedy_batched_stream(prompt, n_new, state, dstate, vstate, depth_cap, vt, None)
10240 }
10241
10242 #[allow(clippy::too_many_arguments)]
10249 #[allow(clippy::type_complexity)] pub fn spec_greedy_batched_stream(
10251 &self,
10252 prompt: &[u32],
10253 n_new: usize,
10254 state: &mut DecodeState,
10255 dstate: &mut DsparkState,
10256 vstate: &mut VerifyState,
10257 depth_cap: usize,
10258 vt: Dsv4Vt,
10259 mut round_cb: Option<&mut dyn FnMut(&[u32]) -> bool>,
10260 ) -> Res<SpecRunGpu> {
10261 let p0 = prompt.len();
10262 assert!(n_new >= 1, "n_new must be positive");
10263 let pre = self.dspark_prefill_prime(prompt, state, dstate)?;
10264 let mut t_tok = {
10265 let lg = &pre.logits;
10266 let mut best = 0usize;
10267 for i in 1..lg.len() {
10268 if lg[i] > lg[best] {
10269 best = i;
10270 }
10271 }
10272 best as u32
10273 };
10274 let mut tokens: Vec<u32> = Vec::with_capacity(n_new);
10275 let mut rounds: Vec<SpecRoundGpu> = Vec::new();
10276 let mut mh_row = 0usize; let mut carry_pending = false;
10278 let profile_bracket = std::env::var("MEMRA_DSV4_BENCH_PROFILE").as_deref() == Ok("1");
10283 let depth_cap = depth_cap.max(1);
10284 if let Dsv4Vt::Slot { tau_logit, floor } = vt {
10285 println!(
10286 "[spec] vt policy: slot (tau_logit {tau_logit:.6}, floor {floor}) — \
10287 per-round verify window from the confidence head"
10288 );
10289 }
10290 while tokens.len() < n_new {
10291 if profile_bracket && rounds.len() == 4 {
10292 cudarc::driver::safe::profiler_start().map_err(e("profiler_start"))?;
10293 }
10294 if profile_bracket && rounds.len() == 12 {
10295 cudarc::driver::safe::profiler_stop().map_err(e("profiler_stop"))?;
10296 }
10297 let cb_from = tokens.len();
10298 if carry_pending {
10299 tokens.push(t_tok);
10300 if let Some(cb) = round_cb.as_deref_mut() {
10301 cb(&tokens[cb_from..]);
10302 }
10303 break;
10304 }
10305 let round_t0 = std::time::Instant::now();
10306 let prof_stream = if dsv4_prof_on() {
10307 Some(self.stages[self.stages.len() - 1].gpu.stream())
10308 } else {
10309 None
10310 };
10311 let _p_round = phase!("round", prof_stream.as_ref());
10312 let m0 = p0 + tokens.len();
10313 let prop = {
10314 let _p = phase!("1.drafter_forward", prof_stream.as_ref());
10315 self.dspark_forward_spec(dstate, t_tok, mh_row, m0 - 1, false)?
10316 };
10317 let k_drafts = prop.out_ids.len() - 1;
10318 tokens.push(t_tok);
10319 if tokens.len() == n_new {
10320 rounds.push(SpecRoundGpu {
10321 start_pos: m0 - 1,
10322 drafts: prop.out_ids[1..].to_vec(),
10323 accepts: 0,
10324 verified: 0,
10325 t_batch: 0,
10326 t_cap: 0,
10327 confidence: prop.confidence.clone(),
10328 emitted: 1,
10329 round_us: round_t0.elapsed().as_micros() as u64,
10330 });
10331 if let Some(cb) = round_cb.as_deref_mut() {
10332 cb(&tokens[cb_from..]);
10333 }
10334 break;
10335 }
10336 let forwards_left = n_new - tokens.len();
10337 let vt_drafts = match vt {
10343 Dsv4Vt::Off => k_drafts,
10344 Dsv4Vt::Slot { tau_logit, floor } => {
10345 vt_slot_drafts(&prop.confidence, tau_logit, floor)
10346 }
10347 };
10348 let t_cap = (vt_drafts + 1)
10349 .min(k_drafts + 1)
10350 .min(depth_cap)
10351 .min(vstate.tmax);
10352 let t_batch = t_cap.min(forwards_left);
10353 let kv = t_batch - 1;
10354 let mut batch_ids = Vec::with_capacity(t_batch);
10355 batch_ids.push(t_tok);
10356 batch_ids.extend_from_slice(&prop.out_ids[1..1 + kv]);
10357 let (_, am) = {
10358 let _p = phase!("2.verify_batch", prof_stream.as_ref());
10359 self.verify_batch_dev(&batch_ids, state, vstate, Some(&mut dstate.taps), false)?
10360 };
10361 let mut c_d = 0usize;
10363 let mut t_next = 0u32;
10364 for i in 0..t_batch {
10365 let a = am[i];
10366 if i < kv && a == batch_ids[i + 1] {
10367 c_d += 1;
10368 continue;
10369 }
10370 t_next = a;
10371 break;
10372 }
10373 let n_commit = c_d + 1;
10374 {
10375 let _p = phase!("3.commit_rollback", prof_stream.as_ref());
10376 self.commit_verify_dev(state, vstate, n_commit)?;
10377 }
10378 {
10380 let _p = phase!("4.ring_writes", prof_stream.as_ref());
10381 for i in 0..n_commit {
10382 self.dspark_write_rings(dstate, i, m0 + i)?;
10383 }
10384 }
10385 {
10386 let _p = phase!("5.round_close_sync", None);
10387 let last = self.stages.len() - 1;
10390 self.stages[last]
10391 .gpu
10392 .stream()
10393 .synchronize()
10394 .map_err(e("round close sync"))?;
10395 }
10396 mh_row = c_d;
10397 for i in 0..c_d {
10398 tokens.push(batch_ids[i + 1]);
10399 }
10400 carry_pending = c_d == kv && t_batch < t_cap;
10404 rounds.push(SpecRoundGpu {
10405 start_pos: m0 - 1,
10406 drafts: prop.out_ids[1..].to_vec(),
10407 accepts: c_d,
10408 verified: (c_d + 1).min(kv),
10409 t_batch,
10410 t_cap,
10411 confidence: prop.confidence.clone(),
10412 emitted: 1 + c_d,
10413 round_us: round_t0.elapsed().as_micros() as u64,
10414 });
10415 t_tok = t_next;
10416 if let Some(cb) = round_cb.as_deref_mut()
10417 && !cb(&tokens[cb_from..])
10418 {
10419 break;
10420 }
10421 }
10422 Ok(SpecRunGpu { tokens, rounds })
10423 }
10424
10425 pub fn spec_greedy_batched(&self, prompt: &[u32], n_new: usize) -> Res<SpecRunGpu> {
10427 let mut state = self.alloc_decode_state()?;
10428 let mut dstate = self.dspark_alloc_state()?;
10429 let mut vstate = self.alloc_verify_state()?;
10430 self.spec_greedy_batched_with(prompt, n_new, &mut state, &mut dstate, &mut vstate)
10431 }
10432
10433 #[allow(clippy::too_many_arguments)]
10449 pub fn spec_sampled_batched_policy(
10450 &self,
10451 prompt: &[u32],
10452 n_new: usize,
10453 state: &mut DecodeState,
10454 dstate: &mut DsparkState,
10455 vstate: &mut VerifyState,
10456 depth_cap: usize,
10457 vt: Dsv4Vt,
10458 sample: &Dsv4SampleCfg,
10459 ) -> Res<SpecRunGpu> {
10460 self.spec_sampled_batched_stream(
10461 prompt, n_new, state, dstate, vstate, depth_cap, vt, sample, None,
10462 )
10463 }
10464
10465 #[allow(clippy::too_many_arguments)]
10469 #[allow(clippy::type_complexity)] pub fn spec_sampled_batched_stream(
10471 &self,
10472 prompt: &[u32],
10473 n_new: usize,
10474 state: &mut DecodeState,
10475 dstate: &mut DsparkState,
10476 vstate: &mut VerifyState,
10477 depth_cap: usize,
10478 vt: Dsv4Vt,
10479 sample: &Dsv4SampleCfg,
10480 mut round_cb: Option<&mut dyn FnMut(&[u32]) -> bool>,
10481 ) -> Res<SpecRunGpu> {
10482 self.spec_sampled_batched_pen(
10483 prompt,
10484 n_new,
10485 state,
10486 dstate,
10487 vstate,
10488 depth_cap,
10489 vt,
10490 sample,
10491 None,
10492 round_cb.take(),
10493 )
10494 }
10495
10496 #[allow(clippy::too_many_arguments)]
10503 #[allow(clippy::type_complexity)] pub fn spec_sampled_batched_pen(
10505 &self,
10506 prompt: &[u32],
10507 n_new: usize,
10508 state: &mut DecodeState,
10509 dstate: &mut DsparkState,
10510 vstate: &mut VerifyState,
10511 depth_cap: usize,
10512 vt: Dsv4Vt,
10513 sample: &Dsv4SampleCfg,
10514 pen: Option<&Dsv4PenaltyCfg>,
10515 mut round_cb: Option<&mut dyn FnMut(&[u32]) -> bool>,
10516 ) -> Res<SpecRunGpu> {
10517 let p0 = prompt.len();
10518 assert!(n_new >= 1, "n_new must be positive");
10519 let pre = self.dspark_prefill_prime(prompt, state, dstate)?;
10520 let mut t_tok = if let Some(pc) = pen {
10522 let mut row = pre.logits.clone();
10523 dsv4_penalize_row(&mut row, prompt, pc);
10524 dsv4_sample_row(&row, p0, sample)?
10525 } else {
10526 dsv4_sample_row(&pre.logits, p0, sample)?
10527 };
10528 let mut tokens: Vec<u32> = Vec::with_capacity(n_new);
10529 let mut rounds: Vec<SpecRoundGpu> = Vec::new();
10530 let mut mh_row = 0usize;
10531 let mut carry_pending = false;
10532 let depth_cap = depth_cap.max(1);
10533 while tokens.len() < n_new {
10534 let cb_from = tokens.len();
10535 if carry_pending {
10536 tokens.push(t_tok);
10537 if let Some(cb) = round_cb.as_deref_mut() {
10538 cb(&tokens[cb_from..]);
10539 }
10540 break;
10541 }
10542 let round_t0 = std::time::Instant::now();
10543 let m0 = p0 + tokens.len();
10544 let prop = self.dspark_forward_spec(dstate, t_tok, mh_row, m0 - 1, false)?;
10545 let k_drafts = prop.out_ids.len() - 1;
10546 tokens.push(t_tok);
10547 if tokens.len() == n_new {
10548 rounds.push(SpecRoundGpu {
10549 start_pos: m0 - 1,
10550 drafts: prop.out_ids[1..].to_vec(),
10551 accepts: 0,
10552 verified: 0,
10553 t_batch: 0,
10554 t_cap: 0,
10555 confidence: prop.confidence.clone(),
10556 emitted: 1,
10557 round_us: round_t0.elapsed().as_micros() as u64,
10558 });
10559 if let Some(cb) = round_cb.as_deref_mut() {
10560 cb(&tokens[cb_from..]);
10561 }
10562 break;
10563 }
10564 let forwards_left = n_new - tokens.len();
10565 let vt_drafts = match vt {
10566 Dsv4Vt::Off => k_drafts,
10567 Dsv4Vt::Slot { tau_logit, floor } => {
10568 vt_slot_drafts(&prop.confidence, tau_logit, floor)
10569 }
10570 };
10571 let t_cap = (vt_drafts + 1)
10572 .min(k_drafts + 1)
10573 .min(depth_cap)
10574 .min(vstate.tmax);
10575 let t_batch = t_cap.min(forwards_left);
10576 let kv = t_batch - 1;
10577 let mut batch_ids = Vec::with_capacity(t_batch);
10578 batch_ids.push(t_tok);
10579 batch_ids.extend_from_slice(&prop.out_ids[1..1 + kv]);
10580 let (rows, _am) =
10581 self.verify_batch_dev(&batch_ids, state, vstate, Some(&mut dstate.taps), true)?;
10582 let rows = rows.expect("verify_batch_dev(want_logits=true) returned rows");
10583 let vocab = rows.len() / t_batch;
10584 let mut c_d = 0usize;
10590 let mut t_next = 0u32;
10591 let mut wround: Vec<u32> = Vec::new();
10594 for i in 0..t_batch {
10595 let s = if let Some(pc) = pen {
10596 let mut row = rows[i * vocab..(i + 1) * vocab].to_vec();
10597 let mut window = Vec::with_capacity(prompt.len() + tokens.len() + wround.len());
10598 window.extend_from_slice(prompt);
10599 window.extend_from_slice(&tokens);
10600 window.extend_from_slice(&wround);
10601 dsv4_penalize_row(&mut row, &window, pc);
10602 dsv4_sample_row(&row, m0 + i + 1, sample)?
10603 } else {
10604 dsv4_sample_row(&rows[i * vocab..(i + 1) * vocab], m0 + i + 1, sample)?
10605 };
10606 if i < kv && s == batch_ids[i + 1] {
10607 c_d += 1;
10608 wround.push(batch_ids[i + 1]);
10609 continue;
10610 }
10611 t_next = s;
10612 break;
10613 }
10614 let n_commit = c_d + 1;
10615 self.commit_verify_dev(state, vstate, n_commit)?;
10616 for i in 0..n_commit {
10617 self.dspark_write_rings(dstate, i, m0 + i)?;
10618 }
10619 {
10620 let last = self.stages.len() - 1;
10621 self.stages[last]
10622 .gpu
10623 .stream()
10624 .synchronize()
10625 .map_err(e("round close sync"))?;
10626 }
10627 mh_row = c_d;
10628 for i in 0..c_d {
10629 tokens.push(batch_ids[i + 1]);
10630 }
10631 carry_pending = c_d == kv && t_batch < t_cap;
10632 rounds.push(SpecRoundGpu {
10633 start_pos: m0 - 1,
10634 drafts: prop.out_ids[1..].to_vec(),
10635 accepts: c_d,
10636 verified: (c_d + 1).min(kv),
10637 t_batch,
10638 t_cap,
10639 confidence: prop.confidence.clone(),
10640 emitted: 1 + c_d,
10641 round_us: round_t0.elapsed().as_micros() as u64,
10642 });
10643 t_tok = t_next;
10644 if let Some(cb) = round_cb.as_deref_mut()
10645 && !cb(&tokens[cb_from..])
10646 {
10647 break;
10648 }
10649 }
10650 Ok(SpecRunGpu { tokens, rounds })
10651 }
10652}
10653
10654impl Dsv4Gpu {
10655 pub fn cache_classes(&self, state: &DecodeState) -> Res<Vec<(String, Vec<f32>)>> {
10661 let d = self.model.cfg();
10662 let hd = d.head_dim as usize;
10663 let win = d.sliding_window as usize;
10664 let mut out = Vec::new();
10665 for (il, cache) in state.caches.iter().enumerate() {
10666 let stage_i = self.layer_stage[il];
10667 let st = &self.stages[stage_i];
10668 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx classes"))?;
10669 let stream = st.gpu.stream();
10670 let lidx = st
10671 .layers
10672 .iter()
10673 .position(|l| l.il == il as u32)
10674 .unwrap_or_else(|| panic!("layer {il} not on stage {stage_i}"));
10675 let layer = &st.layers[lidx];
10676 let read = |sl: cudarc::driver::CudaView<'_, f32>| -> Res<Vec<f32>> {
10677 let mut v = vec![0f32; sl.len()];
10678 stream
10679 .memcpy_dtoh(&sl, &mut v[..])
10680 .map_err(e("dtoh class"))?;
10681 stream.synchronize().map_err(e("sync class"))?;
10682 Ok(v)
10683 };
10684 out.push((format!("l{il}.ring"), read(cache.kvc.slice(0..win * hd))?));
10685 if let Some(cmp) = &layer.cmp {
10686 out.push((
10687 format!("l{il}.cmp_store"),
10688 read(cache.kvc.slice(win * hd..(win + cache.n_blocks) * cmp.d))?,
10689 ));
10690 out.push((
10691 format!("l{il}.cmp_pend_kv"),
10692 read(cache.pend_kv.as_ref().expect("pend kv").slice(..))?,
10693 ));
10694 out.push((
10695 format!("l{il}.cmp_pend_score"),
10696 read(cache.pend_score.as_ref().expect("pend sc").slice(..))?,
10697 ));
10698 }
10699 if let Some(ix) = &layer.idx {
10700 let ikvc = cache.ikvc.as_ref().expect("ikvc");
10701 out.push((
10702 format!("l{il}.idx_store"),
10703 read(ikvc.slice(0..cache.i_blocks * ix.cmp.d))?,
10704 ));
10705 out.push((
10706 format!("l{il}.idx_pend_kv"),
10707 read(cache.ipend_kv.as_ref().expect("ipend kv").slice(..))?,
10708 ));
10709 out.push((
10710 format!("l{il}.idx_pend_score"),
10711 read(cache.ipend_score.as_ref().expect("ipend sc").slice(..))?,
10712 ));
10713 }
10714 }
10715 Ok(out)
10716 }
10717
10718 pub fn dspark_ring_classes(&self, dstate: &DsparkState) -> Res<Vec<(String, Vec<f32>)>> {
10722 let last = self.stages.len() - 1;
10723 let st = &self.stages[last];
10724 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx rings"))?;
10725 let stream = st.gpu.stream();
10726 let d = self.model.cfg();
10727 let hd = d.head_dim as usize;
10728 let win = d.sliding_window as usize;
10729 let mut out = Vec::new();
10730 for (bi, ring) in dstate.rings.iter().enumerate() {
10731 let view = ring.slice(0..win * hd);
10734 let mut v = vec![0f32; view.len()];
10735 stream
10736 .memcpy_dtoh(&view, &mut v[..])
10737 .map_err(e("dtoh ring class"))?;
10738 stream.synchronize().map_err(e("sync ring class"))?;
10739 out.push((format!("dspark.ring{bi}"), v));
10740 }
10741 Ok(out)
10742 }
10743}
10744
10745pub fn resolve_dense_arm(v: Option<&str>, on_device: bool) -> Result<bool, String> {
10751 match v {
10752 None | Some("") => Ok(on_device),
10753 Some("bf16") => Ok(false),
10754 Some("fp8") if !on_device => Err(
10755 "MEMRA_DSV4_DENSE_ARM=fp8 requires MEMRA_DSV4_DECODE_PATH=device (the \
10756 fp8 GEMV twins exist on the device decode/verify paths only; prefill \
10757 and the legacy path consume the bf16 slabs)"
10758 .to_string(),
10759 ),
10760 Some("fp8") => Ok(true),
10761 Some(other) => Err(format!(
10762 "MEMRA_DSV4_DENSE_ARM '{other}' unknown (bf16 | fp8)"
10763 )),
10764 }
10765}
10766
10767#[derive(Clone, Copy, Debug, PartialEq)]
10785pub enum Dsv4Vt {
10786 Off,
10787 Slot {
10790 tau_logit: f32,
10791 floor: usize,
10792 },
10793}
10794
10795pub fn resolve_vt(
10796 policy: Option<&str>,
10797 tau: Option<&str>,
10798 floor: Option<&str>,
10799) -> Result<Dsv4Vt, String> {
10800 match policy {
10801 None | Some("") | Some("off") => {
10802 if let Some(t) = tau {
10803 return Err(format!(
10804 "MEMRA_DSV4_VT_TAU='{t}' set without MEMRA_DSV4_VT=slot (orphan knob \
10805 would be silently inert — refuse instead)"
10806 ));
10807 }
10808 if let Some(f) = floor {
10809 return Err(format!(
10810 "MEMRA_DSV4_VT_FLOOR='{f}' set without MEMRA_DSV4_VT=slot (orphan \
10811 knob would be silently inert — refuse instead)"
10812 ));
10813 }
10814 Ok(Dsv4Vt::Off)
10815 }
10816 Some("slot") => {
10817 let tau_v: f32 = match tau {
10818 None => 0.5,
10819 Some(s) => s
10820 .trim()
10821 .parse::<f32>()
10822 .map_err(|_| format!("MEMRA_DSV4_VT_TAU '{s}' is not a float in (0,1)"))?,
10823 };
10824 if !(tau_v > 0.0 && tau_v < 1.0) {
10825 return Err(format!(
10826 "MEMRA_DSV4_VT_TAU {tau_v} out of range: need 0 < tau < 1 \
10827 (a probability threshold on the per-slot sigmoid)"
10828 ));
10829 }
10830 let floor_v: usize = match floor {
10831 None => 0,
10832 Some(s) => s.trim().parse::<usize>().map_err(|_| {
10833 format!("MEMRA_DSV4_VT_FLOOR '{s}' is not a non-negative integer")
10834 })?,
10835 };
10836 if floor_v >= 5 {
10840 return Err(format!(
10841 "MEMRA_DSV4_VT_FLOOR {floor_v} >= dspark block size 5 would pin the \
10842 window fully open (use MEMRA_DSV4_VT=off to disable)"
10843 ));
10844 }
10845 Ok(Dsv4Vt::Slot {
10846 tau_logit: (tau_v / (1.0 - tau_v)).ln(),
10847 floor: floor_v,
10848 })
10849 }
10850 Some(other) => Err(format!("MEMRA_DSV4_VT '{other}' unknown (off | slot)")),
10851 }
10852}
10853
10854#[derive(Clone, Copy, Debug)]
10874pub struct Dsv4SampleCfg {
10875 pub temperature: f32,
10876 pub top_p: f32,
10877 pub top_k: usize,
10878 pub seed: u64,
10879}
10880
10881fn splitmix64(mut x: u64) -> u64 {
10882 x = x.wrapping_add(0x9e3779b97f4a7c15);
10883 let mut z = x;
10884 z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
10885 z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
10886 z ^ (z >> 31)
10887}
10888
10889pub fn dsv4_pos_uniform(seed: u64, pos: usize) -> f64 {
10891 let h = splitmix64(seed ^ (pos as u64).wrapping_mul(0xa24baed4963ee407));
10892 (h >> 11) as f64 / (1u64 << 53) as f64
10893}
10894
10895#[allow(clippy::neg_cmp_op_on_partial_ord)] pub fn dsv4_sample_row(logits: &[f32], pos: usize, cfg: &Dsv4SampleCfg) -> Result<u32, String> {
10898 if !(cfg.temperature > 0.0) {
10899 return Err(format!(
10900 "dsv4 sampled path: temperature {} refused (need > 0; greedy is served by \
10901 the greedy driver, never a silent argmax fallback)",
10902 cfg.temperature
10903 ));
10904 }
10905 if !(cfg.top_p > 0.0 && cfg.top_p <= 1.0) {
10906 return Err(format!(
10907 "dsv4 sampled path: top_p {} out of (0, 1]",
10908 cfg.top_p
10909 ));
10910 }
10911 let k = if cfg.top_k == 0 || cfg.top_k > logits.len() {
10913 logits.len()
10914 } else {
10915 cfg.top_k
10916 };
10917 let mut idx: Vec<u32> = (0..logits.len() as u32).collect();
10918 idx.sort_by(|&a, &b| {
10919 let (va, vb) = (logits[a as usize], logits[b as usize]);
10920 vb.partial_cmp(&va)
10921 .unwrap_or(std::cmp::Ordering::Equal)
10922 .then(a.cmp(&b))
10923 });
10924 idx.truncate(k);
10925 let m = logits[idx[0] as usize] as f64;
10927 let t = cfg.temperature as f64;
10928 let mut probs: Vec<f64> = idx
10929 .iter()
10930 .map(|&i| (((logits[i as usize] as f64) - m) / t).exp())
10931 .collect();
10932 let z: f64 = probs.iter().sum();
10933 for p in &mut probs {
10934 *p /= z;
10935 }
10936 let mut cum = 0.0f64;
10938 let mut keep = probs.len();
10939 for (i, p) in probs.iter().enumerate() {
10940 cum += p;
10941 if cum >= cfg.top_p as f64 {
10942 keep = i + 1;
10943 break;
10944 }
10945 }
10946 probs.truncate(keep);
10947 idx.truncate(keep);
10948 let z2: f64 = probs.iter().sum();
10949 let u = dsv4_pos_uniform(cfg.seed, pos) * z2;
10950 let mut acc = 0.0f64;
10951 for (i, p) in probs.iter().enumerate() {
10952 acc += p;
10953 if u < acc {
10954 return Ok(idx[i]);
10955 }
10956 }
10957 Ok(idx[keep - 1]) }
10959
10960#[derive(Clone, Copy, Debug)]
10968pub struct Dsv4PenaltyCfg {
10969 pub last_n: usize,
10970 pub repeat: f32,
10971 pub freq: f32,
10972 pub present: f32,
10973}
10974
10975impl Dsv4PenaltyCfg {
10976 pub fn armed(&self) -> bool {
10977 self.last_n > 0 && (self.repeat != 1.0 || self.freq != 0.0 || self.present != 0.0)
10978 }
10979}
10980
10981pub fn dsv4_penalize_row(logits: &mut [f32], window: &[u32], cfg: &Dsv4PenaltyCfg) {
10983 if !cfg.armed() {
10984 return;
10985 }
10986 let start = window.len().saturating_sub(cfg.last_n);
10987 let win = &window[start..];
10988 if win.is_empty() {
10989 return;
10990 }
10991 let mut counts: std::collections::HashMap<u32, i32> = std::collections::HashMap::new();
10992 for &t in win {
10993 *counts.entry(t).or_insert(0) += 1;
10994 }
10995 for (&id, &cnt) in &counts {
10996 let Some(l) = logits.get_mut(id as usize) else {
10997 continue;
10998 };
10999 if cfg.repeat != 1.0 {
11000 if *l > 0.0 {
11001 *l /= cfg.repeat;
11002 } else {
11003 *l *= cfg.repeat;
11004 }
11005 }
11006 *l -= cfg.freq * cnt as f32;
11007 if cnt > 0 {
11008 *l -= cfg.present;
11009 }
11010 }
11011}
11012
11013pub fn vt_slot_drafts(conf: &[f32], tau_logit: f32, floor: usize) -> usize {
11018 let mut k = 0usize;
11019 for &c in conf {
11020 if c >= tau_logit {
11021 k += 1;
11022 } else {
11023 break;
11024 }
11025 }
11026 k.max(floor).min(conf.len())
11027}
11028
11029#[cfg(test)]
11030mod peer_probe_tests {
11031 use super::{dsv4_peer_probe_ladder, dsv4_peer_probe_mismatches, dsv4_peer_probe_pattern};
11032
11033 #[test]
11039 fn peer_probe_pattern_is_keyed_and_mismatches_are_counted() {
11040 let a = dsv4_peer_probe_pattern(4096, 0, 0, 1);
11041 assert_eq!(a.len(), 4096);
11042 assert_eq!(a, dsv4_peer_probe_pattern(4096, 0, 0, 1), "deterministic");
11043 assert_ne!(a, dsv4_peer_probe_pattern(4096, 0, 1, 0), "direction-keyed");
11044 assert_ne!(a, dsv4_peer_probe_pattern(4096, 1, 0, 1), "boundary-keyed");
11045 assert!(a.iter().any(|&b| b != a[0]), "non-constant pattern");
11046
11047 assert_eq!(dsv4_peer_probe_mismatches(&a, &a), 0);
11048 let mut flipped = a.clone();
11049 flipped[17] ^= 1;
11050 assert_eq!(dsv4_peer_probe_mismatches(&a, &flipped), 1);
11051 let poison: Vec<u8> = a.iter().map(|b| !b).collect();
11052 assert_eq!(dsv4_peer_probe_mismatches(&a, &poison), a.len());
11053 assert_eq!(dsv4_peer_probe_mismatches(&a, &a[..4000]), 96);
11054 }
11055
11056 #[test]
11057 fn peer_probe_ladder_contains_live_hc_payloads() {
11058 let ladder = dsv4_peer_probe_ladder(4096, 4);
11059 assert!(ladder.contains(&(64 << 10)), "one-token hc state");
11060 assert!(ladder.contains(&(512 << 10)), "eight-row verify hc state");
11061 assert!(ladder.contains(&(64 << 20)), "maximum prefill handoff");
11062 }
11063}
11064
11065#[cfg(test)]
11066mod dense_arm_default_tests {
11067 use super::resolve_dense_arm;
11068
11069 #[test]
11072 fn ratified_default_dense_arm_is_fp8_on_device() {
11073 assert_eq!(
11074 resolve_dense_arm(None, true),
11075 Ok(true),
11076 "owner-ratified 2026-08-20: unset MEMRA_DSV4_DENSE_ARM defaults the DEVICE \
11077 decode path to fp8 (bit-identical on four boxes, x5 A/B 41.06->47.19, \
11078 item-3 residency green on box7)"
11079 );
11080 assert_eq!(resolve_dense_arm(Some(""), true), Ok(true));
11081 assert_eq!(resolve_dense_arm(None, false), Ok(false));
11083 assert_eq!(resolve_dense_arm(Some("bf16"), true), Ok(false));
11085 assert_eq!(resolve_dense_arm(Some("fp8"), true), Ok(true));
11086 assert!(
11087 resolve_dense_arm(Some("fp8"), false).is_err(),
11088 "legacy+fp8 stays a refusal"
11089 );
11090 assert!(
11091 resolve_dense_arm(Some("q8"), true).is_err(),
11092 "unknown values refuse"
11093 );
11094 }
11095}
11096
11097#[cfg(test)]
11098mod vt_policy_tests {
11099 use super::{Dsv4Vt, resolve_vt, vt_slot_drafts};
11100
11101 #[test]
11104 fn default_vt_is_off_and_byte_inert() {
11105 assert_eq!(resolve_vt(None, None, None), Ok(Dsv4Vt::Off));
11106 assert_eq!(resolve_vt(Some(""), None, None), Ok(Dsv4Vt::Off));
11107 assert_eq!(resolve_vt(Some("off"), None, None), Ok(Dsv4Vt::Off));
11108 }
11109
11110 #[test]
11111 fn slot_defaults_tau_half_floor_zero() {
11112 match resolve_vt(Some("slot"), None, None) {
11115 Ok(Dsv4Vt::Slot { tau_logit, floor }) => {
11116 assert_eq!(tau_logit, 0.0);
11117 assert_eq!(floor, 0);
11118 }
11119 other => panic!("slot default parse broke: {other:?}"),
11120 }
11121 match resolve_vt(Some("slot"), Some("0.6"), Some("2")) {
11123 Ok(Dsv4Vt::Slot { tau_logit, floor }) => {
11124 assert!((tau_logit - (0.6f32 / 0.4).ln()).abs() < 1e-6);
11125 assert_eq!(floor, 2);
11126 }
11127 other => panic!("slot tau/floor parse broke: {other:?}"),
11128 }
11129 }
11130
11131 #[test]
11132 fn unknown_and_out_of_range_refuse_by_name() {
11133 for (p, t, f) in [
11134 (Some("banana"), None, None), (Some("slot"), Some("0"), None), (Some("slot"), Some("1"), None),
11137 (Some("slot"), Some("nan"), None),
11138 (Some("slot"), Some("x"), None),
11139 (Some("slot"), None, Some("5")), (Some("slot"), None, Some("-1")),
11141 (None, Some("0.5"), None), (Some("off"), None, Some("2")), ] {
11144 let r = resolve_vt(p, t, f);
11145 assert!(r.is_err(), "({p:?},{t:?},{f:?}) must refuse, got {r:?}");
11146 let msg = r.unwrap_err();
11147 assert!(
11148 msg.contains("MEMRA_DSV4_VT"),
11149 "refusal must name the knob: {msg}"
11150 );
11151 }
11152 }
11153
11154 #[test]
11157 fn slot_truncation_is_leading_prefix_with_floor() {
11158 let up = 3.0f32; let dn = -3.0f32; assert_eq!(vt_slot_drafts(&[up, up, up, up, up], 0.0, 0), 5);
11161 assert_eq!(vt_slot_drafts(&[dn, dn, dn, dn, dn], 0.0, 0), 0);
11162 assert_eq!(vt_slot_drafts(&[up, up, dn, up, up], 0.0, 0), 2);
11163 assert_eq!(vt_slot_drafts(&[0.0, dn, dn, dn, dn], 0.0, 0), 1);
11165 assert_eq!(vt_slot_drafts(&[dn, dn, dn, dn, dn], 0.0, 2), 2);
11167 assert_eq!(vt_slot_drafts(&[dn, dn], 0.0, 4), 2);
11168 assert_eq!(vt_slot_drafts(&[f32::NAN, up, up, up, up], 0.0, 0), 0);
11170 assert_eq!(vt_slot_drafts(&[], 0.0, 0), 0);
11171 }
11172
11173 #[test]
11176 fn off_arm_t_cap_expression_is_identity() {
11177 for k_drafts in 0usize..=5 {
11178 let vt_drafts = k_drafts; assert_eq!((vt_drafts + 1).min(k_drafts + 1), k_drafts + 1);
11180 }
11181 }
11182}
11183
11184#[cfg(test)]
11185mod penalty_cross_pin_tests {
11186 use super::{Dsv4PenaltyCfg, dsv4_penalize_row};
11187
11188 #[test]
11194 fn penalize_matches_the_sampling_crate_reference() {
11195 let mk_row = |seed: u32| -> Vec<f32> {
11196 (0..64u32)
11197 .map(|i| {
11198 let h = i.wrapping_mul(2654435761).wrapping_add(seed);
11199 ((h % 2000) as f32 / 100.0) - 10.0
11200 })
11201 .collect()
11202 };
11203 for (seed, window, last_n, rep, freq, present) in [
11204 (
11205 1u32,
11206 vec![3u32, 3, 3, 7, 12, 3],
11207 8usize,
11208 1.8f32,
11209 0.4f32,
11210 0.6f32,
11211 ),
11212 (2, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 4, 1.3, 0.0, 0.0),
11213 (3, vec![63, 63, 63, 63], 64, 1.0, 1.1, 0.0),
11214 (4, vec![5], 1, 2.5, 0.7, 1.3),
11215 ] {
11216 let row = mk_row(seed);
11217 let mut ours = row.clone();
11219 dsv4_penalize_row(
11220 &mut ours,
11221 &window,
11222 &Dsv4PenaltyCfg {
11223 last_n,
11224 repeat: rep,
11225 freq,
11226 present,
11227 },
11228 );
11229 let our_pick = ours
11230 .iter()
11231 .enumerate()
11232 .max_by(|a, b| a.1.total_cmp(b.1))
11233 .unwrap()
11234 .0 as u32;
11235 let mut sampler = memra_sampling::Sampler::new(memra_sampling::SamplerConfig {
11237 temperature: 0.0,
11238 top_k: 0,
11239 top_p: 1.0,
11240 min_p: 0.0,
11241 penalty_last_n: last_n,
11242 penalty_repeat: rep,
11243 penalty_freq: freq,
11244 penalty_present: present,
11245 seed: 0,
11246 });
11247 for &t in &window {
11248 sampler.accept(t);
11249 }
11250 let ref_pick = sampler.sample(&row);
11251 assert_eq!(
11252 our_pick, ref_pick,
11253 "penalized argmax diverged from memra-sampling (seed {seed}): \
11254 ours {our_pick} vs reference {ref_pick}"
11255 );
11256 }
11257 }
11258}
11259
11260#[cfg(test)]
11261mod sampled_path_tests {
11262 use super::{Dsv4SampleCfg, dsv4_pos_uniform, dsv4_sample_row};
11263
11264 fn cfg(seed: u64) -> Dsv4SampleCfg {
11265 Dsv4SampleCfg {
11266 temperature: 1.0,
11267 top_p: 0.95,
11268 top_k: 0,
11269 seed,
11270 }
11271 }
11272
11273 #[test]
11276 fn draws_are_position_keyed_and_deterministic() {
11277 let row = [0.1f32, 2.0, -1.0, 1.9, 0.0];
11278 let a = dsv4_sample_row(&row, 40, &cfg(20260822)).unwrap();
11279 for _ in 0..8 {
11280 assert_eq!(dsv4_sample_row(&row, 40, &cfg(20260822)).unwrap(), a);
11281 }
11282 let u0 = dsv4_pos_uniform(20260822, 40);
11284 let u1 = dsv4_pos_uniform(20260822, 41);
11285 let v0 = dsv4_pos_uniform(7, 40);
11286 assert_ne!(u0, u1);
11287 assert_ne!(u0, v0);
11288 assert!((0.0..1.0).contains(&u0));
11289 }
11290
11291 #[test]
11294 fn t0_and_bad_topp_refuse_by_name() {
11295 let row = [0.0f32, 1.0];
11296 let mut c = cfg(1);
11297 c.temperature = 0.0;
11298 let e = dsv4_sample_row(&row, 0, &c).unwrap_err();
11299 assert!(e.contains("temperature"), "{e}");
11300 let mut c2 = cfg(1);
11301 c2.top_p = 0.0;
11302 assert!(dsv4_sample_row(&row, 0, &c2).is_err());
11303 }
11304
11305 #[test]
11308 fn filters_collapse_to_argmax_and_ties_break_low_index() {
11309 let row = [0.0f32, 5.0, 5.0, -2.0];
11310 let mut c = cfg(99);
11311 c.top_k = 1;
11312 for pos in 0..64 {
11313 assert_eq!(dsv4_sample_row(&row, pos, &c).unwrap(), 1);
11314 }
11315 let mut c2 = cfg(99);
11316 c2.top_p = 1e-9; for pos in 0..64 {
11318 assert_eq!(dsv4_sample_row(&row, pos, &c2).unwrap(), 1);
11319 }
11320 }
11321
11322 #[test]
11325 fn draw_frequencies_track_the_filtered_target() {
11326 let row = [3.0f32, 1.0, 0.0, -50.0];
11327 let mut c = cfg(20260822);
11328 c.top_k = 3;
11329 c.top_p = 1.0;
11330 let mut counts = [0usize; 4];
11331 for pos in 0..4096 {
11332 counts[dsv4_sample_row(&row, pos, &c).unwrap() as usize] += 1;
11333 }
11334 assert_eq!(counts[3], 0, "outside top-k must never be drawn");
11335 assert!(counts[0] > 2600, "p(tok0) ~ 0.84, got {}/4096", counts[0]);
11336 assert!(counts[1] > 100, "tail token starved: {}", counts[1]);
11337 }
11338}