1use crate::Engine;
16use cudarc::driver::{CudaSlice, DevicePtr, DevicePtrMut};
17
18static MMQ_ACT_EPOCH: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
21#[allow(clippy::type_complexity)]
22static MMQ_ACT_SLOT: std::sync::Mutex<Option<(u64, u64, usize, usize, CudaSlice<u8>)>> =
23 std::sync::Mutex::new(None);
24static MMQ_FIXUP_SLOT: std::sync::Mutex<Option<cudarc::driver::CudaSlice<u8>>> =
26 std::sync::Mutex::new(None);
27
28unsafe extern "C" {
29 pub fn memra_mmq_nvfp4_act_bytes(in_f: i32, n_tokens: i32) -> usize;
31 pub fn memra_mmq_nvfp4(
38 w_nvfp4_blocks: *const core::ffi::c_void,
39 act_f32: *const f32,
40 y: *mut f32,
41 in_f: i32,
42 out_f: i32,
43 n_tokens: i32,
44 act_scratch: *mut core::ffi::c_void,
45 stream: *mut core::ffi::c_void,
46 out_scale: f32,
47 ) -> i32;
48 pub fn memra_mmq_nvfp4_ex(
54 w_nvfp4_blocks: *const core::ffi::c_void,
55 act_f32: *const f32,
56 y: *mut f32,
57 in_f: i32,
58 out_f: i32,
59 n_tokens: i32,
60 act_scratch: *mut core::ffi::c_void,
61 stream: *mut core::ffi::c_void,
62 out_scale: f32,
63 per_token_scale: i32,
64 ) -> i32;
65 pub fn memra_mmq_nvfp4_ex2(
71 w_nvfp4_blocks: *const core::ffi::c_void,
72 act_f32: *const f32,
73 y: *mut f32,
74 in_f: i32,
75 out_f: i32,
76 n_tokens: i32,
77 act_scratch: *mut core::ffi::c_void,
78 stream: *mut core::ffi::c_void,
79 out_scale: f32,
80 per_token_scale: i32,
81 residual_k: i32,
82 ) -> i32;
83 pub fn memra_mmq_nvfp4_w4a8_act_bytes(in_f: i32, n_tokens: i32) -> usize;
85 pub fn memra_mmq_nvfp4_w4a8(
93 w_nvfp4_blocks: *const core::ffi::c_void,
94 act_f32: *const f32,
95 y: *mut f32,
96 in_f: i32,
97 out_f: i32,
98 n_tokens: i32,
99 act_scratch: *mut core::ffi::c_void,
100 stream: *mut core::ffi::c_void,
101 out_scale: f32,
102 rp: i32,
103 ) -> i32;
104 pub fn memra_mmq_nvfp4_f8f4_act_bytes(in_f: i32, n_tokens: i32) -> usize;
106 pub fn memra_mmq_nvfp4_f8f4(
112 w_nvfp4_blocks: *const core::ffi::c_void,
113 act_f32: *const f32,
114 y: *mut f32,
115 in_f: i32,
116 out_f: i32,
117 n_tokens: i32,
118 act_scratch: *mut core::ffi::c_void,
119 stream: *mut core::ffi::c_void,
120 out_scale: f32,
121 rp: i32,
122 ) -> i32;
123 pub fn memra_mmq_fp8_blk_act_bytes(in_f: i32, n_tokens: i32) -> usize;
126 pub fn memra_mmq_fp8_blk_scale_rows(out_f: i32) -> i32;
128 pub fn memra_mmq_fp8_blk_scale_cols(in_f: i32) -> i32;
129 pub fn memra_mmq_fp8_blk(
136 w_e4m3: *const core::ffi::c_void,
137 blk_scales: *const f32,
138 act_f32: *const f32,
139 y: *mut f32,
140 in_f: i32,
141 out_f: i32,
142 n_tokens: i32,
143 act_scratch: *mut core::ffi::c_void,
144 stream: *mut core::ffi::c_void,
145 out_scale: f32,
146 ) -> i32;
147 pub fn memra_fp8_blk_count_nan(
151 w_e4m3: *const core::ffi::c_void,
152 nbytes: usize,
153 out_count: *mut u32,
154 stream: *mut core::ffi::c_void,
155 ) -> i32;
156 pub fn memra_mmq_q45k_act_bytes(in_f: i32, n_tokens: i32) -> usize;
158 pub fn memra_mmq_q4_K(
161 w_q4k_blocks: *const core::ffi::c_void,
162 act_f32: *const f32,
163 y: *mut f32,
164 in_f: i32,
165 out_f: i32,
166 n_tokens: i32,
167 act_scratch: *mut core::ffi::c_void,
168 stream: *mut core::ffi::c_void,
169 ) -> i32;
170 pub fn memra_mmq_q5_K(
172 w_q5k_blocks: *const core::ffi::c_void,
173 act_f32: *const f32,
174 y: *mut f32,
175 in_f: i32,
176 out_f: i32,
177 n_tokens: i32,
178 act_scratch: *mut core::ffi::c_void,
179 stream: *mut core::ffi::c_void,
180 ) -> i32;
181
182 pub fn memra_mmq_q8_0_act_bytes(in_f: i32, n_tokens: i32) -> usize;
184 pub fn memra_mmq_q8_0(
188 w_q8_0_blocks: *const core::ffi::c_void,
189 act_f32: *const f32,
190 y: *mut f32,
191 in_f: i32,
192 out_f: i32,
193 n_tokens: i32,
194 act_scratch: *mut core::ffi::c_void,
195 stream: *mut core::ffi::c_void,
196 ) -> i32;
197
198 pub fn memra_mmq_q4_0_act_bytes(in_f: i32, n_tokens: i32) -> usize;
200 pub fn memra_mmq_q4_0(
206 w_q4_0: *const core::ffi::c_void,
207 act_f32: *const f32,
208 y: *mut f32,
209 in_f: i32,
210 out_f: i32,
211 n_tokens: i32,
212 act_scratch: *mut core::ffi::c_void,
213 stream: *mut core::ffi::c_void,
214 rp: i32,
215 ) -> i32;
216 pub fn memra_mmq_q4_0_quant_act(
218 act_f32: *const f32,
219 act_scratch: *mut core::ffi::c_void,
220 in_f: i32,
221 n_tokens: i32,
222 stream: *mut core::ffi::c_void,
223 ) -> i32;
224 pub fn memra_mmq_q4_0_gemm(
226 w_q4_0: *const core::ffi::c_void,
227 act_scratch: *const core::ffi::c_void,
228 y: *mut f32,
229 in_f: i32,
230 out_f: i32,
231 n_tokens: i32,
232 stream: *mut core::ffi::c_void,
233 rp: i32,
234 ) -> i32;
235 pub fn memra_mmq_q4_0_fixup_bytes() -> usize;
237 pub fn memra_mmq_q4_0_set_clc(force: i32) -> i32;
242 pub fn memra_mmq_q4_0_gemm_sk(
244 w_q4_0: *const core::ffi::c_void,
245 act_scratch: *const core::ffi::c_void,
246 y: *mut f32,
247 fixup_scratch: *mut core::ffi::c_void,
248 in_f: i32,
249 out_f: i32,
250 n_tokens: i32,
251 stream: *mut core::ffi::c_void,
252 rp: i32,
253 ) -> i32;
254
255 pub fn memra_mmq_iq_experts_act_bytes(in_f: i32, n_tokens: i32) -> usize;
258 pub fn memra_mmq_iq_quantize_act(
260 act_f32: *const f32,
261 act_scratch: *mut core::ffi::c_void,
262 in_f: i32,
263 n_tokens: i32,
264 stream: *mut core::ffi::c_void,
265 ) -> i32;
266 pub fn memra_mmq_iq_fused_act_quant(
270 gate: *const f32,
271 up: *const f32,
272 act_scratch: *mut core::ffi::c_void,
273 in_f: i32,
274 n_tokens: i32,
275 act_kind: i32,
276 stream: *mut core::ffi::c_void,
277 ) -> i32;
278 pub fn memra_mmq_iq4xs_dense(
287 w_blocks: *const core::ffi::c_void,
288 act_f32: *const f32,
289 y: *mut f32,
290 in_f: i32,
291 out_f: i32,
292 n_tokens: i32,
293 row_bytes: i64,
294 act_scratch: *mut core::ffi::c_void,
295 stream: *mut core::ffi::c_void,
296 ) -> i32;
297 pub fn memra_mmq_iq_experts(
298 table: *const u64,
299 proj: i32,
300 n_expert: i32,
301 ex_ids: *const i32,
302 ex_off: *const i32,
303 ex_pairs: *const i32,
304 pair_tok: *const i32,
305 act_scratch: *const core::ffi::c_void,
306 y: *mut f32,
307 in_f: i32,
308 out_f: i32,
309 n_active: i32,
310 n_tokens: i32,
311 qtype: i32,
312 row_bytes: i64,
313 stream: *mut core::ffi::c_void,
314 ) -> i32;
315
316 pub fn memra_moe_f16g_dequant(
318 table: *const u64, proj: i32, n_expert: i32, ex_ids: *const i32,
319 w_f16: *mut core::ffi::c_void, in_f: i32, out_f: i32, n_active: i32,
320 qtype: i32, row_bytes: i64, stream: *mut core::ffi::c_void,
321 ) -> i32;
322 pub fn memra_moe_f16g_gather_act(
323 x: *const f32, pair_tok_or_null: *const i32, act_f16: *mut core::ffi::c_void,
324 row_scale: *mut f32, in_f: i32, n_pairs: i32, stream: *mut core::ffi::c_void,
325 ) -> i32;
326 pub fn memra_moe_f16g_h2f_scaled(
327 src_f16: *const core::ffi::c_void, dst: *mut f32, row_scale: *const f32,
328 ncols: i32, nrows: i32, stream: *mut core::ffi::c_void,
329 ) -> i32;
330 pub fn memra_moe_f16g_gemm(
331 w_f16: *const core::ffi::c_void, act_f16: *const core::ffi::c_void,
332 y_f16: *mut core::ffi::c_void,
333 ex_off_host: *const i32, n_active: i32, in_f: i32, out_f: i32,
334 stream: *mut core::ffi::c_void,
335 ) -> i32;
336 pub fn memra_moe_f16g_h2f(
337 src_f16: *const core::ffi::c_void, dst: *mut f32, n: usize,
338 stream: *mut core::ffi::c_void,
339 ) -> i32;
340 pub fn memra_moe_f16g_gemm_sk(
349 w_f16: *const core::ffi::c_void, act_f16: *const core::ffi::c_void,
350 y_f32: *mut f32, row_scale: *const f32, ex_off_dev: *const i32,
351 ex_off_host: *const i32,
352 n_active: i32, max_m: i32, in_f: i32, out_f: i32, shape_sel: i32, cross: i32,
353 tail: i32, stream: *mut core::ffi::c_void,
354 ) -> i32;
355 pub fn memra_moe_kq_gemm_sk(
362 table: *const u64, proj: i32, n_expert: i32, ex_ids: *const i32,
363 act_f16: *const core::ffi::c_void, y_f32: *mut f32,
364 row_scale: *const f32, ex_off_dev: *const i32, ex_off_host: *const i32,
365 n_active: i32, max_m: i32, in_f: i32, out_f: i32, qtype: i32, cross: i32,
366 tail: i32, row_bytes: i64, stream: *mut core::ffi::c_void,
367 ) -> i32;
368}
369
370pub fn mmq_w4a8_enabled() -> bool {
379 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
380 *ON.get_or_init(|| {
381 std::env::var("MEMRA_MMQ_W4A8")
382 .map(|v| v != "0")
383 .unwrap_or(true)
384 })
385}
386
387pub fn mmq_residual_k() -> i32 {
396 std::env::var("MEMRA_MMQ_RESIDUAL_K")
397 .ok()
398 .and_then(|v| v.parse::<i32>().ok())
399 .unwrap_or(0)
400 .clamp(0, 64)
401}
402
403pub fn mmq_q8_enabled() -> bool {
409 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
410 *ON.get_or_init(|| {
414 std::env::var("MEMRA_PP_Q8MMQ")
415 .map(|v| v != "0")
416 .unwrap_or(true)
417 })
418}
419
420pub fn mmq_iq4xs_enabled() -> bool {
428 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
429 *ON.get_or_init(|| {
430 std::env::var("MEMRA_PP_IQMMQ")
431 .map(|v| v != "0")
432 .unwrap_or(true)
433 })
434}
435
436pub fn mmq_q4_enabled() -> bool {
442 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
443 *ON.get_or_init(|| {
444 std::env::var("MEMRA_PP_Q4MMQ")
445 .map(|v| v != "0")
446 .unwrap_or(true)
447 })
448}
449
450impl Engine {
451 pub fn mmq_supports(&self, w: &crate::model::GpuTensor) -> bool {
454 use crate::model::GpuTensor;
455 if crate::portable_mma_gated() {
456 return false;
457 }
458 let mmq_opt_in = std::env::var("MEMRA_MMQ").is_ok();
459 match w {
460 GpuTensor::Quant { qtype, rp, .. } if *qtype == crate::QT_NVFP4 && *rp => {
468 !cfg!(memra_portable_cuda) && mmq_w4a8_enabled() && w.in_features() % 64 == 0
469 }
470 GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_NVFP4 => {
472 !cfg!(memra_portable_cuda) && (mmq_w4a8_enabled() || mmq_opt_in)
473 && w.in_features() % 64 == 0
474 }
475 GpuTensor::Quant { qtype, .. }
476 if *qtype == crate::QT_Q4_K || *qtype == crate::QT_Q5_K =>
477 {
478 (mmq_w4a8_enabled() || mmq_opt_in) && w.in_features() % 256 == 0
479 }
480 GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_Q8_0 => {
485 mmq_q8_enabled() && w.in_features() % 256 == 0
486 }
487 GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_Q4_0 => {
492 mmq_q4_enabled() && w.in_features() % 256 == 0
493 }
494 GpuTensor::Quant { qtype, .. } if *qtype == crate::QT_IQ4_XS => {
500 mmq_iq4xs_enabled() && Self::iq_fast_enabled() && w.in_features() % 256 == 0
501 }
502 _ => false,
503 }
504 }
505
506 pub fn qmatvec_mmq(
509 &self,
510 w: &crate::model::GpuTensor,
511 x: &CudaSlice<f32>,
512 m: usize,
513 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
514 use crate::model::GpuTensor;
515 let (in_f, out_f) = (w.in_features(), w.out_features());
516 let GpuTensor::Quant {
517 bytes,
518 scale,
519 qtype,
520 rp,
521 ..
522 } = w
523 else {
524 return Err("qmatvec_mmq: not a Quant tensor".into());
525 };
526 let w4a8_explicit = std::env::var("MEMRA_MMQ_W4A8")
531 .map(|v| v != "0")
532 .unwrap_or(false);
533 let use_w4a8 =
534 *rp || w4a8_explicit || (mmq_w4a8_enabled() && std::env::var("MEMRA_MMQ").is_err());
535 match *qtype {
536 q if q == crate::QT_NVFP4 && use_w4a8 => {
539 self.qmatvec_mmq_nvfp4_w4a8(bytes, x, m, in_f, out_f, *scale, *rp)
540 }
541 q if q == crate::QT_NVFP4 => self.qmatvec_mmq_nvfp4(bytes, x, m, in_f, out_f, *scale),
542 q if q == crate::QT_Q4_K || q == crate::QT_Q5_K => {
543 let mut y = self.qmatvec_mmq_q45k_raw(bytes, x, m, in_f, out_f, q)?;
544 if *scale != 1.0 {
545 self.scale_inplace(&mut y, *scale, m * out_f)?;
546 }
547 Ok(y)
548 }
549 q if q == crate::QT_Q8_0 => {
550 if cfg!(memra_hopper_mma) && out_f % 64 == 0 && crate::wgmma_gemm_enabled() {
556 if let GpuTensor::Quant { rp4: Some(m4), .. } = w {
557 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
558 let mut y = self.qmatvec_gemm_q8_0_wgmma_raw(m4, &aq, &ad, m, in_f, out_f)?;
559 if *scale != 1.0 {
560 self.scale_inplace(&mut y, *scale, m * out_f)?;
561 }
562 return Ok(y);
563 }
564 }
565 let mut y = self.qmatvec_mmq_q8_0_raw(bytes, x, m, in_f, out_f)?;
566 if *scale != 1.0 {
567 self.scale_inplace(&mut y, *scale, m * out_f)?;
568 }
569 Ok(y)
570 }
571 q if q == crate::QT_Q4_0 => {
572 let mut y = self.qmatvec_mmq_q4_0_raw(bytes, x, m, in_f, out_f, *rp)?;
573 if *scale != 1.0 {
574 self.scale_inplace(&mut y, *scale, m * out_f)?;
575 }
576 Ok(y)
577 }
578 q if q == crate::QT_IQ4_XS => {
579 let GpuTensor::Quant { row_bytes, .. } = w else { unreachable!() };
580 let mut y = self.qmatvec_mmq_iq4xs_raw(bytes, x, m, in_f, out_f, *row_bytes)?;
581 if *scale != 1.0 {
582 self.scale_inplace(&mut y, *scale, m * out_f)?;
583 }
584 Ok(y)
585 }
586 q => Err(format!("qmatvec_mmq: unsupported qtype {q}").into()),
587 }
588 }
589
590 pub fn qmatvec_mmq_iq4xs_raw(
592 &self,
593 bytes: &CudaSlice<u8>,
594 x: &CudaSlice<f32>,
595 m: usize,
596 in_f: usize,
597 out_f: usize,
598 row_bytes: usize,
599 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
600 assert!(
601 in_f % 256 == 0,
602 "MMQ IQ4_XS requires in_f % 256 == 0, got {in_f}"
603 );
604 let act_bytes = unsafe { memra_mmq_iq_experts_act_bytes(in_f as i32, m as i32) };
605 let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
606 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
607 {
608 let stream = self.gpu.stream();
609 let (w_p, _gw) = bytes.device_ptr(&stream);
610 let (x_p, _gx) = x.device_ptr(&stream);
611 let (y_p, _gy) = y.device_ptr_mut(&stream);
612 let (s_p, _gs) = scratch.device_ptr_mut(&stream);
613 let rc = unsafe {
614 memra_mmq_iq4xs_dense(
615 w_p as *const core::ffi::c_void,
616 x_p as *const f32,
617 y_p as *mut f32,
618 in_f as i32,
619 out_f as i32,
620 m as i32,
621 row_bytes as i64,
622 s_p as *mut core::ffi::c_void,
623 stream.cu_stream() as *mut core::ffi::c_void,
624 )
625 };
626 if rc != 0 {
627 return Err(format!("memra_mmq_iq4xs_dense rc={rc}").into());
628 }
629 }
630 Ok(y)
631 }
632
633 pub fn qmatvec_mmq_q45k_raw(
638 &self,
639 bytes: &CudaSlice<u8>,
640 x: &CudaSlice<f32>,
641 m: usize,
642 in_f: usize,
643 out_f: usize,
644 qtype: i32,
645 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
646 assert!(
647 in_f % 256 == 0,
648 "MMQ Q4_K/Q5_K requires in_f % 256 == 0, got {in_f}"
649 );
650 let act_bytes = unsafe { memra_mmq_q45k_act_bytes(in_f as i32, m as i32) };
651 let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
652 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
653 {
654 let stream = self.gpu.stream();
655 let (w_p, _gw) = bytes.device_ptr(&stream);
656 let (x_p, _gx) = x.device_ptr(&stream);
657 let (y_p, _gy) = y.device_ptr_mut(&stream);
658 let (s_p, _gs) = scratch.device_ptr_mut(&stream);
659 let launcher = if qtype == crate::QT_Q4_K {
660 memra_mmq_q4_K
661 } else {
662 memra_mmq_q5_K
663 };
664 let rc = unsafe {
665 launcher(
666 w_p as *const core::ffi::c_void,
667 x_p as *const f32,
668 y_p as *mut f32,
669 in_f as i32,
670 out_f as i32,
671 m as i32,
672 s_p as *mut core::ffi::c_void,
673 stream.cu_stream() as *mut core::ffi::c_void,
674 )
675 };
676 if rc != 0 {
677 return Err(format!("memra_mmq_q45k(qtype={qtype}) rc={rc}").into());
678 }
679 }
680 Ok(y)
681 }
682
683 pub fn qmatvec_mmq_q8_0_raw(
686 &self,
687 bytes: &CudaSlice<u8>,
688 x: &CudaSlice<f32>,
689 m: usize,
690 in_f: usize,
691 out_f: usize,
692 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
693 assert!(
694 in_f % 32 == 0,
695 "MMQ Q8_0 requires in_f % 32 == 0, got {in_f}"
696 );
697 let act_bytes = unsafe { memra_mmq_q8_0_act_bytes(in_f as i32, m as i32) };
698 let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
699 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
700 {
701 let stream = self.gpu.stream();
702 let (w_p, _gw) = bytes.device_ptr(&stream);
703 let (x_p, _gx) = x.device_ptr(&stream);
704 let (y_p, _gy) = y.device_ptr_mut(&stream);
705 let (s_p, _gs) = scratch.device_ptr_mut(&stream);
706 let rc = unsafe {
707 memra_mmq_q8_0(
708 w_p as *const core::ffi::c_void,
709 x_p as *const f32,
710 y_p as *mut f32,
711 in_f as i32,
712 out_f as i32,
713 m as i32,
714 s_p as *mut core::ffi::c_void,
715 stream.cu_stream() as *mut core::ffi::c_void,
716 )
717 };
718 if rc != 0 {
719 return Err(format!("memra_mmq_q8_0 rc={rc}").into());
720 }
721 }
722 Ok(y)
723 }
724
725 pub fn mmq_act_begin(&self) {
731 use std::sync::atomic::Ordering;
732 MMQ_ACT_EPOCH.fetch_add(1, Ordering::Relaxed);
733 *MMQ_ACT_SLOT.lock().unwrap() = None;
734 }
735
736 pub fn qmatvec_mmq_q4_0_raw(
740 &self,
741 bytes: &CudaSlice<u8>,
742 x: &CudaSlice<f32>,
743 m: usize,
744 in_f: usize,
745 out_f: usize,
746 rp: bool,
747 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
748 use std::sync::atomic::Ordering;
749 assert!(
750 in_f % 32 == 0,
751 "MMQ Q4_0 requires in_f % 32 == 0, got {in_f}"
752 );
753 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
754 let stream = self.gpu.stream();
755 let (x_p, _gx) = x.device_ptr(&stream);
756 let epoch = MMQ_ACT_EPOCH.load(Ordering::Relaxed);
757 let mut slot = MMQ_ACT_SLOT.lock().unwrap();
759 let hit = matches!(&*slot,
760 Some((e, p, mm, inf, _)) if *e == epoch && *p == x_p as u64 && *mm == m && *inf == in_f);
761 if !hit {
762 let act_bytes = unsafe { memra_mmq_q4_0_act_bytes(in_f as i32, m as i32) };
763 let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
764 {
765 let (s_p, _gs) = scratch.device_ptr_mut(&stream);
766 let rc = unsafe {
767 memra_mmq_q4_0_quant_act(
768 x_p as *const f32,
769 s_p as *mut core::ffi::c_void,
770 in_f as i32,
771 m as i32,
772 stream.cu_stream() as *mut core::ffi::c_void,
773 )
774 };
775 if rc != 0 {
776 return Err(
777 format!("memra_mmq_q4_0_quant_act(in_f={in_f}, m={m}) rc={rc}").into()
778 );
779 }
780 }
781 *slot = Some((epoch, x_p as u64, m, in_f, scratch));
782 }
783 let scratch = &slot.as_ref().unwrap().4;
784 {
785 let (w_p, _gw) = bytes.device_ptr(&stream);
786 let (y_p, _gy) = y.device_ptr_mut(&stream);
787 let (s_p, _gs) = scratch.device_ptr(&stream);
788 static SK_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
810 let sk = match crate::MMQ_SK_FORCE.load(std::sync::atomic::Ordering::Relaxed) {
811 0 => false,
812 1 => true,
813 _ => *SK_ON.get_or_init(|| std::env::var("MEMRA_MMQ_SK")
814 .map(|v| v != "0").unwrap_or(!cfg!(memra_hopper_mma))),
815 };
816 let rc = if sk {
817 let mut fx = MMQ_FIXUP_SLOT.lock().unwrap();
818 if fx.is_none() {
819 let nb = unsafe { memra_mmq_q4_0_fixup_bytes() };
820 *fx = Some(self.alloc_uninit::<u8>(nb)?);
821 }
822 let (f_p, _gf) = fx.as_mut().unwrap().device_ptr_mut(&stream);
823 unsafe {
824 memra_mmq_q4_0_gemm_sk(
825 w_p as *const core::ffi::c_void,
826 s_p as *const core::ffi::c_void,
827 y_p as *mut f32,
828 f_p as *mut core::ffi::c_void,
829 in_f as i32,
830 out_f as i32,
831 m as i32,
832 stream.cu_stream() as *mut core::ffi::c_void,
833 rp as i32,
834 )
835 }
836 } else { unsafe {
837 memra_mmq_q4_0_gemm(
838 w_p as *const core::ffi::c_void,
839 s_p as *const core::ffi::c_void,
840 y_p as *mut f32,
841 in_f as i32,
842 out_f as i32,
843 m as i32,
844 stream.cu_stream() as *mut core::ffi::c_void,
845 rp as i32,
846 )
847 } };
848 if rc != 0 {
849 return Err(format!(
850 "memra_mmq_q4_0_gemm(rp={rp}, in_f={in_f}, out_f={out_f}, m={m}, wbytes={}) rc={rc}",
851 bytes.len()
852 )
853 .into());
854 }
855 }
856 Ok(y)
857 }
858
859 pub fn qmatvec_mmq_nvfp4(
865 &self,
866 bytes: &CudaSlice<u8>,
867 x: &CudaSlice<f32>,
868 m: usize,
869 in_f: usize,
870 out_f: usize,
871 scale: f32,
872 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
873 self.qmatvec_mmq_nvfp4_scaled(bytes, x, m, in_f, out_f, scale)
874 }
875
876 pub fn qmatvec_mmq_nvfp4_raw(
878 &self,
879 bytes: &CudaSlice<u8>,
880 x: &CudaSlice<f32>,
881 m: usize,
882 in_f: usize,
883 out_f: usize,
884 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
885 self.qmatvec_mmq_nvfp4_scaled(bytes, x, m, in_f, out_f, 1.0)
886 }
887
888 pub fn qmatvec_mmq_nvfp4_raw_v1(
892 &self,
893 bytes: &CudaSlice<u8>,
894 x: &CudaSlice<f32>,
895 m: usize,
896 in_f: usize,
897 out_f: usize,
898 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
899 self.qmatvec_mmq_nvfp4_inner(bytes, x, m, in_f, out_f, 1.0, false, 0)
900 }
901
902 pub fn qmatvec_mmq_nvfp4_raw_res(
904 &self,
905 bytes: &CudaSlice<u8>,
906 x: &CudaSlice<f32>,
907 m: usize,
908 in_f: usize,
909 out_f: usize,
910 residual_k: i32,
911 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
912 self.qmatvec_mmq_nvfp4_inner(bytes, x, m, in_f, out_f, 1.0, true, residual_k)
913 }
914
915 fn qmatvec_mmq_nvfp4_scaled(
916 &self,
917 bytes: &CudaSlice<u8>,
918 x: &CudaSlice<f32>,
919 m: usize,
920 in_f: usize,
921 out_f: usize,
922 scale: f32,
923 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
924 self.qmatvec_mmq_nvfp4_inner(bytes, x, m, in_f, out_f, scale, true, mmq_residual_k())
925 }
926
927 fn qmatvec_mmq_nvfp4_inner(
928 &self,
929 bytes: &CudaSlice<u8>,
930 x: &CudaSlice<f32>,
931 m: usize,
932 in_f: usize,
933 out_f: usize,
934 scale: f32,
935 per_token_scale: bool,
936 residual_k: i32,
937 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
938 assert!(
939 in_f % 64 == 0,
940 "MMQ NVFP4 requires in_f % 64 == 0, got {in_f}"
941 );
942 let act_bytes = unsafe { memra_mmq_nvfp4_act_bytes(in_f as i32, m as i32) };
943 let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
944 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
945 {
946 let stream = self.gpu.stream();
947 let (w_p, _gw) = bytes.device_ptr(&stream);
948 let (x_p, _gx) = x.device_ptr(&stream);
949 let (y_p, _gy) = y.device_ptr_mut(&stream);
950 let (s_p, _gs) = scratch.device_ptr_mut(&stream);
951 let rc = unsafe {
952 memra_mmq_nvfp4_ex2(
953 w_p as *const core::ffi::c_void,
954 x_p as *const f32,
955 y_p as *mut f32,
956 in_f as i32,
957 out_f as i32,
958 m as i32,
959 s_p as *mut core::ffi::c_void,
960 stream.cu_stream() as *mut core::ffi::c_void,
961 scale,
962 per_token_scale as i32,
963 residual_k,
964 )
965 };
966 if rc != 0 {
967 return Err(format!("memra_mmq_nvfp4_ex2 rc={rc}").into());
968 }
969 }
970 Ok(y)
971 }
972
973 pub fn qmatvec_mmq_nvfp4_w4a8(
978 &self,
979 bytes: &CudaSlice<u8>,
980 x: &CudaSlice<f32>,
981 m: usize,
982 in_f: usize,
983 out_f: usize,
984 scale: f32,
985 rp: bool,
986 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
987 self.qmatvec_mmq_nvfp4_w4a8_scaled(bytes, x, m, in_f, out_f, scale, rp)
988 }
989
990 pub fn qmatvec_mmq_nvfp4_w4a8_raw(
992 &self,
993 bytes: &CudaSlice<u8>,
994 x: &CudaSlice<f32>,
995 m: usize,
996 in_f: usize,
997 out_f: usize,
998 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
999 self.qmatvec_mmq_nvfp4_w4a8_scaled(bytes, x, m, in_f, out_f, 1.0, false)
1000 }
1001
1002 pub fn qmatvec_mmq_nvfp4_w4a8_raw_rp(
1005 &self,
1006 bytes: &CudaSlice<u8>,
1007 x: &CudaSlice<f32>,
1008 m: usize,
1009 in_f: usize,
1010 out_f: usize,
1011 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1012 self.qmatvec_mmq_nvfp4_w4a8_scaled(bytes, x, m, in_f, out_f, 1.0, true)
1013 }
1014
1015 fn qmatvec_mmq_nvfp4_w4a8_scaled(
1016 &self,
1017 bytes: &CudaSlice<u8>,
1018 x: &CudaSlice<f32>,
1019 m: usize,
1020 in_f: usize,
1021 out_f: usize,
1022 scale: f32,
1023 rp: bool,
1024 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1025 assert!(
1026 in_f % 64 == 0,
1027 "MMQ NVFP4 W4A8 requires in_f % 64 == 0, got {in_f}"
1028 );
1029 let act_bytes = unsafe { memra_mmq_nvfp4_w4a8_act_bytes(in_f as i32, m as i32) };
1030 let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1031 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
1032 {
1033 let stream = self.gpu.stream();
1034 let (w_p, _gw) = bytes.device_ptr(&stream);
1035 let (x_p, _gx) = x.device_ptr(&stream);
1036 let (y_p, _gy) = y.device_ptr_mut(&stream);
1037 let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1038 static F8F4: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1041 let f8f4 = *F8F4.get_or_init(|| std::env::var("MEMRA_MMQ_F8F4").as_deref() == Ok("1"));
1042 let rc = unsafe {
1043 if f8f4 {
1044 memra_mmq_nvfp4_f8f4(
1045 w_p as *const core::ffi::c_void,
1046 x_p as *const f32,
1047 y_p as *mut f32,
1048 in_f as i32,
1049 out_f as i32,
1050 m as i32,
1051 s_p as *mut core::ffi::c_void,
1052 stream.cu_stream() as *mut core::ffi::c_void,
1053 scale,
1054 rp as i32,
1055 )
1056 } else {
1057 memra_mmq_nvfp4_w4a8(
1058 w_p as *const core::ffi::c_void,
1059 x_p as *const f32,
1060 y_p as *mut f32,
1061 in_f as i32,
1062 out_f as i32,
1063 m as i32,
1064 s_p as *mut core::ffi::c_void,
1065 stream.cu_stream() as *mut core::ffi::c_void,
1066 scale,
1067 rp as i32,
1068 )
1069 }
1070 };
1071 if rc != 0 {
1072 return Err(format!("memra_mmq_nvfp4_w4a8(f8f4={f8f4}) rc={rc}").into());
1073 }
1074 }
1075 Ok(y)
1076 }
1077
1078 pub fn qmatvec_mmq_fp8_blk(
1082 &self,
1083 w_e4m3: &CudaSlice<u8>,
1084 blk_scales: &CudaSlice<f32>,
1085 x: &CudaSlice<f32>,
1086 m: usize,
1087 in_f: usize,
1088 out_f: usize,
1089 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1090 self.qmatvec_mmq_fp8_blk_scaled(w_e4m3, blk_scales, x, m, in_f, out_f, 1.0)
1091 }
1092
1093 pub fn qmatvec_mmq_fp8_blk_scaled(
1094 &self,
1095 w_e4m3: &CudaSlice<u8>,
1096 blk_scales: &CudaSlice<f32>,
1097 x: &CudaSlice<f32>,
1098 m: usize,
1099 in_f: usize,
1100 out_f: usize,
1101 scale: f32,
1102 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1103 assert!(
1104 in_f % 16 == 0,
1105 "per-block FP8 MMQ requires in_f % 16 == 0, got {in_f}"
1106 );
1107 let want_scales = ((out_f + 127) / 128) * ((in_f + 127) / 128);
1108 assert!(
1109 blk_scales.len() >= want_scales,
1110 "blk_scales too small: {} < {want_scales}",
1111 blk_scales.len()
1112 );
1113 assert!(
1114 w_e4m3.len() >= out_f * in_f,
1115 "e4m3 plane too small: {} < {}",
1116 w_e4m3.len(),
1117 out_f * in_f
1118 );
1119 let act_bytes = unsafe { memra_mmq_fp8_blk_act_bytes(in_f as i32, m as i32) };
1120 let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1121 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
1122 {
1123 let stream = self.gpu.stream();
1124 let (w_p, _gw) = w_e4m3.device_ptr(&stream);
1125 let (sc_p, _gsc) = blk_scales.device_ptr(&stream);
1126 let (x_p, _gx) = x.device_ptr(&stream);
1127 let (y_p, _gy) = y.device_ptr_mut(&stream);
1128 let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1129 let rc = unsafe {
1130 memra_mmq_fp8_blk(
1131 w_p as *const core::ffi::c_void,
1132 sc_p as *const f32,
1133 x_p as *const f32,
1134 y_p as *mut f32,
1135 in_f as i32,
1136 out_f as i32,
1137 m as i32,
1138 s_p as *mut core::ffi::c_void,
1139 stream.cu_stream() as *mut core::ffi::c_void,
1140 scale,
1141 )
1142 };
1143 if rc != 0 {
1144 return Err(format!("memra_mmq_fp8_blk rc={rc}").into());
1145 }
1146 }
1147 Ok(y)
1148 }
1149
1150 pub fn fp8_blk_nan_count(
1154 &self,
1155 w_e4m3: &CudaSlice<u8>,
1156 ) -> Result<u32, Box<dyn std::error::Error>> {
1157 let mut cnt = self.htod_u32_v(&[0u32])?;
1158 let n = w_e4m3.len();
1159 {
1160 let stream = self.gpu.stream();
1161 let (w_p, _gw) = w_e4m3.device_ptr(&stream);
1162 let (c_p, _gc) = cnt.device_ptr_mut(&stream);
1163 let rc = unsafe {
1164 memra_fp8_blk_count_nan(
1165 w_p as *const core::ffi::c_void,
1166 n,
1167 c_p as *mut u32,
1168 stream.cu_stream() as *mut core::ffi::c_void,
1169 )
1170 };
1171 if rc != 0 {
1172 return Err(format!("memra_fp8_blk_count_nan rc={rc}").into());
1173 }
1174 }
1175 Ok(self.dtoh_u32(&cnt)?[0])
1176 }
1177
1178 pub fn mmq_iq_quantize_act(
1181 &self,
1182 x: &CudaSlice<f32>,
1183 in_f: usize,
1184 n_tokens: usize,
1185 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
1186 let act_bytes = unsafe { memra_mmq_iq_experts_act_bytes(in_f as i32, n_tokens as i32) };
1187 let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1188 {
1189 let stream = self.gpu.stream();
1190 let (x_p, _gx) = x.device_ptr(&stream);
1191 let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1192 let rc = unsafe {
1193 memra_mmq_iq_quantize_act(
1194 x_p as *const f32,
1195 s_p as *mut core::ffi::c_void,
1196 in_f as i32,
1197 n_tokens as i32,
1198 stream.cu_stream() as *mut core::ffi::c_void,
1199 )
1200 };
1201 if rc != 0 {
1202 return Err(format!("memra_mmq_iq_quantize_act rc={rc}").into());
1203 }
1204 }
1205 Ok(scratch)
1206 }
1207
1208 pub fn mmq_iq_fused_act_quant(
1214 &self,
1215 gate: &CudaSlice<f32>,
1216 up: &CudaSlice<f32>,
1217 in_f: usize,
1218 n_tokens: usize,
1219 act_kind: i32,
1220 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
1221 let act_bytes = unsafe { memra_mmq_iq_experts_act_bytes(in_f as i32, n_tokens as i32) };
1222 let mut scratch = self.alloc_uninit::<u8>(act_bytes)?;
1223 {
1224 let stream = self.gpu.stream();
1225 let (g_p, _gg) = gate.device_ptr(&stream);
1226 let (u_p, _gu) = up.device_ptr(&stream);
1227 let (s_p, _gs) = scratch.device_ptr_mut(&stream);
1228 let rc = unsafe {
1229 memra_mmq_iq_fused_act_quant(
1230 g_p as *const f32,
1231 u_p as *const f32,
1232 s_p as *mut core::ffi::c_void,
1233 in_f as i32,
1234 n_tokens as i32,
1235 act_kind,
1236 stream.cu_stream() as *mut core::ffi::c_void,
1237 )
1238 };
1239 if rc != 0 {
1240 return Err(format!("memra_mmq_iq_fused_act_quant rc={rc}").into());
1241 }
1242 }
1243 Ok(scratch)
1244 }
1245
1246 #[allow(clippy::too_many_arguments)]
1250 pub fn mmq_iq_experts(
1251 &self,
1252 table: &CudaSlice<u64>,
1253 proj: i32,
1254 n_expert: usize,
1255 ex_ids: &CudaSlice<i32>,
1256 ex_off: &CudaSlice<i32>,
1257 ex_pairs: &CudaSlice<i32>,
1258 pair_tok: &CudaSlice<i32>,
1259 act_scratch: &CudaSlice<u8>,
1260 in_f: usize,
1261 out_f: usize,
1262 n_active: usize,
1263 n_pairs: usize,
1264 n_tokens: usize,
1265 qtype: i32,
1266 row_bytes: usize,
1267 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1268 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
1269 {
1270 let stream = self.gpu.stream();
1271 let (tab_p, _g0) = table.device_ptr(&stream);
1272 let (ei_p, _g1) = ex_ids.device_ptr(&stream);
1273 let (eo_p, _g2) = ex_off.device_ptr(&stream);
1274 let (ep_p, _g3) = ex_pairs.device_ptr(&stream);
1275 let (pt_p, _g4) = pair_tok.device_ptr(&stream);
1276 let (as_p, _g5) = act_scratch.device_ptr(&stream);
1277 let (y_p, _g6) = y.device_ptr_mut(&stream);
1278 let rc = unsafe {
1279 memra_mmq_iq_experts(
1280 tab_p as *const u64,
1281 proj,
1282 n_expert as i32,
1283 ei_p as *const i32,
1284 eo_p as *const i32,
1285 ep_p as *const i32,
1286 pt_p as *const i32,
1287 as_p as *const core::ffi::c_void,
1288 y_p as *mut f32,
1289 in_f as i32,
1290 out_f as i32,
1291 n_active as i32,
1292 n_tokens as i32,
1293 qtype,
1294 row_bytes as i64,
1295 stream.cu_stream() as *mut core::ffi::c_void,
1296 )
1297 };
1298 if rc != 0 {
1299 return Err(format!("memra_mmq_iq_experts rc={rc}").into());
1300 }
1301 }
1302 Ok(y)
1303 }
1304
1305 pub fn moe_f16g_act(
1310 &self,
1311 x: &CudaSlice<f32>,
1312 pair_tok: Option<&CudaSlice<i32>>,
1313 in_f: usize,
1314 n_pairs: usize,
1315 ) -> Result<(CudaSlice<u8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
1316 let mut act = self.alloc_uninit::<u8>(n_pairs * in_f * 2)?;
1317 let mut scales = self.alloc_uninit::<f32>(n_pairs)?;
1318 {
1319 let stream = self.gpu.stream();
1320 let (x_p, _gx) = x.device_ptr(&stream);
1321 let pt_p = match pair_tok {
1322 Some(pt) => { let (p, _g) = pt.device_ptr(&stream); p as *const i32 }
1323 None => std::ptr::null(),
1324 };
1325 let (a_p, _ga) = act.device_ptr_mut(&stream);
1326 let (s_p, _gs) = scales.device_ptr_mut(&stream);
1327 let rc = unsafe {
1328 memra_moe_f16g_gather_act(x_p as *const f32, pt_p,
1329 a_p as *mut core::ffi::c_void, s_p as *mut f32,
1330 in_f as i32, n_pairs as i32,
1331 stream.cu_stream() as *mut core::ffi::c_void)
1332 };
1333 if rc != 0 { return Err(format!("memra_moe_f16g_gather_act rc={rc}").into()); }
1334 }
1335 Ok((act, scales))
1336 }
1337
1338 #[allow(clippy::too_many_arguments)]
1348 pub fn moe_f16_grouped(
1349 &self,
1350 table: &CudaSlice<u64>,
1351 proj: i32,
1352 n_expert: usize,
1353 ex_ids: &CudaSlice<i32>,
1354 ex_off_host: &[i32],
1355 ex_off_dev: &CudaSlice<i32>,
1356 act_f16: &CudaSlice<u8>,
1357 act_scale: &CudaSlice<f32>,
1358 in_f: usize,
1359 out_f: usize,
1360 n_active: usize,
1361 n_pairs: usize,
1362 qtype: i32,
1363 row_bytes: usize,
1364 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1365 let sk = crate::moe_f16g_mode() >= 2 && in_f % 32 == 0;
1366 let (shape_sel, cross) = crate::moe_f16g_sk_params();
1374 if sk && shape_sel >= 0 && crate::moe_f16g_direct_on(qtype)
1375 && (qtype == crate::QT_Q4_K || qtype == crate::QT_Q6_K
1376 || qtype == crate::QT_IQ4_XS || qtype == crate::QT_IQ3_S)
1377 && in_f % 256 == 0 && n_active <= 512 && n_active > 0
1378 {
1379 let max_m = ex_off_host.windows(2).map(|w| w[1] - w[0]).max().unwrap_or(0);
1380 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
1381 {
1382 let stream = self.gpu.stream();
1383 let (tab_p, _g0) = table.device_ptr(&stream);
1384 let (ei_p, _g1) = ex_ids.device_ptr(&stream);
1385 let (a_p, _g2) = act_f16.device_ptr(&stream);
1386 let (s_p, _g3) = act_scale.device_ptr(&stream);
1387 let (off_p, _g4) = ex_off_dev.device_ptr(&stream);
1388 let (y_p, _g5) = y.device_ptr_mut(&stream);
1389 let rc = unsafe {
1390 memra_moe_kq_gemm_sk(tab_p as *const u64, proj, n_expert as i32,
1391 ei_p as *const i32, a_p as *const core::ffi::c_void, y_p as *mut f32,
1392 s_p as *const f32, off_p as *const i32, ex_off_host.as_ptr(),
1393 n_active as i32, max_m, in_f as i32, out_f as i32, qtype, cross,
1394 crate::moe_f16g_tail_on() as i32, row_bytes as i64,
1395 stream.cu_stream() as *mut core::ffi::c_void)
1396 };
1397 if rc != 0 { return Err(format!("memra_moe_kq_gemm_sk rc={rc}").into()); }
1398 }
1399 return Ok(y);
1400 }
1401 if !sk {
1405 static WARM: std::sync::Once = std::sync::Once::new();
1406 let mut warm_err = None;
1407 WARM.call_once(|| {
1408 let r = (|| -> Result<(), Box<dyn std::error::Error>> {
1409 let w = self.alloc_uninit::<u8>(2 * 32 * 64 * 2)?;
1410 let a = self.alloc_uninit::<u8>(4 * 64 * 2)?;
1411 let mut yw = self.alloc_uninit::<u8>(4 * 32 * 2)?;
1412 let off = [0i32, 2, 4];
1413 let stream = self.gpu.stream();
1414 let (w_p, _a1) = w.device_ptr(&stream);
1415 let (a_p, _a2) = a.device_ptr(&stream);
1416 let (y_p, _a3) = yw.device_ptr_mut(&stream);
1417 let rc = unsafe {
1418 memra_moe_f16g_gemm(w_p as *const core::ffi::c_void,
1419 a_p as *const core::ffi::c_void, y_p as *mut core::ffi::c_void,
1420 off.as_ptr(), 2, 64, 32,
1421 stream.cu_stream() as *mut core::ffi::c_void)
1422 };
1423 if rc != 0 { return Err(format!("f16g warmup rc={rc}").into()); }
1424 self.gpu.stream().synchronize()?;
1425 Ok(())
1426 })();
1427 if let Err(e) = r { warm_err = Some(e.to_string()); }
1428 });
1429 if let Some(we) = warm_err { return Err(we.into()); }
1430 }
1431 let w_bytes = n_active * out_f * in_f * 2;
1432 let mut w_f16 = self.alloc_uninit::<u8>(w_bytes)?;
1433 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
1434 {
1435 let stream = self.gpu.stream();
1436 let (tab_p, _g0) = table.device_ptr(&stream);
1437 let (ei_p, _g1) = ex_ids.device_ptr(&stream);
1438 let (w_p, _g2) = w_f16.device_ptr_mut(&stream);
1439 let rc = unsafe {
1440 memra_moe_f16g_dequant(tab_p as *const u64, proj, n_expert as i32,
1441 ei_p as *const i32, w_p as *mut core::ffi::c_void,
1442 in_f as i32, out_f as i32, n_active as i32, qtype, row_bytes as i64,
1443 stream.cu_stream() as *mut core::ffi::c_void)
1444 };
1445 if rc != 0 { return Err(format!("memra_moe_f16g_dequant rc={rc}").into()); }
1446 let (a_p, _g3) = act_f16.device_ptr(&stream);
1447 let (s_p, _g6) = act_scale.device_ptr(&stream);
1448 let (y_p, _g5) = y.device_ptr_mut(&stream);
1449 if sk {
1450 let max_m = ex_off_host.windows(2).map(|w| w[1] - w[0]).max().unwrap_or(0);
1451 let (off_p, _g7) = ex_off_dev.device_ptr(&stream);
1452 let (shape_sel, cross) = crate::moe_f16g_sk_params();
1453 let rc = unsafe {
1454 memra_moe_f16g_gemm_sk(w_p as *const core::ffi::c_void,
1455 a_p as *const core::ffi::c_void, y_p as *mut f32,
1456 s_p as *const f32, off_p as *const i32, ex_off_host.as_ptr(),
1457 n_active as i32, max_m, in_f as i32, out_f as i32, shape_sel, cross,
1458 crate::moe_f16g_tail_on() as i32,
1459 stream.cu_stream() as *mut core::ffi::c_void)
1460 };
1461 if rc != 0 { return Err(format!("memra_moe_f16g_gemm_sk rc={rc}").into()); }
1462 } else {
1463 let mut y16 = self.alloc_uninit::<u8>(n_pairs * out_f * 2)?;
1464 let (y16_p, _g4) = y16.device_ptr_mut(&stream);
1465 let rc = unsafe {
1466 memra_moe_f16g_gemm(w_p as *const core::ffi::c_void,
1467 a_p as *const core::ffi::c_void, y16_p as *mut core::ffi::c_void,
1468 ex_off_host.as_ptr(), n_active as i32, in_f as i32, out_f as i32,
1469 stream.cu_stream() as *mut core::ffi::c_void)
1470 };
1471 if rc != 0 { return Err(format!("memra_moe_f16g_gemm rc={rc}").into()); }
1472 let rc = unsafe {
1473 memra_moe_f16g_h2f_scaled(y16_p as *const core::ffi::c_void, y_p as *mut f32,
1474 s_p as *const f32, out_f as i32, n_pairs as i32,
1475 stream.cu_stream() as *mut core::ffi::c_void)
1476 };
1477 if rc != 0 { return Err(format!("memra_moe_f16g_h2f_scaled rc={rc}").into()); }
1478 }
1479 }
1480 if !sk {
1485 self.gpu.stream().synchronize()?;
1486 }
1487 if std::env::var("MEMRA_F16G_DEBUG").is_ok() {
1488 let wn = n_active * out_f * in_f;
1490 let an = n_pairs * in_f;
1491 let mut wf = self.alloc_uninit::<f32>(wn)?;
1492 let mut af = self.alloc_uninit::<f32>(an)?;
1493 {
1494 let stream = self.gpu.stream();
1495 let (w_p, _a) = w_f16.device_ptr(&stream);
1496 let (a_p, _b) = act_f16.device_ptr(&stream);
1497 let (wf_p, _c) = wf.device_ptr_mut(&stream);
1498 let (af_p, _d) = af.device_ptr_mut(&stream);
1499 unsafe {
1500 memra_moe_f16g_h2f(w_p as *const core::ffi::c_void, wf_p as *mut f32, wn,
1501 stream.cu_stream() as *mut core::ffi::c_void);
1502 memra_moe_f16g_h2f(a_p as *const core::ffi::c_void, af_p as *mut f32, an,
1503 stream.cu_stream() as *mut core::ffi::c_void);
1504 }
1505 }
1506 let (wh, ah, yh) = (self.dtoh(&wf)?, self.dtoh(&af)?, self.dtoh(&y)?);
1507 let scan = |v: &[f32]| -> (usize, f32) {
1508 let bad = v.iter().filter(|x| !x.is_finite()).count();
1509 let mx = v.iter().filter(|x| x.is_finite()).fold(0.0f32, |m, x| m.max(x.abs()));
1510 (bad, mx)
1511 };
1512 let (wb, wm) = scan(&wh); let (ab, am) = scan(&ah); let (yb, ym) = scan(&yh);
1513 eprintln!("[f16g-debug] proj={proj} w: bad={wb} max={wm:.3e} | act: bad={ab} \
1514 max={am:.3e} | y: bad={yb} max={ym:.3e} (na={n_active} np={n_pairs} \
1515 in={in_f} out={out_f})");
1516 }
1517 Ok(y)
1518 }
1519
1520 #[allow(clippy::too_many_arguments)]
1527 pub fn moe_f16g_gemm_sk_raw(&self, w_f16: &CudaSlice<u8>, act_f16: &CudaSlice<u8>,
1528 row_scale: &CudaSlice<f32>, ex_off_host: &[i32], ex_off_dev: &CudaSlice<i32>,
1529 in_f: usize, out_f: usize, n_pairs: usize, shape_sel: i32, cross: i32, tail: i32)
1530 -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1531 let n_active = ex_off_host.len() - 1;
1532 let max_m = ex_off_host.windows(2).map(|w| w[1] - w[0]).max().unwrap_or(0);
1533 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
1534 {
1535 let stream = self.gpu.stream();
1536 let (w_p, _g0) = w_f16.device_ptr(&stream);
1537 let (a_p, _g1) = act_f16.device_ptr(&stream);
1538 let (s_p, _g2) = row_scale.device_ptr(&stream);
1539 let (off_p, _g3) = ex_off_dev.device_ptr(&stream);
1540 let (y_p, _g4) = y.device_ptr_mut(&stream);
1541 let rc = unsafe {
1542 memra_moe_f16g_gemm_sk(w_p as *const core::ffi::c_void,
1543 a_p as *const core::ffi::c_void, y_p as *mut f32,
1544 s_p as *const f32, off_p as *const i32, ex_off_host.as_ptr(),
1545 n_active as i32, max_m, in_f as i32, out_f as i32, shape_sel, cross,
1546 tail, stream.cu_stream() as *mut core::ffi::c_void)
1547 };
1548 if rc != 0 { return Err(format!("memra_moe_f16g_gemm_sk rc={rc}").into()); }
1549 }
1550 Ok(y)
1551 }
1552
1553 #[allow(clippy::too_many_arguments)]
1558 pub fn moe_kq_gemm_sk_raw(&self, table: &CudaSlice<u64>, proj: i32, n_expert: usize,
1559 ex_ids: &CudaSlice<i32>, act_f16: &CudaSlice<u8>, row_scale: &CudaSlice<f32>,
1560 ex_off_host: &[i32], ex_off_dev: &CudaSlice<i32>,
1561 in_f: usize, out_f: usize, n_pairs: usize, qtype: i32, row_bytes: usize, cross: i32,
1562 tail: i32)
1563 -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
1564 let n_active = ex_off_host.len() - 1;
1565 let max_m = ex_off_host.windows(2).map(|w| w[1] - w[0]).max().unwrap_or(0);
1566 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
1567 {
1568 let stream = self.gpu.stream();
1569 let (tab_p, _g0) = table.device_ptr(&stream);
1570 let (ei_p, _g1) = ex_ids.device_ptr(&stream);
1571 let (a_p, _g2) = act_f16.device_ptr(&stream);
1572 let (s_p, _g3) = row_scale.device_ptr(&stream);
1573 let (off_p, _g4) = ex_off_dev.device_ptr(&stream);
1574 let (y_p, _g5) = y.device_ptr_mut(&stream);
1575 let rc = unsafe {
1576 memra_moe_kq_gemm_sk(tab_p as *const u64, proj, n_expert as i32,
1577 ei_p as *const i32, a_p as *const core::ffi::c_void, y_p as *mut f32,
1578 s_p as *const f32, off_p as *const i32, ex_off_host.as_ptr(),
1579 n_active as i32, max_m, in_f as i32, out_f as i32, qtype, cross,
1580 tail, row_bytes as i64,
1581 stream.cu_stream() as *mut core::ffi::c_void)
1582 };
1583 if rc != 0 { return Err(format!("memra_moe_kq_gemm_sk rc={rc}").into()); }
1584 }
1585 Ok(y)
1586 }
1587
1588 pub fn moe_f16g_dequant_raw(&self, table: &CudaSlice<u64>, proj: i32, n_expert: usize,
1592 ex_ids: &CudaSlice<i32>, in_f: usize, out_f: usize, n_active: usize, qtype: i32,
1593 row_bytes: usize)
1594 -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
1595 let mut w_f16 = self.alloc_uninit::<u8>(n_active * out_f * in_f * 2)?;
1596 {
1597 let stream = self.gpu.stream();
1598 let (tab_p, _g0) = table.device_ptr(&stream);
1599 let (ei_p, _g1) = ex_ids.device_ptr(&stream);
1600 let (w_p, _g2) = w_f16.device_ptr_mut(&stream);
1601 let rc = unsafe {
1602 memra_moe_f16g_dequant(tab_p as *const u64, proj, n_expert as i32,
1603 ei_p as *const i32, w_p as *mut core::ffi::c_void,
1604 in_f as i32, out_f as i32, n_active as i32, qtype, row_bytes as i64,
1605 stream.cu_stream() as *mut core::ffi::c_void)
1606 };
1607 if rc != 0 { return Err(format!("memra_moe_f16g_dequant rc={rc}").into()); }
1608 }
1609 Ok(w_f16)
1610 }
1611}