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(|a, b| (b.1.saturating_sub(b.2)).cmp(&(a.1.saturating_sub(a.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 fn dots_dev(
2071 &self,
2072 st: &Stage,
2073 x: &CudaSlice<f32>,
2074 w_f32: &CudaSlice<f32>,
2075 s: usize,
2076 kdim: usize,
2077 n: usize,
2078 y: &mut CudaSlice<f32>,
2079 ) -> Res<()> {
2080 if !self.dots_f32 {
2081 return Self::dots(st, x, w_f32, s, kdim, n, y);
2082 }
2083 let stream = st.gpu.stream();
2084 unsafe {
2085 ck(
2086 "dots_f32acc",
2087 k::memra_dsv4_dots_f32acc(
2088 dpf!(x, &stream),
2089 dp!(w_f32, &stream),
2090 0,
2091 dpm!(y, &stream),
2092 s as i32,
2093 kdim as i32,
2094 n as i32,
2095 sp(&stream),
2096 ),
2097 )?;
2098 }
2099 Ok(())
2100 }
2101
2102 #[allow(clippy::too_many_arguments)]
2107 fn compressor(
2108 &self,
2109 st: &Stage,
2110 cmp: &CmpDev,
2111 x: &CudaSlice<f32>, s: usize,
2113 hidden: usize,
2114 fc_dev: &CudaSlice<f32>,
2115 rd: usize,
2116 eps: f32,
2117 ) -> Res<(
2118 Option<(CudaSlice<f32>, usize)>,
2119 CudaSlice<f32>,
2120 CudaSlice<f32>,
2121 )> {
2122 let stream = st.gpu.stream();
2123 let mut kv = stream
2124 .alloc_zeros::<f32>(s * cmp.latent)
2125 .map_err(e("cmp kv"))?;
2126 let mut score = stream
2127 .alloc_zeros::<f32>(s * cmp.latent)
2128 .map_err(e("cmp score"))?;
2129 Self::dots(st, x, &cmp.wkv, s, hidden, cmp.latent, &mut kv)?;
2130 Self::dots(st, x, &cmp.wgate, s, hidden, cmp.latent, &mut score)?;
2131 if s < cmp.ratio {
2132 return Ok((None, kv, score));
2133 }
2134 let cutoff = s - s % cmp.ratio;
2135 let nb = cutoff / cmp.ratio;
2136 let mut pooled = stream
2137 .alloc_zeros::<f32>(nb * cmp.d)
2138 .map_err(e("cmp out"))?;
2139 unsafe {
2140 ck(
2141 "compressor_pool",
2142 k::memra_dsv4_compressor_pool(
2143 dpf!(kv, &stream),
2144 dpf!(score, &stream),
2145 dpf!(cmp.ape, &stream),
2146 dpm!(pooled, &stream),
2147 nb as i32,
2148 cmp.ratio as i32,
2149 cmp.d as i32,
2150 cmp.latent as i32,
2151 cmp.overlap as i32,
2152 sp(&stream),
2153 ),
2154 )?;
2155 ck(
2156 "rmsnorm cmp",
2157 k::memra_dsv4_rmsnorm(
2158 dpf!(pooled, &stream),
2159 dpf!(cmp.norm, &stream),
2160 dpm!(pooled, &stream),
2161 nb as i32,
2162 cmp.d as i32,
2163 eps,
2164 sp(&stream),
2165 ),
2166 )?;
2167 let positions: Vec<i32> = (0..nb).map(|j| (j * cmp.ratio) as i32).collect();
2168 let pos_dev = upload_i32(&stream, &positions)?;
2169 ck(
2170 "rope cmp",
2171 k::memra_dsv4_rope(
2172 dpm!(pooled, &stream),
2173 nb as i32,
2174 1,
2175 cmp.d as i32,
2176 rd as i32,
2177 dpf!(fc_dev, &stream),
2178 pos_dev.device_ptr(&stream).0 as *const i32,
2179 0,
2180 sp(&stream),
2181 ),
2182 )?;
2183 if cmp.rotate {
2184 let scale = (cmp.d as f32).powf(-0.5);
2186 ck(
2187 "hadamard cmp",
2188 k::memra_dsv4_hadamard(
2189 dpm!(pooled, &stream),
2190 nb as i32,
2191 cmp.d as i32,
2192 scale,
2193 sp(&stream),
2194 ),
2195 )?;
2196 ck(
2197 "fp4 cmp",
2198 k::memra_dsv4_fp4_act_quant(
2199 dpm!(pooled, &stream),
2200 nb as i32,
2201 cmp.d as i64,
2202 cmp.d as i32,
2203 sp(&stream),
2204 ),
2205 )?;
2206 } else {
2207 ck(
2208 "act_quant cmp",
2209 k::memra_dsv4_act_quant(
2210 dpm!(pooled, &stream),
2211 nb as i32,
2212 cmp.d as i64,
2213 (cmp.d - rd) as i32,
2214 64,
2215 (self.variant == ActQuantVariant::ClampOnly) as i32,
2216 sp(&stream),
2217 ),
2218 )?;
2219 }
2220 }
2221 Ok((Some((pooled, nb)), kv, score))
2222 }
2223
2224 #[allow(clippy::too_many_arguments)]
2229 fn populate_cmp_cache(
2230 stream: &std::sync::Arc<CudaStream>,
2231 s: usize,
2232 cmp_ratio: usize,
2233 latent: usize,
2234 d: usize,
2235 pooled: &Option<(CudaSlice<f32>, usize)>,
2236 kv_raw: &CudaSlice<f32>,
2237 score_raw: &CudaSlice<f32>,
2238 store: &mut CudaSlice<f32>,
2239 row0: usize,
2240 blocks: &mut usize,
2241 pend_kv: &mut CudaSlice<f32>,
2242 pend_score: &mut CudaSlice<f32>,
2243 overlap: bool,
2244 ) -> Res<()> {
2245 *blocks = 0;
2246 if let Some((buf, nb)) = pooled {
2247 let src = buf.slice(0..nb * d);
2248 let mut dst = store.slice_mut(row0 * d..(row0 + nb) * d);
2249 stream.memcpy_dtod(&src, &mut dst).map_err(e("cmp store"))?;
2250 *blocks = *nb;
2251 }
2252 let cutoff = s - s % cmp_ratio;
2253 let rem = s - cutoff;
2254 if overlap {
2255 if cutoff >= cmp_ratio {
2256 let a = (cutoff - cmp_ratio) * latent;
2257 let b = cutoff * latent;
2258 let src = kv_raw.slice(a..b);
2259 let mut dst = pend_kv.slice_mut(0..cmp_ratio * latent);
2260 stream
2261 .memcpy_dtod(&src, &mut dst)
2262 .map_err(e("pend kv prev"))?;
2263 let src = score_raw.slice(a..b);
2264 let mut dst = pend_score.slice_mut(0..cmp_ratio * latent);
2265 stream
2266 .memcpy_dtod(&src, &mut dst)
2267 .map_err(e("pend sc prev"))?;
2268 }
2269 if rem > 0 {
2270 let a = cutoff * latent;
2271 let src = kv_raw.slice(a..s * latent);
2272 let mut dst = pend_kv.slice_mut(cmp_ratio * latent..(cmp_ratio + rem) * latent);
2273 stream
2274 .memcpy_dtod(&src, &mut dst)
2275 .map_err(e("pend kv cur"))?;
2276 let src = score_raw.slice(a..s * latent);
2277 let mut dst = pend_score.slice_mut(cmp_ratio * latent..(cmp_ratio + rem) * latent);
2278 stream
2279 .memcpy_dtod(&src, &mut dst)
2280 .map_err(e("pend sc cur"))?;
2281 }
2282 } else if rem > 0 {
2283 let a = cutoff * latent;
2284 let src = kv_raw.slice(a..s * latent);
2285 let mut dst = pend_kv.slice_mut(0..rem * latent);
2286 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend kv"))?;
2287 let src = score_raw.slice(a..s * latent);
2288 let mut dst = pend_score.slice_mut(0..rem * latent);
2289 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend sc"))?;
2290 }
2291 Ok(())
2292 }
2293
2294 #[allow(clippy::too_many_arguments)]
2297 fn hc_pre(
2298 st: &Stage,
2299 h: &CudaSlice<f32>, fn_w: &CudaSlice<f32>,
2301 base: &[f32],
2302 scale: &[f32],
2303 s: usize,
2304 hc: usize,
2305 hidden: usize,
2306 iters: u32,
2307 hc_eps: f32,
2308 ) -> Res<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)> {
2309 let stream = st.gpu.stream();
2310 let w = hc * hidden;
2311 let rows = (2 + hc) * hc;
2312 let mut mixes = stream.alloc_zeros::<f32>(s * rows).map_err(e("mixes"))?;
2313 Self::dots(st, h, fn_w, s, w, rows, &mut mixes)?;
2314 unsafe {
2315 ck(
2316 "rowsq_scale",
2317 k::memra_dsv4_rowsq_scale(
2318 dpf!(h, &stream),
2319 dpm!(mixes, &stream),
2320 s as i32,
2321 w as i32,
2322 rows as i32,
2323 hc_eps,
2324 sp(&stream),
2325 ),
2326 )?;
2327 }
2328 let mixes_h = dtoh_f32(&stream, &mixes)?;
2329 let (pre, post, comb) = hc_split_sinkhorn(&mixes_h, s, hc, scale, base, iters, hc_eps);
2330 let pre_d = upload_f32(&stream, &pre)?;
2331 let post_d = upload_f32(&stream, &post)?;
2332 let comb_d = upload_f32(&stream, &comb)?;
2333 let mut y = stream.alloc_zeros::<f32>(s * hidden).map_err(e("hc y"))?;
2334 unsafe {
2335 ck(
2336 "hc_collapse",
2337 k::memra_dsv4_hc_collapse(
2338 dpf!(h, &stream),
2339 dpf!(pre_d, &stream),
2340 dpm!(y, &stream),
2341 s as i32,
2342 hc as i32,
2343 hidden as i32,
2344 sp(&stream),
2345 ),
2346 )?;
2347 }
2348 Ok((y, post_d, comb_d))
2349 }
2350
2351 #[allow(clippy::too_many_arguments)]
2353 fn route_host(
2354 layer: &LayerDev,
2355 raw_scores: &[f32], ids: &[u32],
2357 s: usize,
2358 ne: usize,
2359 topk: usize,
2360 route_scale: f32,
2361 ) -> (Vec<usize>, Vec<f32>) {
2362 let mut scores = raw_scores.to_vec();
2363 for v in &mut scores {
2364 *v = softplus_f32(*v).sqrt();
2365 }
2366 let mut indices = vec![0usize; s * topk];
2367 if let Some(tid2eid) = &layer.tid2eid {
2368 for t in 0..s {
2369 let row = &tid2eid[ids[t] as usize * topk..(ids[t] as usize + 1) * topk];
2370 let mut seen = std::collections::BTreeSet::new();
2371 for (kk, &ex) in row.iter().enumerate() {
2372 assert!(
2373 (0..ne as i64).contains(&ex),
2374 "layer {}: tid2eid out of range",
2375 layer.il
2376 );
2377 assert!(
2378 seen.insert(ex),
2379 "layer {}: duplicate expert id in tid2eid row {}",
2380 layer.il,
2381 ids[t]
2382 );
2383 indices[t * topk + kk] = ex as usize;
2384 }
2385 }
2386 } else {
2387 let bias = layer.gate_bias.as_ref().expect("score layer needs bias");
2388 for t in 0..s {
2389 let biased: Vec<f32> = (0..ne).map(|ex| scores[t * ne + ex] + bias[ex]).collect();
2390 let mut order: Vec<usize> = (0..ne).collect();
2391 order.sort_by(|&a, &b| {
2392 biased[b]
2393 .partial_cmp(&biased[a])
2394 .unwrap_or(std::cmp::Ordering::Equal)
2395 .then(a.cmp(&b))
2396 });
2397 for kk in 0..topk {
2398 indices[t * topk + kk] = order[kk];
2399 }
2400 }
2401 }
2402 let mut weights = vec![0f32; s * topk];
2403 for t in 0..s {
2404 let mut sum = 0f32;
2405 for kk in 0..topk {
2406 let w = scores[t * ne + indices[t * topk + kk]];
2407 weights[t * topk + kk] = w;
2408 sum += w;
2409 }
2410 for kk in 0..topk {
2411 weights[t * topk + kk] = weights[t * topk + kk] / sum * route_scale;
2412 }
2413 }
2414 (indices, weights)
2415 }
2416
2417 #[allow(clippy::too_many_arguments)]
2420 fn block_forward(
2421 &self,
2422 st: &Stage,
2423 layer: &LayerDev,
2424 h: &CudaSlice<f32>,
2425 s: usize,
2426 ids: &[u32],
2427 mut capture: Option<&mut GpuCapture>,
2428 mut cache: Option<&mut LayerCache>,
2429 ) -> Res<CudaSlice<f32>> {
2430 let d = self.model.cfg();
2431 let mc = &self.model.mc;
2432 let hc = d.hc_mult as usize;
2433 let hidden = mc.n_embd as usize;
2434 let heads = mc.n_head as usize;
2435 let hd = d.head_dim as usize;
2436 let rd = d.qk_rope_head_dim as usize;
2437 let q_lora = d.q_lora_rank as usize;
2438 let win = d.sliding_window as usize;
2439 let o_groups = d.o_groups as usize;
2440 let o_lora = d.o_lora_rank as usize;
2441 let eps = mc.rms_eps;
2442 let iters = d.hc_sinkhorn_iters;
2443 let hc_eps = d.hc_eps;
2444 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx"))?;
2448 let stream = st.gpu.stream();
2449 let fc_dev = if layer.ratio != 0 {
2450 &st.fc_yarn
2451 } else {
2452 &st.fc_plain
2453 };
2454 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
2455
2456 let (y, post, comb) = Self::hc_pre(
2458 st,
2459 h,
2460 &layer.hc_attn_fn,
2461 &layer.hc_attn_base,
2462 &layer.hc_attn_scale,
2463 s,
2464 hc,
2465 hidden,
2466 iters,
2467 hc_eps,
2468 )?;
2469 let mut x = stream.alloc_zeros::<f32>(s * hidden).map_err(e("x"))?;
2470 unsafe {
2471 ck(
2472 "rmsnorm attn",
2473 k::memra_dsv4_rmsnorm(
2474 dpf!(y, &stream),
2475 dpf!(layer.attn_norm, &stream),
2476 dpm!(x, &stream),
2477 s as i32,
2478 hidden as i32,
2479 eps,
2480 sp(&stream),
2481 ),
2482 )?;
2483 }
2484
2485 let wq_a_v = layer.wq_a.staged(&stream)?;
2490 let mut qr = stream.alloc_zeros::<f32>(s * q_lora).map_err(e("qr"))?;
2491 Self::gemm(st, &x, wq_a_v.slab(), 0, s, q_lora, hidden, &mut qr)?;
2492 unsafe {
2493 ck(
2494 "rmsnorm q",
2495 k::memra_dsv4_rmsnorm(
2496 dpf!(qr, &stream),
2497 dpf!(layer.q_norm, &stream),
2498 dpm!(qr, &stream),
2499 s as i32,
2500 q_lora as i32,
2501 eps,
2502 sp(&stream),
2503 ),
2504 )?;
2505 }
2506 let mut qr_b = stream
2508 .alloc_zeros::<u8>(s * q_lora * 2)
2509 .map_err(e("qr_b"))?;
2510 unsafe {
2511 ck(
2512 "cvt qr",
2513 k::memra_dsv4_cvt_bf16(
2514 dpf!(qr, &stream),
2515 qr_b.device_ptr_mut(&stream).0 as *mut c_void,
2516 (s * q_lora) as i64,
2517 sp(&stream),
2518 ),
2519 )?;
2520 }
2521 let wq_b_v = layer.wq_b.staged(&stream)?;
2522 let mut q = stream.alloc_zeros::<f32>(s * heads * hd).map_err(e("q"))?;
2523 Self::gemm_pre(
2524 st,
2525 &qr_b,
2526 wq_b_v.slab().device_ptr(&stream).0 as *const c_void,
2527 s,
2528 heads * hd,
2529 q_lora,
2530 &mut q,
2531 )?;
2532 let positions: Vec<i32> = (0..s as i32).collect();
2533 let pos_dev = upload_i32(&stream, &positions)?;
2534 unsafe {
2535 ck(
2536 "headrms",
2537 k::memra_dsv4_headrms(
2538 dpm!(q, &stream),
2539 (s * heads) as i32,
2540 hd as i32,
2541 eps,
2542 sp(&stream),
2543 ),
2544 )?;
2545 ck(
2546 "rope q",
2547 k::memra_dsv4_rope(
2548 dpm!(q, &stream),
2549 s as i32,
2550 heads as i32,
2551 hd as i32,
2552 rd as i32,
2553 dpf!(fc_dev, &stream),
2554 pos_dev.device_ptr(&stream).0 as *const i32,
2555 0,
2556 sp(&stream),
2557 ),
2558 )?;
2559 }
2560
2561 let wkv_v = layer.wkv.staged(&stream)?;
2563 let mut kv = stream.alloc_zeros::<f32>(s * hd).map_err(e("kv"))?;
2564 Self::gemm(st, &x, wkv_v.slab(), 0, s, hd, hidden, &mut kv)?;
2565 unsafe {
2566 ck(
2567 "rmsnorm kv",
2568 k::memra_dsv4_rmsnorm(
2569 dpf!(kv, &stream),
2570 dpf!(layer.kv_norm, &stream),
2571 dpm!(kv, &stream),
2572 s as i32,
2573 hd as i32,
2574 eps,
2575 sp(&stream),
2576 ),
2577 )?;
2578 ck(
2579 "rope kv",
2580 k::memra_dsv4_rope(
2581 dpm!(kv, &stream),
2582 s as i32,
2583 1,
2584 hd as i32,
2585 rd as i32,
2586 dpf!(fc_dev, &stream),
2587 pos_dev.device_ptr(&stream).0 as *const i32,
2588 0,
2589 sp(&stream),
2590 ),
2591 )?;
2592 ck(
2593 "act_quant kv",
2594 k::memra_dsv4_act_quant(
2595 dpm!(kv, &stream),
2596 s as i32,
2597 hd as i64,
2598 (hd - rd) as i32,
2599 64,
2600 clamp_only,
2601 sp(&stream),
2602 ),
2603 )?;
2604 }
2605 if let Some(c) = cache.as_deref_mut() {
2609 for p in s.saturating_sub(win)..s {
2610 let slot = p % win;
2611 let src = kv.slice(p * hd..(p + 1) * hd);
2612 let mut dst = c.kvc.slice_mut(slot * hd..(slot + 1) * hd);
2613 stream.memcpy_dtod(&src, &mut dst).map_err(e("ring copy"))?;
2614 }
2615 }
2616
2617 let (widx, wslots) = window_topk_idxs(win, s);
2619 let mut idxs: Vec<i64> = widx;
2620 let mut slots = wslots;
2621 let mut n_kv = s;
2622 let mut kv_full = kv;
2623 let mut cap_cmp: Option<(Vec<f32>, usize)> = None;
2624 let mut cap_ikv: Option<(Vec<f32>, usize)> = None;
2625 let mut cap_isc: Option<(Vec<f32>, usize)> = None;
2626 let want_cap = capture
2627 .as_ref()
2628 .map(|c| c.want.contains(&layer.il))
2629 .unwrap_or(false);
2630 if layer.ratio != 0 {
2631 let offset = s;
2632 let (cidx, cslots) = if let Some(ix) = &layer.idx {
2633 let mut qi = stream
2635 .alloc_zeros::<f32>(s * ix.heads * ix.hd)
2636 .map_err(e("qi"))?;
2637 let iwq_b_v = ix.wq_b.staged(&stream)?;
2638 Self::gemm_pre(
2639 st,
2640 &qr_b,
2641 iwq_b_v.slab().device_ptr(&stream).0 as *const c_void,
2642 s,
2643 ix.heads * ix.hd,
2644 q_lora,
2645 &mut qi,
2646 )?;
2647 unsafe {
2648 ck(
2649 "rope qi",
2650 k::memra_dsv4_rope(
2651 dpm!(qi, &stream),
2652 s as i32,
2653 ix.heads as i32,
2654 ix.hd as i32,
2655 rd as i32,
2656 dpf!(fc_dev, &stream),
2657 pos_dev.device_ptr(&stream).0 as *const i32,
2658 0,
2659 sp(&stream),
2660 ),
2661 )?;
2662 let scale = (ix.hd as f32).powf(-0.5);
2663 ck(
2664 "hadamard qi",
2665 k::memra_dsv4_hadamard(
2666 dpm!(qi, &stream),
2667 (s * ix.heads) as i32,
2668 ix.hd as i32,
2669 scale,
2670 sp(&stream),
2671 ),
2672 )?;
2673 ck(
2674 "fp4 qi",
2675 k::memra_dsv4_fp4_act_quant(
2676 dpm!(qi, &stream),
2677 (s * ix.heads) as i32,
2678 ix.hd as i64,
2679 ix.hd as i32,
2680 sp(&stream),
2681 ),
2682 )?;
2683 }
2684 let (ckv_i, ikv_raw, isc_raw) =
2686 self.compressor(st, &ix.cmp, &x, s, hidden, fc_dev, rd, eps)?;
2687 if want_cap {
2688 if let Some((buf, nb)) = &ckv_i {
2689 cap_ikv = Some((dtoh_f32(&stream, buf)?, *nb));
2690 }
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 {
2781 if let Some((buf, nb)) = &ckv {
2782 cap_cmp = Some((dtoh_f32(&stream, buf)?, *nb));
2783 }
2784 }
2785 if let Some(c) = cache.as_deref_mut() {
2786 let mut n_blocks = c.n_blocks;
2787 Self::populate_cmp_cache(
2788 &stream,
2789 s,
2790 acmp.ratio,
2791 acmp.latent,
2792 acmp.d,
2793 &ckv,
2794 &akv_raw,
2795 &asc_raw,
2796 &mut c.kvc,
2797 win,
2798 &mut n_blocks,
2799 c.pend_kv.as_mut().expect("pend"),
2800 c.pend_score.as_mut().expect("pend"),
2801 acmp.overlap,
2802 )?;
2803 c.n_blocks = n_blocks;
2804 }
2805 if let Some((ckv_buf, nb)) = ckv {
2806 let mut merged_kv = stream
2807 .alloc_zeros::<f32>((s + nb) * hd)
2808 .map_err(e("kv_full"))?;
2809 {
2810 let mut head_view = merged_kv.slice_mut(0..s * hd);
2811 stream
2812 .memcpy_dtod(&kv_full.slice(0..s * hd), &mut head_view)
2813 .map_err(e("kv copy"))?;
2814 }
2815 {
2816 let mut tail = merged_kv.slice_mut(s * hd..(s + nb) * hd);
2817 stream
2818 .memcpy_dtod(&ckv_buf.slice(0..nb * hd), &mut tail)
2819 .map_err(e("ckv copy"))?;
2820 }
2821 kv_full = merged_kv;
2822 n_kv += nb;
2823 }
2824 }
2825 let _ = n_kv;
2826 let idxs_i32: Vec<i32> = idxs.iter().map(|&v| v as i32).collect();
2827 let idx_dev = upload_i32(&stream, &idxs_i32)?;
2828
2829 let mut o = stream.alloc_zeros::<f32>(s * heads * hd).map_err(e("o"))?;
2831 let scale = (hd as f64).powf(-0.5) as f32;
2832 unsafe {
2833 ck(
2834 "sink_attn",
2835 k::memra_dsv4_sink_attn(
2836 dpf!(q, &stream),
2837 dpf!(kv_full, &stream),
2838 idx_dev.device_ptr(&stream).0 as *const i32,
2839 dpf!(layer.sink, &stream),
2840 dpm!(o, &stream),
2841 s as i32,
2842 heads as i32,
2843 hd as i32,
2844 slots as i32,
2845 scale,
2846 sp(&stream),
2847 ),
2848 )?;
2849 ck(
2850 "rope o inv",
2851 k::memra_dsv4_rope(
2852 dpm!(o, &stream),
2853 s as i32,
2854 heads as i32,
2855 hd as i32,
2856 rd as i32,
2857 dpf!(fc_dev, &stream),
2858 pos_dev.device_ptr(&stream).0 as *const i32,
2859 1,
2860 sp(&stream),
2861 ),
2862 )?;
2863 }
2864
2865 let gw = heads / o_groups * hd;
2867 let mut og = stream
2868 .alloc_zeros::<f32>(s * o_groups * o_lora)
2869 .map_err(e("og"))?;
2870 let mut o_grp = stream.alloc_zeros::<f32>(s * gw).map_err(e("o_grp"))?;
2871 let mut y_grp = stream.alloc_zeros::<f32>(s * o_lora).map_err(e("y_grp"))?;
2872 let wo_a_v = layer.wo_a.staged(&stream)?; for g in 0..o_groups {
2874 unsafe {
2875 ck(
2876 "take_cols",
2877 k::memra_dsv4_take_cols(
2878 dpf!(o, &stream),
2879 dpm!(o_grp, &stream),
2880 s as i32,
2881 gw as i32,
2882 (heads * hd) as i64,
2883 (g * gw) as i64,
2884 sp(&stream),
2885 ),
2886 )?;
2887 }
2888 Self::gemm(
2889 st,
2890 &o_grp,
2891 wo_a_v.slab(),
2892 g * o_lora * gw,
2893 s,
2894 o_lora,
2895 gw,
2896 &mut y_grp,
2897 )?;
2898 unsafe {
2899 ck(
2900 "place_cols",
2901 k::memra_dsv4_place_cols(
2902 dpf!(y_grp, &stream),
2903 dpm!(og, &stream),
2904 s as i32,
2905 o_lora as i32,
2906 (o_groups * o_lora) as i64,
2907 (g * o_lora) as i64,
2908 sp(&stream),
2909 ),
2910 )?;
2911 }
2912 }
2913 let wo_b_v = layer.wo_b.staged(&stream)?;
2914 let mut attn_out = stream.alloc_zeros::<f32>(s * hidden).map_err(e("ao"))?;
2915 Self::gemm(
2916 st,
2917 &og,
2918 wo_b_v.slab(),
2919 0,
2920 s,
2921 hidden,
2922 o_groups * o_lora,
2923 &mut attn_out,
2924 )?;
2925
2926 let mut cap_attn: Option<Vec<f32>> = None;
2927 if want_cap {
2928 cap_attn = Some(dtoh_f32(&stream, &attn_out)?);
2929 }
2930
2931 let mut h2 = stream
2933 .alloc_zeros::<f32>(s * hc * hidden)
2934 .map_err(e("h2"))?;
2935 unsafe {
2936 ck(
2937 "hc_post attn",
2938 k::memra_dsv4_hc_post(
2939 dpf!(attn_out, &stream),
2940 dpf!(h, &stream),
2941 dpf!(post, &stream),
2942 dpf!(comb, &stream),
2943 dpm!(h2, &stream),
2944 s as i32,
2945 hc as i32,
2946 hidden as i32,
2947 sp(&stream),
2948 ),
2949 )?;
2950 }
2951
2952 let (y2, post2, comb2) = Self::hc_pre(
2954 st,
2955 &h2,
2956 &layer.hc_ffn_fn,
2957 &layer.hc_ffn_base,
2958 &layer.hc_ffn_scale,
2959 s,
2960 hc,
2961 hidden,
2962 iters,
2963 hc_eps,
2964 )?;
2965 let mut xf = stream.alloc_zeros::<f32>(s * hidden).map_err(e("xf"))?;
2966 unsafe {
2967 ck(
2968 "rmsnorm ffn",
2969 k::memra_dsv4_rmsnorm(
2970 dpf!(y2, &stream),
2971 dpf!(layer.ffn_norm, &stream),
2972 dpm!(xf, &stream),
2973 s as i32,
2974 hidden as i32,
2975 eps,
2976 sp(&stream),
2977 ),
2978 )?;
2979 }
2980 if let Some(c) = capture.as_deref_mut() {
2981 if c.want.contains(&layer.il) {
2982 c.moe_x.insert(layer.il, dtoh_f32(&stream, &xf)?);
2983 }
2984 }
2985 let moe_out = self.moe_forward(st, layer, &xf, s, ids)?;
2986 let mut h3 = stream
2987 .alloc_zeros::<f32>(s * hc * hidden)
2988 .map_err(e("h3"))?;
2989 unsafe {
2990 ck(
2991 "hc_post ffn",
2992 k::memra_dsv4_hc_post(
2993 dpf!(moe_out, &stream),
2994 dpf!(h2, &stream),
2995 dpf!(post2, &stream),
2996 dpf!(comb2, &stream),
2997 dpm!(h3, &stream),
2998 s as i32,
2999 hc as i32,
3000 hidden as i32,
3001 sp(&stream),
3002 ),
3003 )?;
3004 }
3005
3006 if let Some(c) = capture {
3007 if c.want.contains(&layer.il) {
3008 c.layer_out.insert(layer.il, dtoh_f32(&stream, &h3)?);
3009 c.x_dbg.insert(layer.il, dtoh_f32(&stream, &x)?);
3010 c.q_dbg.insert(layer.il, dtoh_f32(&stream, &q)?);
3011 {
3012 let mut kvh = vec![0f32; s * hd];
3013 stream
3014 .memcpy_dtoh(&kv_full.slice(0..s * hd), &mut kvh[..])
3015 .map_err(e("dtoh kv_dbg"))?;
3016 stream.synchronize().map_err(e("sync kv_dbg"))?;
3017 c.kv_dbg.insert(layer.il, kvh);
3018 }
3019 c.o_dbg.insert(layer.il, dtoh_f32(&stream, &o)?);
3020 if let Some(a) = cap_attn {
3021 c.attn_out.insert(layer.il, a);
3022 }
3023 if let Some(v) = cap_cmp {
3024 c.compressor_kv.insert(layer.il, v);
3025 }
3026 if let Some(v) = cap_ikv {
3027 c.indexer_kv.insert(layer.il, v);
3028 }
3029 if let Some(v) = cap_isc {
3030 c.index_score.insert(layer.il, v);
3031 }
3032 }
3033 }
3034 Ok(h3)
3035 }
3036
3037 fn moe_forward(
3041 &self,
3042 st: &Stage,
3043 layer: &LayerDev,
3044 x: &CudaSlice<f32>, s: usize,
3046 ids: &[u32],
3047 ) -> Res<CudaSlice<f32>> {
3048 let mc = &self.model.mc;
3049 let d = self.model.cfg();
3050 let moe = mc.moe.as_ref().expect("moe");
3051 let hidden = mc.n_embd as usize;
3052 let ne = moe.expert_count as usize;
3053 let topk = moe.expert_used_count as usize;
3054 let inter = moe.expert_ff_length as usize;
3055 let limit = d.swiglu_limit;
3056 let stream = st.gpu.stream();
3057
3058 let mut raw = stream.alloc_zeros::<f32>(s * ne).map_err(e("gate raw"))?;
3059 Self::dots(st, x, &layer.gate_w, s, hidden, ne, &mut raw)?;
3060 let raw_h = dtoh_f32(&stream, &raw)?;
3061 let (indices, weights) =
3062 Self::route_host(layer, &raw_h, ids, s, ne, topk, d.routed_scaling_factor);
3063
3064 let mut xb = stream
3066 .alloc_zeros::<u8>(s * hidden * 2)
3067 .map_err(e("xb moe"))?;
3068 unsafe {
3069 ck(
3070 "cvt moe x",
3071 k::memra_dsv4_cvt_bf16(
3072 dpf!(x, &stream),
3073 xb.device_ptr_mut(&stream).0 as *mut c_void,
3074 (s * hidden) as i64,
3075 sp(&stream),
3076 ),
3077 )?;
3078 }
3079 let mut y = stream.alloc_zeros::<f32>(s * hidden).map_err(e("moe y"))?;
3080
3081 let wbytes = inter * hidden / 2;
3082 let sbytes = match layer.expert_kind {
3083 ExpertKind::Nvfp4 => inter * hidden / 16,
3084 ExpertKind::Mxfp4 => inter * hidden / 32,
3085 };
3086 let mut uniq: Vec<usize> = indices.clone();
3087 uniq.sort_unstable();
3088 uniq.dedup();
3089 if self.expert_arm == ExpertArm::Native {
3090 let kind = match layer.expert_kind {
3095 ExpertKind::Nvfp4 => 0i32,
3096 ExpertKind::Mxfp4 => 1i32,
3097 };
3098 let kq_x = hidden / 128;
3099 let kq_h = inter / 128;
3100 let mut xq = stream.alloc_zeros::<u8>(s * hidden).map_err(e("xq"))?;
3101 let mut xs = stream.alloc_zeros::<f32>(s * kq_x).map_err(e("xs"))?;
3102 unsafe {
3103 ck(
3104 "act_quant_fp8 x",
3105 k::memra_dsv4_act_quant_fp8(
3106 dpf!(x, &stream),
3107 xq.device_ptr_mut(&stream).0 as *mut c_void,
3108 dpm!(xs, &stream),
3109 s as i32,
3110 hidden as i32,
3111 sp(&stream),
3112 ),
3113 )?;
3114 }
3115 let mut xgq = stream.alloc_zeros::<u8>(s * hidden).map_err(e("xgq"))?;
3116 let mut xgs = stream.alloc_zeros::<f32>(s * kq_x).map_err(e("xgs"))?;
3117 let mut g1 = stream.alloc_zeros::<f32>(s * inter).map_err(e("g1"))?;
3118 let mut g3 = stream.alloc_zeros::<f32>(s * inter).map_err(e("g3"))?;
3119 let mut hbuf = stream.alloc_zeros::<f32>(s * inter).map_err(e("hbuf"))?;
3120 let mut hq = stream.alloc_zeros::<u8>(s * inter).map_err(e("hq"))?;
3121 let mut hs = stream.alloc_zeros::<f32>(s * kq_h).map_err(e("hs"))?;
3122 let mut contrib = stream
3123 .alloc_zeros::<f32>(s * hidden)
3124 .map_err(e("contrib"))?;
3125 for &ex in &uniq {
3126 let toks: Vec<(usize, usize)> = (0..s * topk)
3127 .filter(|i| indices[*i] == ex)
3128 .map(|i| (i / topk, i % topk))
3129 .collect();
3130 let g = toks.len();
3131 let tok_rows: Vec<i32> = toks.iter().map(|&(t, _)| t as i32).collect();
3132 let wrow: Vec<f32> = toks.iter().map(|&(t, kk)| weights[t * topk + kk]).collect();
3133 let rows_dev = upload_i32(&stream, &tok_rows)?;
3134 let wrow_dev = upload_f32(&stream, &wrow)?;
3135 unsafe {
3136 ck(
3137 "gather xq",
3138 k::memra_dsv4_gather_rows_u8(
3139 dp!(xq, &stream),
3140 rows_dev.device_ptr(&stream).0 as *const i32,
3141 xgq.device_ptr_mut(&stream).0 as *mut c_void,
3142 g as i32,
3143 hidden as i64,
3144 sp(&stream),
3145 ),
3146 )?;
3147 ck(
3148 "gather xs",
3149 k::memra_dsv4_gather_rows_u8(
3150 xs.device_ptr(&stream).0 as *const c_void,
3151 rows_dev.device_ptr(&stream).0 as *const i32,
3152 xgs.device_ptr_mut(&stream).0 as *mut c_void,
3153 g as i32,
3154 (kq_x * 4) as i64,
3155 sp(&stream),
3156 ),
3157 )?;
3158 for (pi, dst) in [(0usize, &mut g1), (2usize, &mut g3)] {
3160 let woff = (ex * 3 + pi) * wbytes;
3161 let soff = (ex * 3 + pi) * sbytes;
3162 ck(
3163 "fp4_gemm w1/w3",
3164 k::memra_dsv4_fp4_gemm(
3165 dp!(xgq, &stream),
3166 dpf!(xgs, &stream),
3167 (layer.experts_w.device_ptr(&stream).0 as usize + woff)
3168 as *const c_void,
3169 (layer.experts_sc.device_ptr(&stream).0 as usize + soff)
3170 as *const c_void,
3171 layer.experts_s2[ex * 3 + pi],
3172 kind,
3173 dpm!(*dst, &stream),
3174 g as i32,
3175 inter as i32,
3176 hidden as i32,
3177 sp(&stream),
3178 ),
3179 )?;
3180 }
3181 ck(
3182 "swiglu",
3183 k::memra_dsv4_swiglu(
3184 dpf!(g1, &stream),
3185 dpf!(g3, &stream),
3186 dpm!(hbuf, &stream),
3187 g as i32,
3188 inter as i32,
3189 limit,
3190 wrow_dev.device_ptr(&stream).0 as *const f32,
3191 sp(&stream),
3192 ),
3193 )?;
3194 ck(
3195 "act_quant_fp8 h",
3196 k::memra_dsv4_act_quant_fp8(
3197 dpf!(hbuf, &stream),
3198 hq.device_ptr_mut(&stream).0 as *mut c_void,
3199 dpm!(hs, &stream),
3200 g as i32,
3201 inter as i32,
3202 sp(&stream),
3203 ),
3204 )?;
3205 let woff2 = (ex * 3 + 1) * wbytes;
3206 let soff2 = (ex * 3 + 1) * sbytes;
3207 ck(
3208 "fp4_gemm w2",
3209 k::memra_dsv4_fp4_gemm(
3210 dp!(hq, &stream),
3211 dpf!(hs, &stream),
3212 (layer.experts_w.device_ptr(&stream).0 as usize + woff2)
3213 as *const c_void,
3214 (layer.experts_sc.device_ptr(&stream).0 as usize + soff2)
3215 as *const c_void,
3216 layer.experts_s2[ex * 3 + 1],
3217 kind,
3218 dpm!(contrib, &stream),
3219 g as i32,
3220 hidden as i32,
3221 inter as i32,
3222 sp(&stream),
3223 ),
3224 )?;
3225 ck(
3226 "scatter",
3227 k::memra_dsv4_scatter_add(
3228 dpm!(y, &stream),
3229 dpf!(contrib, &stream),
3230 rows_dev.device_ptr(&stream).0 as *const i32,
3231 g as i32,
3232 hidden as i32,
3233 sp(&stream),
3234 ),
3235 )?;
3236 }
3237 }
3238 return self.moe_shared_and_finish(st, layer, &xb, s, y);
3239 }
3240 let mut xg = stream.alloc_zeros::<u8>(s * hidden * 2).map_err(e("xg"))?;
3242 let mut g1 = stream.alloc_zeros::<f32>(s * inter).map_err(e("g1"))?;
3243 let mut g3 = stream.alloc_zeros::<f32>(s * inter).map_err(e("g3"))?;
3244 let mut hbuf = stream.alloc_zeros::<f32>(s * inter).map_err(e("hbuf"))?;
3245 let mut hb = stream.alloc_zeros::<u8>(s * inter * 2).map_err(e("hb"))?;
3246 let mut contrib = stream
3247 .alloc_zeros::<f32>(s * hidden)
3248 .map_err(e("contrib"))?;
3249 for &ex in &uniq {
3250 let toks: Vec<(usize, usize)> = (0..s * topk)
3251 .filter(|i| indices[*i] == ex)
3252 .map(|i| (i / topk, i % topk))
3253 .collect();
3254 let g = toks.len();
3255 let tok_rows: Vec<i32> = toks.iter().map(|&(t, _)| t as i32).collect();
3256 let wrow: Vec<f32> = toks.iter().map(|&(t, kk)| weights[t * topk + kk]).collect();
3257 let rows_dev = upload_i32(&stream, &tok_rows)?;
3258 let wrow_dev = upload_f32(&stream, &wrow)?;
3259 unsafe {
3260 ck(
3261 "gather",
3262 k::memra_dsv4_gather_bf16(
3263 dp!(xb, &stream),
3264 rows_dev.device_ptr(&stream).0 as *const i32,
3265 xg.device_ptr_mut(&stream).0 as *mut c_void,
3266 g as i32,
3267 hidden as i32,
3268 sp(&stream),
3269 ),
3270 )?;
3271 for (pi, (rows, cols)) in [(inter, hidden), (hidden, inter), (inter, hidden)]
3273 .iter()
3274 .enumerate()
3275 {
3276 let woff = (ex * 3 + pi) * wbytes;
3277 let soff = (ex * 3 + pi) * sbytes;
3278 let wp =
3279 (layer.experts_w.device_ptr(&stream).0 as usize + woff) as *const c_void;
3280 let scp =
3281 (layer.experts_sc.device_ptr(&stream).0 as usize + soff) as *const c_void;
3282 let dst = st.deq[pi].device_ptr(&stream).0 as *mut c_void;
3283 match layer.expert_kind {
3284 ExpertKind::Nvfp4 => ck(
3285 "nvfp4 deq",
3286 k::memra_dsv4_nvfp4_deq_bf16(
3287 wp,
3288 scp,
3289 layer.experts_s2[ex * 3 + pi],
3290 *rows as i32,
3291 *cols as i32,
3292 dst,
3293 sp(&stream),
3294 ),
3295 )?,
3296 ExpertKind::Mxfp4 => ck(
3297 "mxfp4 deq",
3298 k::memra_dsv4_mxfp4_deq_bf16(
3299 wp,
3300 scp,
3301 *rows as i32,
3302 *cols as i32,
3303 dst,
3304 sp(&stream),
3305 ),
3306 )?,
3307 }
3308 }
3309 ck(
3310 "gemm w1",
3311 k::memra_dsv4_gemm_bf16(
3312 st.deq[0].device_ptr(&stream).0 as *const c_void,
3313 dp!(xg, &stream),
3314 dpm!(g1, &stream),
3315 g as i32,
3316 inter as i32,
3317 hidden as i32,
3318 st.dev as i32,
3319 st.ws.device_ptr(&stream).0 as *mut c_void,
3320 st.ws.len(),
3321 sp(&stream),
3322 ),
3323 )?;
3324 ck(
3325 "gemm w3",
3326 k::memra_dsv4_gemm_bf16(
3327 st.deq[2].device_ptr(&stream).0 as *const c_void,
3328 dp!(xg, &stream),
3329 dpm!(g3, &stream),
3330 g as i32,
3331 inter as i32,
3332 hidden as i32,
3333 st.dev as i32,
3334 st.ws.device_ptr(&stream).0 as *mut c_void,
3335 st.ws.len(),
3336 sp(&stream),
3337 ),
3338 )?;
3339 ck(
3340 "swiglu",
3341 k::memra_dsv4_swiglu(
3342 dpf!(g1, &stream),
3343 dpf!(g3, &stream),
3344 dpm!(hbuf, &stream),
3345 g as i32,
3346 inter as i32,
3347 limit,
3348 wrow_dev.device_ptr(&stream).0 as *const f32,
3349 sp(&stream),
3350 ),
3351 )?;
3352 ck(
3353 "cvt h",
3354 k::memra_dsv4_cvt_bf16(
3355 dpf!(hbuf, &stream),
3356 hb.device_ptr_mut(&stream).0 as *mut c_void,
3357 (g * inter) as i64,
3358 sp(&stream),
3359 ),
3360 )?;
3361 ck(
3362 "gemm w2",
3363 k::memra_dsv4_gemm_bf16(
3364 st.deq[1].device_ptr(&stream).0 as *const c_void,
3365 dp!(hb, &stream),
3366 dpm!(contrib, &stream),
3367 g as i32,
3368 hidden as i32,
3369 inter as i32,
3370 st.dev as i32,
3371 st.ws.device_ptr(&stream).0 as *mut c_void,
3372 st.ws.len(),
3373 sp(&stream),
3374 ),
3375 )?;
3376 ck(
3377 "scatter",
3378 k::memra_dsv4_scatter_add(
3379 dpm!(y, &stream),
3380 dpf!(contrib, &stream),
3381 rows_dev.device_ptr(&stream).0 as *const i32,
3382 g as i32,
3383 hidden as i32,
3384 sp(&stream),
3385 ),
3386 )?;
3387 }
3388 }
3389 self.moe_shared_and_finish(st, layer, &xb, s, y)
3390 }
3391
3392 fn moe_shared_and_finish(
3396 &self,
3397 st: &Stage,
3398 layer: &LayerDev,
3399 xb: &CudaSlice<u8>,
3400 s: usize,
3401 mut y: CudaSlice<f32>,
3402 ) -> Res<CudaSlice<f32>> {
3403 let d = self.model.cfg();
3404 let hidden = self.model.mc.n_embd as usize;
3405 let limit = d.swiglu_limit;
3406 let stream = st.gpu.stream();
3407 let sh_inter = {
3408 let (shape, _) = self
3410 .model
3411 .st
3412 .raw("layers.0.ffn.shared_experts.w1.weight")
3413 .map(|(i, _)| (i.shape.clone(), ()))
3414 .expect("shared w1");
3415 shape[0] as usize
3416 };
3417 let mut sg1 = stream.alloc_zeros::<f32>(s * sh_inter).map_err(e("sg1"))?;
3418 let mut sg3 = stream.alloc_zeros::<f32>(s * sh_inter).map_err(e("sg3"))?;
3419 let mut shbuf = stream.alloc_zeros::<f32>(s * sh_inter).map_err(e("shb"))?;
3420 let mut shb16 = stream
3421 .alloc_zeros::<u8>(s * sh_inter * 2)
3422 .map_err(e("shb16"))?;
3423 let mut sh_out = stream.alloc_zeros::<f32>(s * hidden).map_err(e("sh_out"))?;
3424 let sw = [
3426 layer.shared_w[0].staged(&stream)?,
3427 layer.shared_w[1].staged(&stream)?,
3428 layer.shared_w[2].staged(&stream)?,
3429 ];
3430 Self::gemm_pre(
3431 st,
3432 xb,
3433 sw[0].slab().device_ptr(&stream).0 as *const c_void,
3434 s,
3435 sh_inter,
3436 hidden,
3437 &mut sg1,
3438 )?;
3439 Self::gemm_pre(
3440 st,
3441 xb,
3442 sw[2].slab().device_ptr(&stream).0 as *const c_void,
3443 s,
3444 sh_inter,
3445 hidden,
3446 &mut sg3,
3447 )?;
3448 unsafe {
3449 ck(
3450 "swiglu sh",
3451 k::memra_dsv4_swiglu(
3452 dpf!(sg1, &stream),
3453 dpf!(sg3, &stream),
3454 dpm!(shbuf, &stream),
3455 s as i32,
3456 sh_inter as i32,
3457 limit,
3458 std::ptr::null(),
3459 sp(&stream),
3460 ),
3461 )?;
3462 ck(
3463 "cvt sh",
3464 k::memra_dsv4_cvt_bf16(
3465 dpf!(shbuf, &stream),
3466 shb16.device_ptr_mut(&stream).0 as *mut c_void,
3467 (s * sh_inter) as i64,
3468 sp(&stream),
3469 ),
3470 )?;
3471 }
3472 Self::gemm_pre(
3473 st,
3474 &shb16,
3475 sw[1].slab().device_ptr(&stream).0 as *const c_void,
3476 s,
3477 hidden,
3478 sh_inter,
3479 &mut sh_out,
3480 )?;
3481 unsafe {
3482 ck(
3483 "add shared",
3484 k::memra_dsv4_add_inplace(
3485 dpm!(y, &stream),
3486 dpf!(sh_out, &stream),
3487 (s * hidden) as i64,
3488 sp(&stream),
3489 ),
3490 )?;
3491 }
3492 Ok(y)
3493 }
3494
3495 pub fn forward(
3498 &self,
3499 ids: &[u32],
3500 capture: Option<&mut GpuCapture>,
3501 early_exit_after: Option<u32>,
3502 ) -> Res<Option<ForwardOut>> {
3503 self.forward_impl(ids, capture, early_exit_after, None)
3504 }
3505
3506 pub fn prefill_with_cache(&self, ids: &[u32], state: &mut DecodeState) -> Res<ForwardOut> {
3509 assert_eq!(state.pos, 0, "prefill_with_cache needs a fresh DecodeState");
3510 assert!(!ids.is_empty(), "empty prompt");
3511 let out = self
3512 .forward_impl(ids, None, None, Some(state))?
3513 .expect("prefill logits");
3514 state.pos = ids.len();
3515 Ok(out)
3516 }
3517
3518 fn forward_impl(
3519 &self,
3520 ids: &[u32],
3521 mut capture: Option<&mut GpuCapture>,
3522 early_exit_after: Option<u32>,
3523 mut state: Option<&mut DecodeState>,
3524 ) -> Res<Option<ForwardOut>> {
3525 let mc = &self.model.mc;
3526 let d = self.model.cfg();
3527 let s = ids.len();
3528 assert!(s <= self.max_seq, "seq {s} > max_seq {}", self.max_seq);
3529 let hidden = mc.n_embd as usize;
3530 let hc = d.hc_mult as usize;
3531 let n_trunk = mc.n_layer - mc.nextn_predict_layers;
3532
3533 let st0 = &self.stages[0];
3535 st0.gpu.ctx.bind_to_thread().map_err(e("bind ctx0"))?;
3536 let stream0 = st0.gpu.stream();
3537 let ids_i32: Vec<i32> = ids.iter().map(|&x| x as i32).collect();
3538 let ids_dev = upload_i32(&stream0, &ids_i32)?;
3539 let mut emb = stream0.alloc_zeros::<f32>(s * hidden).map_err(e("emb"))?;
3540 unsafe {
3541 ck(
3542 "embed_rows",
3543 k::memra_dsv4_embed_rows(
3544 st0.embed
3545 .as_ref()
3546 .expect("embed on stage 0")
3547 .device_ptr(&stream0)
3548 .0 as *const c_void,
3549 ids_dev.device_ptr(&stream0).0 as *const i32,
3550 dpm!(emb, &stream0),
3551 s as i32,
3552 hidden as i32,
3553 sp(&stream0),
3554 ),
3555 )?;
3556 }
3557 if let Some(c) = capture.as_deref_mut() {
3558 if c.embed_out.is_none() {
3559 c.embed_out = Some(dtoh_f32(&stream0, &emb)?);
3560 }
3561 }
3562 let mut h = stream0
3563 .alloc_zeros::<f32>(s * hc * hidden)
3564 .map_err(e("h0"))?;
3565 unsafe {
3566 ck(
3567 "repeat_hc",
3568 k::memra_dsv4_repeat_hc(
3569 dpf!(emb, &stream0),
3570 dpm!(h, &stream0),
3571 s as i32,
3572 hc as i32,
3573 hidden as i32,
3574 sp(&stream0),
3575 ),
3576 )?;
3577 }
3578
3579 let mut cur_stage = 0usize;
3581 for il in 0..n_trunk {
3582 let stage = self.layer_stage[il as usize];
3583 if stage != cur_stage {
3584 let src_stream = self.stages[cur_stage].gpu.stream();
3585 let host = dtoh_f32(&src_stream, &h)?;
3586 let dst_stream = self.stages[stage].gpu.stream();
3587 self.stages[stage]
3588 .gpu
3589 .ctx
3590 .bind_to_thread()
3591 .map_err(e("bind"))?;
3592 h = upload_f32(&dst_stream, &host)?;
3593 cur_stage = stage;
3594 }
3595 let st = &self.stages[stage];
3596 let lidx = st
3597 .layers
3598 .iter()
3599 .position(|l| l.il == il)
3600 .unwrap_or_else(|| panic!("layer {il} not on stage {stage}"));
3601 let layer_cache = state.as_deref_mut().map(|ds| &mut ds.caches[il as usize]);
3602 h = self.block_forward(
3603 st,
3604 &st.layers[lidx],
3605 &h,
3606 s,
3607 ids,
3608 capture.as_deref_mut(),
3609 layer_cache,
3610 )?;
3611 if early_exit_after == Some(il) {
3612 self.stages[cur_stage]
3613 .gpu
3614 .stream()
3615 .synchronize()
3616 .map_err(e("sync"))?;
3617 return Ok(None);
3618 }
3619 }
3620
3621 let last = self.stages.len() - 1;
3623 if cur_stage != last {
3624 let src_stream = self.stages[cur_stage].gpu.stream();
3625 let host = dtoh_f32(&src_stream, &h)?;
3626 let dst_stream = self.stages[last].gpu.stream();
3627 h = upload_f32(&dst_stream, &host)?;
3628 }
3629 let hc_head_fn = self.stages[last].hc_head_fn.as_ref().expect("hc_head_fn");
3630 let trunk_norm = self.stages[last].trunk_norm.as_ref().expect("trunk norm");
3631 let logits = self.head_logits_from(
3632 &h,
3633 s,
3634 hc_head_fn,
3635 &self.hc_head_base,
3636 &self.hc_head_scale,
3637 trunk_norm,
3638 )?;
3639 Ok(Some(ForwardOut { logits, h_last: h }))
3640 }
3641
3642 fn head_logits_from(
3646 &self,
3647 h: &CudaSlice<f32>,
3648 s: usize,
3649 fn_w: &CudaSlice<f32>,
3650 base: &[f32],
3651 scale: &[f32],
3652 norm: &CudaSlice<f32>,
3653 ) -> Res<Vec<f32>> {
3654 self.head_logits_row(h, s, s - 1, fn_w, base, scale, norm)
3655 }
3656
3657 #[allow(clippy::too_many_arguments)]
3661 fn head_logits_row(
3662 &self,
3663 h: &CudaSlice<f32>,
3664 s: usize,
3665 row: usize,
3666 fn_w: &CudaSlice<f32>,
3667 base: &[f32],
3668 scale: &[f32],
3669 norm: &CudaSlice<f32>,
3670 ) -> Res<Vec<f32>> {
3671 let d = self.model.cfg();
3672 let mc = &self.model.mc;
3673 let hc = d.hc_mult as usize;
3674 let hidden = mc.n_embd as usize;
3675 let eps = mc.rms_eps;
3676 let last = self.stages.len() - 1;
3677 let st = &self.stages[last];
3678 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx head"))?;
3679 let stream = st.gpu.stream();
3680 let w = hc * hidden;
3681 let mut mixes = stream.alloc_zeros::<f32>(s * hc).map_err(e("hm"))?;
3682 Self::dots(st, h, fn_w, s, w, hc, &mut mixes)?;
3683 unsafe {
3684 ck(
3685 "rowsq head",
3686 k::memra_dsv4_rowsq_scale(
3687 dpf!(h, &stream),
3688 dpm!(mixes, &stream),
3689 s as i32,
3690 w as i32,
3691 hc as i32,
3692 eps,
3693 sp(&stream),
3694 ),
3695 )?;
3696 }
3697 let mut mixes_h = dtoh_f32(&stream, &mixes)?;
3700 for t in 0..s {
3701 for c in 0..hc {
3702 let m = mixes_h[t * hc + c];
3703 mixes_h[t * hc + c] = sigmoid_f32(m * scale[0] + base[c]) + d.hc_eps;
3704 }
3705 }
3706 let pre_d = upload_f32(&stream, &mixes_h)?;
3707 let mut collapsed = stream.alloc_zeros::<f32>(s * hidden).map_err(e("col"))?;
3708 unsafe {
3709 ck(
3710 "hc_collapse head",
3711 k::memra_dsv4_hc_collapse(
3712 dpf!(h, &stream),
3713 dpf!(pre_d, &stream),
3714 dpm!(collapsed, &stream),
3715 s as i32,
3716 hc as i32,
3717 hidden as i32,
3718 sp(&stream),
3719 ),
3720 )?;
3721 ck(
3722 "rmsnorm head",
3723 k::memra_dsv4_rmsnorm(
3724 dpf!(collapsed, &stream),
3725 dpf!(norm, &stream),
3726 dpm!(collapsed, &stream),
3727 s as i32,
3728 hidden as i32,
3729 eps,
3730 sp(&stream),
3731 ),
3732 )?;
3733 }
3734 assert!(row < s, "logits row {row} out of range (s = {s})");
3736 let vocab = {
3737 let (info, _) = self.model.st.raw("head.weight").expect("head");
3738 info.shape[0] as usize
3739 };
3740 let last_row = collapsed.slice(row * hidden..(row + 1) * hidden);
3741 let mut logits = stream.alloc_zeros::<f32>(vocab).map_err(e("logits"))?;
3742 unsafe {
3743 ck(
3744 "head dots",
3745 k::memra_dsv4_dots_f32(
3746 last_row.device_ptr(&stream).0 as *const f32,
3747 st.head.as_ref().expect("head").device_ptr(&stream).0 as *const c_void,
3748 1,
3749 dpm!(logits, &stream),
3750 1,
3751 hidden as i32,
3752 vocab as i32,
3753 sp(&stream),
3754 ),
3755 )?;
3756 }
3757 dtoh_f32(&stream, &logits)
3758 }
3759
3760 pub fn mtp_logits_last(&self, h_trunk: &CudaSlice<f32>, ids: &[u32]) -> Res<Vec<f32>> {
3764 self.mtp_logits_last_cap(h_trunk, ids, None)
3765 }
3766
3767 pub fn mtp_logits_last_cap(
3770 &self,
3771 h_trunk: &CudaSlice<f32>,
3772 ids: &[u32],
3773 capture: Option<&mut GpuCapture>,
3774 ) -> Res<Vec<f32>> {
3775 let mtp = self.mtp.as_ref().expect("MTP not loaded");
3776 let d = self.model.cfg();
3777 let mc = &self.model.mc;
3778 let hc = d.hc_mult as usize;
3779 let hidden = mc.n_embd as usize;
3780 let eps = mc.rms_eps;
3781 let s = ids.len();
3782 let last = self.stages.len() - 1;
3783 let st = &self.stages[last];
3784 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx mtp"))?;
3785 let stream = st.gpu.stream();
3786
3787 let e_host = self.model.embed_rows(ids);
3790 let mut e_dev = upload_f32(&stream, &e_host)?;
3791 unsafe {
3792 ck(
3793 "rmsnorm enorm",
3794 k::memra_dsv4_rmsnorm(
3795 dpf!(e_dev, &stream),
3796 dpf!(mtp.enorm, &stream),
3797 dpm!(e_dev, &stream),
3798 s as i32,
3799 hidden as i32,
3800 eps,
3801 sp(&stream),
3802 ),
3803 )?;
3804 }
3805 let mut xh = stream
3807 .alloc_zeros::<f32>(s * hc * hidden)
3808 .map_err(e("mtp xh"))?;
3809 unsafe {
3810 ck(
3811 "rmsnorm hnorm",
3812 k::memra_dsv4_rmsnorm(
3813 dpf!(h_trunk, &stream),
3814 dpf!(mtp.hnorm, &stream),
3815 dpm!(xh, &stream),
3816 (s * hc) as i32,
3817 hidden as i32,
3818 eps,
3819 sp(&stream),
3820 ),
3821 )?;
3822 }
3823 let mut ep = stream.alloc_zeros::<f32>(s * hidden).map_err(e("mtp ep"))?;
3825 Self::gemm(st, &e_dev, &mtp.e_proj, 0, s, hidden, hidden, &mut ep)?;
3826 let mut hp = stream
3827 .alloc_zeros::<f32>(s * hc * hidden)
3828 .map_err(e("mtp hp"))?;
3829 Self::gemm(st, &xh, &mtp.h_proj, 0, s * hc, hidden, hidden, &mut hp)?;
3830 let mut xm = stream
3832 .alloc_zeros::<f32>(s * hc * hidden)
3833 .map_err(e("mtp xm"))?;
3834 unsafe {
3835 ck(
3836 "repeat ep",
3837 k::memra_dsv4_repeat_hc(
3838 dpf!(ep, &stream),
3839 dpm!(xm, &stream),
3840 s as i32,
3841 hc as i32,
3842 hidden as i32,
3843 sp(&stream),
3844 ),
3845 )?;
3846 ck(
3847 "add hp",
3848 k::memra_dsv4_add_inplace(
3849 dpm!(xm, &stream),
3850 dpf!(hp, &stream),
3851 (s * hc * hidden) as i64,
3852 sp(&stream),
3853 ),
3854 )?;
3855 }
3856 let xm = self.block_forward(st, &mtp.layer, &xm, s, ids, capture, None)?;
3857 self.head_logits_from(
3858 &xm,
3859 s,
3860 &mtp.hc_head_fn,
3861 &mtp.hc_head_base,
3862 &mtp.hc_head_scale,
3863 &mtp.norm,
3864 )
3865 }
3866
3867 pub fn trunk_logits_row(&self, h: &CudaSlice<f32>, s: usize, row: usize) -> Res<Vec<f32>> {
3869 let last = self.stages.len() - 1;
3870 let hc_head_fn = self.stages[last].hc_head_fn.as_ref().expect("hc_head_fn");
3871 let trunk_norm = self.stages[last].trunk_norm.as_ref().expect("trunk norm");
3872 self.head_logits_row(
3873 h,
3874 s,
3875 row,
3876 hc_head_fn,
3877 &self.hc_head_base,
3878 &self.hc_head_scale,
3879 trunk_norm,
3880 )
3881 }
3882
3883 pub fn alloc_decode_state(&self) -> Res<DecodeState> {
3889 let d = self.model.cfg();
3890 let mc = &self.model.mc;
3891 let win = d.sliding_window as usize;
3892 let hd = d.head_dim as usize;
3893 let n_trunk = mc.n_layer - mc.nextn_predict_layers;
3894 let mut caches = Vec::with_capacity(n_trunk as usize);
3895 let mut cache_bytes = vec![0u64; self.stages.len()];
3896 let trans_rows = self.verify_tmax();
3901 for il in 0..n_trunk {
3902 let stage_i = self.layer_stage[il as usize];
3903 let st = &self.stages[stage_i];
3904 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx cache"))?;
3905 let stream = st.gpu.stream();
3906 let lidx = st
3907 .layers
3908 .iter()
3909 .position(|l| l.il == il)
3910 .unwrap_or_else(|| panic!("layer {il} not on stage {stage_i}"));
3911 let layer = &st.layers[lidx];
3912 let ratio = layer.ratio;
3913 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 fn cmp_decode(
4115 &self,
4116 st: &Stage,
4117 cmp: &CmpDev,
4118 x: &CudaSlice<f32>, pos: usize,
4120 hidden: usize,
4121 fc_dev: &CudaSlice<f32>,
4122 rd: usize,
4123 eps: f32,
4124 pend_kv: &mut CudaSlice<f32>,
4125 pend_score: &mut CudaSlice<f32>,
4126 store: &mut CudaSlice<f32>,
4127 row0: usize,
4128 blocks: &mut usize,
4129 ) -> Res<()> {
4130 let stream = st.gpu.stream();
4131 let (ratio, d, latent) = (cmp.ratio, cmp.d, cmp.latent);
4132 let mut kv_row = stream.alloc_zeros::<f32>(latent).map_err(e("dkv"))?;
4133 let mut sc_row = stream.alloc_zeros::<f32>(latent).map_err(e("dsc"))?;
4134 Self::dots(st, x, &cmp.wkv, 1, hidden, latent, &mut kv_row)?;
4135 Self::dots(st, x, &cmp.wgate, 1, hidden, latent, &mut sc_row)?;
4136 let slot = if cmp.overlap {
4137 ratio + pos % ratio
4138 } else {
4139 pos % ratio
4140 };
4141 {
4142 let src = kv_row.slice(0..latent);
4143 let mut dst = pend_kv.slice_mut(slot * latent..(slot + 1) * latent);
4144 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend kv"))?;
4145 let src = sc_row.slice(0..latent);
4146 let mut dst = pend_score.slice_mut(slot * latent..(slot + 1) * latent);
4147 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend sc"))?;
4148 }
4149 if (pos + 1) % ratio != 0 {
4150 return Ok(());
4151 }
4152 let j = pos / ratio;
4153 let nb_launch = if cmp.overlap { 2usize } else { 1 };
4154 let row_off = if cmp.overlap { d } else { 0 };
4155 let mut out = stream
4156 .alloc_zeros::<f32>(nb_launch * d)
4157 .map_err(e("emit"))?;
4158 unsafe {
4159 ck(
4160 "compressor_pool dec",
4161 k::memra_dsv4_compressor_pool(
4162 dpf!(pend_kv, &stream),
4163 dpf!(pend_score, &stream),
4164 dpf!(cmp.ape, &stream),
4165 dpm!(out, &stream),
4166 nb_launch as i32,
4167 ratio as i32,
4168 d as i32,
4169 latent as i32,
4170 cmp.overlap as i32,
4171 sp(&stream),
4172 ),
4173 )?;
4174 let row_c = (out.device_ptr(&stream).0 as usize + row_off * 4) as *const f32;
4176 let row_m = (out.device_ptr_mut(&stream).0 as usize + row_off * 4) as *mut f32;
4177 ck(
4178 "rmsnorm dec cmp",
4179 k::memra_dsv4_rmsnorm(
4180 row_c,
4181 dpf!(cmp.norm, &stream),
4182 row_m,
4183 1,
4184 d as i32,
4185 eps,
4186 sp(&stream),
4187 ),
4188 )?;
4189 let pos_dev = upload_i32(&stream, &[(j * ratio) as i32])?;
4190 ck(
4191 "rope dec cmp",
4192 k::memra_dsv4_rope(
4193 row_m,
4194 1,
4195 1,
4196 d as i32,
4197 rd as i32,
4198 dpf!(fc_dev, &stream),
4199 pos_dev.device_ptr(&stream).0 as *const i32,
4200 0,
4201 sp(&stream),
4202 ),
4203 )?;
4204 if cmp.rotate {
4205 let scale = (d as f32).powf(-0.5);
4206 ck(
4207 "hadamard dec cmp",
4208 k::memra_dsv4_hadamard(row_m, 1, d as i32, scale, sp(&stream)),
4209 )?;
4210 ck(
4211 "fp4 dec cmp",
4212 k::memra_dsv4_fp4_act_quant(row_m, 1, d as i64, d as i32, sp(&stream)),
4213 )?;
4214 } else {
4215 ck(
4216 "act_quant dec cmp",
4217 k::memra_dsv4_act_quant(
4218 row_m,
4219 1,
4220 d as i64,
4221 (d - rd) as i32,
4222 64,
4223 (self.variant == ActQuantVariant::ClampOnly) as i32,
4224 sp(&stream),
4225 ),
4226 )?;
4227 }
4228 }
4229 {
4230 let src = out.slice(row_off..row_off + d);
4231 let mut dst = store.slice_mut((row0 + j) * d..(row0 + j + 1) * d);
4232 stream
4233 .memcpy_dtod(&src, &mut dst)
4234 .map_err(e("emit store"))?;
4235 }
4236 if cmp.overlap {
4237 let mut tmp = stream
4239 .alloc_zeros::<f32>(ratio * latent)
4240 .map_err(e("shift tmp"))?;
4241 {
4242 let src = pend_kv.slice(ratio * latent..2 * ratio * latent);
4243 stream.memcpy_dtod(&src, &mut tmp).map_err(e("shift1"))?;
4244 }
4245 {
4246 let mut dst = pend_kv.slice_mut(0..ratio * latent);
4247 stream
4248 .memcpy_dtod(&tmp.slice(0..ratio * latent), &mut dst)
4249 .map_err(e("shift2"))?;
4250 }
4251 {
4252 let src = pend_score.slice(ratio * latent..2 * ratio * latent);
4253 stream.memcpy_dtod(&src, &mut tmp).map_err(e("shift3"))?;
4254 }
4255 {
4256 let mut dst = pend_score.slice_mut(0..ratio * latent);
4257 stream
4258 .memcpy_dtod(&tmp.slice(0..ratio * latent), &mut dst)
4259 .map_err(e("shift4"))?;
4260 }
4261 }
4262 *blocks = j + 1;
4263 Ok(())
4264 }
4265
4266 #[allow(clippy::too_many_arguments)]
4272 fn block_decode(
4273 &self,
4274 st: &Stage,
4275 layer: &LayerDev,
4276 cache: &mut LayerCache,
4277 h: &CudaSlice<f32>,
4278 pos: usize,
4279 tok: u32,
4280 mut dump: Option<&mut Vec<(String, Vec<f32>)>>,
4281 ) -> Res<CudaSlice<f32>> {
4282 let d = self.model.cfg();
4283 let mc = &self.model.mc;
4284 let hc = d.hc_mult as usize;
4285 let hidden = mc.n_embd as usize;
4286 let heads = mc.n_head as usize;
4287 let hd = d.head_dim as usize;
4288 let rd = d.qk_rope_head_dim as usize;
4289 let q_lora = d.q_lora_rank as usize;
4290 let win = d.sliding_window as usize;
4291 let o_groups = d.o_groups as usize;
4292 let o_lora = d.o_lora_rank as usize;
4293 let eps = mc.rms_eps;
4294 let iters = d.hc_sinkhorn_iters;
4295 let hc_eps = d.hc_eps;
4296 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx"))?;
4297 let stream = st.gpu.stream();
4298 let fc_dev = if layer.ratio != 0 {
4299 &st.fc_yarn
4300 } else {
4301 &st.fc_plain
4302 };
4303 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
4304 let LayerCache {
4305 kvc,
4306 n_blocks,
4307 pend_kv,
4308 pend_score,
4309 ikvc,
4310 i_blocks,
4311 ipend_kv,
4312 ipend_score,
4313 } = cache;
4314
4315 let (y, post, comb) = Self::hc_pre(
4317 st,
4318 h,
4319 &layer.hc_attn_fn,
4320 &layer.hc_attn_base,
4321 &layer.hc_attn_scale,
4322 1,
4323 hc,
4324 hidden,
4325 iters,
4326 hc_eps,
4327 )?;
4328 let mut x = stream.alloc_zeros::<f32>(hidden).map_err(e("x"))?;
4329 unsafe {
4330 ck(
4331 "rmsnorm attn",
4332 k::memra_dsv4_rmsnorm(
4333 dpf!(y, &stream),
4334 dpf!(layer.attn_norm, &stream),
4335 dpm!(x, &stream),
4336 1,
4337 hidden as i32,
4338 eps,
4339 sp(&stream),
4340 ),
4341 )?;
4342 }
4343 if let Some(dm) = dump.as_deref_mut() {
4344 dm.push((format!("layer{}.x", layer.il), dtoh_f32(&stream, &x)?));
4345 }
4346
4347 let mut qr = stream.alloc_zeros::<f32>(q_lora).map_err(e("qr"))?;
4350 Self::gemm(st, &x, layer.wq_a.dev(), 0, 1, q_lora, hidden, &mut qr)?;
4351 unsafe {
4352 ck(
4353 "rmsnorm q",
4354 k::memra_dsv4_rmsnorm(
4355 dpf!(qr, &stream),
4356 dpf!(layer.q_norm, &stream),
4357 dpm!(qr, &stream),
4358 1,
4359 q_lora as i32,
4360 eps,
4361 sp(&stream),
4362 ),
4363 )?;
4364 }
4365 let mut qr_b = stream.alloc_zeros::<u8>(q_lora * 2).map_err(e("qr_b"))?;
4366 unsafe {
4367 ck(
4368 "cvt qr",
4369 k::memra_dsv4_cvt_bf16(
4370 dpf!(qr, &stream),
4371 qr_b.device_ptr_mut(&stream).0 as *mut c_void,
4372 q_lora as i64,
4373 sp(&stream),
4374 ),
4375 )?;
4376 }
4377 let mut q = stream.alloc_zeros::<f32>(heads * hd).map_err(e("q"))?;
4378 Self::gemm_pre(
4379 st,
4380 &qr_b,
4381 layer.wq_b.dev().device_ptr(&stream).0 as *const c_void,
4382 1,
4383 heads * hd,
4384 q_lora,
4385 &mut q,
4386 )?;
4387 let pos_dev = upload_i32(&stream, &[pos as i32])?;
4388 unsafe {
4389 ck(
4390 "headrms",
4391 k::memra_dsv4_headrms(dpm!(q, &stream), heads as i32, hd as i32, eps, sp(&stream)),
4392 )?;
4393 ck(
4394 "rope q",
4395 k::memra_dsv4_rope(
4396 dpm!(q, &stream),
4397 1,
4398 heads as i32,
4399 hd as i32,
4400 rd as i32,
4401 dpf!(fc_dev, &stream),
4402 pos_dev.device_ptr(&stream).0 as *const i32,
4403 0,
4404 sp(&stream),
4405 ),
4406 )?;
4407 }
4408
4409 if let Some(dm) = dump.as_deref_mut() {
4410 dm.push((format!("layer{}.q", layer.il), dtoh_f32(&stream, &q)?));
4411 }
4412 let mut kv = stream.alloc_zeros::<f32>(hd).map_err(e("kv"))?;
4414 Self::gemm(st, &x, layer.wkv.dev(), 0, 1, hd, hidden, &mut kv)?;
4415 unsafe {
4416 ck(
4417 "rmsnorm kv",
4418 k::memra_dsv4_rmsnorm(
4419 dpf!(kv, &stream),
4420 dpf!(layer.kv_norm, &stream),
4421 dpm!(kv, &stream),
4422 1,
4423 hd as i32,
4424 eps,
4425 sp(&stream),
4426 ),
4427 )?;
4428 ck(
4429 "rope kv",
4430 k::memra_dsv4_rope(
4431 dpm!(kv, &stream),
4432 1,
4433 1,
4434 hd as i32,
4435 rd as i32,
4436 dpf!(fc_dev, &stream),
4437 pos_dev.device_ptr(&stream).0 as *const i32,
4438 0,
4439 sp(&stream),
4440 ),
4441 )?;
4442 ck(
4443 "act_quant kv",
4444 k::memra_dsv4_act_quant(
4445 dpm!(kv, &stream),
4446 1,
4447 hd as i64,
4448 (hd - rd) as i32,
4449 64,
4450 clamp_only,
4451 sp(&stream),
4452 ),
4453 )?;
4454 }
4455 {
4456 let slot = pos % win;
4457 let src = kv.slice(0..hd);
4458 let mut dst = kvc.slice_mut(slot * hd..(slot + 1) * hd);
4459 stream
4460 .memcpy_dtod(&src, &mut dst)
4461 .map_err(e("ring write"))?;
4462 }
4463 if let Some(dm) = dump.as_deref_mut() {
4464 dm.push((format!("layer{}.kv", layer.il), dtoh_f32(&stream, &kv)?));
4465 }
4466
4467 let mut idxs: Vec<i64> = vec![-1; win];
4469 if pos >= win - 1 {
4470 let sp_ = pos % win;
4471 let mut k_ = 0usize;
4472 for s_ in (sp_ + 1)..win {
4473 idxs[k_] = s_ as i64;
4474 k_ += 1;
4475 }
4476 for s_ in 0..=sp_ {
4477 idxs[k_] = s_ as i64;
4478 k_ += 1;
4479 }
4480 } else {
4481 for (p, v) in idxs.iter_mut().enumerate().take(pos + 1) {
4482 *v = p as i64;
4483 }
4484 }
4485
4486 if layer.ratio != 0 {
4487 let cidx: Vec<i64> = if let Some(ix) = &layer.idx {
4488 let mut qi = stream
4490 .alloc_zeros::<f32>(ix.heads * ix.hd)
4491 .map_err(e("qi"))?;
4492 Self::gemm_pre(
4493 st,
4494 &qr_b,
4495 ix.wq_b.dev().device_ptr(&stream).0 as *const c_void,
4496 1,
4497 ix.heads * ix.hd,
4498 q_lora,
4499 &mut qi,
4500 )?;
4501 unsafe {
4502 ck(
4503 "rope qi",
4504 k::memra_dsv4_rope(
4505 dpm!(qi, &stream),
4506 1,
4507 ix.heads as i32,
4508 ix.hd as i32,
4509 rd as i32,
4510 dpf!(fc_dev, &stream),
4511 pos_dev.device_ptr(&stream).0 as *const i32,
4512 0,
4513 sp(&stream),
4514 ),
4515 )?;
4516 let scale = (ix.hd as f32).powf(-0.5);
4517 ck(
4518 "hadamard qi",
4519 k::memra_dsv4_hadamard(
4520 dpm!(qi, &stream),
4521 ix.heads as i32,
4522 ix.hd as i32,
4523 scale,
4524 sp(&stream),
4525 ),
4526 )?;
4527 ck(
4528 "fp4 qi",
4529 k::memra_dsv4_fp4_act_quant(
4530 dpm!(qi, &stream),
4531 ix.heads as i32,
4532 ix.hd as i64,
4533 ix.hd as i32,
4534 sp(&stream),
4535 ),
4536 )?;
4537 }
4538 self.cmp_decode(
4540 st,
4541 &ix.cmp,
4542 &x,
4543 pos,
4544 hidden,
4545 fc_dev,
4546 rd,
4547 eps,
4548 ipend_kv.as_mut().expect("ipend"),
4549 ipend_score.as_mut().expect("ipend"),
4550 ikvc.as_mut().expect("ikvc"),
4551 0,
4552 i_blocks,
4553 )?;
4554 let nb = *i_blocks;
4555 debug_assert_eq!(nb, (pos + 1) / layer.ratio, "indexer block count");
4556 if nb > 0 {
4557 let mut wproj = stream.alloc_zeros::<f32>(ix.heads).map_err(e("wp"))?;
4558 Self::gemm(
4559 st,
4560 &x,
4561 ix.weights_proj.dev(),
4562 0,
4563 1,
4564 ix.heads,
4565 hidden,
4566 &mut wproj,
4567 )?;
4568 let wscale = ((ix.hd as f64).powf(-0.5) * (ix.heads as f64).powf(-0.5)) as f32;
4569 let mut score = stream.alloc_zeros::<f32>(nb).map_err(e("iscore"))?;
4570 unsafe {
4571 ck(
4572 "indexer_score dec",
4573 k::memra_dsv4_indexer_score(
4574 dpf!(qi, &stream),
4575 dpf!(ikvc.as_ref().expect("ikvc"), &stream),
4576 dpf!(wproj, &stream),
4577 wscale,
4578 dpm!(score, &stream),
4579 1,
4580 ix.heads as i32,
4581 ix.hd as i32,
4582 nb as i32,
4583 layer.ratio as i32,
4584 nb as i32, sp(&stream),
4586 ),
4587 )?;
4588 }
4589 let score_h = dtoh_f32(&stream, &score)?;
4590 let kk = ix.topk.min(nb);
4592 let mut order: Vec<usize> = (0..nb).collect();
4593 order.sort_by(|&a, &b| {
4594 score_h[b]
4595 .partial_cmp(&score_h[a])
4596 .unwrap_or(std::cmp::Ordering::Equal)
4597 .then(a.cmp(&b))
4598 });
4599 order
4600 .into_iter()
4601 .take(kk)
4602 .map(|j| (j + win) as i64)
4603 .collect()
4604 } else {
4605 Vec::new()
4606 }
4607 } else {
4608 let nb = (pos + 1) / layer.ratio;
4610 (0..nb).map(|j| (j + win) as i64).collect()
4611 };
4612 self.cmp_decode(
4614 st,
4615 layer.cmp.as_ref().expect("ratio!=0 has compressor"),
4616 &x,
4617 pos,
4618 hidden,
4619 fc_dev,
4620 rd,
4621 eps,
4622 pend_kv.as_mut().expect("pend"),
4623 pend_score.as_mut().expect("pend"),
4624 kvc,
4625 win,
4626 n_blocks,
4627 )?;
4628 debug_assert_eq!(*n_blocks, (pos + 1) / layer.ratio, "attn block count");
4629 idxs.extend_from_slice(&cidx);
4630 }
4631 let slots = idxs.len();
4632 let idxs_i32: Vec<i32> = idxs.iter().map(|&v| v as i32).collect();
4633 let idx_dev = upload_i32(&stream, &idxs_i32)?;
4634
4635 let mut o = stream.alloc_zeros::<f32>(heads * hd).map_err(e("o"))?;
4637 let scale = (hd as f64).powf(-0.5) as f32;
4638 unsafe {
4639 ck(
4640 "sink_attn dec",
4641 k::memra_dsv4_sink_attn(
4642 dpf!(q, &stream),
4643 dpf!(kvc, &stream),
4644 idx_dev.device_ptr(&stream).0 as *const i32,
4645 dpf!(layer.sink, &stream),
4646 dpm!(o, &stream),
4647 1,
4648 heads as i32,
4649 hd as i32,
4650 slots as i32,
4651 scale,
4652 sp(&stream),
4653 ),
4654 )?;
4655 ck(
4656 "rope o inv",
4657 k::memra_dsv4_rope(
4658 dpm!(o, &stream),
4659 1,
4660 heads as i32,
4661 hd as i32,
4662 rd as i32,
4663 dpf!(fc_dev, &stream),
4664 pos_dev.device_ptr(&stream).0 as *const i32,
4665 1,
4666 sp(&stream),
4667 ),
4668 )?;
4669 }
4670
4671 if let Some(dm) = dump.as_deref_mut() {
4672 dm.push((format!("layer{}.o", layer.il), dtoh_f32(&stream, &o)?));
4673 }
4674 let gw = heads / o_groups * hd;
4676 let mut og = stream
4677 .alloc_zeros::<f32>(o_groups * o_lora)
4678 .map_err(e("og"))?;
4679 let mut o_grp = stream.alloc_zeros::<f32>(gw).map_err(e("o_grp"))?;
4680 let mut y_grp = stream.alloc_zeros::<f32>(o_lora).map_err(e("y_grp"))?;
4681 for g in 0..o_groups {
4682 unsafe {
4683 ck(
4684 "take_cols",
4685 k::memra_dsv4_take_cols(
4686 dpf!(o, &stream),
4687 dpm!(o_grp, &stream),
4688 1,
4689 gw as i32,
4690 (heads * hd) as i64,
4691 (g * gw) as i64,
4692 sp(&stream),
4693 ),
4694 )?;
4695 }
4696 Self::gemm(
4697 st,
4698 &o_grp,
4699 layer.wo_a.dev(),
4700 g * o_lora * gw,
4701 1,
4702 o_lora,
4703 gw,
4704 &mut y_grp,
4705 )?;
4706 unsafe {
4707 ck(
4708 "place_cols",
4709 k::memra_dsv4_place_cols(
4710 dpf!(y_grp, &stream),
4711 dpm!(og, &stream),
4712 1,
4713 o_lora as i32,
4714 (o_groups * o_lora) as i64,
4715 (g * o_lora) as i64,
4716 sp(&stream),
4717 ),
4718 )?;
4719 }
4720 }
4721 let mut attn_out = stream.alloc_zeros::<f32>(hidden).map_err(e("ao"))?;
4722 Self::gemm(
4723 st,
4724 &og,
4725 layer.wo_b.dev(),
4726 0,
4727 1,
4728 hidden,
4729 o_groups * o_lora,
4730 &mut attn_out,
4731 )?;
4732
4733 if let Some(dm) = dump.as_deref_mut() {
4734 dm.push((
4735 format!("layer{}.attn_out", layer.il),
4736 dtoh_f32(&stream, &attn_out)?,
4737 ));
4738 }
4739 let mut h2 = stream.alloc_zeros::<f32>(hc * hidden).map_err(e("h2"))?;
4741 unsafe {
4742 ck(
4743 "hc_post attn",
4744 k::memra_dsv4_hc_post(
4745 dpf!(attn_out, &stream),
4746 dpf!(h, &stream),
4747 dpf!(post, &stream),
4748 dpf!(comb, &stream),
4749 dpm!(h2, &stream),
4750 1,
4751 hc as i32,
4752 hidden as i32,
4753 sp(&stream),
4754 ),
4755 )?;
4756 }
4757
4758 let (y2, post2, comb2) = Self::hc_pre(
4760 st,
4761 &h2,
4762 &layer.hc_ffn_fn,
4763 &layer.hc_ffn_base,
4764 &layer.hc_ffn_scale,
4765 1,
4766 hc,
4767 hidden,
4768 iters,
4769 hc_eps,
4770 )?;
4771 let mut xf = stream.alloc_zeros::<f32>(hidden).map_err(e("xf"))?;
4772 unsafe {
4773 ck(
4774 "rmsnorm ffn",
4775 k::memra_dsv4_rmsnorm(
4776 dpf!(y2, &stream),
4777 dpf!(layer.ffn_norm, &stream),
4778 dpm!(xf, &stream),
4779 1,
4780 hidden as i32,
4781 eps,
4782 sp(&stream),
4783 ),
4784 )?;
4785 }
4786 let moe_out = self.moe_forward(st, layer, &xf, 1, &[tok])?;
4787 if let Some(dm) = dump.as_deref_mut() {
4788 dm.push((
4789 format!("layer{}.moe_out", layer.il),
4790 dtoh_f32(&stream, &moe_out)?,
4791 ));
4792 }
4793 let mut h3 = stream.alloc_zeros::<f32>(hc * hidden).map_err(e("h3"))?;
4794 unsafe {
4795 ck(
4796 "hc_post ffn",
4797 k::memra_dsv4_hc_post(
4798 dpf!(moe_out, &stream),
4799 dpf!(h2, &stream),
4800 dpf!(post2, &stream),
4801 dpf!(comb2, &stream),
4802 dpm!(h3, &stream),
4803 1,
4804 hc as i32,
4805 hidden as i32,
4806 sp(&stream),
4807 ),
4808 )?;
4809 }
4810 if let Some(dm) = dump.as_deref_mut() {
4811 dm.push((format!("layer{}.h3", layer.il), dtoh_f32(&stream, &h3)?));
4812 }
4813 Ok(h3)
4814 }
4815
4816 pub fn decode_step(&self, tok: u32, state: &mut DecodeState) -> Res<Vec<f32>> {
4821 self.decode_step_impl(tok, state, None)
4822 }
4823
4824 pub fn decode_step_probe(
4826 &self,
4827 tok: u32,
4828 state: &mut DecodeState,
4829 ) -> Res<(Vec<f32>, Vec<(String, Vec<f32>)>)> {
4830 let mut dump = Vec::new();
4831 let logits = self.decode_step_impl(tok, state, Some(&mut dump))?;
4832 Ok((logits, dump))
4833 }
4834
4835 #[allow(clippy::too_many_arguments)]
4841 fn gemm_dev(
4842 st: &Stage,
4843 x_f32: *const f32,
4844 xb: &mut CudaSlice<u8>,
4845 w: DW,
4846 m: usize,
4847 n: usize,
4848 kdim: usize,
4849 y_ptr: *mut f32,
4850 ) -> Res<()> {
4851 assert_eq!(m, 1, "gemm_dev is the m=1 decode path");
4852 let stream = st.gpu.stream();
4853 unsafe {
4854 ck(
4855 "cvt_bf16 dev",
4856 k::memra_dsv4_cvt_bf16(
4857 x_f32,
4858 xb.device_ptr_mut(&stream).0 as *mut c_void,
4859 kdim as i64,
4860 sp(&stream),
4861 ),
4862 )?;
4863 }
4864 let xb_ptr = xb.device_ptr(&stream).0 as *const c_void;
4865 Self::gemv_pre_dev(st, xb_ptr, w, n, kdim, y_ptr)
4866 }
4867
4868 fn gemv_pre_dev(
4872 st: &Stage,
4873 xb_ptr: *const c_void,
4874 w: DW,
4875 n: usize,
4876 kdim: usize,
4877 y_ptr: *mut f32,
4878 ) -> Res<()> {
4879 let stream = st.gpu.stream();
4880 unsafe {
4881 match w {
4882 DW::Bf16(w_ptr) => ck(
4883 "gemv_bf16 pre dev",
4884 k::memra_dsv4_gemv_bf16(
4885 w_ptr,
4886 xb_ptr,
4887 y_ptr,
4888 n as i32,
4889 kdim as i32,
4890 sp(&stream),
4891 ),
4892 )?,
4893 DW::Fp8 {
4894 codes,
4895 scales,
4896 sc_cols,
4897 } => ck(
4898 "gemv_fp8 pre dev",
4899 k::memra_dsv4_gemv_fp8(
4900 codes,
4901 scales,
4902 sc_cols,
4903 xb_ptr,
4904 y_ptr,
4905 n as i32,
4906 kdim as i32,
4907 sp(&stream),
4908 ),
4909 )?,
4910 }
4911 }
4912 Ok(())
4913 }
4914
4915 #[allow(clippy::too_many_arguments)]
4920 #[allow(clippy::too_many_arguments)]
4925 unsafe fn rmsnorm_arm(
4926 &self,
4927 x: *const f32,
4928 w: *const f32,
4929 dst: *mut f32,
4930 rows: i32,
4931 ncols: i32,
4932 eps: f32,
4933 sv: *mut c_void,
4934 ) -> i32 {
4935 unsafe {
4936 if self.chains_f32 {
4937 k::memra_dsv4_rmsnorm_f32acc(x, w, dst, rows, ncols, eps, sv)
4938 } else {
4939 k::memra_dsv4_rmsnorm(x, w, dst, rows, ncols, eps, sv)
4940 }
4941 }
4942 }
4943
4944 unsafe fn headrms_arm(&self, x: *mut f32, rows: i32, d: i32, eps: f32, sv: *mut c_void) -> i32 {
4945 unsafe {
4946 if self.chains_f32 {
4947 k::memra_dsv4_headrms_f32acc(x, rows, d, eps, sv)
4948 } else {
4949 k::memra_dsv4_headrms(x, rows, d, eps, sv)
4950 }
4951 }
4952 }
4953
4954 #[allow(clippy::too_many_arguments)]
4955 unsafe fn rowsq_scale_arm(
4956 &self,
4957 x: *const f32,
4958 mixes: *mut f32,
4959 s: i32,
4960 w: i32,
4961 rows: i32,
4962 eps: f32,
4963 sv: *mut c_void,
4964 ) -> i32 {
4965 unsafe {
4966 if self.chains_f32 {
4967 k::memra_dsv4_rowsq_scale_f32acc(x, mixes, s, w, rows, eps, sv)
4968 } else {
4969 k::memra_dsv4_rowsq_scale(x, mixes, s, w, rows, eps, sv)
4970 }
4971 }
4972 }
4973
4974 #[allow(clippy::too_many_arguments)]
4975 unsafe fn indexer_score_arm(
4976 &self,
4977 q: *const f32,
4978 ckv: *const f32,
4979 w: *const f32,
4980 wscale: f32,
4981 score: *mut f32,
4982 s: i32,
4983 heads: i32,
4984 hd: i32,
4985 nb: i32,
4986 ratio: i32,
4987 lim0: i32,
4988 sv: *mut c_void,
4989 ) -> i32 {
4990 unsafe {
4991 if self.chains_f32 {
4992 k::memra_dsv4_indexer_score_f32acc(
4993 q, ckv, w, wscale, score, s, heads, hd, nb, ratio, lim0, sv,
4994 )
4995 } else {
4996 k::memra_dsv4_indexer_score(
4997 q, ckv, w, wscale, score, s, heads, hd, nb, ratio, lim0, sv,
4998 )
4999 }
5000 }
5001 }
5002
5003 #[allow(clippy::too_many_arguments)]
5006 unsafe fn sink_attn_dec_arm(
5007 &self,
5008 q: *const f32,
5009 kv: *const f32,
5010 idxs: *const i32,
5011 sink: *const f32,
5012 scores: *mut f32,
5013 evals: *mut f32,
5014 den: *mut f64,
5015 o: *mut f32,
5016 heads: i32,
5017 hd: i32,
5018 slots: i32,
5019 scale: f32,
5020 sv: *mut c_void,
5021 ) -> i32 {
5022 unsafe {
5023 if self.chains_f32 {
5024 k::memra_dsv4_sink_attn_dec_f32acc(
5025 q,
5026 kv,
5027 idxs,
5028 sink,
5029 scores,
5030 evals,
5031 den as *mut f32,
5032 o,
5033 heads,
5034 hd,
5035 slots,
5036 scale,
5037 sv,
5038 )
5039 } else {
5040 k::memra_dsv4_sink_attn_dec(
5041 q, kv, idxs, sink, scores, evals, den, o, heads, hd, slots, scale, sv,
5042 )
5043 }
5044 }
5045 }
5046
5047 fn hc_pre_dev(
5048 &self,
5049 st: &Stage,
5050 h: &CudaSlice<f32>,
5051 fn_w: &CudaSlice<f32>,
5052 base_host: &[f32],
5053 scale_host: &[f32],
5054 base_dev: &CudaSlice<f32>,
5055 scale_dev: &CudaSlice<f32>,
5056 mixes: &mut CudaSlice<f32>,
5057 pre: &mut CudaSlice<f32>,
5058 post: &mut CudaSlice<f32>,
5059 comb: &mut CudaSlice<f32>,
5060 y_hc: &mut CudaSlice<f32>,
5061 hc: usize,
5062 hidden: usize,
5063 iters: u32,
5064 hc_eps: f32,
5065 host_math: bool,
5066 ) -> Res<()> {
5067 let stream = st.gpu.stream();
5068 let w = hc * hidden;
5069 let rows = (2 + hc) * hc;
5070 self.dots_dev(st, h, fn_w, 1, w, rows, mixes)?;
5071 unsafe {
5072 ck(
5073 "rowsq_scale dev",
5074 self.rowsq_scale_arm(
5075 dpf!(h, &stream),
5076 dpm!(*mixes, &stream),
5077 1,
5078 w as i32,
5079 rows as i32,
5080 hc_eps,
5081 sp(&stream),
5082 ),
5083 )?;
5084 }
5085 if host_math {
5086 let mixes_h = dtoh_f32(&stream, mixes)?;
5087 let (pre_h, post_h, comb_h) =
5088 hc_split_sinkhorn(&mixes_h, 1, hc, scale_host, base_host, iters, hc_eps);
5089 stream.memcpy_htod(&pre_h, pre).map_err(e("htod pre"))?;
5090 stream.memcpy_htod(&post_h, post).map_err(e("htod post"))?;
5091 stream.memcpy_htod(&comb_h, comb).map_err(e("htod comb"))?;
5092 } else {
5093 unsafe {
5094 ck(
5095 "hc_sinkhorn",
5096 k::memra_dsv4_hc_sinkhorn(
5097 dpf!(*mixes, &stream),
5098 dpf!(scale_dev, &stream),
5099 dpf!(base_dev, &stream),
5100 dpm!(*pre, &stream),
5101 dpm!(*post, &stream),
5102 dpm!(*comb, &stream),
5103 hc as i32,
5104 iters as i32,
5105 hc_eps,
5106 sp(&stream),
5107 ),
5108 )?;
5109 }
5110 }
5111 unsafe {
5112 ck(
5113 "hc_collapse dev",
5114 k::memra_dsv4_hc_collapse(
5115 dpf!(h, &stream),
5116 dpf!(*pre, &stream),
5117 dpm!(*y_hc, &stream),
5118 1,
5119 hc as i32,
5120 hidden as i32,
5121 sp(&stream),
5122 ),
5123 )?;
5124 }
5125 Ok(())
5126 }
5127
5128 #[allow(clippy::too_many_arguments)]
5132 fn cmp_decode_dev(
5133 &self,
5134 st: &Stage,
5135 cmp: &CmpDev,
5136 x: &CudaSlice<f32>,
5137 pos: usize,
5138 hidden: usize,
5139 fc_dev: &CudaSlice<f32>,
5140 rd: usize,
5141 eps: f32,
5142 kv_row: &mut CudaSlice<f32>,
5143 sc_row: &mut CudaSlice<f32>,
5144 emit: &mut CudaSlice<f32>,
5145 shift: &mut CudaSlice<f32>,
5146 pend_kv: &mut CudaSlice<f32>,
5147 pend_score: &mut CudaSlice<f32>,
5148 store: &mut CudaSlice<f32>,
5149 row0: usize,
5150 blocks: &mut usize,
5151 ) -> Res<()> {
5152 let stream = st.gpu.stream();
5153 let (ratio, d, latent) = (cmp.ratio, cmp.d, cmp.latent);
5154 self.dots_dev(st, x, &cmp.wkv, 1, hidden, latent, kv_row)?;
5155 self.dots_dev(st, x, &cmp.wgate, 1, hidden, latent, sc_row)?;
5156 let slot = if cmp.overlap {
5157 ratio + pos % ratio
5158 } else {
5159 pos % ratio
5160 };
5161 {
5162 let src = kv_row.slice(0..latent);
5163 let mut dst = pend_kv.slice_mut(slot * latent..(slot + 1) * latent);
5164 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend kv"))?;
5165 let src = sc_row.slice(0..latent);
5166 let mut dst = pend_score.slice_mut(slot * latent..(slot + 1) * latent);
5167 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend sc"))?;
5168 }
5169 if (pos + 1) % ratio != 0 {
5170 return Ok(());
5171 }
5172 let j = pos / ratio;
5173 let nb_launch = if cmp.overlap { 2usize } else { 1 };
5174 let row_off = if cmp.overlap { d } else { 0 };
5175 unsafe {
5176 ck(
5177 "compressor_pool dec",
5178 k::memra_dsv4_compressor_pool(
5179 dpf!(*pend_kv, &stream),
5180 dpf!(*pend_score, &stream),
5181 dpf!(cmp.ape, &stream),
5182 dpm!(*emit, &stream),
5183 nb_launch as i32,
5184 ratio as i32,
5185 d as i32,
5186 latent as i32,
5187 cmp.overlap as i32,
5188 sp(&stream),
5189 ),
5190 )?;
5191 let row_c = (emit.device_ptr(&stream).0 as usize + row_off * 4) as *const f32;
5192 let row_m = (emit.device_ptr_mut(&stream).0 as usize + row_off * 4) as *mut f32;
5193 ck(
5194 "rmsnorm dec cmp",
5195 self.rmsnorm_arm(
5196 row_c,
5197 dpf!(cmp.norm, &stream),
5198 row_m,
5199 1,
5200 d as i32,
5201 eps,
5202 sp(&stream),
5203 ),
5204 )?;
5205 ck(
5206 "rope_at dec cmp",
5207 k::memra_dsv4_rope_at(
5208 row_m,
5209 1,
5210 d as i32,
5211 rd as i32,
5212 dpf!(fc_dev, &stream),
5213 (j * ratio) as i32,
5214 0,
5215 sp(&stream),
5216 ),
5217 )?;
5218 if cmp.rotate {
5219 let scale = (d as f32).powf(-0.5);
5220 ck(
5221 "hadamard dec cmp",
5222 k::memra_dsv4_hadamard(row_m, 1, d as i32, scale, sp(&stream)),
5223 )?;
5224 ck(
5225 "fp4 dec cmp",
5226 k::memra_dsv4_fp4_act_quant(row_m, 1, d as i64, d as i32, sp(&stream)),
5227 )?;
5228 } else {
5229 ck(
5230 "act_quant dec cmp",
5231 k::memra_dsv4_act_quant(
5232 row_m,
5233 1,
5234 d as i64,
5235 (d - rd) as i32,
5236 64,
5237 (self.variant == ActQuantVariant::ClampOnly) as i32,
5238 sp(&stream),
5239 ),
5240 )?;
5241 }
5242 }
5243 {
5244 let src = emit.slice(row_off..row_off + d);
5245 let mut dst = store.slice_mut((row0 + j) * d..(row0 + j + 1) * d);
5246 stream
5247 .memcpy_dtod(&src, &mut dst)
5248 .map_err(e("emit store"))?;
5249 }
5250 if cmp.overlap {
5251 {
5252 let src = pend_kv.slice(ratio * latent..2 * ratio * latent);
5253 let mut dst = shift.slice_mut(0..ratio * latent);
5254 stream.memcpy_dtod(&src, &mut dst).map_err(e("shift1"))?;
5255 }
5256 {
5257 let src = shift.slice(0..ratio * latent);
5258 let mut dst = pend_kv.slice_mut(0..ratio * latent);
5259 stream.memcpy_dtod(&src, &mut dst).map_err(e("shift2"))?;
5260 }
5261 {
5262 let src = pend_score.slice(ratio * latent..2 * ratio * latent);
5263 let mut dst = shift.slice_mut(0..ratio * latent);
5264 stream.memcpy_dtod(&src, &mut dst).map_err(e("shift3"))?;
5265 }
5266 {
5267 let src = shift.slice(0..ratio * latent);
5268 let mut dst = pend_score.slice_mut(0..ratio * latent);
5269 stream.memcpy_dtod(&src, &mut dst).map_err(e("shift4"))?;
5270 }
5271 }
5272 *blocks = j + 1;
5273 Ok(())
5274 }
5275
5276 #[allow(clippy::too_many_arguments)]
5281 fn block_decode_dev(
5282 &self,
5283 st: &Stage,
5284 layer: &LayerDev,
5285 cache: &mut LayerCache,
5286 ws: &mut StepWs,
5287 input_rx: bool,
5288 pos: usize,
5289 tok: u32,
5290 host_math: bool,
5291 ) -> Res<()> {
5292 let d = self.model.cfg();
5293 let mc = &self.model.mc;
5294 let hc = d.hc_mult as usize;
5295 let hidden = mc.n_embd as usize;
5296 let heads = mc.n_head as usize;
5297 let hd = d.head_dim as usize;
5298 let rd = d.qk_rope_head_dim as usize;
5299 let q_lora = d.q_lora_rank as usize;
5300 let win = d.sliding_window as usize;
5301 let o_groups = d.o_groups as usize;
5302 let o_lora = d.o_lora_rank as usize;
5303 let eps = mc.rms_eps;
5304 let iters = d.hc_sinkhorn_iters;
5305 let hc_eps = d.hc_eps;
5306 let stream = st.gpu.stream();
5307 let fc_dev: *const f32 = if layer.ratio != 0 {
5308 st.fc_yarn.device_ptr(&stream).0 as *const f32
5309 } else {
5310 st.fc_plain.device_ptr(&stream).0 as *const f32
5311 };
5312 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
5313 let LayerCache {
5314 kvc,
5315 n_blocks,
5316 pend_kv,
5317 pend_score,
5318 ikvc,
5319 i_blocks,
5320 ipend_kv,
5321 ipend_score,
5322 } = cache;
5323
5324 {
5326 let StepWs {
5328 h_a,
5329 h_rx,
5330 mixes,
5331 pre,
5332 post,
5333 comb,
5334 y_hc,
5335 ..
5336 } = ws;
5337 let h_in: &CudaSlice<f32> = if input_rx { h_rx } else { h_a };
5338 self.hc_pre_dev(
5339 st,
5340 h_in,
5341 &layer.hc_attn_fn,
5342 &layer.hc_attn_base,
5343 &layer.hc_attn_scale,
5344 &layer.hc_attn_base_dev,
5345 &layer.hc_attn_scale_dev,
5346 mixes,
5347 pre,
5348 post,
5349 comb,
5350 y_hc,
5351 hc,
5352 hidden,
5353 iters,
5354 hc_eps,
5355 host_math,
5356 )?;
5357 }
5358 unsafe {
5359 ck(
5360 "rmsnorm attn dev",
5361 self.rmsnorm_arm(
5362 dpf!(ws.y_hc, &stream),
5363 dpf!(layer.attn_norm, &stream),
5364 dpm!(ws.x, &stream),
5365 1,
5366 hidden as i32,
5367 eps,
5368 sp(&stream),
5369 ),
5370 )?;
5371 }
5372
5373 Self::gemm_dev(
5375 st,
5376 ws.x.device_ptr(&stream).0 as *const f32,
5377 &mut ws.gemm_xb,
5378 dwsel(self.dense_fp8, &stream, &layer.wq_a, &layer.wq_a_fp8),
5379 1,
5380 q_lora,
5381 hidden,
5382 ws.qr.device_ptr_mut(&stream).0 as *mut f32,
5383 )?;
5384 unsafe {
5385 ck(
5386 "rmsnorm q dev",
5387 self.rmsnorm_arm(
5388 dpf!(ws.qr, &stream),
5389 dpf!(layer.q_norm, &stream),
5390 dpm!(ws.qr, &stream),
5391 1,
5392 q_lora as i32,
5393 eps,
5394 sp(&stream),
5395 ),
5396 )?;
5397 ck(
5398 "cvt qr dev",
5399 k::memra_dsv4_cvt_bf16(
5400 dpf!(ws.qr, &stream),
5401 ws.qr_b.device_ptr_mut(&stream).0 as *mut c_void,
5402 q_lora as i64,
5403 sp(&stream),
5404 ),
5405 )?;
5406 }
5407 Self::gemv_pre_dev(
5408 st,
5409 ws.qr_b.device_ptr(&stream).0 as *const c_void,
5410 dwsel(self.dense_fp8, &stream, &layer.wq_b, &layer.wq_b_fp8),
5411 heads * hd,
5412 q_lora,
5413 ws.q.device_ptr_mut(&stream).0 as *mut f32,
5414 )?;
5415 unsafe {
5416 ck(
5417 "headrms dev",
5418 self.headrms_arm(
5419 dpm!(ws.q, &stream),
5420 heads as i32,
5421 hd as i32,
5422 eps,
5423 sp(&stream),
5424 ),
5425 )?;
5426 ck(
5427 "rope_at q dev",
5428 k::memra_dsv4_rope_at(
5429 dpm!(ws.q, &stream),
5430 heads as i32,
5431 hd as i32,
5432 rd as i32,
5433 fc_dev,
5434 pos as i32,
5435 0,
5436 sp(&stream),
5437 ),
5438 )?;
5439 }
5440
5441 Self::gemm_dev(
5443 st,
5444 ws.x.device_ptr(&stream).0 as *const f32,
5445 &mut ws.gemm_xb,
5446 dwsel(self.dense_fp8, &stream, &layer.wkv, &layer.wkv_fp8),
5447 1,
5448 hd,
5449 hidden,
5450 ws.kv.device_ptr_mut(&stream).0 as *mut f32,
5451 )?;
5452 unsafe {
5453 ck(
5454 "rmsnorm kv dev",
5455 self.rmsnorm_arm(
5456 dpf!(ws.kv, &stream),
5457 dpf!(layer.kv_norm, &stream),
5458 dpm!(ws.kv, &stream),
5459 1,
5460 hd as i32,
5461 eps,
5462 sp(&stream),
5463 ),
5464 )?;
5465 ck(
5466 "rope_at kv dev",
5467 k::memra_dsv4_rope_at(
5468 dpm!(ws.kv, &stream),
5469 1,
5470 hd as i32,
5471 rd as i32,
5472 fc_dev,
5473 pos as i32,
5474 0,
5475 sp(&stream),
5476 ),
5477 )?;
5478 ck(
5479 "act_quant kv dev",
5480 k::memra_dsv4_act_quant(
5481 dpm!(ws.kv, &stream),
5482 1,
5483 hd as i64,
5484 (hd - rd) as i32,
5485 64,
5486 clamp_only,
5487 sp(&stream),
5488 ),
5489 )?;
5490 }
5491 {
5492 let slot = pos % win;
5493 let src = ws.kv.slice(0..hd);
5494 let mut dst = kvc.slice_mut(slot * hd..(slot + 1) * hd);
5495 stream
5496 .memcpy_dtod(&src, &mut dst)
5497 .map_err(e("ring write"))?;
5498 }
5499
5500 let mut slots = win;
5502 if layer.ratio != 0 {
5503 if let Some(ix) = &layer.idx {
5504 Self::gemv_pre_dev(
5506 st,
5507 ws.qr_b.device_ptr(&stream).0 as *const c_void,
5508 dwsel(self.dense_fp8, &stream, &ix.wq_b, &ix.wq_b_fp8),
5509 ix.heads * ix.hd,
5510 q_lora,
5511 ws.qi.device_ptr_mut(&stream).0 as *mut f32,
5512 )?;
5513 unsafe {
5514 ck(
5515 "rope_at qi dev",
5516 k::memra_dsv4_rope_at(
5517 dpm!(ws.qi, &stream),
5518 ix.heads as i32,
5519 ix.hd as i32,
5520 rd as i32,
5521 fc_dev,
5522 pos as i32,
5523 0,
5524 sp(&stream),
5525 ),
5526 )?;
5527 let scale = (ix.hd as f32).powf(-0.5);
5528 ck(
5529 "hadamard qi dev",
5530 k::memra_dsv4_hadamard(
5531 dpm!(ws.qi, &stream),
5532 ix.heads as i32,
5533 ix.hd as i32,
5534 scale,
5535 sp(&stream),
5536 ),
5537 )?;
5538 ck(
5539 "fp4 qi dev",
5540 k::memra_dsv4_fp4_act_quant(
5541 dpm!(ws.qi, &stream),
5542 ix.heads as i32,
5543 ix.hd as i64,
5544 ix.hd as i32,
5545 sp(&stream),
5546 ),
5547 )?;
5548 }
5549 {
5551 let StepWs {
5552 x,
5553 cmp_kv_row,
5554 cmp_sc_row,
5555 cmp_emit,
5556 cmp_shift,
5557 ..
5558 } = ws;
5559 self.cmp_decode_dev(
5560 st,
5561 &ix.cmp,
5562 x,
5563 pos,
5564 hidden,
5565 if layer.ratio != 0 {
5566 &st.fc_yarn
5567 } else {
5568 &st.fc_plain
5569 },
5570 rd,
5571 eps,
5572 cmp_kv_row,
5573 cmp_sc_row,
5574 cmp_emit,
5575 cmp_shift,
5576 ipend_kv.as_mut().expect("ipend"),
5577 ipend_score.as_mut().expect("ipend"),
5578 ikvc.as_mut().expect("ikvc"),
5579 0,
5580 i_blocks,
5581 )?;
5582 }
5583 let nb = *i_blocks;
5584 debug_assert_eq!(nb, (pos + 1) / layer.ratio, "indexer block count");
5585 unsafe {
5587 ck(
5588 "build_idx win",
5589 k::memra_dsv4_build_idx(
5590 ws.idx.device_ptr_mut(&stream).0 as *mut i32,
5591 pos as i32,
5592 win as i32,
5593 -1,
5594 win as i32,
5595 sp(&stream),
5596 ),
5597 )?;
5598 }
5599 if nb > 0 {
5600 Self::gemm_dev(
5601 st,
5602 ws.x.device_ptr(&stream).0 as *const f32,
5603 &mut ws.gemm_xb,
5604 dwsel(
5605 self.dense_fp8,
5606 &stream,
5607 &ix.weights_proj,
5608 &ix.weights_proj_fp8,
5609 ),
5610 1,
5611 ix.heads,
5612 hidden,
5613 ws.wproj.device_ptr_mut(&stream).0 as *mut f32,
5614 )?;
5615 let wscale = ((ix.hd as f64).powf(-0.5) * (ix.heads as f64).powf(-0.5)) as f32;
5616 unsafe {
5617 ck(
5618 "indexer_score dev",
5619 self.indexer_score_arm(
5620 dpf!(ws.qi, &stream),
5621 dpf!(ikvc.as_ref().expect("ikvc"), &stream),
5622 dpf!(ws.wproj, &stream),
5623 wscale,
5624 dpm!(ws.score, &stream),
5625 1,
5626 ix.heads as i32,
5627 ix.hd as i32,
5628 nb as i32,
5629 layer.ratio as i32,
5630 nb as i32,
5631 sp(&stream),
5632 ),
5633 )?;
5634 }
5635 let kk = ix.topk.min(nb);
5636 if host_math {
5637 let score_h = {
5640 let view = ws.score.slice(0..nb);
5641 let mut v = vec![0f32; nb];
5642 stream
5643 .memcpy_dtoh(&view, &mut v[..])
5644 .map_err(e("dtoh sc"))?;
5645 stream.synchronize().map_err(e("sync sc"))?;
5646 v
5647 };
5648 let mut order: Vec<usize> = (0..nb).collect();
5649 order.sort_by(|&a, &b| {
5650 score_h[b]
5651 .partial_cmp(&score_h[a])
5652 .unwrap_or(std::cmp::Ordering::Equal)
5653 .then(a.cmp(&b))
5654 });
5655 let cidx: Vec<i32> = order
5656 .into_iter()
5657 .take(kk)
5658 .map(|j| (j + win) as i32)
5659 .collect();
5660 let mut dst = ws.idx.slice_mut(win..win + kk);
5661 stream.memcpy_htod(&cidx, &mut dst).map_err(e("htod idx"))?;
5662 } else {
5663 unsafe {
5664 let idx_tail =
5665 (ws.idx.device_ptr_mut(&stream).0 as usize + win * 4) as *mut i32;
5666 ck(
5667 "topk_idx dev",
5668 k::memra_dsv4_topk_idx(
5669 dpf!(ws.score, &stream),
5670 nb as i32,
5671 kk as i32,
5672 win as i32,
5673 idx_tail,
5674 sp(&stream),
5675 ),
5676 )?;
5677 }
5678 }
5679 slots = win + kk;
5680 }
5681 } else {
5682 let nb = (pos + 1) / layer.ratio;
5685 unsafe {
5686 ck(
5687 "build_idx coarse",
5688 k::memra_dsv4_build_idx(
5689 ws.idx.device_ptr_mut(&stream).0 as *mut i32,
5690 pos as i32,
5691 win as i32,
5692 nb as i32,
5693 (win + nb) as i32,
5694 sp(&stream),
5695 ),
5696 )?;
5697 }
5698 slots = win + nb;
5699 }
5700 {
5702 let StepWs {
5703 x,
5704 cmp_kv_row,
5705 cmp_sc_row,
5706 cmp_emit,
5707 cmp_shift,
5708 ..
5709 } = ws;
5710 self.cmp_decode_dev(
5711 st,
5712 layer.cmp.as_ref().expect("ratio!=0 has compressor"),
5713 x,
5714 pos,
5715 hidden,
5716 &st.fc_yarn,
5717 rd,
5718 eps,
5719 cmp_kv_row,
5720 cmp_sc_row,
5721 cmp_emit,
5722 cmp_shift,
5723 pend_kv.as_mut().expect("pend"),
5724 pend_score.as_mut().expect("pend"),
5725 kvc,
5726 win,
5727 n_blocks,
5728 )?;
5729 }
5730 debug_assert_eq!(*n_blocks, (pos + 1) / layer.ratio, "attn block count");
5731 } else {
5732 unsafe {
5734 ck(
5735 "build_idx window-only",
5736 k::memra_dsv4_build_idx(
5737 ws.idx.device_ptr_mut(&stream).0 as *mut i32,
5738 pos as i32,
5739 win as i32,
5740 -1,
5741 win as i32,
5742 sp(&stream),
5743 ),
5744 )?;
5745 }
5746 }
5747
5748 let scale = (hd as f64).powf(-0.5) as f32;
5751 unsafe {
5752 ck(
5753 "sink_attn_dec dev",
5754 self.sink_attn_dec_arm(
5755 dpf!(ws.q, &stream),
5756 dpf!(kvc, &stream),
5757 ws.idx.device_ptr(&stream).0 as *const i32,
5758 dpf!(layer.sink, &stream),
5759 dpm!(ws.sink_scores, &stream),
5760 dpm!(ws.sink_evals, &stream),
5761 ws.sink_den.device_ptr_mut(&stream).0 as *mut f64,
5762 dpm!(ws.o, &stream),
5763 heads as i32,
5764 hd as i32,
5765 slots as i32,
5766 scale,
5767 sp(&stream),
5768 ),
5769 )?;
5770 ck(
5771 "rope_at o inv dev",
5772 k::memra_dsv4_rope_at(
5773 dpm!(ws.o, &stream),
5774 heads as i32,
5775 hd as i32,
5776 rd as i32,
5777 fc_dev,
5778 pos as i32,
5779 1,
5780 sp(&stream),
5781 ),
5782 )?;
5783 }
5784
5785 let gw = heads / o_groups * hd;
5789 unsafe {
5790 ck(
5791 "cvt o dev",
5792 k::memra_dsv4_cvt_bf16(
5793 dpf!(ws.o, &stream),
5794 ws.o_b.device_ptr_mut(&stream).0 as *mut c_void,
5795 (heads * hd) as i64,
5796 sp(&stream),
5797 ),
5798 )?;
5799 }
5800 let wo_a_dw = dwsel(self.dense_fp8, &stream, &layer.wo_a, &layer.wo_a_fp8);
5801 for g in 0..o_groups {
5802 Self::gemv_pre_dev(
5803 st,
5804 (ws.o_b.device_ptr(&stream).0 as usize + g * gw * 2) as *const c_void,
5805 wo_a_dw.offset_rows(g * o_lora, gw),
5806 o_lora,
5807 gw,
5808 (ws.og.device_ptr_mut(&stream).0 as usize + g * o_lora * 4) as *mut f32,
5809 )?;
5810 }
5811 Self::gemm_dev(
5812 st,
5813 ws.og.device_ptr(&stream).0 as *const f32,
5814 &mut ws.gemm_xb,
5815 dwsel(self.dense_fp8, &stream, &layer.wo_b, &layer.wo_b_fp8),
5816 1,
5817 hidden,
5818 o_groups * o_lora,
5819 ws.attn_out.device_ptr_mut(&stream).0 as *mut f32,
5820 )?;
5821
5822 {
5824 let StepWs {
5825 h_a,
5826 h_b,
5827 h_rx,
5828 attn_out,
5829 post,
5830 comb,
5831 ..
5832 } = ws;
5833 let h_in: &CudaSlice<f32> = if input_rx { h_rx } else { h_a };
5834 unsafe {
5835 ck(
5836 "hc_post attn dev",
5837 k::memra_dsv4_hc_post(
5838 dpf!(attn_out, &stream),
5839 dpf!(h_in, &stream),
5840 dpf!(post, &stream),
5841 dpf!(comb, &stream),
5842 dpm!(*h_b, &stream),
5843 1,
5844 hc as i32,
5845 hidden as i32,
5846 sp(&stream),
5847 ),
5848 )?;
5849 }
5850 }
5851
5852 {
5854 let StepWs {
5855 h_b,
5856 mixes,
5857 pre,
5858 post,
5859 comb,
5860 y_hc,
5861 ..
5862 } = ws;
5863 self.hc_pre_dev(
5864 st,
5865 h_b,
5866 &layer.hc_ffn_fn,
5867 &layer.hc_ffn_base,
5868 &layer.hc_ffn_scale,
5869 &layer.hc_ffn_base_dev,
5870 &layer.hc_ffn_scale_dev,
5871 mixes,
5872 pre,
5873 post,
5874 comb,
5875 y_hc,
5876 hc,
5877 hidden,
5878 iters,
5879 hc_eps,
5880 host_math,
5881 )?;
5882 }
5883 unsafe {
5884 ck(
5885 "rmsnorm ffn dev",
5886 self.rmsnorm_arm(
5887 dpf!(ws.y_hc, &stream),
5888 dpf!(layer.ffn_norm, &stream),
5889 dpm!(ws.xf, &stream),
5890 1,
5891 hidden as i32,
5892 eps,
5893 sp(&stream),
5894 ),
5895 )?;
5896 }
5897 self.moe_forward_dev(st, layer, ws, tok, host_math)?;
5898 {
5899 let StepWs {
5900 h_a,
5901 h_b,
5902 y,
5903 post,
5904 comb,
5905 ..
5906 } = ws;
5907 unsafe {
5908 ck(
5909 "hc_post ffn dev",
5910 k::memra_dsv4_hc_post(
5911 dpf!(y, &stream),
5912 dpf!(h_b, &stream),
5913 dpf!(post, &stream),
5914 dpf!(comb, &stream),
5915 dpm!(*h_a, &stream),
5916 1,
5917 hc as i32,
5918 hidden as i32,
5919 sp(&stream),
5920 ),
5921 )?;
5922 }
5923 }
5924 Ok(())
5925 }
5926
5927 fn moe_forward_dev(
5933 &self,
5934 st: &Stage,
5935 layer: &LayerDev,
5936 ws: &mut StepWs,
5937 tok: u32,
5938 host_math: bool,
5939 ) -> Res<()> {
5940 let mc = &self.model.mc;
5941 let d = self.model.cfg();
5942 let moe = mc.moe.as_ref().expect("moe");
5943 let hidden = mc.n_embd as usize;
5944 let ne = moe.expert_count as usize;
5945 let topk = moe.expert_used_count as usize;
5946 let inter = moe.expert_ff_length as usize;
5947 let limit = d.swiglu_limit;
5948 let stream = st.gpu.stream();
5949 let kind = match layer.expert_kind {
5950 ExpertKind::Nvfp4 => 0i32,
5951 ExpertKind::Mxfp4 => 1i32,
5952 };
5953 let wstride = (inter * hidden / 2) as i64;
5954 let sstride = match layer.expert_kind {
5955 ExpertKind::Nvfp4 => (inter * hidden / 16) as i64,
5956 ExpertKind::Mxfp4 => (inter * hidden / 32) as i64,
5957 };
5958
5959 self.dots_dev(st, &ws.xf, &layer.gate_w, 1, hidden, ne, &mut ws.raw)?;
5960 if host_math {
5961 let raw_h = dtoh_f32(&stream, &ws.raw)?;
5962 let (indices, weights) =
5963 Self::route_host(layer, &raw_h, &[tok], 1, ne, topk, d.routed_scaling_factor);
5964 let sel: Vec<i32> = indices.iter().map(|&x| x as i32).collect();
5965 let mut order: Vec<i32> = (0..topk as i32).collect();
5966 order.sort_by_key(|&s| indices[s as usize]);
5967 stream
5968 .memcpy_htod(&sel, &mut ws.sel)
5969 .map_err(e("htod sel"))?;
5970 stream
5971 .memcpy_htod(&weights, &mut ws.selw)
5972 .map_err(e("htod selw"))?;
5973 stream
5974 .memcpy_htod(&order, &mut ws.order)
5975 .map_err(e("htod order"))?;
5976 } else {
5977 unsafe {
5978 ck(
5979 "route dev",
5980 k::memra_dsv4_route(
5981 dpf!(ws.raw, &stream),
5982 layer
5983 .gate_bias_dev
5984 .as_ref()
5985 .map(|b| b.device_ptr(&stream).0 as *const f32)
5986 .unwrap_or(std::ptr::null()),
5987 layer
5988 .tid2eid_dev
5989 .as_ref()
5990 .map(|t| t.device_ptr(&stream).0 as *const i32)
5991 .unwrap_or(std::ptr::null()),
5992 ws.tok.device_ptr(&stream).0 as *const i32,
5993 ne as i32,
5994 topk as i32,
5995 d.routed_scaling_factor,
5996 ws.sel.device_ptr_mut(&stream).0 as *mut i32,
5997 ws.selw.device_ptr_mut(&stream).0 as *mut f32,
5998 ws.order.device_ptr_mut(&stream).0 as *mut i32,
5999 sp(&stream),
6000 ),
6001 )?;
6002 }
6003 }
6004
6005 unsafe {
6006 ck(
6007 "act_quant_fp8 x dev",
6008 k::memra_dsv4_act_quant_fp8(
6009 dpf!(ws.xf, &stream),
6010 ws.xq.device_ptr_mut(&stream).0 as *mut c_void,
6011 dpm!(ws.xs, &stream),
6012 1,
6013 hidden as i32,
6014 sp(&stream),
6015 ),
6016 )?;
6017 for (proj, dst) in [(0i32, &mut ws.g1), (2i32, &mut ws.g3)] {
6018 ck(
6019 "fp4_gemm_sel w1/w3",
6020 k::memra_dsv4_fp4_gemm_sel(
6021 dp!(ws.xq, &stream),
6022 dpf!(ws.xs, &stream),
6023 dp!(layer.experts_w, &stream),
6024 dp!(layer.experts_sc, &stream),
6025 dpf!(layer.experts_s2_dev, &stream),
6026 ws.sel.device_ptr(&stream).0 as *const i32,
6027 proj,
6028 0,
6029 kind,
6030 dpm!(*dst, &stream),
6031 topk as i32,
6032 inter as i32,
6033 hidden as i32,
6034 wstride,
6035 sstride,
6036 sp(&stream),
6037 ),
6038 )?;
6039 }
6040 ck(
6041 "swiglu dev",
6042 k::memra_dsv4_swiglu(
6043 dpf!(ws.g1, &stream),
6044 dpf!(ws.g3, &stream),
6045 dpm!(ws.hbuf, &stream),
6046 topk as i32,
6047 inter as i32,
6048 limit,
6049 ws.selw.device_ptr(&stream).0 as *const f32,
6050 sp(&stream),
6051 ),
6052 )?;
6053 ck(
6054 "act_quant_fp8 h dev",
6055 k::memra_dsv4_act_quant_fp8(
6056 dpf!(ws.hbuf, &stream),
6057 ws.hq.device_ptr_mut(&stream).0 as *mut c_void,
6058 dpm!(ws.hs, &stream),
6059 topk as i32,
6060 inter as i32,
6061 sp(&stream),
6062 ),
6063 )?;
6064 ck(
6065 "fp4_gemm_sel w2",
6066 k::memra_dsv4_fp4_gemm_sel(
6067 dp!(ws.hq, &stream),
6068 dpf!(ws.hs, &stream),
6069 dp!(layer.experts_w, &stream),
6070 dp!(layer.experts_sc, &stream),
6071 dpf!(layer.experts_s2_dev, &stream),
6072 ws.sel.device_ptr(&stream).0 as *const i32,
6073 1,
6074 1,
6075 kind,
6076 dpm!(ws.contrib, &stream),
6077 topk as i32,
6078 hidden as i32,
6079 inter as i32,
6080 wstride,
6081 sstride,
6082 sp(&stream),
6083 ),
6084 )?;
6085 ck(
6086 "combine dev",
6087 k::memra_dsv4_combine_rows(
6088 dpf!(ws.contrib, &stream),
6089 ws.order.device_ptr(&stream).0 as *const i32,
6090 topk as i32,
6091 dpm!(ws.y, &stream),
6092 hidden as i64,
6093 sp(&stream),
6094 ),
6095 )?;
6096 ck(
6098 "cvt xb dev",
6099 k::memra_dsv4_cvt_bf16(
6100 dpf!(ws.xf, &stream),
6101 ws.xb.device_ptr_mut(&stream).0 as *mut c_void,
6102 hidden as i64,
6103 sp(&stream),
6104 ),
6105 )?;
6106 }
6107 let sh_inter = ws.sg1.len();
6108 Self::gemv_pre_dev(
6109 st,
6110 ws.xb.device_ptr(&stream).0 as *const c_void,
6111 dwsel(
6112 self.dense_fp8,
6113 &stream,
6114 &layer.shared_w[0],
6115 &layer.shared_fp8[0],
6116 ),
6117 sh_inter,
6118 hidden,
6119 ws.sg1.device_ptr_mut(&stream).0 as *mut f32,
6120 )?;
6121 Self::gemv_pre_dev(
6122 st,
6123 ws.xb.device_ptr(&stream).0 as *const c_void,
6124 dwsel(
6125 self.dense_fp8,
6126 &stream,
6127 &layer.shared_w[2],
6128 &layer.shared_fp8[2],
6129 ),
6130 sh_inter,
6131 hidden,
6132 ws.sg3.device_ptr_mut(&stream).0 as *mut f32,
6133 )?;
6134 unsafe {
6135 ck(
6136 "swiglu sh dev",
6137 k::memra_dsv4_swiglu(
6138 dpf!(ws.sg1, &stream),
6139 dpf!(ws.sg3, &stream),
6140 dpm!(ws.shbuf, &stream),
6141 1,
6142 sh_inter as i32,
6143 limit,
6144 std::ptr::null(),
6145 sp(&stream),
6146 ),
6147 )?;
6148 ck(
6149 "cvt sh dev",
6150 k::memra_dsv4_cvt_bf16(
6151 dpf!(ws.shbuf, &stream),
6152 ws.shb16.device_ptr_mut(&stream).0 as *mut c_void,
6153 sh_inter as i64,
6154 sp(&stream),
6155 ),
6156 )?;
6157 }
6158 Self::gemv_pre_dev(
6159 st,
6160 ws.shb16.device_ptr(&stream).0 as *const c_void,
6161 dwsel(
6162 self.dense_fp8,
6163 &stream,
6164 &layer.shared_w[1],
6165 &layer.shared_fp8[1],
6166 ),
6167 hidden,
6168 sh_inter,
6169 ws.sh_out.device_ptr_mut(&stream).0 as *mut f32,
6170 )?;
6171 unsafe {
6172 ck(
6173 "add shared dev",
6174 k::memra_dsv4_add_inplace(
6175 dpm!(ws.y, &stream),
6176 dpf!(ws.sh_out, &stream),
6177 hidden as i64,
6178 sp(&stream),
6179 ),
6180 )?;
6181 }
6182 Ok(())
6183 }
6184
6185 fn head_logits_dev(&self, ws: &mut StepWs, host_math: bool) -> Res<()> {
6189 let d = self.model.cfg();
6190 let mc = &self.model.mc;
6191 let hc = d.hc_mult as usize;
6192 let hidden = mc.n_embd as usize;
6193 let eps = mc.rms_eps;
6194 let last = self.stages.len() - 1;
6195 let st = &self.stages[last];
6196 let stream = st.gpu.stream();
6197 let w = hc * hidden;
6198 let fn_w = st.hc_head_fn.as_ref().expect("hc_head_fn");
6199 let norm = st.trunk_norm.as_ref().expect("trunk norm");
6200 self.dots_dev(st, &ws.h_a, fn_w, 1, w, hc, &mut ws.head_mixes)?;
6201 unsafe {
6202 ck(
6203 "rowsq head dev",
6204 self.rowsq_scale_arm(
6205 dpf!(ws.h_a, &stream),
6206 dpm!(ws.head_mixes, &stream),
6207 1,
6208 w as i32,
6209 hc as i32,
6210 eps,
6211 sp(&stream),
6212 ),
6213 )?;
6214 }
6215 if host_math {
6216 let mut mixes_h = dtoh_f32(&stream, &ws.head_mixes)?;
6217 for c in 0..hc {
6218 let m = mixes_h[c];
6219 mixes_h[c] =
6220 sigmoid_f32(m * self.hc_head_scale[0] + self.hc_head_base[c]) + d.hc_eps;
6221 }
6222 stream
6223 .memcpy_htod(&mixes_h, &mut ws.head_pre)
6224 .map_err(e("htod head pre"))?;
6225 } else {
6226 unsafe {
6227 ck(
6228 "hc_head_pre dev",
6229 k::memra_dsv4_hc_head_pre(
6230 dpf!(ws.head_mixes, &stream),
6231 st.hc_head_scale_dev
6232 .as_ref()
6233 .expect("head scale dev")
6234 .device_ptr(&stream)
6235 .0 as *const f32,
6236 st.hc_head_base_dev
6237 .as_ref()
6238 .expect("head base dev")
6239 .device_ptr(&stream)
6240 .0 as *const f32,
6241 dpm!(ws.head_pre, &stream),
6242 hc as i32,
6243 d.hc_eps,
6244 sp(&stream),
6245 ),
6246 )?;
6247 }
6248 }
6249 unsafe {
6250 ck(
6251 "hc_collapse head dev",
6252 k::memra_dsv4_hc_collapse(
6253 dpf!(ws.h_a, &stream),
6254 dpf!(ws.head_pre, &stream),
6255 dpm!(ws.collapsed, &stream),
6256 1,
6257 hc as i32,
6258 hidden as i32,
6259 sp(&stream),
6260 ),
6261 )?;
6262 ck(
6263 "rmsnorm head dev",
6264 self.rmsnorm_arm(
6265 dpf!(ws.collapsed, &stream),
6266 dpf!(norm, &stream),
6267 dpm!(ws.collapsed, &stream),
6268 1,
6269 hidden as i32,
6270 eps,
6271 sp(&stream),
6272 ),
6273 )?;
6274 let head_ptr = st.head.as_ref().expect("head").device_ptr(&stream).0 as *const c_void;
6275 if self.dots_f32 {
6276 ck(
6277 "head dots f32acc dev",
6278 k::memra_dsv4_dots_f32acc(
6279 dpf!(ws.collapsed, &stream),
6280 head_ptr,
6281 1,
6282 dpm!(ws.logits, &stream),
6283 1,
6284 hidden as i32,
6285 ws.logits.len() as i32,
6286 sp(&stream),
6287 ),
6288 )?;
6289 } else {
6290 ck(
6291 "head dots dev",
6292 k::memra_dsv4_dots_f32(
6293 dpf!(ws.collapsed, &stream),
6294 head_ptr,
6295 1,
6296 dpm!(ws.logits, &stream),
6297 1,
6298 hidden as i32,
6299 ws.logits.len() as i32,
6300 sp(&stream),
6301 ),
6302 )?;
6303 }
6304 }
6305 Ok(())
6306 }
6307
6308 fn decode_step_fast(
6312 &self,
6313 tok: u32,
6314 state: &mut DecodeState,
6315 want_logits: bool,
6316 host_math: bool,
6317 ) -> Res<(Option<Vec<f32>>, u32)> {
6318 self.decode_step_fast_tap(tok, state, want_logits, host_math, None)
6319 }
6320
6321 fn decode_step_fast_tap(
6327 &self,
6328 tok: u32,
6329 state: &mut DecodeState,
6330 want_logits: bool,
6331 host_math: bool,
6332 mut taps: Option<(&mut CudaSlice<f32>, usize)>,
6333 ) -> Res<(Option<Vec<f32>>, u32)> {
6334 let mc = &self.model.mc;
6335 let d = self.model.cfg();
6336 let pos = state.pos;
6337 assert!(pos > 0, "decode_step needs prefill_with_cache first");
6338 assert!(pos < self.max_seq, "pos {pos} >= max_seq {}", self.max_seq);
6339 let hidden = mc.n_embd as usize;
6340 let hc = d.hc_mult as usize;
6341 let n_trunk = mc.n_layer - mc.nextn_predict_layers;
6342 let ws_all = state.ws.as_mut().expect("device path needs StepWs");
6343
6344 let st0 = &self.stages[0];
6346 st0.gpu.ctx.bind_to_thread().map_err(e("bind ctx0"))?;
6347 let stream0 = st0.gpu.stream();
6348 {
6349 let ws0 = &mut ws_all[0];
6350 stream0
6351 .memcpy_htod(&[tok as i32], &mut ws0.tok)
6352 .map_err(e("htod tok"))?;
6353 unsafe {
6354 ck(
6355 "embed_rows dev",
6356 k::memra_dsv4_embed_rows(
6357 st0.embed
6358 .as_ref()
6359 .expect("embed on stage 0")
6360 .device_ptr(&stream0)
6361 .0 as *const c_void,
6362 ws0.tok.device_ptr(&stream0).0 as *const i32,
6363 dpm!(ws0.emb, &stream0),
6364 1,
6365 hidden as i32,
6366 sp(&stream0),
6367 ),
6368 )?;
6369 ck(
6370 "repeat_hc dev",
6371 k::memra_dsv4_repeat_hc(
6372 dpf!(ws0.emb, &stream0),
6373 dpm!(ws0.h_a, &stream0),
6374 1,
6375 hc as i32,
6376 hidden as i32,
6377 sp(&stream0),
6378 ),
6379 )?;
6380 }
6381 }
6382
6383 let mut cur_stage = 0usize;
6384 let mut input_rx = false;
6385 for il in 0..n_trunk {
6386 let stage = self.layer_stage[il as usize];
6387 if stage != cur_stage {
6388 let bytes = hc * hidden * std::mem::size_of::<f32>();
6391 let src_stream = self.stages[cur_stage].gpu.stream();
6392 let dst_stream = self.stages[stage].gpu.stream();
6393 let (ws_src, ws_dst) = ws_all.split_at_mut(stage);
6394 let src_ws = &ws_src[cur_stage];
6395 let dst_ws = &mut ws_dst[0];
6396 self.stages[cur_stage]
6397 .gpu
6398 .ctx
6399 .bind_to_thread()
6400 .map_err(e("bind tx"))?;
6401 let (sp_, _g0) = src_ws.h_a.device_ptr(&src_stream);
6402 let (dp_, _g1) = dst_ws.h_rx.device_ptr_mut(&src_stream);
6403 unsafe {
6404 cudarc::driver::result::memcpy_peer_async(
6405 self.stages[stage].gpu.ctx.cu_ctx(),
6406 dp_,
6407 self.stages[cur_stage].gpu.ctx.cu_ctx(),
6408 sp_,
6409 bytes,
6410 src_stream.cu_stream(),
6411 )
6412 .map_err(e("peer copy h"))?;
6413 }
6414 let bnd = stage - 1;
6415 self.boundary_ev[bnd]
6416 .record(&src_stream)
6417 .map_err(e("ev record"))?;
6418 dst_stream
6419 .wait(&self.boundary_ev[bnd])
6420 .map_err(e("ev wait"))?;
6421 self.stages[stage]
6422 .gpu
6423 .ctx
6424 .bind_to_thread()
6425 .map_err(e("bind rx"))?;
6426 cur_stage = stage;
6427 input_rx = true;
6428 }
6429 let st = &self.stages[stage];
6430 let lidx = st
6431 .layers
6432 .iter()
6433 .position(|l| l.il == il)
6434 .unwrap_or_else(|| panic!("layer {il} not on stage {stage}"));
6435 self.block_decode_dev(
6436 st,
6437 &st.layers[lidx],
6438 &mut state.caches[il as usize],
6439 &mut ws_all[stage],
6440 input_rx,
6441 pos,
6442 tok,
6443 host_math,
6444 )?;
6445 input_rx = false;
6446 if let Some((t, base)) = taps.as_mut() {
6449 if let Some(ds) = &self.dspark {
6450 if let Some(k) = ds.targets.iter().position(|&tl| tl == il as usize) {
6451 let stream = self.stages[stage].gpu.stream();
6452 let hidden_i = hidden as i32;
6453 unsafe {
6454 ck(
6455 "hc_mean tap dev",
6456 k::memra_dsv4_hc_mean(
6457 dpf!(ws_all[stage].h_a, &stream),
6458 (t.device_ptr_mut(&stream).0 as usize
6459 + (*base + k * hidden) * 4)
6460 as *mut f32,
6461 1,
6462 hc as i32,
6463 hidden_i,
6464 sp(&stream),
6465 ),
6466 )?;
6467 }
6468 }
6469 }
6470 }
6471 }
6472
6473 let last = self.stages.len() - 1;
6474 assert_eq!(cur_stage, last, "device path expects the head stage last");
6475 self.head_logits_dev(&mut ws_all[last], host_math)?;
6476 let stream_last = self.stages[last].gpu.stream();
6477 state.pos += 1;
6478 if want_logits {
6479 let logits = dtoh_f32(&stream_last, &ws_all[last].logits)?;
6480 let mut best = 0usize;
6481 for i in 1..logits.len() {
6482 if logits[i] > logits[best] {
6483 best = i;
6484 }
6485 }
6486 Ok((Some(logits), best as u32))
6487 } else {
6488 unsafe {
6489 ck(
6490 "argmax dev",
6491 k::memra_dsv4_argmax(
6492 dpf!(ws_all[last].logits, &stream_last),
6493 ws_all[last].logits.len() as i64,
6494 ws_all[last].argmax.device_ptr_mut(&stream_last).0 as *mut i32,
6495 sp(&stream_last),
6496 ),
6497 )?;
6498 }
6499 let mut out = [0i32; 1];
6500 stream_last
6501 .memcpy_dtoh(&ws_all[last].argmax, &mut out[..])
6502 .map_err(e("dtoh argmax"))?;
6503 stream_last.synchronize().map_err(e("sync argmax"))?;
6504 Ok((None, out[0] as u32))
6505 }
6506 }
6507
6508 pub fn decode_step_greedy(&self, tok: u32, state: &mut DecodeState) -> Res<u32> {
6513 match self.decode_path {
6514 DecodePath::Legacy => {
6515 let logits = self.decode_step_impl(tok, state, None)?;
6516 let mut best = 0usize;
6517 for i in 1..logits.len() {
6518 if logits[i] > logits[best] {
6519 best = i;
6520 }
6521 }
6522 Ok(best as u32)
6523 }
6524 DecodePath::Device { host_math } => {
6525 Ok(self.decode_step_fast(tok, state, false, host_math)?.1)
6526 }
6527 }
6528 }
6529
6530 fn decode_step_impl(
6531 &self,
6532 tok: u32,
6533 state: &mut DecodeState,
6534 mut dump: Option<&mut Vec<(String, Vec<f32>)>>,
6535 ) -> Res<Vec<f32>> {
6536 if let DecodePath::Device { host_math } = self.decode_path {
6537 assert!(
6538 dump.is_none(),
6539 "decode_step_probe is a legacy-path diagnostic (set MEMRA_DSV4_DECODE_PATH=legacy)"
6540 );
6541 let (logits, _) = self.decode_step_fast(tok, state, true, host_math)?;
6542 return Ok(logits.expect("want_logits"));
6543 }
6544 let mc = &self.model.mc;
6545 let d = self.model.cfg();
6546 let pos = state.pos;
6547 assert!(pos > 0, "decode_step needs prefill_with_cache first");
6548 assert!(pos < self.max_seq, "pos {pos} >= max_seq {}", self.max_seq);
6549 let hidden = mc.n_embd as usize;
6550 let hc = d.hc_mult as usize;
6551 let n_trunk = mc.n_layer - mc.nextn_predict_layers;
6552
6553 let st0 = &self.stages[0];
6555 st0.gpu.ctx.bind_to_thread().map_err(e("bind ctx0"))?;
6556 let stream0 = st0.gpu.stream();
6557 let ids_dev = upload_i32(&stream0, &[tok as i32])?;
6558 let mut emb = stream0.alloc_zeros::<f32>(hidden).map_err(e("emb"))?;
6559 unsafe {
6560 ck(
6561 "embed_rows",
6562 k::memra_dsv4_embed_rows(
6563 st0.embed
6564 .as_ref()
6565 .expect("embed on stage 0")
6566 .device_ptr(&stream0)
6567 .0 as *const c_void,
6568 ids_dev.device_ptr(&stream0).0 as *const i32,
6569 dpm!(emb, &stream0),
6570 1,
6571 hidden as i32,
6572 sp(&stream0),
6573 ),
6574 )?;
6575 }
6576 let mut h = stream0.alloc_zeros::<f32>(hc * hidden).map_err(e("h0"))?;
6577 unsafe {
6578 ck(
6579 "repeat_hc",
6580 k::memra_dsv4_repeat_hc(
6581 dpf!(emb, &stream0),
6582 dpm!(h, &stream0),
6583 1,
6584 hc as i32,
6585 hidden as i32,
6586 sp(&stream0),
6587 ),
6588 )?;
6589 }
6590
6591 let mut cur_stage = 0usize;
6592 for il in 0..n_trunk {
6593 let stage = self.layer_stage[il as usize];
6594 if stage != cur_stage {
6595 let src_stream = self.stages[cur_stage].gpu.stream();
6596 let host = dtoh_f32(&src_stream, &h)?;
6597 let dst_stream = self.stages[stage].gpu.stream();
6598 self.stages[stage]
6599 .gpu
6600 .ctx
6601 .bind_to_thread()
6602 .map_err(e("bind"))?;
6603 h = upload_f32(&dst_stream, &host)?;
6604 cur_stage = stage;
6605 }
6606 let st = &self.stages[stage];
6607 let lidx = st
6608 .layers
6609 .iter()
6610 .position(|l| l.il == il)
6611 .unwrap_or_else(|| panic!("layer {il} not on stage {stage}"));
6612 h = self.block_decode(
6613 st,
6614 &st.layers[lidx],
6615 &mut state.caches[il as usize],
6616 &h,
6617 pos,
6618 tok,
6619 dump.as_deref_mut(),
6620 )?;
6621 }
6622
6623 let last = self.stages.len() - 1;
6624 if cur_stage != last {
6625 let src_stream = self.stages[cur_stage].gpu.stream();
6626 let host = dtoh_f32(&src_stream, &h)?;
6627 let dst_stream = self.stages[last].gpu.stream();
6628 h = upload_f32(&dst_stream, &host)?;
6629 }
6630 let hc_head_fn = self.stages[last].hc_head_fn.as_ref().expect("hc_head_fn");
6631 let trunk_norm = self.stages[last].trunk_norm.as_ref().expect("trunk norm");
6632 let logits = self.head_logits_from(
6633 &h,
6634 1,
6635 hc_head_fn,
6636 &self.hc_head_base,
6637 &self.hc_head_scale,
6638 trunk_norm,
6639 )?;
6640 state.pos += 1;
6641 Ok(logits)
6642 }
6643}
6644
6645impl Dsv4Gpu {
6654 fn dspark(&self) -> &DsparkDev {
6655 self.dspark
6656 .as_ref()
6657 .expect("MEMRA_DSV4_DRAFTER=dspark not loaded")
6658 }
6659
6660 #[allow(clippy::too_many_arguments)]
6668 fn dspark_head_dots(
6669 &self,
6670 st: &Stage,
6671 x: *const f32,
6672 w: *const c_void,
6673 w_is_bf16: i32,
6674 s: usize,
6675 kdim: usize,
6676 n: usize,
6677 y: *mut f32,
6678 ) -> Res<()> {
6679 let stream = st.gpu.stream();
6680 unsafe {
6681 if self.dspark_head_f32 {
6682 ck(
6683 "dspark head dots f32acc_mrow",
6684 k::memra_dsv4_dots_f32acc_mrow(
6685 x,
6686 w,
6687 w_is_bf16,
6688 y,
6689 s as i32,
6690 kdim as i32,
6691 n as i32,
6692 sp(&stream),
6693 ),
6694 )
6695 } else {
6696 ck(
6697 "dspark head dots f32_mrow",
6698 k::memra_dsv4_dots_f32_mrow(
6699 x,
6700 w,
6701 w_is_bf16,
6702 y,
6703 s as i32,
6704 kdim as i32,
6705 n as i32,
6706 sp(&stream),
6707 ),
6708 )
6709 }
6710 }
6711 }
6712
6713 pub fn dspark_alloc_state(&self) -> Res<DsparkState> {
6716 let ds = self.dspark();
6717 let d = self.model.cfg();
6718 let hd = d.head_dim as usize;
6719 let win = d.sliding_window as usize;
6720 let hidden = self.model.mc.n_embd as usize;
6721 let last = self.stages.len() - 1;
6722 let stream = self.stages[last].gpu.stream();
6723 let mut rings = Vec::with_capacity(ds.blocks.len());
6724 for _ in 0..ds.blocks.len() {
6725 rings.push(
6726 stream
6727 .alloc_zeros::<f32>((win + ds.block_size) * hd)
6728 .map_err(e("dspark ring"))?,
6729 );
6730 }
6731 let taps = stream
6732 .alloc_zeros::<f32>((ds.block_size + 1) * ds.targets.len() * hidden)
6733 .map_err(e("dspark taps"))?;
6734 Ok(DsparkState { rings, taps })
6735 }
6736
6737 fn dspark_main_x(&self, main_hidden: &CudaSlice<f32>, s: usize) -> Res<CudaSlice<f32>> {
6739 let ds = self.dspark();
6740 let hidden = self.model.mc.n_embd as usize;
6741 let k = ds.targets.len() * hidden;
6742 let last = self.stages.len() - 1;
6743 let st = &self.stages[last];
6744 let stream = st.gpu.stream();
6745 let mut mx = stream.alloc_zeros::<f32>(s * hidden).map_err(e("main_x"))?;
6746 Self::gemm(st, main_hidden, &ds.main_proj, 0, s, hidden, k, &mut mx)?;
6747 unsafe {
6748 ck(
6749 "rmsnorm main_x",
6750 k::memra_dsv4_rmsnorm(
6751 dpf!(mx, &stream),
6752 dpf!(ds.main_norm, &stream),
6753 dpm!(mx, &stream),
6754 s as i32,
6755 hidden as i32,
6756 self.model.mc.rms_eps,
6757 sp(&stream),
6758 ),
6759 )?;
6760 }
6761 Ok(mx)
6762 }
6763
6764 fn dspark_main_kv(
6767 &self,
6768 blk: &LayerDev,
6769 main_x: &CudaSlice<f32>,
6770 s: usize,
6771 positions: &[i32],
6772 ) -> Res<CudaSlice<f32>> {
6773 let d = self.model.cfg();
6774 let hd = d.head_dim as usize;
6775 let rd = d.qk_rope_head_dim as usize;
6776 let hidden = self.model.mc.n_embd as usize;
6777 let eps = self.model.mc.rms_eps;
6778 let last = self.stages.len() - 1;
6779 let st = &self.stages[last];
6780 let stream = st.gpu.stream();
6781 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
6782 let mut kv = stream.alloc_zeros::<f32>(s * hd).map_err(e("dspark kv"))?;
6786 Self::gemm(st, main_x, blk.wkv.dev(), 0, s, hd, hidden, &mut kv)?;
6787 let pos_dev = upload_i32(&stream, positions)?;
6788 unsafe {
6789 ck(
6790 "rmsnorm dspark kv",
6791 k::memra_dsv4_rmsnorm(
6792 dpf!(kv, &stream),
6793 dpf!(blk.kv_norm, &stream),
6794 dpm!(kv, &stream),
6795 s as i32,
6796 hd as i32,
6797 eps,
6798 sp(&stream),
6799 ),
6800 )?;
6801 ck(
6802 "rope dspark kv",
6803 k::memra_dsv4_rope(
6804 dpm!(kv, &stream),
6805 s as i32,
6806 1,
6807 hd as i32,
6808 rd as i32,
6809 dpf!(st.fc_plain, &stream),
6810 pos_dev.device_ptr(&stream).0 as *const i32,
6811 0,
6812 sp(&stream),
6813 ),
6814 )?;
6815 ck(
6816 "act_quant dspark kv",
6817 k::memra_dsv4_act_quant(
6818 dpm!(kv, &stream),
6819 s as i32,
6820 hd as i64,
6821 (hd - rd) as i32,
6822 64,
6823 clamp_only,
6824 sp(&stream),
6825 ),
6826 )?;
6827 }
6828 Ok(kv)
6829 }
6830
6831 pub fn dspark_prime_prefill(
6834 &self,
6835 state: &mut DsparkState,
6836 main_hidden: &CudaSlice<f32>,
6837 s: usize,
6838 ) -> Res<()> {
6839 let d = self.model.cfg();
6840 let hd = d.head_dim as usize;
6841 let win = d.sliding_window as usize;
6842 let last = self.stages.len() - 1;
6843 let stream = self.stages[last].gpu.stream();
6844 let mx = self.dspark_main_x(main_hidden, s)?;
6845 let positions: Vec<i32> = (0..s as i32).collect();
6846 let n_blocks = self.dspark().blocks.len();
6847 for bi in 0..n_blocks {
6848 let blk = &self.dspark().blocks[bi];
6849 let kv = self.dspark_main_kv(blk, &mx, s, &positions)?;
6850 for p in s.saturating_sub(win)..s {
6851 let slot = p % win;
6852 let src = kv.slice(p * hd..(p + 1) * hd);
6853 let mut dst = state.rings[bi].slice_mut(slot * hd..(slot + 1) * hd);
6854 stream
6855 .memcpy_dtod(&src, &mut dst)
6856 .map_err(e("prime ring"))?;
6857 }
6858 }
6859 Ok(())
6860 }
6861
6862 pub fn dspark_prefill_prime(
6873 &self,
6874 ids: &[u32],
6875 state: &mut DecodeState,
6876 dstate: &mut DsparkState,
6877 ) -> Res<ForwardOut> {
6878 assert_eq!(
6879 state.pos, 0,
6880 "dspark_prefill_prime needs a fresh DecodeState"
6881 );
6882 assert!(!ids.is_empty(), "empty prompt");
6883 let hidden = self.model.mc.n_embd as usize;
6884 let hc = self.model.cfg().hc_mult as usize;
6885 let s = ids.len();
6886 let targets = self.dspark().targets.clone();
6887 let n_t = targets.len();
6888 let mut cap = GpuCapture {
6889 want: targets.iter().map(|&t| t as u32).collect(),
6890 ..Default::default()
6891 };
6892 let out = self
6893 .forward_impl(ids, Some(&mut cap), None, Some(state))?
6894 .expect("prefill logits");
6895 state.pos = s;
6896
6897 let last = self.stages.len() - 1;
6898 let stream = self.stages[last].gpu.stream();
6899 self.stages[last]
6900 .gpu
6901 .ctx
6902 .bind_to_thread()
6903 .map_err(e("bind ctx prime"))?;
6904 let mut main_hidden = stream
6905 .alloc_zeros::<f32>(s * n_t * hidden)
6906 .map_err(e("prefill main_hidden"))?;
6907 let mut tmp = stream
6908 .alloc_zeros::<f32>(s * hidden)
6909 .map_err(e("tap tmp"))?;
6910 for (k, &il) in targets.iter().enumerate() {
6911 let h = cap
6912 .layer_out
6913 .get(&(il as u32))
6914 .unwrap_or_else(|| panic!("prefill capture missing target layer {il}"));
6915 assert_eq!(
6916 h.len(),
6917 s * hc * hidden,
6918 "target layer {il} capture is not [s, hc, hidden]"
6919 );
6920 let h_dev = upload_f32(&stream, h)?;
6921 unsafe {
6922 ck(
6923 "hc_mean prefill tap",
6924 k::memra_dsv4_hc_mean(
6925 dpf!(h_dev, &stream),
6926 dpm!(tmp, &stream),
6927 s as i32,
6928 hc as i32,
6929 hidden as i32,
6930 sp(&stream),
6931 ),
6932 )?;
6933 ck(
6934 "place_cols prefill tap",
6935 k::memra_dsv4_place_cols(
6936 dpf!(tmp, &stream),
6937 dpm!(main_hidden, &stream),
6938 s as i32,
6939 hidden as i32,
6940 (n_t * hidden) as i64,
6941 (k * hidden) as i64,
6942 sp(&stream),
6943 ),
6944 )?;
6945 }
6946 }
6947 self.dspark_prime_prefill(dstate, &main_hidden, s)?;
6948 {
6953 let src = main_hidden.slice((s - 1) * n_t * hidden..s * n_t * hidden);
6954 let mut dst = dstate.taps.slice_mut(0..n_t * hidden);
6955 stream
6956 .memcpy_dtod(&src, &mut dst)
6957 .map_err(e("seed tap row"))?;
6958 }
6959 stream.synchronize().map_err(e("prime sync"))?;
6960 Ok(out)
6961 }
6962
6963 pub fn dspark_write_rings(
6967 &self,
6968 state: &mut DsparkState,
6969 tap_row: usize,
6970 pos: usize,
6971 ) -> Res<()> {
6972 let d = self.model.cfg();
6973 let hd = d.head_dim as usize;
6974 let win = d.sliding_window as usize;
6975 let hidden = self.model.mc.n_embd as usize;
6976 let n_t = self.dspark().targets.len();
6977 let last = self.stages.len() - 1;
6978 let stream = self.stages[last].gpu.stream();
6979 let tap = {
6980 let mut row = stream
6982 .alloc_zeros::<f32>(n_t * hidden)
6983 .map_err(e("tap row"))?;
6984 let src = state
6985 .taps
6986 .slice(tap_row * n_t * hidden..(tap_row + 1) * n_t * hidden);
6987 stream.memcpy_dtod(&src, &mut row).map_err(e("tap copy"))?;
6988 row
6989 };
6990 let mx = self.dspark_main_x(&tap, 1)?;
6991 let n_blocks = self.dspark().blocks.len();
6992 for bi in 0..n_blocks {
6993 let blk = &self.dspark().blocks[bi];
6994 let kv = self.dspark_main_kv(blk, &mx, 1, &[pos as i32])?;
6995 let slot = pos % win;
6996 let src = kv.slice(0..hd);
6997 let mut dst = state.rings[bi].slice_mut(slot * hd..(slot + 1) * hd);
6998 stream
6999 .memcpy_dtod(&src, &mut dst)
7000 .map_err(e("ring write"))?;
7001 }
7002 Ok(())
7003 }
7004
7005 #[allow(clippy::too_many_arguments)]
7009 fn dspark_block_forward(
7010 &self,
7011 blk: &LayerDev,
7012 ring: &mut CudaSlice<f32>,
7013 h: &CudaSlice<f32>,
7014 block: usize,
7015 pos: usize,
7016 ) -> Res<CudaSlice<f32>> {
7017 let d = self.model.cfg();
7018 let mc = &self.model.mc;
7019 let hc = d.hc_mult as usize;
7020 let hidden = mc.n_embd as usize;
7021 let heads = mc.n_head as usize;
7022 let hd = d.head_dim as usize;
7023 let rd = d.qk_rope_head_dim as usize;
7024 let q_lora = d.q_lora_rank as usize;
7025 let win = d.sliding_window as usize;
7026 let o_groups = d.o_groups as usize;
7027 let o_lora = d.o_lora_rank as usize;
7028 let eps = mc.rms_eps;
7029 let iters = d.hc_sinkhorn_iters;
7030 let hc_eps = d.hc_eps;
7031 let last = self.stages.len() - 1;
7032 let st = &self.stages[last];
7033 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx dspark"))?;
7034 let stream = st.gpu.stream();
7035 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
7036 let positions: Vec<i32> = (1..=block as i32).map(|j| pos as i32 + j).collect();
7038 let pos_dev = upload_i32(&stream, &positions)?;
7039
7040 let (y, post, comb) = Self::hc_pre(
7042 st,
7043 h,
7044 &blk.hc_attn_fn,
7045 &blk.hc_attn_base,
7046 &blk.hc_attn_scale,
7047 block,
7048 hc,
7049 hidden,
7050 iters,
7051 hc_eps,
7052 )?;
7053 let mut x = stream.alloc_zeros::<f32>(block * hidden).map_err(e("x"))?;
7054 unsafe {
7055 ck(
7056 "rmsnorm dspark attn",
7057 k::memra_dsv4_rmsnorm(
7058 dpf!(y, &stream),
7059 dpf!(blk.attn_norm, &stream),
7060 dpm!(x, &stream),
7061 block as i32,
7062 hidden as i32,
7063 eps,
7064 sp(&stream),
7065 ),
7066 )?;
7067 }
7068 let mut qr = stream.alloc_zeros::<f32>(block * q_lora).map_err(e("qr"))?;
7070 Self::gemm(st, &x, blk.wq_a.dev(), 0, block, q_lora, hidden, &mut qr)?;
7071 unsafe {
7072 ck(
7073 "rmsnorm dspark q",
7074 k::memra_dsv4_rmsnorm(
7075 dpf!(qr, &stream),
7076 dpf!(blk.q_norm, &stream),
7077 dpm!(qr, &stream),
7078 block as i32,
7079 q_lora as i32,
7080 eps,
7081 sp(&stream),
7082 ),
7083 )?;
7084 }
7085 let mut qr_b = stream
7086 .alloc_zeros::<u8>(block * q_lora * 2)
7087 .map_err(e("qr_b"))?;
7088 unsafe {
7089 ck(
7090 "cvt dspark qr",
7091 k::memra_dsv4_cvt_bf16(
7092 dpf!(qr, &stream),
7093 qr_b.device_ptr_mut(&stream).0 as *mut c_void,
7094 (block * q_lora) as i64,
7095 sp(&stream),
7096 ),
7097 )?;
7098 }
7099 let mut q = stream
7100 .alloc_zeros::<f32>(block * heads * hd)
7101 .map_err(e("q"))?;
7102 Self::gemm_pre(
7103 st,
7104 &qr_b,
7105 blk.wq_b.dev().device_ptr(&stream).0 as *const c_void,
7106 block,
7107 heads * hd,
7108 q_lora,
7109 &mut q,
7110 )?;
7111 unsafe {
7112 ck(
7113 "headrms dspark",
7114 k::memra_dsv4_headrms(
7115 dpm!(q, &stream),
7116 (block * heads) as i32,
7117 hd as i32,
7118 eps,
7119 sp(&stream),
7120 ),
7121 )?;
7122 ck(
7123 "rope dspark q",
7124 k::memra_dsv4_rope(
7125 dpm!(q, &stream),
7126 block as i32,
7127 heads as i32,
7128 hd as i32,
7129 rd as i32,
7130 dpf!(st.fc_plain, &stream),
7131 pos_dev.device_ptr(&stream).0 as *const i32,
7132 0,
7133 sp(&stream),
7134 ),
7135 )?;
7136 }
7137 {
7139 let mut kv = stream.alloc_zeros::<f32>(block * hd).map_err(e("dkv"))?;
7140 Self::gemm(st, &x, blk.wkv.dev(), 0, block, hd, hidden, &mut kv)?;
7141 unsafe {
7142 ck(
7143 "rmsnorm dspark dkv",
7144 k::memra_dsv4_rmsnorm(
7145 dpf!(kv, &stream),
7146 dpf!(blk.kv_norm, &stream),
7147 dpm!(kv, &stream),
7148 block as i32,
7149 hd as i32,
7150 eps,
7151 sp(&stream),
7152 ),
7153 )?;
7154 ck(
7155 "rope dspark dkv",
7156 k::memra_dsv4_rope(
7157 dpm!(kv, &stream),
7158 block as i32,
7159 1,
7160 hd as i32,
7161 rd as i32,
7162 dpf!(st.fc_plain, &stream),
7163 pos_dev.device_ptr(&stream).0 as *const i32,
7164 0,
7165 sp(&stream),
7166 ),
7167 )?;
7168 ck(
7169 "act_quant dspark dkv",
7170 k::memra_dsv4_act_quant(
7171 dpm!(kv, &stream),
7172 block as i32,
7173 hd as i64,
7174 (hd - rd) as i32,
7175 64,
7176 clamp_only,
7177 sp(&stream),
7178 ),
7179 )?;
7180 }
7181 let src = kv.slice(0..block * hd);
7182 let mut dst = ring.slice_mut(win * hd..(win + block) * hd);
7183 stream.memcpy_dtod(&src, &mut dst).map_err(e("draft kv"))?;
7184 }
7185 let n_ring = win.min(pos + 1);
7189 let mut idx_row: Vec<i32> = (0..n_ring as i32).collect();
7190 idx_row.extend((0..block as i32).map(|j| win as i32 + j));
7191 let slots = idx_row.len();
7192 let mut idxs = Vec::with_capacity(block * slots);
7193 for _ in 0..block {
7194 idxs.extend_from_slice(&idx_row);
7195 }
7196 let idx_dev = upload_i32(&stream, &idxs)?;
7197 let mut o = stream
7198 .alloc_zeros::<f32>(block * heads * hd)
7199 .map_err(e("o"))?;
7200 let scale = (hd as f64).powf(-0.5) as f32;
7201 unsafe {
7202 ck(
7203 "sink_attn dspark",
7204 k::memra_dsv4_sink_attn(
7205 dpf!(q, &stream),
7206 dpf!(ring, &stream),
7207 idx_dev.device_ptr(&stream).0 as *const i32,
7208 dpf!(blk.sink, &stream),
7209 dpm!(o, &stream),
7210 block as i32,
7211 heads as i32,
7212 hd as i32,
7213 slots as i32,
7214 scale,
7215 sp(&stream),
7216 ),
7217 )?;
7218 ck(
7219 "rope dspark o inv",
7220 k::memra_dsv4_rope(
7221 dpm!(o, &stream),
7222 block as i32,
7223 heads as i32,
7224 hd as i32,
7225 rd as i32,
7226 dpf!(st.fc_plain, &stream),
7227 pos_dev.device_ptr(&stream).0 as *const i32,
7228 1,
7229 sp(&stream),
7230 ),
7231 )?;
7232 }
7233 let gw = heads / o_groups * hd;
7235 let mut og = stream
7236 .alloc_zeros::<f32>(block * o_groups * o_lora)
7237 .map_err(e("og"))?;
7238 let mut o_grp = stream.alloc_zeros::<f32>(block * gw).map_err(e("o_grp"))?;
7239 let mut y_grp = stream
7240 .alloc_zeros::<f32>(block * o_lora)
7241 .map_err(e("y_grp"))?;
7242 for g in 0..o_groups {
7243 unsafe {
7244 ck(
7245 "take_cols dspark",
7246 k::memra_dsv4_take_cols(
7247 dpf!(o, &stream),
7248 dpm!(o_grp, &stream),
7249 block as i32,
7250 gw as i32,
7251 (heads * hd) as i64,
7252 (g * gw) as i64,
7253 sp(&stream),
7254 ),
7255 )?;
7256 }
7257 Self::gemm(
7258 st,
7259 &o_grp,
7260 blk.wo_a.dev(),
7261 g * o_lora * gw,
7262 block,
7263 o_lora,
7264 gw,
7265 &mut y_grp,
7266 )?;
7267 unsafe {
7268 ck(
7269 "place_cols dspark",
7270 k::memra_dsv4_place_cols(
7271 dpf!(y_grp, &stream),
7272 dpm!(og, &stream),
7273 block as i32,
7274 o_lora as i32,
7275 (o_groups * o_lora) as i64,
7276 (g * o_lora) as i64,
7277 sp(&stream),
7278 ),
7279 )?;
7280 }
7281 }
7282 let mut attn_out = stream.alloc_zeros::<f32>(block * hidden).map_err(e("ao"))?;
7283 Self::gemm(
7284 st,
7285 &og,
7286 blk.wo_b.dev(),
7287 0,
7288 block,
7289 hidden,
7290 o_groups * o_lora,
7291 &mut attn_out,
7292 )?;
7293 let mut h2 = stream
7294 .alloc_zeros::<f32>(block * hc * hidden)
7295 .map_err(e("h2"))?;
7296 unsafe {
7297 ck(
7298 "hc_post dspark attn",
7299 k::memra_dsv4_hc_post(
7300 dpf!(attn_out, &stream),
7301 dpf!(h, &stream),
7302 dpf!(post, &stream),
7303 dpf!(comb, &stream),
7304 dpm!(h2, &stream),
7305 block as i32,
7306 hc as i32,
7307 hidden as i32,
7308 sp(&stream),
7309 ),
7310 )?;
7311 }
7312 let (y2, post2, comb2) = Self::hc_pre(
7314 st,
7315 &h2,
7316 &blk.hc_ffn_fn,
7317 &blk.hc_ffn_base,
7318 &blk.hc_ffn_scale,
7319 block,
7320 hc,
7321 hidden,
7322 iters,
7323 hc_eps,
7324 )?;
7325 let mut xf = stream.alloc_zeros::<f32>(block * hidden).map_err(e("xf"))?;
7326 unsafe {
7327 ck(
7328 "rmsnorm dspark ffn",
7329 k::memra_dsv4_rmsnorm(
7330 dpf!(y2, &stream),
7331 dpf!(blk.ffn_norm, &stream),
7332 dpm!(xf, &stream),
7333 block as i32,
7334 hidden as i32,
7335 eps,
7336 sp(&stream),
7337 ),
7338 )?;
7339 }
7340 let ids = vec![0u32; block];
7341 let moe_out = self.moe_forward(st, blk, &xf, block, &ids)?;
7342 let mut h3 = stream
7343 .alloc_zeros::<f32>(block * hc * hidden)
7344 .map_err(e("h3"))?;
7345 unsafe {
7346 ck(
7347 "hc_post dspark ffn",
7348 k::memra_dsv4_hc_post(
7349 dpf!(moe_out, &stream),
7350 dpf!(h2, &stream),
7351 dpf!(post2, &stream),
7352 dpf!(comb2, &stream),
7353 dpm!(h3, &stream),
7354 block as i32,
7355 hc as i32,
7356 hidden as i32,
7357 sp(&stream),
7358 ),
7359 )?;
7360 }
7361 Ok(h3)
7362 }
7363
7364 pub fn dspark_forward_spec(
7370 &self,
7371 state: &mut DsparkState,
7372 input_token: u32,
7373 tap_row: usize,
7374 pos: usize,
7375 capture: bool,
7376 ) -> Res<DsparkProposal> {
7377 let ds = self.dspark();
7378 let mc = &self.model.mc;
7379 let d = self.model.cfg();
7380 let hc = d.hc_mult as usize;
7381 let hidden = mc.n_embd as usize;
7382 let eps = mc.rms_eps;
7383 let block = ds.block_size;
7384 let rank = ds.rank;
7385 let vocab = ds.vocab;
7386 let n_t = ds.targets.len();
7387 let last = self.stages.len() - 1;
7388 let st = &self.stages[last];
7389 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx spec"))?;
7390 let stream = st.gpu.stream();
7391
7392 let prof = if dsv4_prof_on() {
7393 Some(stream.clone())
7394 } else {
7395 None
7396 };
7397 let tap = {
7399 let _p = phase!("1a.tap_copy", prof.as_ref());
7400 let mut row = stream.alloc_zeros::<f32>(n_t * hidden).map_err(e("tapr"))?;
7401 let src = state
7402 .taps
7403 .slice(tap_row * n_t * hidden..(tap_row + 1) * n_t * hidden);
7404 stream.memcpy_dtod(&src, &mut row).map_err(e("tap cp"))?;
7405 row
7406 };
7407 let mx = {
7408 let _p = phase!("1b.main_x", prof.as_ref());
7409 self.dspark_main_x(&tap, 1)?
7410 };
7411 let (cap_main_hidden, cap_main_x) = if capture {
7412 (
7413 Some(dtoh_f32(&stream, &tap)?),
7414 Some(dtoh_f32(&stream, &mx)?),
7415 )
7416 } else {
7417 (None, None)
7418 };
7419 let _p_embed = phase!("1c.embed_h2d_repeat", prof.as_ref());
7422 let mut draft_ids = vec![ds.noise_token; block];
7423 draft_ids[0] = input_token;
7424 let e_rows = self.model.embed_rows(&draft_ids);
7425 let e_dev = upload_f32(&stream, &e_rows)?;
7426 let mut h = stream
7427 .alloc_zeros::<f32>(block * hc * hidden)
7428 .map_err(e("h0"))?;
7429 unsafe {
7430 ck(
7431 "repeat_hc dspark",
7432 k::memra_dsv4_repeat_hc(
7433 dpf!(e_dev, &stream),
7434 dpm!(h, &stream),
7435 block as i32,
7436 hc as i32,
7437 hidden as i32,
7438 sp(&stream),
7439 ),
7440 )?;
7441 }
7442 drop(_p_embed);
7443 let mut block_outs: Vec<Vec<f32>> = Vec::new();
7444 let _p_blocks = phase!("1d.drafter_blocks", prof.as_ref());
7445 let n_blocks = ds.blocks.len();
7446 for bi in 0..n_blocks {
7447 let mut ring = std::mem::replace(
7449 &mut state.rings[bi],
7450 stream.alloc_zeros::<f32>(0).map_err(e("swap"))?,
7451 );
7452 let out =
7453 self.dspark_block_forward(&self.dspark().blocks[bi], &mut ring, &h, block, pos);
7454 state.rings[bi] = ring;
7455 h = out?;
7456 if capture {
7457 block_outs.push(dtoh_f32(&stream, &h)?);
7458 }
7459 }
7460 drop(_p_blocks);
7461 let w = hc * hidden;
7464 let _p_mix = phase!("1e.exit_mix_dots", prof.as_ref());
7465 let mut mixes = stream.alloc_zeros::<f32>(block * hc).map_err(e("mx"))?;
7466 self.dspark_head_dots(
7470 st,
7471 h.device_ptr(&stream).0 as *const f32,
7472 ds.hc_head_fn.device_ptr(&stream).0 as *const c_void,
7473 0,
7474 block,
7475 w,
7476 hc,
7477 mixes.device_ptr_mut(&stream).0 as *mut f32,
7478 )?;
7479 unsafe {
7480 ck(
7481 "rowsq dspark head",
7482 k::memra_dsv4_rowsq_scale(
7483 dpf!(h, &stream),
7484 dpm!(mixes, &stream),
7485 block as i32,
7486 w as i32,
7487 hc as i32,
7488 eps,
7489 sp(&stream),
7490 ),
7491 )?;
7492 }
7493 drop(_p_mix);
7494 let _p_mixrt = phase!("1f.mix_D2H_host_H2D", prof.as_ref());
7495 let mut mixes_h = dtoh_f32(&stream, &mixes)?;
7496 for t in 0..block {
7497 for c in 0..hc {
7498 let m = mixes_h[t * hc + c];
7499 mixes_h[t * hc + c] =
7500 sigmoid_f32(m * ds.hc_head_scale[0] + ds.hc_head_base[c]) + d.hc_eps;
7501 }
7502 }
7503 let pre_d = upload_f32(&stream, &mixes_h)?;
7504 drop(_p_mixrt);
7505 let _p_cn = phase!("1g.collapse_norm", prof.as_ref());
7506 let mut xc = stream.alloc_zeros::<f32>(block * hidden).map_err(e("xc"))?;
7507 unsafe {
7508 ck(
7509 "hc_collapse dspark",
7510 k::memra_dsv4_hc_collapse(
7511 dpf!(h, &stream),
7512 dpf!(pre_d, &stream),
7513 dpm!(xc, &stream),
7514 block as i32,
7515 hc as i32,
7516 hidden as i32,
7517 sp(&stream),
7518 ),
7519 )?;
7520 }
7521 let mut normed = stream.alloc_zeros::<f32>(block * hidden).map_err(e("nr"))?;
7522 unsafe {
7523 ck(
7524 "rmsnorm dspark head",
7525 k::memra_dsv4_rmsnorm(
7526 dpf!(xc, &stream),
7527 dpf!(ds.norm, &stream),
7528 dpm!(normed, &stream),
7529 block as i32,
7530 hidden as i32,
7531 eps,
7532 sp(&stream),
7533 ),
7534 )?;
7535 }
7536 drop(_p_cn);
7537 let _p_head = phase!("1h.exit_head_dots", prof.as_ref());
7538 let mut logits = stream.alloc_zeros::<f32>(block * vocab).map_err(e("lg"))?;
7539 self.dspark_head_dots(
7543 st,
7544 normed.device_ptr(&stream).0 as *const f32,
7545 st.head.as_ref().expect("head").device_ptr(&stream).0 as *const c_void,
7546 1,
7547 block,
7548 hidden,
7549 vocab,
7550 logits.device_ptr_mut(&stream).0 as *mut f32,
7551 )?;
7552 drop(_p_head);
7553 let cap_logits_pre = if capture {
7556 Some(dtoh_f32(&stream, &logits)?)
7557 } else {
7558 None
7559 };
7560 let chain_device = dsv4_dspark_chain_device();
7573 let markov_rowblk = dsv4_dspark_markov_rowblk();
7574 let mut w1_row = stream.alloc_zeros::<f32>(rank).map_err(e("w1r"))?;
7575 let mut bias = stream.alloc_zeros::<f32>(vocab).map_err(e("bias"))?;
7576 let mut am_dev = stream.alloc_zeros::<i32>(block + 1).map_err(e("am"))?;
7579 {
7580 let mut dst = am_dev.slice_mut(0..1);
7581 stream
7582 .memcpy_htod(&[input_token as i32][..], &mut dst)
7583 .map_err(e("htod am0"))?;
7584 }
7585 let mut out_ids = vec![input_token];
7586 let mut margins = Vec::with_capacity(block);
7587 let mut top1_logits = Vec::with_capacity(block);
7588 let mut conf_in = stream.alloc_zeros::<f32>(hidden + rank).map_err(e("cin"))?;
7589 let mut conf_out = stream.alloc_zeros::<f32>(block).map_err(e("cout"))?;
7590 let mut confidence = Vec::with_capacity(block);
7591 let _p_mk = phase!("1i.markov_chain", prof.as_ref());
7592 for i in 0..block {
7593 {
7594 let _p = phase!("1i1.markov_w1_gather", prof.as_ref());
7595 if chain_device {
7596 unsafe {
7597 ck(
7598 "markov w1 gather dev",
7599 k::memra_dsv4_gather_row_by_idx(
7600 dpf!(ds.markov_w1, &stream),
7601 am_dev.device_ptr(&stream).0 as *const i32,
7602 i as i32,
7603 dpm!(w1_row, &stream),
7604 rank as i32,
7605 sp(&stream),
7606 ),
7607 )?;
7608 }
7609 } else {
7610 let prev = out_ids[i] as usize;
7611 let src = ds.markov_w1.slice(prev * rank..(prev + 1) * rank);
7612 stream.memcpy_dtod(&src, &mut w1_row).map_err(e("w1 cp"))?;
7613 }
7614 }
7615 {
7616 let _p = phase!("1i2.markov_bias_gemv", prof.as_ref());
7617 if markov_rowblk {
7618 unsafe {
7619 ck(
7620 "dots_f32 markov rowblk",
7621 k::memra_dsv4_dots_f32_rowblk(
7622 dpf!(w1_row, &stream),
7623 dp!(ds.markov_w2, &stream),
7624 0,
7625 dpm!(bias, &stream),
7626 1,
7627 rank as i32,
7628 vocab as i32,
7629 sp(&stream),
7630 ),
7631 )?;
7632 }
7633 } else {
7634 Self::dots(st, &w1_row, &ds.markov_w2, 1, rank, vocab, &mut bias)?;
7635 }
7636 }
7637 let _p_aa = phase!("1i3.markov_add_argmax", prof.as_ref());
7638 unsafe {
7639 ck(
7640 "markov add dspark",
7641 k::memra_dsv4_add_inplace(
7642 (logits.device_ptr_mut(&stream).0 as usize + i * vocab * 4) as *mut f32,
7643 dpf!(bias, &stream),
7644 vocab as i64,
7645 sp(&stream),
7646 ),
7647 )?;
7648 ck(
7649 "argmax dspark",
7650 k::memra_dsv4_argmax(
7651 (logits.device_ptr(&stream).0 as usize + i * vocab * 4) as *const f32,
7652 vocab as i64,
7653 (am_dev.device_ptr_mut(&stream).0 as usize + (i + 1) * 4) as *mut i32,
7654 sp(&stream),
7655 ),
7656 )?;
7657 }
7658 drop(_p_aa);
7659 if !chain_device {
7660 let _p_d2h = phase!("1i4.markov_argmax_D2H_SYNC", None);
7661 let mut am = [0i32; 1];
7662 let view = am_dev.slice(i + 1..i + 2);
7663 stream
7664 .memcpy_dtoh(&view, &mut am[..])
7665 .map_err(e("dtoh am"))?;
7666 stream.synchronize().map_err(e("sync am"))?;
7667 out_ids.push(am[0] as u32);
7668 }
7669 {
7671 let _p = phase!("1i5.conf_in_copies", prof.as_ref());
7672 let src = xc.slice(i * hidden..(i + 1) * hidden);
7673 let mut dst = conf_in.slice_mut(0..hidden);
7674 stream.memcpy_dtod(&src, &mut dst).map_err(e("cin x"))?;
7675 let src = w1_row.slice(0..rank);
7676 let mut dst = conf_in.slice_mut(hidden..hidden + rank);
7677 stream.memcpy_dtod(&src, &mut dst).map_err(e("cin m"))?;
7678 }
7679 {
7680 let _p = phase!("1i6.conf_dots", prof.as_ref());
7685 unsafe {
7686 ck(
7687 "dots_f32 conf dspark",
7688 k::memra_dsv4_dots_f32(
7689 dpf!(conf_in, &stream),
7690 dp!(ds.conf_w, &stream),
7691 0,
7692 (conf_out.device_ptr_mut(&stream).0 as usize + i * 4) as *mut f32,
7693 1,
7694 (hidden + rank) as i32,
7695 1,
7696 sp(&stream),
7697 ),
7698 )?;
7699 }
7700 }
7701 if !chain_device {
7702 let _p = phase!("1i7.conf_D2H_SYNC", None);
7703 let mut c = [0f32; 1];
7704 let view = conf_out.slice(i..i + 1);
7705 stream
7706 .memcpy_dtoh(&view, &mut c[..])
7707 .map_err(e("dtoh cf"))?;
7708 stream.synchronize().map_err(e("sync cf"))?;
7709 confidence.push(c[0]);
7710 }
7711 }
7712 if chain_device {
7713 let _p = phase!("1i8.chain_D2H_SYNC_once", None);
7715 let mut ids = vec![0i32; block];
7716 let view = am_dev.slice(1..block + 1);
7717 stream
7718 .memcpy_dtoh(&view, &mut ids[..])
7719 .map_err(e("dtoh chain ids"))?;
7720 let mut cf = vec![0f32; block];
7721 stream
7722 .memcpy_dtoh(&conf_out, &mut cf[..])
7723 .map_err(e("dtoh chain conf"))?;
7724 stream.synchronize().map_err(e("sync chain"))?;
7725 out_ids.extend(ids.iter().map(|&x| x as u32));
7726 confidence.extend_from_slice(&cf);
7727 }
7728 drop(_p_mk);
7729 let membeds: Vec<f32> = if capture {
7734 let mut m = Vec::with_capacity(block * rank);
7735 for i in 0..block {
7736 let prev = out_ids[i] as usize;
7737 m.extend_from_slice(&ds.markov_w1_host[prev * rank..(prev + 1) * rank]);
7738 }
7739 m
7740 } else {
7741 Vec::new()
7742 };
7743 let cap = if capture {
7744 let logits_post = dtoh_f32(&stream, &logits)?;
7745 for i in 0..block {
7746 let row = &logits_post[i * vocab..(i + 1) * vocab];
7747 let top = out_ids[i + 1];
7748 let mut second = f32::NEG_INFINITY;
7749 for (vv, &val) in row.iter().enumerate() {
7750 if vv as u32 != top && val > second {
7751 second = val;
7752 }
7753 }
7754 margins.push(row[top as usize] - second);
7755 top1_logits.push(row[top as usize]);
7756 }
7757 Some(DsparkCaptureOut {
7758 main_hidden: cap_main_hidden.unwrap(),
7759 main_x: cap_main_x.unwrap(),
7760 block_outs,
7761 x_collapsed: dtoh_f32(&stream, &xc)?,
7762 logits_pre: cap_logits_pre.unwrap(),
7763 logits_post,
7764 markov_embed: membeds,
7765 })
7766 } else {
7767 None
7768 };
7769 Ok(DsparkProposal {
7770 out_ids,
7771 confidence,
7772 margins,
7773 top1_logits,
7774 capture: cap,
7775 })
7776 }
7777
7778 pub fn decode_step_tap(
7781 &self,
7782 tok: u32,
7783 state: &mut DecodeState,
7784 dspark_state: &mut DsparkState,
7785 tap_row: usize,
7786 ) -> Res<Vec<f32>> {
7787 let DecodePath::Device { host_math } = self.decode_path else {
7788 return Err("decode_step_tap requires MEMRA_DSV4_DECODE_PATH=device".into());
7789 };
7790 let n_t = self.dspark().targets.len();
7791 let hidden = self.model.mc.n_embd as usize;
7792 let (logits, _) = self.decode_step_fast_tap(
7793 tok,
7794 state,
7795 true,
7796 host_math,
7797 Some((&mut dspark_state.taps, tap_row * n_t * hidden)),
7798 )?;
7799 Ok(logits.expect("want_logits"))
7800 }
7801
7802 pub fn decode_step_greedy_tap(
7804 &self,
7805 tok: u32,
7806 state: &mut DecodeState,
7807 dspark_state: &mut DsparkState,
7808 tap_row: usize,
7809 ) -> Res<u32> {
7810 let DecodePath::Device { host_math } = self.decode_path else {
7811 return Err("decode_step_greedy_tap requires MEMRA_DSV4_DECODE_PATH=device".into());
7812 };
7813 let ds = self.dspark();
7814 let hidden = self.model.mc.n_embd as usize;
7815 let n_t = ds.targets.len();
7816 let (_, tok_next) = self.decode_step_fast_tap(
7817 tok,
7818 state,
7819 false,
7820 host_math,
7821 Some((&mut dspark_state.taps, tap_row * n_t * hidden)),
7822 )?;
7823 Ok(tok_next)
7824 }
7825}
7826
7827pub struct VerifyWs {
7861 pub tmax: usize,
7862 h_a: CudaSlice<f32>,
7863 h_b: CudaSlice<f32>,
7864 h_rx: CudaSlice<f32>,
7865 emb: CudaSlice<f32>,
7866 mixes: CudaSlice<f32>,
7867 pre: CudaSlice<f32>,
7868 post: CudaSlice<f32>,
7869 comb: CudaSlice<f32>,
7870 y_hc: CudaSlice<f32>,
7871 x: CudaSlice<f32>,
7872 xf: CudaSlice<f32>,
7873 qr: CudaSlice<f32>,
7874 qr_b: CudaSlice<u8>,
7875 q: CudaSlice<f32>,
7876 kv: CudaSlice<f32>,
7877 qi: CudaSlice<f32>,
7878 wproj: CudaSlice<f32>,
7879 score: CudaSlice<f32>,
7880 idx: CudaSlice<i32>,
7881 idx_stride: usize,
7882 o: CudaSlice<f32>,
7883 o_b: CudaSlice<u8>,
7884 og: CudaSlice<f32>,
7885 attn_out: CudaSlice<f32>,
7886 gemm_xb: CudaSlice<u8>,
7887 raw: CudaSlice<f32>,
7888 sel: CudaSlice<i32>,
7889 selw: CudaSlice<f32>,
7890 order: CudaSlice<i32>,
7891 xq: CudaSlice<u8>,
7892 xs: CudaSlice<f32>,
7893 g1: CudaSlice<f32>,
7894 g3: CudaSlice<f32>,
7895 hbuf: CudaSlice<f32>,
7896 hq: CudaSlice<u8>,
7897 hs: CudaSlice<f32>,
7898 contrib: CudaSlice<f32>,
7899 y: CudaSlice<f32>,
7900 xb: CudaSlice<u8>,
7901 sg1: CudaSlice<f32>,
7902 sg3: CudaSlice<f32>,
7903 shbuf: CudaSlice<f32>,
7904 shb16: CudaSlice<u8>,
7905 sh_out: CudaSlice<f32>,
7906 cmp_emit: CudaSlice<f32>,
7907 cmp_shift: CudaSlice<f32>,
7908 sink_scores: CudaSlice<f32>,
7909 sink_evals: CudaSlice<f32>,
7910 sink_den: CudaSlice<f64>,
7911 head_mixes: CudaSlice<f32>,
7912 head_pre: CudaSlice<f32>,
7913 collapsed: CudaSlice<f32>,
7914 logits: CudaSlice<f32>,
7915 tok: CudaSlice<i32>,
7916 pos_dev: CudaSlice<i32>,
7917 argmax: CudaSlice<i32>,
7918 bounce: CudaSlice<f32>,
7922 slot_rows: CudaSlice<i32>,
7923 tap_tmp: CudaSlice<f32>,
7925}
7926
7927struct CmpCkptDev {
7932 kv_snap: CudaSlice<f32>,
7933 sc_snap: CudaSlice<f32>,
7934 rows_kv: CudaSlice<f32>,
7935 rows_sc: CudaSlice<f32>,
7936 latent: usize,
7937 ratio: usize,
7938 overlap: bool,
7939 n_blocks0: usize,
7940}
7941
7942struct LayerCkptDev {
7945 cmp: Option<CmpCkptDev>,
7946 idx: Option<CmpCkptDev>,
7947 trans_base: usize,
7949}
7950
7951pub struct VerifyState {
7953 ws: Vec<VerifyWs>,
7954 layers: Vec<LayerCkptDev>,
7955 pub tmax: usize,
7956 open: Option<(usize, usize)>,
7958 pub bytes: Vec<u64>,
7960}
7961
7962impl Dsv4Gpu {
7963 pub fn verify_tmax(&self) -> usize {
7966 self.dspark.as_ref().map(|d| d.block_size + 1).unwrap_or(0)
7967 }
7968
7969 pub fn alloc_verify_state(&self) -> Res<VerifyState> {
7972 let tmax = self.verify_tmax();
7973 if tmax == 0 {
7974 return Err("alloc_verify_state needs MEMRA_DSV4_DRAFTER=dspark".into());
7975 }
7976 if !matches!(self.decode_path, DecodePath::Device { .. }) {
7977 return Err(
7978 "batched verify is a device-path rung (MEMRA_DSV4_DECODE_PATH=device)".into(),
7979 );
7980 }
7981 let d = self.model.cfg();
7982 let mc = &self.model.mc;
7983 let moe = mc.moe.as_ref().expect("moe");
7984 let hc = d.hc_mult as usize;
7985 let hidden = mc.n_embd as usize;
7986 let heads = mc.n_head as usize;
7987 let hd = d.head_dim as usize;
7988 let q_lora = d.q_lora_rank as usize;
7989 let win = d.sliding_window as usize;
7990 let o_groups = d.o_groups as usize;
7991 let o_lora = d.o_lora_rank as usize;
7992 let iheads = d.index_n_heads as usize;
7993 let ihd = d.index_head_dim as usize;
7994 let topk = moe.expert_used_count as usize;
7995 let ne = moe.expert_count as usize;
7996 let inter = moe.expert_ff_length as usize;
7997 let itopk = d.index_topk as usize;
7998 let n_trunk = (mc.n_layer - mc.nextn_predict_layers) as usize;
7999 let vocab = {
8000 let (info, _) = self.model.st.raw("head.weight").expect("head");
8001 info.shape[0] as usize
8002 };
8003 let sh_inter = {
8004 let (info, _) = self
8005 .model
8006 .st
8007 .raw("layers.0.ffn.shared_experts.w1.weight")
8008 .expect("shared w1");
8009 info.shape[0] as usize
8010 };
8011 let mut max_d = 0usize;
8012 let mut max_shift = 0usize;
8013 let mut min_ratio = usize::MAX;
8014 for st in &self.stages {
8015 for l in &st.layers {
8016 for cmp in l.cmp.iter().chain(l.idx.as_ref().map(|ix| &ix.cmp)) {
8017 max_d = max_d.max(cmp.d);
8018 if cmp.overlap {
8019 max_shift = max_shift.max(cmp.ratio * cmp.latent);
8020 }
8021 min_ratio = min_ratio.min(cmp.ratio);
8022 }
8023 }
8024 }
8025 assert!(min_ratio != usize::MAX, "no compressor layers?");
8026 let score_cap = self.max_seq / min_ratio + 1;
8027 let idx_tail = itopk.max(self.max_seq / 128 + 1);
8028 let idx_stride = win + idx_tail;
8029 let max_gemm_k = (o_groups * o_lora).max(hidden).max(q_lora).max(sh_inter);
8030 let mut bytes = vec![0u64; self.stages.len()];
8031 let mut ws = Vec::with_capacity(self.stages.len());
8032 for st in &self.stages {
8033 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx vws"))?;
8034 let s = st.gpu.stream();
8035 let acc = std::cell::Cell::new(0u64);
8036 let f = |n: usize| {
8037 acc.set(acc.get() + (n * 4) as u64);
8038 s.alloc_zeros::<f32>(n).map_err(e("vws f32"))
8039 };
8040 let b = |n: usize| {
8041 acc.set(acc.get() + n as u64);
8042 s.alloc_zeros::<u8>(n).map_err(e("vws u8"))
8043 };
8044 let i = |n: usize| {
8045 acc.set(acc.get() + (n * 4) as u64);
8046 s.alloc_zeros::<i32>(n).map_err(e("vws i32"))
8047 };
8048 let w = VerifyWs {
8049 tmax,
8050 h_a: f(tmax * hc * hidden)?,
8051 h_b: f(tmax * hc * hidden)?,
8052 h_rx: f(tmax * hc * hidden)?,
8053 emb: f(tmax * hidden)?,
8054 mixes: f(tmax * (2 + hc) * hc)?,
8055 pre: f(tmax * hc)?,
8056 post: f(tmax * hc)?,
8057 comb: f(tmax * hc * hc)?,
8058 y_hc: f(tmax * hidden)?,
8059 x: f(tmax * hidden)?,
8060 xf: f(tmax * hidden)?,
8061 qr: f(tmax * q_lora)?,
8062 qr_b: b(tmax * q_lora * 2)?,
8063 q: f(tmax * heads * hd)?,
8064 kv: f(tmax * hd)?,
8065 qi: f(tmax * iheads * ihd)?,
8066 wproj: f(tmax * iheads)?,
8067 score: f(score_cap)?,
8068 idx: i(tmax * idx_stride)?,
8069 idx_stride,
8070 o: f(tmax * heads * hd)?,
8071 o_b: b(tmax * heads * hd * 2)?,
8072 og: f(tmax * o_groups * o_lora)?,
8073 attn_out: f(tmax * hidden)?,
8074 gemm_xb: b(tmax * max_gemm_k * 2)?,
8075 raw: f(tmax * ne)?,
8076 sel: i(tmax * topk)?,
8077 selw: f(tmax * topk)?,
8078 order: i(tmax * topk)?,
8079 xq: b(tmax * hidden)?,
8080 xs: f(tmax * hidden / 128)?,
8081 g1: f(tmax * topk * inter)?,
8082 g3: f(tmax * topk * inter)?,
8083 hbuf: f(tmax * topk * inter)?,
8084 hq: b(tmax * topk * inter)?,
8085 hs: f(tmax * topk * inter / 128)?,
8086 contrib: f(tmax * topk * hidden)?,
8087 y: f(tmax * hidden)?,
8088 xb: b(tmax * hidden * 2)?,
8089 sg1: f(tmax * sh_inter)?,
8090 sg3: f(tmax * sh_inter)?,
8091 shbuf: f(tmax * sh_inter)?,
8092 shb16: b(tmax * sh_inter * 2)?,
8093 sh_out: f(tmax * hidden)?,
8094 cmp_emit: f(2 * max_d)?,
8095 cmp_shift: f(max_shift.max(1))?,
8096 sink_scores: f(tmax * heads * idx_stride)?,
8097 sink_evals: f(tmax * heads * idx_stride)?,
8098 sink_den: {
8099 acc.set(acc.get() + (tmax * heads * 8) as u64);
8100 s.alloc_zeros::<f64>(tmax * heads).map_err(e("vws f64"))?
8101 },
8102 head_mixes: f(tmax * hc)?,
8103 head_pre: f(tmax * hc)?,
8104 collapsed: f(tmax * hidden)?,
8105 logits: f(tmax * vocab)?,
8106 tok: i(tmax)?,
8107 pos_dev: i(tmax)?,
8108 argmax: i(tmax)?,
8109 bounce: f(tmax * hd)?,
8110 slot_rows: i(tmax)?,
8111 tap_tmp: f(tmax * hidden)?,
8112 };
8113 bytes[st.dev] += acc.get();
8114 ws.push(w);
8115 }
8116 let mut layers = Vec::with_capacity(n_trunk);
8118 for il in 0..n_trunk {
8119 let stage_i = self.layer_stage[il];
8120 let st = &self.stages[stage_i];
8121 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx ckpt"))?;
8122 let stream = st.gpu.stream();
8123 let lidx = st
8124 .layers
8125 .iter()
8126 .position(|l| l.il == il as u32)
8127 .unwrap_or_else(|| panic!("layer {il} not on stage {stage_i}"));
8128 let layer = &st.layers[lidx];
8129 let cap_blocks = self.max_seq.checked_div(layer.ratio).unwrap_or(0);
8130 let mk = |cmp: &CmpDev| -> Res<CmpCkptDev> {
8131 let slots = if cmp.overlap {
8132 2 * cmp.ratio
8133 } else {
8134 cmp.ratio
8135 };
8136 Ok(CmpCkptDev {
8137 kv_snap: stream
8138 .alloc_zeros::<f32>(slots * cmp.latent)
8139 .map_err(e("ckpt kv snap"))?,
8140 sc_snap: stream
8141 .alloc_zeros::<f32>(slots * cmp.latent)
8142 .map_err(e("ckpt sc snap"))?,
8143 rows_kv: stream
8144 .alloc_zeros::<f32>(tmax * cmp.latent)
8145 .map_err(e("ckpt rows kv"))?,
8146 rows_sc: stream
8147 .alloc_zeros::<f32>(tmax * cmp.latent)
8148 .map_err(e("ckpt rows sc"))?,
8149 latent: cmp.latent,
8150 ratio: cmp.ratio,
8151 overlap: cmp.overlap,
8152 n_blocks0: 0,
8153 })
8154 };
8155 let cmp = match &layer.cmp {
8156 Some(c) => Some(mk(c)?),
8157 None => None,
8158 };
8159 let idxc = match &layer.idx {
8160 Some(ix) => Some(mk(&ix.cmp)?),
8161 None => None,
8162 };
8163 for c in cmp.iter().chain(idxc.iter()) {
8164 let slots = if c.overlap { 2 * c.ratio } else { c.ratio };
8165 bytes[st.dev] += ((2 * slots * c.latent + 2 * tmax * c.latent) * 4) as u64;
8166 }
8167 layers.push(LayerCkptDev {
8168 cmp,
8169 idx: idxc,
8170 trans_base: d.sliding_window as usize + cap_blocks,
8171 });
8172 }
8173 for st in &self.stages {
8174 st.gpu.stream().synchronize().map_err(e("vws sync"))?;
8175 }
8176 Ok(VerifyState {
8177 ws,
8178 layers,
8179 tmax,
8180 open: None,
8181 bytes,
8182 })
8183 }
8184
8185 #[allow(clippy::too_many_arguments)]
8189 fn gemv_m_dev(
8190 st: &Stage,
8191 w: DW,
8192 x_ptr: *const c_void,
8193 y_ptr: *mut f32,
8194 m: usize,
8195 n: usize,
8196 kdim: usize,
8197 xstride: usize,
8198 ystride: usize,
8199 ) -> Res<()> {
8200 let stream = st.gpu.stream();
8201 unsafe {
8202 match w {
8203 DW::Bf16(w_ptr) => ck(
8204 "gemv_bf16_m dev",
8205 k::memra_dsv4_gemv_bf16_m(
8206 w_ptr,
8207 x_ptr,
8208 y_ptr,
8209 m as i32,
8210 n as i32,
8211 kdim as i32,
8212 xstride as i32,
8213 ystride as i32,
8214 sp(&stream),
8215 ),
8216 ),
8217 DW::Fp8 {
8218 codes,
8219 scales,
8220 sc_cols,
8221 } => ck(
8222 "gemv_fp8_m dev",
8223 k::memra_dsv4_gemv_fp8_m(
8224 codes,
8225 scales,
8226 sc_cols,
8227 x_ptr,
8228 y_ptr,
8229 m as i32,
8230 n as i32,
8231 kdim as i32,
8232 xstride as i32,
8233 ystride as i32,
8234 sp(&stream),
8235 ),
8236 ),
8237 }
8238 }
8239 }
8240
8241 #[allow(clippy::too_many_arguments)]
8243 fn gemm_m_dev(
8244 st: &Stage,
8245 x_f32: *const f32,
8246 xb: &mut CudaSlice<u8>,
8247 w: DW,
8248 m: usize,
8249 n: usize,
8250 kdim: usize,
8251 y_ptr: *mut f32,
8252 ) -> Res<()> {
8253 let stream = st.gpu.stream();
8254 unsafe {
8255 ck(
8256 "cvt_bf16 m dev",
8257 k::memra_dsv4_cvt_bf16(
8258 x_f32,
8259 xb.device_ptr_mut(&stream).0 as *mut c_void,
8260 (m * kdim) as i64,
8261 sp(&stream),
8262 ),
8263 )?;
8264 }
8265 Self::gemv_m_dev(
8266 st,
8267 w,
8268 xb.device_ptr(&stream).0 as *const c_void,
8269 y_ptr,
8270 m,
8271 n,
8272 kdim,
8273 0,
8274 0,
8275 )
8276 }
8277
8278 #[allow(clippy::too_many_arguments)]
8280 fn dots_m_dev(
8281 &self,
8282 st: &Stage,
8283 x: *const f32,
8284 w_f32: *const c_void,
8285 w_is_bf16: i32,
8286 s: usize,
8287 kdim: usize,
8288 n: usize,
8289 y: *mut f32,
8290 ) -> Res<()> {
8291 let stream = st.gpu.stream();
8292 unsafe {
8293 if self.dots_f32 {
8294 ck(
8295 "dots_f32acc_mrow",
8296 k::memra_dsv4_dots_f32acc_mrow(
8297 x,
8298 w_f32,
8299 w_is_bf16,
8300 y,
8301 s as i32,
8302 kdim as i32,
8303 n as i32,
8304 sp(&stream),
8305 ),
8306 )
8307 } else {
8308 ck(
8309 "dots_f32_mrow",
8310 k::memra_dsv4_dots_f32_mrow(
8311 x,
8312 w_f32,
8313 w_is_bf16,
8314 y,
8315 s as i32,
8316 kdim as i32,
8317 n as i32,
8318 sp(&stream),
8319 ),
8320 )
8321 }
8322 }
8323 }
8324}
8325
8326impl Dsv4Gpu {
8327 #[allow(clippy::too_many_arguments)]
8331 fn hc_pre_batch_dev(
8332 &self,
8333 st: &Stage,
8334 h_ptr: *const f32,
8335 fn_w: &CudaSlice<f32>,
8336 base_host: &[f32],
8337 scale_host: &[f32],
8338 base_dev: &CudaSlice<f32>,
8339 scale_dev: &CudaSlice<f32>,
8340 vws: &mut VerifyWs,
8341 t: usize,
8342 hc: usize,
8343 hidden: usize,
8344 iters: u32,
8345 hc_eps: f32,
8346 host_math: bool,
8347 ) -> Res<()> {
8348 let stream = st.gpu.stream();
8349 let w = hc * hidden;
8350 let rows = (2 + hc) * hc;
8351 self.dots_m_dev(
8352 st,
8353 h_ptr,
8354 fn_w.device_ptr(&stream).0 as *const c_void,
8355 0,
8356 t,
8357 w,
8358 rows,
8359 vws.mixes.device_ptr_mut(&stream).0 as *mut f32,
8360 )?;
8361 unsafe {
8362 ck(
8363 "rowsq_scale batch",
8364 self.rowsq_scale_arm(
8365 h_ptr,
8366 dpm!(vws.mixes, &stream),
8367 t as i32,
8368 w as i32,
8369 rows as i32,
8370 hc_eps,
8371 sp(&stream),
8372 ),
8373 )?;
8374 }
8375 if host_math {
8376 let mut mixes_h = vec![0f32; t * rows];
8377 let view = vws.mixes.slice(0..t * rows);
8378 stream
8379 .memcpy_dtoh(&view, &mut mixes_h[..])
8380 .map_err(e("dtoh mixes batch"))?;
8381 stream.synchronize().map_err(e("sync mixes batch"))?;
8382 let (pre_h, post_h, comb_h) =
8383 hc_split_sinkhorn(&mixes_h, t, hc, scale_host, base_host, iters, hc_eps);
8384 let mut dp = vws.pre.slice_mut(0..t * hc);
8385 stream
8386 .memcpy_htod(&pre_h, &mut dp)
8387 .map_err(e("htod pre b"))?;
8388 let mut dp = vws.post.slice_mut(0..t * hc);
8389 stream
8390 .memcpy_htod(&post_h, &mut dp)
8391 .map_err(e("htod post b"))?;
8392 let mut dp = vws.comb.slice_mut(0..t * hc * hc);
8393 stream
8394 .memcpy_htod(&comb_h, &mut dp)
8395 .map_err(e("htod comb b"))?;
8396 } else {
8397 unsafe {
8398 ck(
8399 "hc_sinkhorn_m",
8400 k::memra_dsv4_hc_sinkhorn_m(
8401 dpf!(vws.mixes, &stream),
8402 dpf!(scale_dev, &stream),
8403 dpf!(base_dev, &stream),
8404 dpm!(vws.pre, &stream),
8405 dpm!(vws.post, &stream),
8406 dpm!(vws.comb, &stream),
8407 t as i32,
8408 hc as i32,
8409 iters as i32,
8410 hc_eps,
8411 sp(&stream),
8412 ),
8413 )?;
8414 }
8415 }
8416 unsafe {
8417 ck(
8418 "hc_collapse batch",
8419 k::memra_dsv4_hc_collapse(
8420 h_ptr,
8421 dpf!(vws.pre, &stream),
8422 dpm!(vws.y_hc, &stream),
8423 t as i32,
8424 hc as i32,
8425 hidden as i32,
8426 sp(&stream),
8427 ),
8428 )?;
8429 }
8430 Ok(())
8431 }
8432
8433 #[allow(clippy::too_many_arguments)]
8439 fn cmp_decode_batch_dev(
8440 &self,
8441 st: &Stage,
8442 cmp: &CmpDev,
8443 x_ptr: *const f32,
8444 t: usize,
8445 pos0: usize,
8446 hidden: usize,
8447 fc_dev: &CudaSlice<f32>,
8448 rd: usize,
8449 eps: f32,
8450 ck_dev: &mut CmpCkptDev,
8451 emit: &mut CudaSlice<f32>,
8452 shift: &mut CudaSlice<f32>,
8453 pend_kv: &mut CudaSlice<f32>,
8454 pend_score: &mut CudaSlice<f32>,
8455 store: &mut CudaSlice<f32>,
8456 row0: usize,
8457 blocks: &mut usize,
8458 ) -> Res<()> {
8459 let stream = st.gpu.stream();
8460 let (ratio, d, latent) = (cmp.ratio, cmp.d, cmp.latent);
8461 stream
8463 .memcpy_dtod(pend_kv, &mut ck_dev.kv_snap)
8464 .map_err(e("ckpt snap kv"))?;
8465 stream
8466 .memcpy_dtod(pend_score, &mut ck_dev.sc_snap)
8467 .map_err(e("ckpt snap sc"))?;
8468 ck_dev.n_blocks0 = *blocks;
8469 self.dots_m_dev(
8470 st,
8471 x_ptr,
8472 cmp.wkv.device_ptr(&stream).0 as *const c_void,
8473 0,
8474 t,
8475 hidden,
8476 latent,
8477 ck_dev.rows_kv.device_ptr_mut(&stream).0 as *mut f32,
8478 )?;
8479 self.dots_m_dev(
8480 st,
8481 x_ptr,
8482 cmp.wgate.device_ptr(&stream).0 as *const c_void,
8483 0,
8484 t,
8485 hidden,
8486 latent,
8487 ck_dev.rows_sc.device_ptr_mut(&stream).0 as *mut f32,
8488 )?;
8489 for i in 0..t {
8490 let pos = pos0 + i;
8491 let slot = if cmp.overlap {
8492 ratio + pos % ratio
8493 } else {
8494 pos % ratio
8495 };
8496 {
8497 let src = ck_dev.rows_kv.slice(i * latent..(i + 1) * latent);
8498 let mut dst = pend_kv.slice_mut(slot * latent..(slot + 1) * latent);
8499 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend kv b"))?;
8500 let src = ck_dev.rows_sc.slice(i * latent..(i + 1) * latent);
8501 let mut dst = pend_score.slice_mut(slot * latent..(slot + 1) * latent);
8502 stream.memcpy_dtod(&src, &mut dst).map_err(e("pend sc b"))?;
8503 }
8504 if (pos + 1) % ratio != 0 {
8505 continue;
8506 }
8507 let j = pos / ratio;
8508 let nb_launch = if cmp.overlap { 2usize } else { 1 };
8509 let row_off = if cmp.overlap { d } else { 0 };
8510 unsafe {
8511 ck(
8512 "compressor_pool batch",
8513 k::memra_dsv4_compressor_pool(
8514 dpf!(*pend_kv, &stream),
8515 dpf!(*pend_score, &stream),
8516 dpf!(cmp.ape, &stream),
8517 dpm!(*emit, &stream),
8518 nb_launch as i32,
8519 ratio as i32,
8520 d as i32,
8521 latent as i32,
8522 cmp.overlap as i32,
8523 sp(&stream),
8524 ),
8525 )?;
8526 let row_c = (emit.device_ptr(&stream).0 as usize + row_off * 4) as *const f32;
8527 let row_m = (emit.device_ptr_mut(&stream).0 as usize + row_off * 4) as *mut f32;
8528 ck(
8529 "rmsnorm batch cmp",
8530 self.rmsnorm_arm(
8531 row_c,
8532 dpf!(cmp.norm, &stream),
8533 row_m,
8534 1,
8535 d as i32,
8536 eps,
8537 sp(&stream),
8538 ),
8539 )?;
8540 ck(
8541 "rope_at batch cmp",
8542 k::memra_dsv4_rope_at(
8543 row_m,
8544 1,
8545 d as i32,
8546 rd as i32,
8547 dpf!(fc_dev, &stream),
8548 (j * ratio) as i32,
8549 0,
8550 sp(&stream),
8551 ),
8552 )?;
8553 if cmp.rotate {
8554 let scale = (d as f32).powf(-0.5);
8555 ck(
8556 "hadamard batch cmp",
8557 k::memra_dsv4_hadamard(row_m, 1, d as i32, scale, sp(&stream)),
8558 )?;
8559 ck(
8560 "fp4 batch cmp",
8561 k::memra_dsv4_fp4_act_quant(row_m, 1, d as i64, d as i32, sp(&stream)),
8562 )?;
8563 } else {
8564 ck(
8565 "act_quant batch cmp",
8566 k::memra_dsv4_act_quant(
8567 row_m,
8568 1,
8569 d as i64,
8570 (d - rd) as i32,
8571 64,
8572 (self.variant == ActQuantVariant::ClampOnly) as i32,
8573 sp(&stream),
8574 ),
8575 )?;
8576 }
8577 }
8578 {
8579 let src = emit.slice(row_off..row_off + d);
8580 let mut dst = store.slice_mut((row0 + j) * d..(row0 + j + 1) * d);
8581 stream
8582 .memcpy_dtod(&src, &mut dst)
8583 .map_err(e("emit store b"))?;
8584 }
8585 if cmp.overlap {
8586 {
8587 let src = pend_kv.slice(ratio * latent..2 * ratio * latent);
8588 let mut dst = shift.slice_mut(0..ratio * latent);
8589 stream.memcpy_dtod(&src, &mut dst).map_err(e("bshift1"))?;
8590 }
8591 {
8592 let src = shift.slice(0..ratio * latent);
8593 let mut dst = pend_kv.slice_mut(0..ratio * latent);
8594 stream.memcpy_dtod(&src, &mut dst).map_err(e("bshift2"))?;
8595 }
8596 {
8597 let src = pend_score.slice(ratio * latent..2 * ratio * latent);
8598 let mut dst = shift.slice_mut(0..ratio * latent);
8599 stream.memcpy_dtod(&src, &mut dst).map_err(e("bshift3"))?;
8600 }
8601 {
8602 let src = shift.slice(0..ratio * latent);
8603 let mut dst = pend_score.slice_mut(0..ratio * latent);
8604 stream.memcpy_dtod(&src, &mut dst).map_err(e("bshift4"))?;
8605 }
8606 }
8607 *blocks = j + 1;
8608 }
8609 Ok(())
8610 }
8611
8612 #[allow(clippy::too_many_arguments)]
8618 fn cmp_rollback_replay_dev(
8619 &self,
8620 st: &Stage,
8621 ck_dev: &CmpCkptDev,
8622 n_commit: usize,
8623 t: usize,
8624 pos0: usize,
8625 shift: &mut CudaSlice<f32>,
8626 pend_kv: &mut CudaSlice<f32>,
8627 pend_score: &mut CudaSlice<f32>,
8628 blocks: &mut usize,
8629 ) -> Res<()> {
8630 if n_commit == t {
8631 return Ok(()); }
8633 let stream = st.gpu.stream();
8634 let (ratio, latent, overlap) = (ck_dev.ratio, ck_dev.latent, ck_dev.overlap);
8635 stream
8636 .memcpy_dtod(&ck_dev.kv_snap, pend_kv)
8637 .map_err(e("rb kv snap"))?;
8638 stream
8639 .memcpy_dtod(&ck_dev.sc_snap, pend_score)
8640 .map_err(e("rb sc snap"))?;
8641 *blocks = ck_dev.n_blocks0;
8642 for i in 0..n_commit {
8643 let pos = pos0 + i;
8644 let slot = if overlap {
8645 ratio + pos % ratio
8646 } else {
8647 pos % ratio
8648 };
8649 {
8650 let src = ck_dev.rows_kv.slice(i * latent..(i + 1) * latent);
8651 let mut dst = pend_kv.slice_mut(slot * latent..(slot + 1) * latent);
8652 stream.memcpy_dtod(&src, &mut dst).map_err(e("rb row kv"))?;
8653 let src = ck_dev.rows_sc.slice(i * latent..(i + 1) * latent);
8654 let mut dst = pend_score.slice_mut(slot * latent..(slot + 1) * latent);
8655 stream.memcpy_dtod(&src, &mut dst).map_err(e("rb row sc"))?;
8656 }
8657 if (pos + 1) % ratio != 0 {
8658 continue;
8659 }
8660 if overlap {
8661 {
8662 let src = pend_kv.slice(ratio * latent..2 * ratio * latent);
8663 let mut dst = shift.slice_mut(0..ratio * latent);
8664 stream.memcpy_dtod(&src, &mut dst).map_err(e("rbshift1"))?;
8665 }
8666 {
8667 let src = shift.slice(0..ratio * latent);
8668 let mut dst = pend_kv.slice_mut(0..ratio * latent);
8669 stream.memcpy_dtod(&src, &mut dst).map_err(e("rbshift2"))?;
8670 }
8671 {
8672 let src = pend_score.slice(ratio * latent..2 * ratio * latent);
8673 let mut dst = shift.slice_mut(0..ratio * latent);
8674 stream.memcpy_dtod(&src, &mut dst).map_err(e("rbshift3"))?;
8675 }
8676 {
8677 let src = shift.slice(0..ratio * latent);
8678 let mut dst = pend_score.slice_mut(0..ratio * latent);
8679 stream.memcpy_dtod(&src, &mut dst).map_err(e("rbshift4"))?;
8680 }
8681 }
8682 *blocks += 1;
8683 }
8684 Ok(())
8685 }
8686}
8687
8688impl Dsv4Gpu {
8689 #[allow(clippy::too_many_arguments)]
8695 fn block_verify_dev(
8696 &self,
8697 st: &Stage,
8698 layer: &LayerDev,
8699 cache: &mut LayerCache,
8700 lck: &mut LayerCkptDev,
8701 vws: &mut VerifyWs,
8702 input_rx: bool,
8703 pos0: usize,
8704 t: usize,
8705 toks: &[u32],
8706 host_math: bool,
8707 ) -> Res<()> {
8708 let d = self.model.cfg();
8709 let mc = &self.model.mc;
8710 let hc = d.hc_mult as usize;
8711 let hidden = mc.n_embd as usize;
8712 let heads = mc.n_head as usize;
8713 let hd = d.head_dim as usize;
8714 let rd = d.qk_rope_head_dim as usize;
8715 let q_lora = d.q_lora_rank as usize;
8716 let win = d.sliding_window as usize;
8717 let o_groups = d.o_groups as usize;
8718 let o_lora = d.o_lora_rank as usize;
8719 let eps = mc.rms_eps;
8720 let iters = d.hc_sinkhorn_iters;
8721 let hc_eps = d.hc_eps;
8722 let stream = st.gpu.stream();
8723 let fc_dev: *const f32 = if layer.ratio != 0 {
8724 st.fc_yarn.device_ptr(&stream).0 as *const f32
8725 } else {
8726 st.fc_plain.device_ptr(&stream).0 as *const f32
8727 };
8728 let clamp_only = (self.variant == ActQuantVariant::ClampOnly) as i32;
8729 let trans_base = lck.trans_base;
8730 let LayerCache {
8731 kvc,
8732 n_blocks,
8733 pend_kv,
8734 pend_score,
8735 ikvc,
8736 i_blocks,
8737 ipend_kv,
8738 ipend_score,
8739 } = cache;
8740
8741 let h_in_ptr: *const f32 = if input_rx {
8743 vws.h_rx.device_ptr(&stream).0 as *const f32
8744 } else {
8745 vws.h_a.device_ptr(&stream).0 as *const f32
8746 };
8747 self.hc_pre_batch_dev(
8748 st,
8749 h_in_ptr,
8750 &layer.hc_attn_fn,
8751 &layer.hc_attn_base,
8752 &layer.hc_attn_scale,
8753 &layer.hc_attn_base_dev,
8754 &layer.hc_attn_scale_dev,
8755 vws,
8756 t,
8757 hc,
8758 hidden,
8759 iters,
8760 hc_eps,
8761 host_math,
8762 )?;
8763 unsafe {
8764 ck(
8765 "rmsnorm attn batch",
8766 self.rmsnorm_arm(
8767 dpf!(vws.y_hc, &stream),
8768 dpf!(layer.attn_norm, &stream),
8769 dpm!(vws.x, &stream),
8770 t as i32,
8771 hidden as i32,
8772 eps,
8773 sp(&stream),
8774 ),
8775 )?;
8776 }
8777
8778 Self::gemm_m_dev(
8780 st,
8781 vws.x.device_ptr(&stream).0 as *const f32,
8782 &mut vws.gemm_xb,
8783 dwsel(self.dense_fp8, &stream, &layer.wq_a, &layer.wq_a_fp8),
8784 t,
8785 q_lora,
8786 hidden,
8787 vws.qr.device_ptr_mut(&stream).0 as *mut f32,
8788 )?;
8789 unsafe {
8790 ck(
8791 "rmsnorm q batch",
8792 self.rmsnorm_arm(
8793 dpf!(vws.qr, &stream),
8794 dpf!(layer.q_norm, &stream),
8795 dpm!(vws.qr, &stream),
8796 t as i32,
8797 q_lora as i32,
8798 eps,
8799 sp(&stream),
8800 ),
8801 )?;
8802 ck(
8803 "cvt qr batch",
8804 k::memra_dsv4_cvt_bf16(
8805 dpf!(vws.qr, &stream),
8806 vws.qr_b.device_ptr_mut(&stream).0 as *mut c_void,
8807 (t * q_lora) as i64,
8808 sp(&stream),
8809 ),
8810 )?;
8811 }
8812 Self::gemv_m_dev(
8813 st,
8814 dwsel(self.dense_fp8, &stream, &layer.wq_b, &layer.wq_b_fp8),
8815 vws.qr_b.device_ptr(&stream).0 as *const c_void,
8816 vws.q.device_ptr_mut(&stream).0 as *mut f32,
8817 t,
8818 heads * hd,
8819 q_lora,
8820 0,
8821 0,
8822 )?;
8823 unsafe {
8824 ck(
8825 "headrms batch",
8826 self.headrms_arm(
8827 dpm!(vws.q, &stream),
8828 (t * heads) as i32,
8829 hd as i32,
8830 eps,
8831 sp(&stream),
8832 ),
8833 )?;
8834 ck(
8835 "rope q batch",
8836 k::memra_dsv4_rope(
8837 dpm!(vws.q, &stream),
8838 t as i32,
8839 heads as i32,
8840 hd as i32,
8841 rd as i32,
8842 fc_dev,
8843 vws.pos_dev.device_ptr(&stream).0 as *const i32,
8844 0,
8845 sp(&stream),
8846 ),
8847 )?;
8848 }
8849
8850 Self::gemm_m_dev(
8852 st,
8853 vws.x.device_ptr(&stream).0 as *const f32,
8854 &mut vws.gemm_xb,
8855 dwsel(self.dense_fp8, &stream, &layer.wkv, &layer.wkv_fp8),
8856 t,
8857 hd,
8858 hidden,
8859 vws.kv.device_ptr_mut(&stream).0 as *mut f32,
8860 )?;
8861 unsafe {
8862 ck(
8863 "rmsnorm kv batch",
8864 self.rmsnorm_arm(
8865 dpf!(vws.kv, &stream),
8866 dpf!(layer.kv_norm, &stream),
8867 dpm!(vws.kv, &stream),
8868 t as i32,
8869 hd as i32,
8870 eps,
8871 sp(&stream),
8872 ),
8873 )?;
8874 ck(
8875 "rope kv batch",
8876 k::memra_dsv4_rope(
8877 dpm!(vws.kv, &stream),
8878 t as i32,
8879 1,
8880 hd as i32,
8881 rd as i32,
8882 fc_dev,
8883 vws.pos_dev.device_ptr(&stream).0 as *const i32,
8884 0,
8885 sp(&stream),
8886 ),
8887 )?;
8888 ck(
8889 "act_quant kv batch",
8890 k::memra_dsv4_act_quant(
8891 dpm!(vws.kv, &stream),
8892 t as i32,
8893 hd as i64,
8894 (hd - rd) as i32,
8895 64,
8896 clamp_only,
8897 sp(&stream),
8898 ),
8899 )?;
8900 }
8901 {
8902 let src = vws.kv.slice(0..t * hd);
8903 let mut dst = kvc.slice_mut(trans_base * hd..(trans_base + t) * hd);
8904 stream
8905 .memcpy_dtod(&src, &mut dst)
8906 .map_err(e("transient ring write"))?;
8907 }
8908
8909 let mut slots = win;
8911 if layer.ratio != 0 {
8912 let ratio = layer.ratio;
8913 let nbs: Vec<usize> = (0..t).map(|i| (pos0 + i + 1) / ratio).collect();
8916 if let Some(ix) = &layer.idx {
8917 Self::gemv_m_dev(
8919 st,
8920 dwsel(self.dense_fp8, &stream, &ix.wq_b, &ix.wq_b_fp8),
8921 vws.qr_b.device_ptr(&stream).0 as *const c_void,
8922 vws.qi.device_ptr_mut(&stream).0 as *mut f32,
8923 t,
8924 ix.heads * ix.hd,
8925 q_lora,
8926 0,
8927 0,
8928 )?;
8929 unsafe {
8930 ck(
8931 "rope qi batch",
8932 k::memra_dsv4_rope(
8933 dpm!(vws.qi, &stream),
8934 t as i32,
8935 ix.heads as i32,
8936 ix.hd as i32,
8937 rd as i32,
8938 fc_dev,
8939 vws.pos_dev.device_ptr(&stream).0 as *const i32,
8940 0,
8941 sp(&stream),
8942 ),
8943 )?;
8944 let scale = (ix.hd as f32).powf(-0.5);
8945 ck(
8946 "hadamard qi batch",
8947 k::memra_dsv4_hadamard(
8948 dpm!(vws.qi, &stream),
8949 (t * ix.heads) as i32,
8950 ix.hd as i32,
8951 scale,
8952 sp(&stream),
8953 ),
8954 )?;
8955 ck(
8956 "fp4 qi batch",
8957 k::memra_dsv4_fp4_act_quant(
8958 dpm!(vws.qi, &stream),
8959 (t * ix.heads) as i32,
8960 ix.hd as i64,
8961 ix.hd as i32,
8962 sp(&stream),
8963 ),
8964 )?;
8965 }
8966 Self::gemm_m_dev(
8968 st,
8969 vws.x.device_ptr(&stream).0 as *const f32,
8970 &mut vws.gemm_xb,
8971 dwsel(
8972 self.dense_fp8,
8973 &stream,
8974 &ix.weights_proj,
8975 &ix.weights_proj_fp8,
8976 ),
8977 t,
8978 ix.heads,
8979 hidden,
8980 vws.wproj.device_ptr_mut(&stream).0 as *mut f32,
8981 )?;
8982 {
8984 let VerifyWs {
8985 x,
8986 cmp_emit,
8987 cmp_shift,
8988 ..
8989 } = vws;
8990 self.cmp_decode_batch_dev(
8991 st,
8992 &ix.cmp,
8993 x.device_ptr(&stream).0 as *const f32,
8994 t,
8995 pos0,
8996 hidden,
8997 &st.fc_yarn,
8998 rd,
8999 eps,
9000 lck.idx.as_mut().expect("idx ckpt"),
9001 cmp_emit,
9002 cmp_shift,
9003 ipend_kv.as_mut().expect("ipend"),
9004 ipend_score.as_mut().expect("ipend"),
9005 ikvc.as_mut().expect("ikvc"),
9006 0,
9007 i_blocks,
9008 )?;
9009 }
9010 debug_assert_eq!(*i_blocks, nbs[t - 1], "indexer block count (batch)");
9011 let kks: Vec<usize> = nbs.iter().map(|&nb| ix.topk.min(nb)).collect();
9012 let tail_max = kks.iter().cloned().max().unwrap_or(0);
9013 slots = win + tail_max;
9014 for i in 0..t {
9015 let pos = pos0 + i;
9016 let idx_off = i * vws.idx_stride;
9017 unsafe {
9018 ck(
9019 "build_idx_redirect fine",
9020 k::memra_dsv4_build_idx_redirect(
9021 (vws.idx.device_ptr_mut(&stream).0 as usize + idx_off * 4)
9022 as *mut i32,
9023 pos as i32,
9024 win as i32,
9025 0, slots as i32,
9027 pos0 as i32,
9028 trans_base as i32,
9029 sp(&stream),
9030 ),
9031 )?;
9032 }
9033 let nb = nbs[i];
9034 if nb == 0 {
9035 continue;
9036 }
9037 let wscale = ((ix.hd as f64).powf(-0.5) * (ix.heads as f64).powf(-0.5)) as f32;
9038 unsafe {
9039 ck(
9040 "indexer_score batch",
9041 self.indexer_score_arm(
9042 (vws.qi.device_ptr(&stream).0 as usize + i * ix.heads * ix.hd * 4)
9043 as *const f32,
9044 dpf!(ikvc.as_ref().expect("ikvc"), &stream),
9045 (vws.wproj.device_ptr(&stream).0 as usize + i * ix.heads * 4)
9046 as *const f32,
9047 wscale,
9048 dpm!(vws.score, &stream),
9049 1,
9050 ix.heads as i32,
9051 ix.hd as i32,
9052 nb as i32,
9053 ratio as i32,
9054 nb as i32,
9055 sp(&stream),
9056 ),
9057 )?;
9058 }
9059 let kk = kks[i];
9060 if host_math {
9061 let score_h = {
9062 let view = vws.score.slice(0..nb);
9063 let mut v = vec![0f32; nb];
9064 stream
9065 .memcpy_dtoh(&view, &mut v[..])
9066 .map_err(e("dtoh sc b"))?;
9067 stream.synchronize().map_err(e("sync sc b"))?;
9068 v
9069 };
9070 let mut order: Vec<usize> = (0..nb).collect();
9071 order.sort_by(|&a, &b| {
9072 score_h[b]
9073 .partial_cmp(&score_h[a])
9074 .unwrap_or(std::cmp::Ordering::Equal)
9075 .then(a.cmp(&b))
9076 });
9077 let cidx: Vec<i32> = order
9078 .into_iter()
9079 .take(kk)
9080 .map(|j| (j + win) as i32)
9081 .collect();
9082 let mut dst = vws.idx.slice_mut(idx_off + win..idx_off + win + kk);
9083 stream
9084 .memcpy_htod(&cidx, &mut dst)
9085 .map_err(e("htod idx b"))?;
9086 } else {
9087 unsafe {
9088 let idx_tail_ptr = (vws.idx.device_ptr_mut(&stream).0 as usize
9089 + (idx_off + win) * 4)
9090 as *mut i32;
9091 ck(
9092 "topk_idx batch",
9093 k::memra_dsv4_topk_idx(
9094 dpf!(vws.score, &stream),
9095 nb as i32,
9096 kk as i32,
9097 win as i32,
9098 idx_tail_ptr,
9099 sp(&stream),
9100 ),
9101 )?;
9102 }
9103 }
9104 }
9105 } else {
9106 let tail_max = nbs.iter().cloned().max().unwrap_or(0);
9107 slots = win + tail_max;
9108 for (i, &nb_i) in nbs.iter().enumerate() {
9109 let pos = pos0 + i;
9110 let idx_off = i * vws.idx_stride;
9111 unsafe {
9112 ck(
9113 "build_idx_redirect coarse",
9114 k::memra_dsv4_build_idx_redirect(
9115 (vws.idx.device_ptr_mut(&stream).0 as usize + idx_off * 4)
9116 as *mut i32,
9117 pos as i32,
9118 win as i32,
9119 nb_i as i32,
9120 slots as i32,
9121 pos0 as i32,
9122 trans_base as i32,
9123 sp(&stream),
9124 ),
9125 )?;
9126 }
9127 }
9128 }
9129 {
9131 let VerifyWs {
9132 x,
9133 cmp_emit,
9134 cmp_shift,
9135 ..
9136 } = vws;
9137 self.cmp_decode_batch_dev(
9138 st,
9139 layer.cmp.as_ref().expect("ratio!=0 has compressor"),
9140 x.device_ptr(&stream).0 as *const f32,
9141 t,
9142 pos0,
9143 hidden,
9144 &st.fc_yarn,
9145 rd,
9146 eps,
9147 lck.cmp.as_mut().expect("cmp ckpt"),
9148 cmp_emit,
9149 cmp_shift,
9150 pend_kv.as_mut().expect("pend"),
9151 pend_score.as_mut().expect("pend"),
9152 kvc,
9153 win,
9154 n_blocks,
9155 )?;
9156 }
9157 debug_assert_eq!(*n_blocks, nbs[t - 1], "attn block count (batch)");
9158 } else {
9159 for i in 0..t {
9160 let pos = pos0 + i;
9161 let idx_off = i * vws.idx_stride;
9162 unsafe {
9163 ck(
9164 "build_idx_redirect window-only",
9165 k::memra_dsv4_build_idx_redirect(
9166 (vws.idx.device_ptr_mut(&stream).0 as usize + idx_off * 4) as *mut i32,
9167 pos as i32,
9168 win as i32,
9169 -1,
9170 win as i32,
9171 pos0 as i32,
9172 trans_base as i32,
9173 sp(&stream),
9174 ),
9175 )?;
9176 }
9177 }
9178 }
9179
9180 let scale = (hd as f64).powf(-0.5) as f32;
9183 unsafe {
9184 if self.chains_f32 {
9185 ck(
9186 "sink_attn_dec_mq_f32acc",
9187 k::memra_dsv4_sink_attn_dec_mq_f32acc(
9188 dpf!(vws.q, &stream),
9189 dpf!(kvc, &stream),
9190 vws.idx.device_ptr(&stream).0 as *const i32,
9191 dpf!(layer.sink, &stream),
9192 dpm!(vws.sink_scores, &stream),
9193 dpm!(vws.sink_evals, &stream),
9194 vws.sink_den.device_ptr_mut(&stream).0 as *mut f32,
9195 dpm!(vws.o, &stream),
9196 t as i32,
9197 heads as i32,
9198 hd as i32,
9199 slots as i32,
9200 vws.idx_stride as i32,
9201 scale,
9202 sp(&stream),
9203 ),
9204 )?;
9205 } else {
9206 ck(
9207 "sink_attn_dec_mq",
9208 k::memra_dsv4_sink_attn_dec_mq(
9209 dpf!(vws.q, &stream),
9210 dpf!(kvc, &stream),
9211 vws.idx.device_ptr(&stream).0 as *const i32,
9212 dpf!(layer.sink, &stream),
9213 dpm!(vws.sink_scores, &stream),
9214 dpm!(vws.sink_evals, &stream),
9215 vws.sink_den.device_ptr_mut(&stream).0 as *mut f64,
9216 dpm!(vws.o, &stream),
9217 t as i32,
9218 heads as i32,
9219 hd as i32,
9220 slots as i32,
9221 vws.idx_stride as i32,
9222 scale,
9223 sp(&stream),
9224 ),
9225 )?;
9226 }
9227 ck(
9228 "rope o inv batch",
9229 k::memra_dsv4_rope(
9230 dpm!(vws.o, &stream),
9231 t as i32,
9232 heads as i32,
9233 hd as i32,
9234 rd as i32,
9235 fc_dev,
9236 vws.pos_dev.device_ptr(&stream).0 as *const i32,
9237 1,
9238 sp(&stream),
9239 ),
9240 )?;
9241 }
9242
9243 let gw = heads / o_groups * hd;
9245 unsafe {
9246 ck(
9247 "cvt o batch",
9248 k::memra_dsv4_cvt_bf16(
9249 dpf!(vws.o, &stream),
9250 vws.o_b.device_ptr_mut(&stream).0 as *mut c_void,
9251 (t * heads * hd) as i64,
9252 sp(&stream),
9253 ),
9254 )?;
9255 }
9256 let wo_a_dw = dwsel(self.dense_fp8, &stream, &layer.wo_a, &layer.wo_a_fp8);
9257 for g in 0..o_groups {
9258 Self::gemv_m_dev(
9259 st,
9260 wo_a_dw.offset_rows(g * o_lora, gw),
9261 (vws.o_b.device_ptr(&stream).0 as usize + g * gw * 2) as *const c_void,
9262 (vws.og.device_ptr_mut(&stream).0 as usize + g * o_lora * 4) as *mut f32,
9263 t,
9264 o_lora,
9265 gw,
9266 heads * hd,
9267 o_groups * o_lora,
9268 )?;
9269 }
9270 Self::gemm_m_dev(
9271 st,
9272 vws.og.device_ptr(&stream).0 as *const f32,
9273 &mut vws.gemm_xb,
9274 dwsel(self.dense_fp8, &stream, &layer.wo_b, &layer.wo_b_fp8),
9275 t,
9276 hidden,
9277 o_groups * o_lora,
9278 vws.attn_out.device_ptr_mut(&stream).0 as *mut f32,
9279 )?;
9280
9281 unsafe {
9283 ck(
9284 "hc_post attn batch",
9285 k::memra_dsv4_hc_post(
9286 dpf!(vws.attn_out, &stream),
9287 h_in_ptr,
9288 dpf!(vws.post, &stream),
9289 dpf!(vws.comb, &stream),
9290 dpm!(vws.h_b, &stream),
9291 t as i32,
9292 hc as i32,
9293 hidden as i32,
9294 sp(&stream),
9295 ),
9296 )?;
9297 }
9298
9299 let h_b_ptr = vws.h_b.device_ptr(&stream).0 as *const f32;
9301 self.hc_pre_batch_dev(
9302 st,
9303 h_b_ptr,
9304 &layer.hc_ffn_fn,
9305 &layer.hc_ffn_base,
9306 &layer.hc_ffn_scale,
9307 &layer.hc_ffn_base_dev,
9308 &layer.hc_ffn_scale_dev,
9309 vws,
9310 t,
9311 hc,
9312 hidden,
9313 iters,
9314 hc_eps,
9315 host_math,
9316 )?;
9317 unsafe {
9318 ck(
9319 "rmsnorm ffn batch",
9320 self.rmsnorm_arm(
9321 dpf!(vws.y_hc, &stream),
9322 dpf!(layer.ffn_norm, &stream),
9323 dpm!(vws.xf, &stream),
9324 t as i32,
9325 hidden as i32,
9326 eps,
9327 sp(&stream),
9328 ),
9329 )?;
9330 }
9331 self.moe_verify_dev(st, layer, vws, t, toks, host_math)?;
9332 unsafe {
9333 ck(
9334 "hc_post ffn batch",
9335 k::memra_dsv4_hc_post(
9336 dpf!(vws.y, &stream),
9337 dpf!(vws.h_b, &stream),
9338 dpf!(vws.post, &stream),
9339 dpf!(vws.comb, &stream),
9340 dpm!(vws.h_a, &stream),
9341 t as i32,
9342 hc as i32,
9343 hidden as i32,
9344 sp(&stream),
9345 ),
9346 )?;
9347 }
9348 Ok(())
9349 }
9350
9351 fn moe_verify_dev(
9356 &self,
9357 st: &Stage,
9358 layer: &LayerDev,
9359 vws: &mut VerifyWs,
9360 t: usize,
9361 toks: &[u32],
9362 host_math: bool,
9363 ) -> Res<()> {
9364 let mc = &self.model.mc;
9365 let d = self.model.cfg();
9366 let moe = mc.moe.as_ref().expect("moe");
9367 let hidden = mc.n_embd as usize;
9368 let ne = moe.expert_count as usize;
9369 let topk = moe.expert_used_count as usize;
9370 let inter = moe.expert_ff_length as usize;
9371 let limit = d.swiglu_limit;
9372 let stream = st.gpu.stream();
9373 let kind = match layer.expert_kind {
9374 ExpertKind::Nvfp4 => 0i32,
9375 ExpertKind::Mxfp4 => 1i32,
9376 };
9377 let wstride = (inter * hidden / 2) as i64;
9378 let sstride = match layer.expert_kind {
9379 ExpertKind::Nvfp4 => (inter * hidden / 16) as i64,
9380 ExpertKind::Mxfp4 => (inter * hidden / 32) as i64,
9381 };
9382 let slots = t * topk;
9383
9384 self.dots_m_dev(
9385 st,
9386 vws.xf.device_ptr(&stream).0 as *const f32,
9387 layer.gate_w.device_ptr(&stream).0 as *const c_void,
9388 0,
9389 t,
9390 hidden,
9391 ne,
9392 vws.raw.device_ptr_mut(&stream).0 as *mut f32,
9393 )?;
9394 if host_math {
9395 let raw_h = {
9396 let view = vws.raw.slice(0..t * ne);
9397 let mut v = vec![0f32; t * ne];
9398 stream
9399 .memcpy_dtoh(&view, &mut v[..])
9400 .map_err(e("dtoh raw b"))?;
9401 stream.synchronize().map_err(e("sync raw b"))?;
9402 v
9403 };
9404 let (indices, weights) =
9405 Self::route_host(layer, &raw_h, toks, t, ne, topk, d.routed_scaling_factor);
9406 let sel: Vec<i32> = indices.iter().map(|&x| x as i32).collect();
9407 let mut order = vec![0i32; t * topk];
9408 for p in 0..t {
9409 let mut o: Vec<i32> = (0..topk as i32).collect();
9410 o.sort_by_key(|&s| indices[p * topk + s as usize]);
9411 order[p * topk..(p + 1) * topk].copy_from_slice(&o);
9412 }
9413 let mut dst = vws.sel.slice_mut(0..t * topk);
9414 stream
9415 .memcpy_htod(&sel, &mut dst)
9416 .map_err(e("htod sel b"))?;
9417 let mut dst = vws.selw.slice_mut(0..t * topk);
9418 stream
9419 .memcpy_htod(&weights, &mut dst)
9420 .map_err(e("htod selw b"))?;
9421 let mut dst = vws.order.slice_mut(0..t * topk);
9422 stream
9423 .memcpy_htod(&order, &mut dst)
9424 .map_err(e("htod order b"))?;
9425 } else {
9426 unsafe {
9427 ck(
9428 "route_m",
9429 k::memra_dsv4_route_m(
9430 dpf!(vws.raw, &stream),
9431 layer
9432 .gate_bias_dev
9433 .as_ref()
9434 .map(|b| b.device_ptr(&stream).0 as *const f32)
9435 .unwrap_or(std::ptr::null()),
9436 layer
9437 .tid2eid_dev
9438 .as_ref()
9439 .map(|x| x.device_ptr(&stream).0 as *const i32)
9440 .unwrap_or(std::ptr::null()),
9441 vws.tok.device_ptr(&stream).0 as *const i32,
9442 t as i32,
9443 ne as i32,
9444 topk as i32,
9445 d.routed_scaling_factor,
9446 vws.sel.device_ptr_mut(&stream).0 as *mut i32,
9447 vws.selw.device_ptr_mut(&stream).0 as *mut f32,
9448 vws.order.device_ptr_mut(&stream).0 as *mut i32,
9449 sp(&stream),
9450 ),
9451 )?;
9452 }
9453 }
9454
9455 unsafe {
9456 ck(
9457 "act_quant_fp8 x batch",
9458 k::memra_dsv4_act_quant_fp8(
9459 dpf!(vws.xf, &stream),
9460 vws.xq.device_ptr_mut(&stream).0 as *mut c_void,
9461 dpm!(vws.xs, &stream),
9462 t as i32,
9463 hidden as i32,
9464 sp(&stream),
9465 ),
9466 )?;
9467 for (proj, dst) in [(0i32, &mut vws.g1), (2i32, &mut vws.g3)] {
9468 ck(
9469 "fp4_gemm_sel_g w1/w3",
9470 k::memra_dsv4_fp4_gemm_sel_g(
9471 dp!(vws.xq, &stream),
9472 dpf!(vws.xs, &stream),
9473 dp!(layer.experts_w, &stream),
9474 dp!(layer.experts_sc, &stream),
9475 dpf!(layer.experts_s2_dev, &stream),
9476 vws.sel.device_ptr(&stream).0 as *const i32,
9477 proj,
9478 0,
9479 kind,
9480 dpm!(*dst, &stream),
9481 slots as i32,
9482 inter as i32,
9483 hidden as i32,
9484 wstride,
9485 sstride,
9486 topk as i32,
9487 sp(&stream),
9488 ),
9489 )?;
9490 }
9491 ck(
9492 "swiglu batch",
9493 k::memra_dsv4_swiglu(
9494 dpf!(vws.g1, &stream),
9495 dpf!(vws.g3, &stream),
9496 dpm!(vws.hbuf, &stream),
9497 slots as i32,
9498 inter as i32,
9499 limit,
9500 vws.selw.device_ptr(&stream).0 as *const f32,
9501 sp(&stream),
9502 ),
9503 )?;
9504 ck(
9505 "act_quant_fp8 h batch",
9506 k::memra_dsv4_act_quant_fp8(
9507 dpf!(vws.hbuf, &stream),
9508 vws.hq.device_ptr_mut(&stream).0 as *mut c_void,
9509 dpm!(vws.hs, &stream),
9510 slots as i32,
9511 inter as i32,
9512 sp(&stream),
9513 ),
9514 )?;
9515 ck(
9516 "fp4_gemm_sel_g w2",
9517 k::memra_dsv4_fp4_gemm_sel_g(
9518 dp!(vws.hq, &stream),
9519 dpf!(vws.hs, &stream),
9520 dp!(layer.experts_w, &stream),
9521 dp!(layer.experts_sc, &stream),
9522 dpf!(layer.experts_s2_dev, &stream),
9523 vws.sel.device_ptr(&stream).0 as *const i32,
9524 1,
9525 1,
9526 kind,
9527 dpm!(vws.contrib, &stream),
9528 slots as i32,
9529 hidden as i32,
9530 inter as i32,
9531 wstride,
9532 sstride,
9533 0,
9534 sp(&stream),
9535 ),
9536 )?;
9537 ck(
9538 "combine_rows_m",
9539 k::memra_dsv4_combine_rows_m(
9540 dpf!(vws.contrib, &stream),
9541 vws.order.device_ptr(&stream).0 as *const i32,
9542 topk as i32,
9543 dpm!(vws.y, &stream),
9544 hidden as i64,
9545 t as i32,
9546 sp(&stream),
9547 ),
9548 )?;
9549 ck(
9550 "cvt xb batch",
9551 k::memra_dsv4_cvt_bf16(
9552 dpf!(vws.xf, &stream),
9553 vws.xb.device_ptr_mut(&stream).0 as *mut c_void,
9554 (t * hidden) as i64,
9555 sp(&stream),
9556 ),
9557 )?;
9558 }
9559 let sh_inter = vws.sg1.len() / vws.tmax;
9560 Self::gemv_m_dev(
9561 st,
9562 dwsel(
9563 self.dense_fp8,
9564 &stream,
9565 &layer.shared_w[0],
9566 &layer.shared_fp8[0],
9567 ),
9568 vws.xb.device_ptr(&stream).0 as *const c_void,
9569 vws.sg1.device_ptr_mut(&stream).0 as *mut f32,
9570 t,
9571 sh_inter,
9572 hidden,
9573 0,
9574 0,
9575 )?;
9576 Self::gemv_m_dev(
9577 st,
9578 dwsel(
9579 self.dense_fp8,
9580 &stream,
9581 &layer.shared_w[2],
9582 &layer.shared_fp8[2],
9583 ),
9584 vws.xb.device_ptr(&stream).0 as *const c_void,
9585 vws.sg3.device_ptr_mut(&stream).0 as *mut f32,
9586 t,
9587 sh_inter,
9588 hidden,
9589 0,
9590 0,
9591 )?;
9592 unsafe {
9593 ck(
9594 "swiglu sh batch",
9595 k::memra_dsv4_swiglu(
9596 dpf!(vws.sg1, &stream),
9597 dpf!(vws.sg3, &stream),
9598 dpm!(vws.shbuf, &stream),
9599 t as i32,
9600 sh_inter as i32,
9601 limit,
9602 std::ptr::null(),
9603 sp(&stream),
9604 ),
9605 )?;
9606 ck(
9607 "cvt sh batch",
9608 k::memra_dsv4_cvt_bf16(
9609 dpf!(vws.shbuf, &stream),
9610 vws.shb16.device_ptr_mut(&stream).0 as *mut c_void,
9611 (t * sh_inter) as i64,
9612 sp(&stream),
9613 ),
9614 )?;
9615 }
9616 Self::gemv_m_dev(
9617 st,
9618 dwsel(
9619 self.dense_fp8,
9620 &stream,
9621 &layer.shared_w[1],
9622 &layer.shared_fp8[1],
9623 ),
9624 vws.shb16.device_ptr(&stream).0 as *const c_void,
9625 vws.sh_out.device_ptr_mut(&stream).0 as *mut f32,
9626 t,
9627 hidden,
9628 sh_inter,
9629 0,
9630 0,
9631 )?;
9632 unsafe {
9633 ck(
9634 "add shared batch",
9635 k::memra_dsv4_add_inplace(
9636 dpm!(vws.y, &stream),
9637 dpf!(vws.sh_out, &stream),
9638 (t * hidden) as i64,
9639 sp(&stream),
9640 ),
9641 )?;
9642 }
9643 Ok(())
9644 }
9645
9646 fn head_logits_batch_dev(&self, vws: &mut VerifyWs, t: usize, host_math: bool) -> Res<()> {
9650 let d = self.model.cfg();
9651 let mc = &self.model.mc;
9652 let hc = d.hc_mult as usize;
9653 let hidden = mc.n_embd as usize;
9654 let eps = mc.rms_eps;
9655 let last = self.stages.len() - 1;
9656 let st = &self.stages[last];
9657 let stream = st.gpu.stream();
9658 let w = hc * hidden;
9659 let fn_w = st.hc_head_fn.as_ref().expect("hc_head_fn");
9660 let norm = st.trunk_norm.as_ref().expect("trunk norm");
9661 let vocab = vws.logits.len() / vws.tmax;
9662 self.dots_m_dev(
9663 st,
9664 vws.h_a.device_ptr(&stream).0 as *const f32,
9665 fn_w.device_ptr(&stream).0 as *const c_void,
9666 0,
9667 t,
9668 w,
9669 hc,
9670 vws.head_mixes.device_ptr_mut(&stream).0 as *mut f32,
9671 )?;
9672 unsafe {
9673 ck(
9674 "rowsq head batch",
9675 self.rowsq_scale_arm(
9676 dpf!(vws.h_a, &stream),
9677 dpm!(vws.head_mixes, &stream),
9678 t as i32,
9679 w as i32,
9680 hc as i32,
9681 eps,
9682 sp(&stream),
9683 ),
9684 )?;
9685 }
9686 if host_math {
9687 let mut mixes_h = vec![0f32; t * hc];
9688 let view = vws.head_mixes.slice(0..t * hc);
9689 stream
9690 .memcpy_dtoh(&view, &mut mixes_h[..])
9691 .map_err(e("dtoh head mixes b"))?;
9692 stream.synchronize().map_err(e("sync head mixes b"))?;
9693 for p in 0..t {
9694 for c in 0..hc {
9695 let m = mixes_h[p * hc + c];
9696 mixes_h[p * hc + c] =
9697 sigmoid_f32(m * self.hc_head_scale[0] + self.hc_head_base[c]) + d.hc_eps;
9698 }
9699 }
9700 let mut dst = vws.head_pre.slice_mut(0..t * hc);
9701 stream
9702 .memcpy_htod(&mixes_h, &mut dst)
9703 .map_err(e("htod head pre b"))?;
9704 } else {
9705 unsafe {
9706 ck(
9707 "hc_head_pre_m",
9708 k::memra_dsv4_hc_head_pre_m(
9709 dpf!(vws.head_mixes, &stream),
9710 st.hc_head_scale_dev
9711 .as_ref()
9712 .expect("head scale dev")
9713 .device_ptr(&stream)
9714 .0 as *const f32,
9715 st.hc_head_base_dev
9716 .as_ref()
9717 .expect("head base dev")
9718 .device_ptr(&stream)
9719 .0 as *const f32,
9720 dpm!(vws.head_pre, &stream),
9721 t as i32,
9722 hc as i32,
9723 d.hc_eps,
9724 sp(&stream),
9725 ),
9726 )?;
9727 }
9728 }
9729 unsafe {
9730 ck(
9731 "hc_collapse head batch",
9732 k::memra_dsv4_hc_collapse(
9733 dpf!(vws.h_a, &stream),
9734 dpf!(vws.head_pre, &stream),
9735 dpm!(vws.collapsed, &stream),
9736 t as i32,
9737 hc as i32,
9738 hidden as i32,
9739 sp(&stream),
9740 ),
9741 )?;
9742 ck(
9743 "rmsnorm head batch",
9744 self.rmsnorm_arm(
9745 dpf!(vws.collapsed, &stream),
9746 dpf!(norm, &stream),
9747 dpm!(vws.collapsed, &stream),
9748 t as i32,
9749 hidden as i32,
9750 eps,
9751 sp(&stream),
9752 ),
9753 )?;
9754 }
9755 let head_ptr = st.head.as_ref().expect("head").device_ptr(&stream).0 as *const c_void;
9756 self.dots_m_dev(
9757 st,
9758 vws.collapsed.device_ptr(&stream).0 as *const f32,
9759 head_ptr,
9760 1,
9761 t,
9762 hidden,
9763 vocab,
9764 vws.logits.device_ptr_mut(&stream).0 as *mut f32,
9765 )?;
9766 Ok(())
9767 }
9768}
9769
9770pub struct SpecRoundGpu {
9772 pub start_pos: usize,
9773 pub drafts: Vec<u32>,
9774 pub accepts: usize,
9775 pub verified: usize,
9776 pub t_batch: usize,
9778 pub t_cap: usize,
9782 pub confidence: Vec<f32>,
9787 pub emitted: usize,
9789 pub round_us: u64,
9793}
9794
9795pub struct SpecRunGpu {
9796 pub tokens: Vec<u32>,
9797 pub rounds: Vec<SpecRoundGpu>,
9798}
9799
9800impl Dsv4Gpu {
9801 pub fn verify_batch_dev(
9810 &self,
9811 toks: &[u32],
9812 state: &mut DecodeState,
9813 vstate: &mut VerifyState,
9814 taps: Option<&mut CudaSlice<f32>>,
9815 want_logits: bool,
9816 ) -> Res<(Option<Vec<f32>>, Vec<u32>)> {
9817 let DecodePath::Device { host_math } = self.decode_path else {
9818 return Err("verify_batch_dev requires MEMRA_DSV4_DECODE_PATH=device".into());
9819 };
9820 let mc = &self.model.mc;
9821 let d = self.model.cfg();
9822 let t = toks.len();
9823 assert!(
9824 t >= 1 && t <= vstate.tmax,
9825 "round depth {t} > tmax {}",
9826 vstate.tmax
9827 );
9828 assert!(vstate.open.is_none(), "verify_batch_dev with an open round");
9829 let pos0 = state.pos;
9830 assert!(pos0 > 0, "batched verify needs prefill_with_cache first");
9831 assert!(
9832 pos0 + t <= self.max_seq,
9833 "round [{pos0}, {}) exceeds max_seq {}",
9834 pos0 + t,
9835 self.max_seq
9836 );
9837 let hidden = mc.n_embd as usize;
9838 let hc = d.hc_mult as usize;
9839 let n_trunk = (mc.n_layer - mc.nextn_predict_layers) as usize;
9840 let tok_i32: Vec<i32> = toks.iter().map(|&x| x as i32).collect();
9841 let pos_i32: Vec<i32> = (0..t).map(|i| (pos0 + i) as i32).collect();
9842
9843 for (si, st) in self.stages.iter().enumerate() {
9846 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx round"))?;
9847 let stream = st.gpu.stream();
9848 let vws = &mut vstate.ws[si];
9849 let mut dst = vws.tok.slice_mut(0..t);
9850 stream
9851 .memcpy_htod(&tok_i32, &mut dst)
9852 .map_err(e("htod tok round"))?;
9853 let mut dst = vws.pos_dev.slice_mut(0..t);
9854 stream
9855 .memcpy_htod(&pos_i32, &mut dst)
9856 .map_err(e("htod pos round"))?;
9857 }
9858
9859 {
9861 let st0 = &self.stages[0];
9862 st0.gpu.ctx.bind_to_thread().map_err(e("bind ctx0 round"))?;
9863 let stream0 = st0.gpu.stream();
9864 let vws0 = &mut vstate.ws[0];
9865 unsafe {
9866 ck(
9867 "embed_rows batch",
9868 k::memra_dsv4_embed_rows(
9869 st0.embed
9870 .as_ref()
9871 .expect("embed on stage 0")
9872 .device_ptr(&stream0)
9873 .0 as *const c_void,
9874 vws0.tok.device_ptr(&stream0).0 as *const i32,
9875 dpm!(vws0.emb, &stream0),
9876 t as i32,
9877 hidden as i32,
9878 sp(&stream0),
9879 ),
9880 )?;
9881 ck(
9882 "repeat_hc batch",
9883 k::memra_dsv4_repeat_hc(
9884 dpf!(vws0.emb, &stream0),
9885 dpm!(vws0.h_a, &stream0),
9886 t as i32,
9887 hc as i32,
9888 hidden as i32,
9889 sp(&stream0),
9890 ),
9891 )?;
9892 }
9893 }
9894
9895 let targets = self.dspark.as_ref().map(|ds| ds.targets.clone());
9896 let n_t = targets.as_ref().map(|x| x.len()).unwrap_or(0);
9897 let mut taps = taps;
9898 let mut cur_stage = 0usize;
9899 let mut input_rx = false;
9900 for il in 0..n_trunk {
9901 let stage = self.layer_stage[il];
9902 if stage != cur_stage {
9903 let bytes = t * hc * hidden * std::mem::size_of::<f32>();
9904 let src_stream = self.stages[cur_stage].gpu.stream();
9905 let dst_stream = self.stages[stage].gpu.stream();
9906 let (ws_src, ws_dst) = vstate.ws.split_at_mut(stage);
9907 let src_ws = &ws_src[cur_stage];
9908 let dst_ws = &mut ws_dst[0];
9909 self.stages[cur_stage]
9910 .gpu
9911 .ctx
9912 .bind_to_thread()
9913 .map_err(e("bind tx round"))?;
9914 let (sp_, _g0) = src_ws.h_a.device_ptr(&src_stream);
9915 let (dp_, _g1) = dst_ws.h_rx.device_ptr_mut(&src_stream);
9916 unsafe {
9917 cudarc::driver::result::memcpy_peer_async(
9918 self.stages[stage].gpu.ctx.cu_ctx(),
9919 dp_,
9920 self.stages[cur_stage].gpu.ctx.cu_ctx(),
9921 sp_,
9922 bytes,
9923 src_stream.cu_stream(),
9924 )
9925 .map_err(e("peer copy h round"))?;
9926 }
9927 let bnd = stage - 1;
9928 self.boundary_ev[bnd]
9929 .record(&src_stream)
9930 .map_err(e("ev record round"))?;
9931 dst_stream
9932 .wait(&self.boundary_ev[bnd])
9933 .map_err(e("ev wait round"))?;
9934 self.stages[stage]
9935 .gpu
9936 .ctx
9937 .bind_to_thread()
9938 .map_err(e("bind rx round"))?;
9939 cur_stage = stage;
9940 input_rx = true;
9941 }
9942 let st = &self.stages[stage];
9943 let lidx = st
9944 .layers
9945 .iter()
9946 .position(|l| l.il == il as u32)
9947 .unwrap_or_else(|| panic!("layer {il} not on stage {stage}"));
9948 self.block_verify_dev(
9949 st,
9950 &st.layers[lidx],
9951 &mut state.caches[il],
9952 &mut vstate.layers[il],
9953 &mut vstate.ws[stage],
9954 input_rx,
9955 pos0,
9956 t,
9957 toks,
9958 host_math,
9959 )?;
9960 input_rx = false;
9961 if let (Some(tp), Some(tg)) = (taps.as_mut(), targets.as_ref()) {
9963 if let Some(kk) = tg.iter().position(|&tl| tl == il) {
9964 let stream = self.stages[stage].gpu.stream();
9965 let vws = &mut vstate.ws[stage];
9966 unsafe {
9967 ck(
9968 "hc_mean tap batch",
9969 k::memra_dsv4_hc_mean(
9970 dpf!(vws.h_a, &stream),
9971 dpm!(vws.tap_tmp, &stream),
9972 t as i32,
9973 hc as i32,
9974 hidden as i32,
9975 sp(&stream),
9976 ),
9977 )?;
9978 ck(
9979 "place_cols tap batch",
9980 k::memra_dsv4_place_cols(
9981 dpf!(vws.tap_tmp, &stream),
9982 dpm!(**tp, &stream),
9983 t as i32,
9984 hidden as i32,
9985 (n_t * hidden) as i64,
9986 (kk * hidden) as i64,
9987 sp(&stream),
9988 ),
9989 )?;
9990 }
9991 }
9992 }
9993 }
9994
9995 let last = self.stages.len() - 1;
9996 assert_eq!(cur_stage, last, "device path expects the head stage last");
9997 self.head_logits_batch_dev(&mut vstate.ws[last], t, host_math)?;
9998 let stream_last = self.stages[last].gpu.stream();
9999 let vws = &mut vstate.ws[last];
10000 let vocab = vws.logits.len() / vws.tmax;
10001 let logits = if want_logits {
10002 let mut v = vec![0f32; t * vocab];
10003 let view = vws.logits.slice(0..t * vocab);
10004 stream_last
10005 .memcpy_dtoh(&view, &mut v[..])
10006 .map_err(e("dtoh logits batch"))?;
10007 stream_last.synchronize().map_err(e("sync logits batch"))?;
10008 Some(v)
10009 } else {
10010 None
10011 };
10012 let mut am = vec![0i32; t];
10013 if let Some(lg) = &logits {
10014 for (i, slot) in am.iter_mut().enumerate() {
10015 let row = &lg[i * vocab..(i + 1) * vocab];
10016 let mut best = 0usize;
10017 for j in 1..vocab {
10018 if row[j] > row[best] {
10019 best = j;
10020 }
10021 }
10022 *slot = best as i32;
10023 }
10024 } else {
10025 unsafe {
10026 for i in 0..t {
10027 ck(
10028 "argmax batch",
10029 k::memra_dsv4_argmax(
10030 (vws.logits.device_ptr(&stream_last).0 as usize + i * vocab * 4)
10031 as *const f32,
10032 vocab as i64,
10033 (vws.argmax.device_ptr_mut(&stream_last).0 as usize + i * 4)
10034 as *mut i32,
10035 sp(&stream_last),
10036 ),
10037 )?;
10038 }
10039 }
10040 let view = vws.argmax.slice(0..t);
10041 stream_last
10042 .memcpy_dtoh(&view, &mut am[..])
10043 .map_err(e("dtoh argmax batch"))?;
10044 stream_last.synchronize().map_err(e("sync argmax batch"))?;
10045 }
10046 vstate.open = Some((pos0, t));
10047 Ok((logits, am.into_iter().map(|x| x as u32).collect()))
10048 }
10049
10050 pub fn commit_verify_dev(
10055 &self,
10056 state: &mut DecodeState,
10057 vstate: &mut VerifyState,
10058 n_commit: usize,
10059 ) -> Res<()> {
10060 let (pos0, t) = vstate
10061 .open
10062 .take()
10063 .ok_or_else(|| "commit_verify_dev without an open round".to_string())?;
10064 assert!(
10065 n_commit >= 1 && n_commit <= t,
10066 "commit {n_commit} outside round width {t}"
10067 );
10068 let d = self.model.cfg();
10069 let mc = &self.model.mc;
10070 let hd = d.head_dim as usize;
10071 let win = d.sliding_window as usize;
10072 let n_trunk = (mc.n_layer - mc.nextn_predict_layers) as usize;
10073 let slot_rows: Vec<i32> = (0..n_commit).map(|j| ((pos0 + j) % win) as i32).collect();
10074 for il in 0..n_trunk {
10075 let stage = self.layer_stage[il];
10076 let st = &self.stages[stage];
10077 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx commit"))?;
10078 let stream = st.gpu.stream();
10079 let lck = &mut vstate.layers[il];
10080 let vws = &mut vstate.ws[stage];
10081 let cache = &mut state.caches[il];
10082 let trans_base = lck.trans_base;
10083 {
10086 let src = cache
10087 .kvc
10088 .slice(trans_base * hd..(trans_base + n_commit) * hd);
10089 let mut dst = vws.bounce.slice_mut(0..n_commit * hd);
10090 stream
10091 .memcpy_dtod(&src, &mut dst)
10092 .map_err(e("commit bounce"))?;
10093 }
10094 {
10095 let mut dst = vws.slot_rows.slice_mut(0..n_commit);
10096 stream
10097 .memcpy_htod(&slot_rows, &mut dst)
10098 .map_err(e("htod slot rows"))?;
10099 }
10100 unsafe {
10101 ck(
10102 "scatter_rows commit",
10103 k::memra_dsv4_scatter_rows(
10104 dpf!(vws.bounce, &stream),
10105 dpm!(cache.kvc, &stream),
10106 vws.slot_rows.device_ptr(&stream).0 as *const i32,
10107 n_commit as i32,
10108 hd as i32,
10109 sp(&stream),
10110 ),
10111 )?;
10112 }
10113 if let Some(ckd) = &lck.cmp {
10114 self.cmp_rollback_replay_dev(
10115 st,
10116 ckd,
10117 n_commit,
10118 t,
10119 pos0,
10120 &mut vws.cmp_shift,
10121 cache.pend_kv.as_mut().expect("pend kv"),
10122 cache.pend_score.as_mut().expect("pend sc"),
10123 &mut cache.n_blocks,
10124 )?;
10125 }
10126 if let Some(ckd) = &lck.idx {
10127 self.cmp_rollback_replay_dev(
10128 st,
10129 ckd,
10130 n_commit,
10131 t,
10132 pos0,
10133 &mut vws.cmp_shift,
10134 cache.ipend_kv.as_mut().expect("ipend kv"),
10135 cache.ipend_score.as_mut().expect("ipend sc"),
10136 &mut cache.i_blocks,
10137 )?;
10138 }
10139 }
10140 for st in &self.stages {
10141 st.gpu
10142 .ctx
10143 .bind_to_thread()
10144 .map_err(e("bind ctx commit sync"))?;
10145 st.gpu.stream().synchronize().map_err(e("commit sync"))?;
10146 }
10147 state.pos = pos0 + n_commit;
10148 Ok(())
10149 }
10150
10151 pub fn spec_greedy_batched_with(
10165 &self,
10166 prompt: &[u32],
10167 n_new: usize,
10168 state: &mut DecodeState,
10169 dstate: &mut DsparkState,
10170 vstate: &mut VerifyState,
10171 ) -> Res<SpecRunGpu> {
10172 let depth_cap = std::env::var("MEMRA_DSV4_SPEC_DEPTH")
10177 .ok()
10178 .and_then(|v| v.trim().parse::<usize>().ok())
10179 .filter(|t| *t > 0)
10180 .unwrap_or(usize::MAX)
10181 .max(1);
10182 if depth_cap != usize::MAX {
10183 println!("[spec] verify depth capped at T={depth_cap} (MEMRA_DSV4_SPEC_DEPTH)");
10184 }
10185 self.spec_greedy_batched_depth(prompt, n_new, state, dstate, vstate, depth_cap)
10186 }
10187
10188 pub fn spec_greedy_batched_depth(
10196 &self,
10197 prompt: &[u32],
10198 n_new: usize,
10199 state: &mut DecodeState,
10200 dstate: &mut DsparkState,
10201 vstate: &mut VerifyState,
10202 depth_cap: usize,
10203 ) -> Res<SpecRunGpu> {
10204 let vt = resolve_vt(
10207 std::env::var("MEMRA_DSV4_VT").ok().as_deref(),
10208 std::env::var("MEMRA_DSV4_VT_TAU").ok().as_deref(),
10209 std::env::var("MEMRA_DSV4_VT_FLOOR").ok().as_deref(),
10210 )?;
10211 self.spec_greedy_batched_policy(prompt, n_new, state, dstate, vstate, depth_cap, vt)
10212 }
10213
10214 #[allow(clippy::too_many_arguments)]
10219 #[allow(clippy::too_many_arguments)]
10220 pub fn spec_greedy_batched_policy(
10221 &self,
10222 prompt: &[u32],
10223 n_new: usize,
10224 state: &mut DecodeState,
10225 dstate: &mut DsparkState,
10226 vstate: &mut VerifyState,
10227 depth_cap: usize,
10228 vt: Dsv4Vt,
10229 ) -> Res<SpecRunGpu> {
10230 self.spec_greedy_batched_stream(prompt, n_new, state, dstate, vstate, depth_cap, vt, None)
10231 }
10232
10233 #[allow(clippy::too_many_arguments)]
10240 pub fn spec_greedy_batched_stream(
10241 &self,
10242 prompt: &[u32],
10243 n_new: usize,
10244 state: &mut DecodeState,
10245 dstate: &mut DsparkState,
10246 vstate: &mut VerifyState,
10247 depth_cap: usize,
10248 vt: Dsv4Vt,
10249 mut round_cb: Option<&mut dyn FnMut(&[u32]) -> bool>,
10250 ) -> Res<SpecRunGpu> {
10251 let p0 = prompt.len();
10252 assert!(n_new >= 1, "n_new must be positive");
10253 let pre = self.dspark_prefill_prime(prompt, state, dstate)?;
10254 let mut t_tok = {
10255 let lg = &pre.logits;
10256 let mut best = 0usize;
10257 for i in 1..lg.len() {
10258 if lg[i] > lg[best] {
10259 best = i;
10260 }
10261 }
10262 best as u32
10263 };
10264 let mut tokens: Vec<u32> = Vec::with_capacity(n_new);
10265 let mut rounds: Vec<SpecRoundGpu> = Vec::new();
10266 let mut mh_row = 0usize; let mut carry_pending = false;
10268 let profile_bracket = std::env::var("MEMRA_DSV4_BENCH_PROFILE").as_deref() == Ok("1");
10273 let depth_cap = depth_cap.max(1);
10274 if let Dsv4Vt::Slot { tau_logit, floor } = vt {
10275 println!(
10276 "[spec] vt policy: slot (tau_logit {tau_logit:.6}, floor {floor}) — \
10277 per-round verify window from the confidence head"
10278 );
10279 }
10280 while tokens.len() < n_new {
10281 if profile_bracket && rounds.len() == 4 {
10282 cudarc::driver::safe::profiler_start().map_err(e("profiler_start"))?;
10283 }
10284 if profile_bracket && rounds.len() == 12 {
10285 cudarc::driver::safe::profiler_stop().map_err(e("profiler_stop"))?;
10286 }
10287 let cb_from = tokens.len();
10288 if carry_pending {
10289 tokens.push(t_tok);
10290 if let Some(cb) = round_cb.as_deref_mut() {
10291 cb(&tokens[cb_from..]);
10292 }
10293 break;
10294 }
10295 let round_t0 = std::time::Instant::now();
10296 let prof_stream = if dsv4_prof_on() {
10297 Some(self.stages[self.stages.len() - 1].gpu.stream())
10298 } else {
10299 None
10300 };
10301 let _p_round = phase!("round", prof_stream.as_ref());
10302 let m0 = p0 + tokens.len();
10303 let prop = {
10304 let _p = phase!("1.drafter_forward", prof_stream.as_ref());
10305 self.dspark_forward_spec(dstate, t_tok, mh_row, m0 - 1, false)?
10306 };
10307 let k_drafts = prop.out_ids.len() - 1;
10308 tokens.push(t_tok);
10309 if tokens.len() == n_new {
10310 rounds.push(SpecRoundGpu {
10311 start_pos: m0 - 1,
10312 drafts: prop.out_ids[1..].to_vec(),
10313 accepts: 0,
10314 verified: 0,
10315 t_batch: 0,
10316 t_cap: 0,
10317 confidence: prop.confidence.clone(),
10318 emitted: 1,
10319 round_us: round_t0.elapsed().as_micros() as u64,
10320 });
10321 if let Some(cb) = round_cb.as_deref_mut() {
10322 cb(&tokens[cb_from..]);
10323 }
10324 break;
10325 }
10326 let forwards_left = n_new - tokens.len();
10327 let vt_drafts = match vt {
10333 Dsv4Vt::Off => k_drafts,
10334 Dsv4Vt::Slot { tau_logit, floor } => {
10335 vt_slot_drafts(&prop.confidence, tau_logit, floor)
10336 }
10337 };
10338 let t_cap = (vt_drafts + 1)
10339 .min(k_drafts + 1)
10340 .min(depth_cap)
10341 .min(vstate.tmax);
10342 let t_batch = t_cap.min(forwards_left);
10343 let kv = t_batch - 1;
10344 let mut batch_ids = Vec::with_capacity(t_batch);
10345 batch_ids.push(t_tok);
10346 batch_ids.extend_from_slice(&prop.out_ids[1..1 + kv]);
10347 let (_, am) = {
10348 let _p = phase!("2.verify_batch", prof_stream.as_ref());
10349 self.verify_batch_dev(&batch_ids, state, vstate, Some(&mut dstate.taps), false)?
10350 };
10351 let mut c_d = 0usize;
10353 let mut t_next = 0u32;
10354 for i in 0..t_batch {
10355 let a = am[i];
10356 if i < kv && a == batch_ids[i + 1] {
10357 c_d += 1;
10358 continue;
10359 }
10360 t_next = a;
10361 break;
10362 }
10363 let n_commit = c_d + 1;
10364 {
10365 let _p = phase!("3.commit_rollback", prof_stream.as_ref());
10366 self.commit_verify_dev(state, vstate, n_commit)?;
10367 }
10368 {
10370 let _p = phase!("4.ring_writes", prof_stream.as_ref());
10371 for i in 0..n_commit {
10372 self.dspark_write_rings(dstate, i, m0 + i)?;
10373 }
10374 }
10375 {
10376 let _p = phase!("5.round_close_sync", None);
10377 let last = self.stages.len() - 1;
10380 self.stages[last]
10381 .gpu
10382 .stream()
10383 .synchronize()
10384 .map_err(e("round close sync"))?;
10385 }
10386 mh_row = c_d;
10387 for i in 0..c_d {
10388 tokens.push(batch_ids[i + 1]);
10389 }
10390 carry_pending = c_d == kv && t_batch < t_cap;
10394 rounds.push(SpecRoundGpu {
10395 start_pos: m0 - 1,
10396 drafts: prop.out_ids[1..].to_vec(),
10397 accepts: c_d,
10398 verified: (c_d + 1).min(kv),
10399 t_batch,
10400 t_cap,
10401 confidence: prop.confidence.clone(),
10402 emitted: 1 + c_d,
10403 round_us: round_t0.elapsed().as_micros() as u64,
10404 });
10405 t_tok = t_next;
10406 if let Some(cb) = round_cb.as_deref_mut() {
10407 if !cb(&tokens[cb_from..]) {
10408 break;
10409 }
10410 }
10411 }
10412 Ok(SpecRunGpu { tokens, rounds })
10413 }
10414
10415 pub fn spec_greedy_batched(&self, prompt: &[u32], n_new: usize) -> Res<SpecRunGpu> {
10417 let mut state = self.alloc_decode_state()?;
10418 let mut dstate = self.dspark_alloc_state()?;
10419 let mut vstate = self.alloc_verify_state()?;
10420 self.spec_greedy_batched_with(prompt, n_new, &mut state, &mut dstate, &mut vstate)
10421 }
10422
10423 #[allow(clippy::too_many_arguments)]
10439 pub fn spec_sampled_batched_policy(
10440 &self,
10441 prompt: &[u32],
10442 n_new: usize,
10443 state: &mut DecodeState,
10444 dstate: &mut DsparkState,
10445 vstate: &mut VerifyState,
10446 depth_cap: usize,
10447 vt: Dsv4Vt,
10448 sample: &Dsv4SampleCfg,
10449 ) -> Res<SpecRunGpu> {
10450 self.spec_sampled_batched_stream(
10451 prompt, n_new, state, dstate, vstate, depth_cap, vt, sample, None,
10452 )
10453 }
10454
10455 #[allow(clippy::too_many_arguments)]
10459 pub fn spec_sampled_batched_stream(
10460 &self,
10461 prompt: &[u32],
10462 n_new: usize,
10463 state: &mut DecodeState,
10464 dstate: &mut DsparkState,
10465 vstate: &mut VerifyState,
10466 depth_cap: usize,
10467 vt: Dsv4Vt,
10468 sample: &Dsv4SampleCfg,
10469 mut round_cb: Option<&mut dyn FnMut(&[u32]) -> bool>,
10470 ) -> Res<SpecRunGpu> {
10471 self.spec_sampled_batched_pen(
10472 prompt,
10473 n_new,
10474 state,
10475 dstate,
10476 vstate,
10477 depth_cap,
10478 vt,
10479 sample,
10480 None,
10481 round_cb.take(),
10482 )
10483 }
10484
10485 #[allow(clippy::too_many_arguments)]
10492 pub fn spec_sampled_batched_pen(
10493 &self,
10494 prompt: &[u32],
10495 n_new: usize,
10496 state: &mut DecodeState,
10497 dstate: &mut DsparkState,
10498 vstate: &mut VerifyState,
10499 depth_cap: usize,
10500 vt: Dsv4Vt,
10501 sample: &Dsv4SampleCfg,
10502 pen: Option<&Dsv4PenaltyCfg>,
10503 mut round_cb: Option<&mut dyn FnMut(&[u32]) -> bool>,
10504 ) -> Res<SpecRunGpu> {
10505 let p0 = prompt.len();
10506 assert!(n_new >= 1, "n_new must be positive");
10507 let pre = self.dspark_prefill_prime(prompt, state, dstate)?;
10508 let mut t_tok = if let Some(pc) = pen {
10510 let mut row = pre.logits.clone();
10511 dsv4_penalize_row(&mut row, prompt, pc);
10512 dsv4_sample_row(&row, p0, sample)?
10513 } else {
10514 dsv4_sample_row(&pre.logits, p0, sample)?
10515 };
10516 let mut tokens: Vec<u32> = Vec::with_capacity(n_new);
10517 let mut rounds: Vec<SpecRoundGpu> = Vec::new();
10518 let mut mh_row = 0usize;
10519 let mut carry_pending = false;
10520 let depth_cap = depth_cap.max(1);
10521 while tokens.len() < n_new {
10522 let cb_from = tokens.len();
10523 if carry_pending {
10524 tokens.push(t_tok);
10525 if let Some(cb) = round_cb.as_deref_mut() {
10526 cb(&tokens[cb_from..]);
10527 }
10528 break;
10529 }
10530 let round_t0 = std::time::Instant::now();
10531 let m0 = p0 + tokens.len();
10532 let prop = self.dspark_forward_spec(dstate, t_tok, mh_row, m0 - 1, false)?;
10533 let k_drafts = prop.out_ids.len() - 1;
10534 tokens.push(t_tok);
10535 if tokens.len() == n_new {
10536 rounds.push(SpecRoundGpu {
10537 start_pos: m0 - 1,
10538 drafts: prop.out_ids[1..].to_vec(),
10539 accepts: 0,
10540 verified: 0,
10541 t_batch: 0,
10542 t_cap: 0,
10543 confidence: prop.confidence.clone(),
10544 emitted: 1,
10545 round_us: round_t0.elapsed().as_micros() as u64,
10546 });
10547 if let Some(cb) = round_cb.as_deref_mut() {
10548 cb(&tokens[cb_from..]);
10549 }
10550 break;
10551 }
10552 let forwards_left = n_new - tokens.len();
10553 let vt_drafts = match vt {
10554 Dsv4Vt::Off => k_drafts,
10555 Dsv4Vt::Slot { tau_logit, floor } => {
10556 vt_slot_drafts(&prop.confidence, tau_logit, floor)
10557 }
10558 };
10559 let t_cap = (vt_drafts + 1)
10560 .min(k_drafts + 1)
10561 .min(depth_cap)
10562 .min(vstate.tmax);
10563 let t_batch = t_cap.min(forwards_left);
10564 let kv = t_batch - 1;
10565 let mut batch_ids = Vec::with_capacity(t_batch);
10566 batch_ids.push(t_tok);
10567 batch_ids.extend_from_slice(&prop.out_ids[1..1 + kv]);
10568 let (rows, _am) =
10569 self.verify_batch_dev(&batch_ids, state, vstate, Some(&mut dstate.taps), true)?;
10570 let rows = rows.expect("verify_batch_dev(want_logits=true) returned rows");
10571 let vocab = rows.len() / t_batch;
10572 let mut c_d = 0usize;
10578 let mut t_next = 0u32;
10579 let mut wround: Vec<u32> = Vec::new();
10582 for i in 0..t_batch {
10583 let s = if let Some(pc) = pen {
10584 let mut row = rows[i * vocab..(i + 1) * vocab].to_vec();
10585 let mut window = Vec::with_capacity(prompt.len() + tokens.len() + wround.len());
10586 window.extend_from_slice(prompt);
10587 window.extend_from_slice(&tokens);
10588 window.extend_from_slice(&wround);
10589 dsv4_penalize_row(&mut row, &window, pc);
10590 dsv4_sample_row(&row, m0 + i + 1, sample)?
10591 } else {
10592 dsv4_sample_row(&rows[i * vocab..(i + 1) * vocab], m0 + i + 1, sample)?
10593 };
10594 if i < kv && s == batch_ids[i + 1] {
10595 c_d += 1;
10596 wround.push(batch_ids[i + 1]);
10597 continue;
10598 }
10599 t_next = s;
10600 break;
10601 }
10602 let n_commit = c_d + 1;
10603 self.commit_verify_dev(state, vstate, n_commit)?;
10604 for i in 0..n_commit {
10605 self.dspark_write_rings(dstate, i, m0 + i)?;
10606 }
10607 {
10608 let last = self.stages.len() - 1;
10609 self.stages[last]
10610 .gpu
10611 .stream()
10612 .synchronize()
10613 .map_err(e("round close sync"))?;
10614 }
10615 mh_row = c_d;
10616 for i in 0..c_d {
10617 tokens.push(batch_ids[i + 1]);
10618 }
10619 carry_pending = c_d == kv && t_batch < t_cap;
10620 rounds.push(SpecRoundGpu {
10621 start_pos: m0 - 1,
10622 drafts: prop.out_ids[1..].to_vec(),
10623 accepts: c_d,
10624 verified: (c_d + 1).min(kv),
10625 t_batch,
10626 t_cap,
10627 confidence: prop.confidence.clone(),
10628 emitted: 1 + c_d,
10629 round_us: round_t0.elapsed().as_micros() as u64,
10630 });
10631 t_tok = t_next;
10632 if let Some(cb) = round_cb.as_deref_mut() {
10633 if !cb(&tokens[cb_from..]) {
10634 break;
10635 }
10636 }
10637 }
10638 Ok(SpecRunGpu { tokens, rounds })
10639 }
10640}
10641
10642impl Dsv4Gpu {
10643 pub fn cache_classes(&self, state: &DecodeState) -> Res<Vec<(String, Vec<f32>)>> {
10649 let d = self.model.cfg();
10650 let hd = d.head_dim as usize;
10651 let win = d.sliding_window as usize;
10652 let mut out = Vec::new();
10653 for (il, cache) in state.caches.iter().enumerate() {
10654 let stage_i = self.layer_stage[il];
10655 let st = &self.stages[stage_i];
10656 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx classes"))?;
10657 let stream = st.gpu.stream();
10658 let lidx = st
10659 .layers
10660 .iter()
10661 .position(|l| l.il == il as u32)
10662 .unwrap_or_else(|| panic!("layer {il} not on stage {stage_i}"));
10663 let layer = &st.layers[lidx];
10664 let read = |sl: cudarc::driver::CudaView<'_, f32>| -> Res<Vec<f32>> {
10665 let mut v = vec![0f32; sl.len()];
10666 stream
10667 .memcpy_dtoh(&sl, &mut v[..])
10668 .map_err(e("dtoh class"))?;
10669 stream.synchronize().map_err(e("sync class"))?;
10670 Ok(v)
10671 };
10672 out.push((format!("l{il}.ring"), read(cache.kvc.slice(0..win * hd))?));
10673 if let Some(cmp) = &layer.cmp {
10674 out.push((
10675 format!("l{il}.cmp_store"),
10676 read(cache.kvc.slice(win * hd..(win + cache.n_blocks) * cmp.d))?,
10677 ));
10678 out.push((
10679 format!("l{il}.cmp_pend_kv"),
10680 read(cache.pend_kv.as_ref().expect("pend kv").slice(..))?,
10681 ));
10682 out.push((
10683 format!("l{il}.cmp_pend_score"),
10684 read(cache.pend_score.as_ref().expect("pend sc").slice(..))?,
10685 ));
10686 }
10687 if let Some(ix) = &layer.idx {
10688 let ikvc = cache.ikvc.as_ref().expect("ikvc");
10689 out.push((
10690 format!("l{il}.idx_store"),
10691 read(ikvc.slice(0..cache.i_blocks * ix.cmp.d))?,
10692 ));
10693 out.push((
10694 format!("l{il}.idx_pend_kv"),
10695 read(cache.ipend_kv.as_ref().expect("ipend kv").slice(..))?,
10696 ));
10697 out.push((
10698 format!("l{il}.idx_pend_score"),
10699 read(cache.ipend_score.as_ref().expect("ipend sc").slice(..))?,
10700 ));
10701 }
10702 }
10703 Ok(out)
10704 }
10705
10706 pub fn dspark_ring_classes(&self, dstate: &DsparkState) -> Res<Vec<(String, Vec<f32>)>> {
10710 let last = self.stages.len() - 1;
10711 let st = &self.stages[last];
10712 st.gpu.ctx.bind_to_thread().map_err(e("bind ctx rings"))?;
10713 let stream = st.gpu.stream();
10714 let d = self.model.cfg();
10715 let hd = d.head_dim as usize;
10716 let win = d.sliding_window as usize;
10717 let mut out = Vec::new();
10718 for (bi, ring) in dstate.rings.iter().enumerate() {
10719 let view = ring.slice(0..win * hd);
10722 let mut v = vec![0f32; view.len()];
10723 stream
10724 .memcpy_dtoh(&view, &mut v[..])
10725 .map_err(e("dtoh ring class"))?;
10726 stream.synchronize().map_err(e("sync ring class"))?;
10727 out.push((format!("dspark.ring{bi}"), v));
10728 }
10729 Ok(out)
10730 }
10731}
10732
10733pub fn resolve_dense_arm(v: Option<&str>, on_device: bool) -> Result<bool, String> {
10739 match v {
10740 None | Some("") => Ok(on_device),
10741 Some("bf16") => Ok(false),
10742 Some("fp8") if !on_device => Err(
10743 "MEMRA_DSV4_DENSE_ARM=fp8 requires MEMRA_DSV4_DECODE_PATH=device (the \
10744 fp8 GEMV twins exist on the device decode/verify paths only; prefill \
10745 and the legacy path consume the bf16 slabs)"
10746 .to_string(),
10747 ),
10748 Some("fp8") => Ok(true),
10749 Some(other) => Err(format!(
10750 "MEMRA_DSV4_DENSE_ARM '{other}' unknown (bf16 | fp8)"
10751 )),
10752 }
10753}
10754
10755#[derive(Clone, Copy, Debug, PartialEq)]
10773pub enum Dsv4Vt {
10774 Off,
10775 Slot {
10778 tau_logit: f32,
10779 floor: usize,
10780 },
10781}
10782
10783pub fn resolve_vt(
10784 policy: Option<&str>,
10785 tau: Option<&str>,
10786 floor: Option<&str>,
10787) -> Result<Dsv4Vt, String> {
10788 match policy {
10789 None | Some("") | Some("off") => {
10790 if let Some(t) = tau {
10791 return Err(format!(
10792 "MEMRA_DSV4_VT_TAU='{t}' set without MEMRA_DSV4_VT=slot (orphan knob \
10793 would be silently inert — refuse instead)"
10794 ));
10795 }
10796 if let Some(f) = floor {
10797 return Err(format!(
10798 "MEMRA_DSV4_VT_FLOOR='{f}' set without MEMRA_DSV4_VT=slot (orphan \
10799 knob would be silently inert — refuse instead)"
10800 ));
10801 }
10802 Ok(Dsv4Vt::Off)
10803 }
10804 Some("slot") => {
10805 let tau_v: f32 = match tau {
10806 None => 0.5,
10807 Some(s) => s
10808 .trim()
10809 .parse::<f32>()
10810 .map_err(|_| format!("MEMRA_DSV4_VT_TAU '{s}' is not a float in (0,1)"))?,
10811 };
10812 if !(tau_v > 0.0 && tau_v < 1.0) {
10813 return Err(format!(
10814 "MEMRA_DSV4_VT_TAU {tau_v} out of range: need 0 < tau < 1 \
10815 (a probability threshold on the per-slot sigmoid)"
10816 ));
10817 }
10818 let floor_v: usize = match floor {
10819 None => 0,
10820 Some(s) => s.trim().parse::<usize>().map_err(|_| {
10821 format!("MEMRA_DSV4_VT_FLOOR '{s}' is not a non-negative integer")
10822 })?,
10823 };
10824 if floor_v >= 5 {
10828 return Err(format!(
10829 "MEMRA_DSV4_VT_FLOOR {floor_v} >= dspark block size 5 would pin the \
10830 window fully open (use MEMRA_DSV4_VT=off to disable)"
10831 ));
10832 }
10833 Ok(Dsv4Vt::Slot {
10834 tau_logit: (tau_v / (1.0 - tau_v)).ln(),
10835 floor: floor_v,
10836 })
10837 }
10838 Some(other) => Err(format!("MEMRA_DSV4_VT '{other}' unknown (off | slot)")),
10839 }
10840}
10841
10842#[derive(Clone, Copy, Debug)]
10862pub struct Dsv4SampleCfg {
10863 pub temperature: f32,
10864 pub top_p: f32,
10865 pub top_k: usize,
10866 pub seed: u64,
10867}
10868
10869fn splitmix64(mut x: u64) -> u64 {
10870 x = x.wrapping_add(0x9e3779b97f4a7c15);
10871 let mut z = x;
10872 z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
10873 z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
10874 z ^ (z >> 31)
10875}
10876
10877pub fn dsv4_pos_uniform(seed: u64, pos: usize) -> f64 {
10879 let h = splitmix64(seed ^ (pos as u64).wrapping_mul(0xa24baed4963ee407));
10880 (h >> 11) as f64 / (1u64 << 53) as f64
10881}
10882
10883pub fn dsv4_sample_row(logits: &[f32], pos: usize, cfg: &Dsv4SampleCfg) -> Result<u32, String> {
10885 if !(cfg.temperature > 0.0) {
10886 return Err(format!(
10887 "dsv4 sampled path: temperature {} refused (need > 0; greedy is served by \
10888 the greedy driver, never a silent argmax fallback)",
10889 cfg.temperature
10890 ));
10891 }
10892 if !(cfg.top_p > 0.0 && cfg.top_p <= 1.0) {
10893 return Err(format!(
10894 "dsv4 sampled path: top_p {} out of (0, 1]",
10895 cfg.top_p
10896 ));
10897 }
10898 let k = if cfg.top_k == 0 || cfg.top_k > logits.len() {
10900 logits.len()
10901 } else {
10902 cfg.top_k
10903 };
10904 let mut idx: Vec<u32> = (0..logits.len() as u32).collect();
10905 idx.sort_by(|&a, &b| {
10906 let (va, vb) = (logits[a as usize], logits[b as usize]);
10907 vb.partial_cmp(&va)
10908 .unwrap_or(std::cmp::Ordering::Equal)
10909 .then(a.cmp(&b))
10910 });
10911 idx.truncate(k);
10912 let m = logits[idx[0] as usize] as f64;
10914 let t = cfg.temperature as f64;
10915 let mut probs: Vec<f64> = idx
10916 .iter()
10917 .map(|&i| (((logits[i as usize] as f64) - m) / t).exp())
10918 .collect();
10919 let z: f64 = probs.iter().sum();
10920 for p in &mut probs {
10921 *p /= z;
10922 }
10923 let mut cum = 0.0f64;
10925 let mut keep = probs.len();
10926 for (i, p) in probs.iter().enumerate() {
10927 cum += p;
10928 if cum >= cfg.top_p as f64 {
10929 keep = i + 1;
10930 break;
10931 }
10932 }
10933 probs.truncate(keep);
10934 idx.truncate(keep);
10935 let z2: f64 = probs.iter().sum();
10936 let u = dsv4_pos_uniform(cfg.seed, pos) * z2;
10937 let mut acc = 0.0f64;
10938 for (i, p) in probs.iter().enumerate() {
10939 acc += p;
10940 if u < acc {
10941 return Ok(idx[i]);
10942 }
10943 }
10944 Ok(idx[keep - 1]) }
10946
10947#[derive(Clone, Copy, Debug)]
10955pub struct Dsv4PenaltyCfg {
10956 pub last_n: usize,
10957 pub repeat: f32,
10958 pub freq: f32,
10959 pub present: f32,
10960}
10961
10962impl Dsv4PenaltyCfg {
10963 pub fn armed(&self) -> bool {
10964 self.last_n > 0 && (self.repeat != 1.0 || self.freq != 0.0 || self.present != 0.0)
10965 }
10966}
10967
10968pub fn dsv4_penalize_row(logits: &mut [f32], window: &[u32], cfg: &Dsv4PenaltyCfg) {
10970 if !cfg.armed() {
10971 return;
10972 }
10973 let start = window.len().saturating_sub(cfg.last_n);
10974 let win = &window[start..];
10975 if win.is_empty() {
10976 return;
10977 }
10978 let mut counts: std::collections::HashMap<u32, i32> = std::collections::HashMap::new();
10979 for &t in win {
10980 *counts.entry(t).or_insert(0) += 1;
10981 }
10982 for (&id, &cnt) in &counts {
10983 let Some(l) = logits.get_mut(id as usize) else {
10984 continue;
10985 };
10986 if cfg.repeat != 1.0 {
10987 if *l > 0.0 {
10988 *l /= cfg.repeat;
10989 } else {
10990 *l *= cfg.repeat;
10991 }
10992 }
10993 *l -= cfg.freq * cnt as f32;
10994 if cnt > 0 {
10995 *l -= cfg.present;
10996 }
10997 }
10998}
10999
11000pub fn vt_slot_drafts(conf: &[f32], tau_logit: f32, floor: usize) -> usize {
11005 let mut k = 0usize;
11006 for &c in conf {
11007 if c >= tau_logit {
11008 k += 1;
11009 } else {
11010 break;
11011 }
11012 }
11013 k.max(floor).min(conf.len())
11014}
11015
11016#[cfg(test)]
11017mod peer_probe_tests {
11018 use super::{dsv4_peer_probe_ladder, dsv4_peer_probe_mismatches, dsv4_peer_probe_pattern};
11019
11020 #[test]
11026 fn peer_probe_pattern_is_keyed_and_mismatches_are_counted() {
11027 let a = dsv4_peer_probe_pattern(4096, 0, 0, 1);
11028 assert_eq!(a.len(), 4096);
11029 assert_eq!(a, dsv4_peer_probe_pattern(4096, 0, 0, 1), "deterministic");
11030 assert_ne!(a, dsv4_peer_probe_pattern(4096, 0, 1, 0), "direction-keyed");
11031 assert_ne!(a, dsv4_peer_probe_pattern(4096, 1, 0, 1), "boundary-keyed");
11032 assert!(a.iter().any(|&b| b != a[0]), "non-constant pattern");
11033
11034 assert_eq!(dsv4_peer_probe_mismatches(&a, &a), 0);
11035 let mut flipped = a.clone();
11036 flipped[17] ^= 1;
11037 assert_eq!(dsv4_peer_probe_mismatches(&a, &flipped), 1);
11038 let poison: Vec<u8> = a.iter().map(|b| !b).collect();
11039 assert_eq!(dsv4_peer_probe_mismatches(&a, &poison), a.len());
11040 assert_eq!(dsv4_peer_probe_mismatches(&a, &a[..4000]), 96);
11041 }
11042
11043 #[test]
11044 fn peer_probe_ladder_contains_live_hc_payloads() {
11045 let ladder = dsv4_peer_probe_ladder(4096, 4);
11046 assert!(ladder.contains(&(64 << 10)), "one-token hc state");
11047 assert!(ladder.contains(&(512 << 10)), "eight-row verify hc state");
11048 assert!(ladder.contains(&(64 << 20)), "maximum prefill handoff");
11049 }
11050}
11051
11052#[cfg(test)]
11053mod dense_arm_default_tests {
11054 use super::resolve_dense_arm;
11055
11056 #[test]
11059 fn ratified_default_dense_arm_is_fp8_on_device() {
11060 assert_eq!(
11061 resolve_dense_arm(None, true),
11062 Ok(true),
11063 "owner-ratified 2026-08-20: unset MEMRA_DSV4_DENSE_ARM defaults the DEVICE \
11064 decode path to fp8 (bit-identical on four boxes, x5 A/B 41.06->47.19, \
11065 item-3 residency green on box7)"
11066 );
11067 assert_eq!(resolve_dense_arm(Some(""), true), Ok(true));
11068 assert_eq!(resolve_dense_arm(None, false), Ok(false));
11070 assert_eq!(resolve_dense_arm(Some("bf16"), true), Ok(false));
11072 assert_eq!(resolve_dense_arm(Some("fp8"), true), Ok(true));
11073 assert!(
11074 resolve_dense_arm(Some("fp8"), false).is_err(),
11075 "legacy+fp8 stays a refusal"
11076 );
11077 assert!(
11078 resolve_dense_arm(Some("q8"), true).is_err(),
11079 "unknown values refuse"
11080 );
11081 }
11082}
11083
11084#[cfg(test)]
11085mod vt_policy_tests {
11086 use super::{Dsv4Vt, resolve_vt, vt_slot_drafts};
11087
11088 #[test]
11091 fn default_vt_is_off_and_byte_inert() {
11092 assert_eq!(resolve_vt(None, None, None), Ok(Dsv4Vt::Off));
11093 assert_eq!(resolve_vt(Some(""), None, None), Ok(Dsv4Vt::Off));
11094 assert_eq!(resolve_vt(Some("off"), None, None), Ok(Dsv4Vt::Off));
11095 }
11096
11097 #[test]
11098 fn slot_defaults_tau_half_floor_zero() {
11099 match resolve_vt(Some("slot"), None, None) {
11102 Ok(Dsv4Vt::Slot { tau_logit, floor }) => {
11103 assert_eq!(tau_logit, 0.0);
11104 assert_eq!(floor, 0);
11105 }
11106 other => panic!("slot default parse broke: {other:?}"),
11107 }
11108 match resolve_vt(Some("slot"), Some("0.6"), Some("2")) {
11110 Ok(Dsv4Vt::Slot { tau_logit, floor }) => {
11111 assert!((tau_logit - (0.6f32 / 0.4).ln()).abs() < 1e-6);
11112 assert_eq!(floor, 2);
11113 }
11114 other => panic!("slot tau/floor parse broke: {other:?}"),
11115 }
11116 }
11117
11118 #[test]
11119 fn unknown_and_out_of_range_refuse_by_name() {
11120 for (p, t, f) in [
11121 (Some("banana"), None, None), (Some("slot"), Some("0"), None), (Some("slot"), Some("1"), None),
11124 (Some("slot"), Some("nan"), None),
11125 (Some("slot"), Some("x"), None),
11126 (Some("slot"), None, Some("5")), (Some("slot"), None, Some("-1")),
11128 (None, Some("0.5"), None), (Some("off"), None, Some("2")), ] {
11131 let r = resolve_vt(p, t, f);
11132 assert!(r.is_err(), "({p:?},{t:?},{f:?}) must refuse, got {r:?}");
11133 let msg = r.unwrap_err();
11134 assert!(
11135 msg.contains("MEMRA_DSV4_VT"),
11136 "refusal must name the knob: {msg}"
11137 );
11138 }
11139 }
11140
11141 #[test]
11144 fn slot_truncation_is_leading_prefix_with_floor() {
11145 let up = 3.0f32; let dn = -3.0f32; assert_eq!(vt_slot_drafts(&[up, up, up, up, up], 0.0, 0), 5);
11148 assert_eq!(vt_slot_drafts(&[dn, dn, dn, dn, dn], 0.0, 0), 0);
11149 assert_eq!(vt_slot_drafts(&[up, up, dn, up, up], 0.0, 0), 2);
11150 assert_eq!(vt_slot_drafts(&[0.0, dn, dn, dn, dn], 0.0, 0), 1);
11152 assert_eq!(vt_slot_drafts(&[dn, dn, dn, dn, dn], 0.0, 2), 2);
11154 assert_eq!(vt_slot_drafts(&[dn, dn], 0.0, 4), 2);
11155 assert_eq!(vt_slot_drafts(&[f32::NAN, up, up, up, up], 0.0, 0), 0);
11157 assert_eq!(vt_slot_drafts(&[], 0.0, 0), 0);
11158 }
11159
11160 #[test]
11163 fn off_arm_t_cap_expression_is_identity() {
11164 for k_drafts in 0usize..=5 {
11165 let vt_drafts = k_drafts; assert_eq!((vt_drafts + 1).min(k_drafts + 1), k_drafts + 1);
11167 }
11168 }
11169}
11170
11171#[cfg(test)]
11172mod penalty_cross_pin_tests {
11173 use super::{Dsv4PenaltyCfg, dsv4_penalize_row};
11174
11175 #[test]
11181 fn penalize_matches_the_sampling_crate_reference() {
11182 let mk_row = |seed: u32| -> Vec<f32> {
11183 (0..64u32)
11184 .map(|i| {
11185 let h = i.wrapping_mul(2654435761).wrapping_add(seed);
11186 ((h % 2000) as f32 / 100.0) - 10.0
11187 })
11188 .collect()
11189 };
11190 for (seed, window, last_n, rep, freq, present) in [
11191 (
11192 1u32,
11193 vec![3u32, 3, 3, 7, 12, 3],
11194 8usize,
11195 1.8f32,
11196 0.4f32,
11197 0.6f32,
11198 ),
11199 (2, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 4, 1.3, 0.0, 0.0),
11200 (3, vec![63, 63, 63, 63], 64, 1.0, 1.1, 0.0),
11201 (4, vec![5], 1, 2.5, 0.7, 1.3),
11202 ] {
11203 let row = mk_row(seed);
11204 let mut ours = row.clone();
11206 dsv4_penalize_row(
11207 &mut ours,
11208 &window,
11209 &Dsv4PenaltyCfg {
11210 last_n,
11211 repeat: rep,
11212 freq,
11213 present,
11214 },
11215 );
11216 let our_pick = ours
11217 .iter()
11218 .enumerate()
11219 .max_by(|a, b| a.1.total_cmp(b.1))
11220 .unwrap()
11221 .0 as u32;
11222 let mut sampler = memra_sampling::Sampler::new(memra_sampling::SamplerConfig {
11224 temperature: 0.0,
11225 top_k: 0,
11226 top_p: 1.0,
11227 min_p: 0.0,
11228 penalty_last_n: last_n,
11229 penalty_repeat: rep,
11230 penalty_freq: freq,
11231 penalty_present: present,
11232 seed: 0,
11233 });
11234 for &t in &window {
11235 sampler.accept(t);
11236 }
11237 let ref_pick = sampler.sample(&row);
11238 assert_eq!(
11239 our_pick, ref_pick,
11240 "penalized argmax diverged from memra-sampling (seed {seed}): \
11241 ours {our_pick} vs reference {ref_pick}"
11242 );
11243 }
11244 }
11245}
11246
11247#[cfg(test)]
11248mod sampled_path_tests {
11249 use super::{Dsv4SampleCfg, dsv4_pos_uniform, dsv4_sample_row};
11250
11251 fn cfg(seed: u64) -> Dsv4SampleCfg {
11252 Dsv4SampleCfg {
11253 temperature: 1.0,
11254 top_p: 0.95,
11255 top_k: 0,
11256 seed,
11257 }
11258 }
11259
11260 #[test]
11263 fn draws_are_position_keyed_and_deterministic() {
11264 let row = [0.1f32, 2.0, -1.0, 1.9, 0.0];
11265 let a = dsv4_sample_row(&row, 40, &cfg(20260822)).unwrap();
11266 for _ in 0..8 {
11267 assert_eq!(dsv4_sample_row(&row, 40, &cfg(20260822)).unwrap(), a);
11268 }
11269 let u0 = dsv4_pos_uniform(20260822, 40);
11271 let u1 = dsv4_pos_uniform(20260822, 41);
11272 let v0 = dsv4_pos_uniform(7, 40);
11273 assert_ne!(u0, u1);
11274 assert_ne!(u0, v0);
11275 assert!((0.0..1.0).contains(&u0));
11276 }
11277
11278 #[test]
11281 fn t0_and_bad_topp_refuse_by_name() {
11282 let row = [0.0f32, 1.0];
11283 let mut c = cfg(1);
11284 c.temperature = 0.0;
11285 let e = dsv4_sample_row(&row, 0, &c).unwrap_err();
11286 assert!(e.contains("temperature"), "{e}");
11287 let mut c2 = cfg(1);
11288 c2.top_p = 0.0;
11289 assert!(dsv4_sample_row(&row, 0, &c2).is_err());
11290 }
11291
11292 #[test]
11295 fn filters_collapse_to_argmax_and_ties_break_low_index() {
11296 let row = [0.0f32, 5.0, 5.0, -2.0];
11297 let mut c = cfg(99);
11298 c.top_k = 1;
11299 for pos in 0..64 {
11300 assert_eq!(dsv4_sample_row(&row, pos, &c).unwrap(), 1);
11301 }
11302 let mut c2 = cfg(99);
11303 c2.top_p = 1e-9; for pos in 0..64 {
11305 assert_eq!(dsv4_sample_row(&row, pos, &c2).unwrap(), 1);
11306 }
11307 }
11308
11309 #[test]
11312 fn draw_frequencies_track_the_filtered_target() {
11313 let row = [3.0f32, 1.0, 0.0, -50.0];
11314 let mut c = cfg(20260822);
11315 c.top_k = 3;
11316 c.top_p = 1.0;
11317 let mut counts = [0usize; 4];
11318 for pos in 0..4096 {
11319 counts[dsv4_sample_row(&row, pos, &c).unwrap() as usize] += 1;
11320 }
11321 assert_eq!(counts[3], 0, "outside top-k must never be drawn");
11322 assert!(counts[0] > 2600, "p(tok0) ~ 0.84, got {}/4096", counts[0]);
11323 assert!(counts[1] > 100, "tail token starved: {}", counts[1]);
11324 }
11325}