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