1use cudarc::driver::{
4 CudaContext, CudaFunction, CudaModule, CudaSlice, CudaStream, LaunchConfig, PushKernelArg,
5};
6use cudarc::nvrtc::Ptx;
7use std::sync::{Arc, Mutex};
8
9#[cfg(debug_assertions)]
10pub(crate) fn debug_assert_tensor_stream_device<T>(
11 tensor: &CudaSlice<T>,
12 stream: &CudaStream,
13 site: &str,
14) {
15 let tensor_dev = tensor.ordinal();
16 let stream_dev = stream.context().ordinal();
17 assert_eq!(
18 tensor_dev, stream_dev,
19 "PP cross-device tensor read at {site}: tensor on dev{tensor_dev}, stream on dev{stream_dev}"
20 );
21}
22
23pub use memra_gguf;
24pub use memra_runtime;
25
26pub mod forward;
27pub mod hybrid;
28pub mod hybrid_forward;
29pub mod model;
30pub mod sigrouter_contract;
31pub mod vision;
32pub mod vision_gemma;
33pub mod vision_pre;
34pub mod cache {
37 pub use memra_kv::*;
38}
39pub mod decode;
40pub mod decode_batch;
41pub mod dflash;
42pub mod eagle;
43pub mod gemma_spec;
44pub mod graph_update;
45pub mod mla;
49pub mod moesd;
50pub mod parallel;
51pub mod pp;
52pub mod round_stream;
53pub mod spec;
54pub use memra_sampling as sampler;
55
56pub fn moe_f16g_mode() -> u8 {
100 static M: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
101 *M.get_or_init(|| match std::env::var("MEMRA_MOE_F16G").as_deref() {
102 Ok("0") => 0,
103 Ok("2") => 2,
104 Ok("3") => 3,
105 Ok(_) => 1,
106 Err(_) => 2,
109 })
110}
111pub fn moe_f16g_sk_params() -> (i32, i32) {
125 static P: std::sync::OnceLock<(i32, i32)> = std::sync::OnceLock::new();
126 *P.get_or_init(|| match std::env::var("MEMRA_F16G_SK").as_deref() {
127 Ok("0") => (-1, 0),
128 Ok("32") => (0, i32::MAX),
129 Ok("128") => (0, 1),
130 _ => {
131 let cross = std::env::var("MEMRA_F16G_SK_CROSS")
132 .ok()
133 .and_then(|v| v.parse().ok())
134 .unwrap_or(64);
135 (0, cross)
136 }
137 })
138}
139pub fn moe_f16g_direct_on(qtype: i32) -> bool {
150 static M: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
151 let m = *M.get_or_init(|| match std::env::var("MEMRA_F16G_DIRECT").as_deref() {
152 Ok("0") => 0,
153 Ok("kq") => 1,
154 _ => 2,
155 });
156 match m {
157 0 => false,
158 1 => qtype == QT_Q4_K || qtype == QT_Q6_K,
159 _ => true,
160 }
161}
162pub fn moe_f16g_tail_on() -> bool {
171 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
172 *ON.get_or_init(|| std::env::var("MEMRA_F16G_TAIL").as_deref() != Ok("0"))
173}
174
175pub fn moe_f16g_gemma_on() -> bool {
182 static M: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
183 *M.get_or_init(|| !matches!(std::env::var("MEMRA_MOE_F16G").as_deref(), Ok("0") | Err(_)))
184}
185
186pub fn moe_fuse_actq_on() -> bool {
190 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
191 *ON.get_or_init(|| std::env::var("MEMRA_MOE_FUSE_ACTQ").as_deref() != Ok("0"))
192}
193
194pub fn router_prefill_exact_on() -> bool {
204 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
205 *ON.get_or_init(|| std::env::var("MEMRA_ROUTER_PREFILL_EXACT").as_deref() != Ok("0"))
206}
207
208pub fn router_kernel_on() -> bool {
209 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
210 *ON.get_or_init(|| {
211 let on = std::env::var("MEMRA_ROUTER_KERNEL").as_deref() != Ok("0");
212 if !on {
213 eprintln!("[memra] router kernel OFF (rollback: per-column cuBLAS gemv)");
214 }
215 on
216 })
217}
218
219pub const ROUTER_BATCH_MIN_T: usize = 8;
234pub fn router_batch_on() -> bool {
235 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
236 *ON.get_or_init(|| std::env::var("MEMRA_ROUTER_BATCH").as_deref() != Ok("0"))
237}
238mod cpu_experts;
239#[cfg(memra_cutlass)]
240pub mod cutlass_ffi;
241pub mod f16_ffi;
242pub mod fp8_ffi;
243pub mod mmq_ffi;
244pub mod moe_cache;
245pub mod prime_graph;
246pub mod spill;
247mod spill_pread;
248
249const FATBIN: &[u8] = include_bytes!(env!("MEMRA_ENGINE_FATBIN"));
256const HYBRID_FATBIN: &[u8] = include_bytes!(env!("MEMRA_HYBRID_FATBIN"));
257const QMATVEC_FATBIN: &[u8] = include_bytes!(env!("MEMRA_QMATVEC_FATBIN"));
258const FLASH_FATBIN: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN"));
259const GEMM_FATBIN: &[u8] = include_bytes!(env!("MEMRA_GEMM_FATBIN"));
260const ROUTER_FATBIN: &[u8] = include_bytes!(env!("MEMRA_ROUTER_FATBIN"));
261const SAMPLE_FATBIN: &[u8] = include_bytes!(env!("MEMRA_SAMPLE_FATBIN"));
263
264fn gemm_fatbin_bytes() -> std::borrow::Cow<'static, [u8]> {
270 assert!(
271 !(portable_mma_gated() && std::env::var_os("MEMRA_GEMM_FATBIN").is_some()),
272 "MEMRA_GEMM_FATBIN overrides are not allowed in the portable CUDA lane"
273 );
274 match std::env::var("MEMRA_GEMM_FATBIN") {
275 Ok(path) => std::borrow::Cow::Owned(
276 std::fs::read(&path).unwrap_or_else(|e| panic!("MEMRA_GEMM_FATBIN read {path}: {e}")),
277 ),
278 Err(_) => std::borrow::Cow::Borrowed(GEMM_FATBIN),
279 }
280}
281
282pub(crate) const fn portable_mma_gated() -> bool {
289 cfg!(memra_portable_cuda) && !cfg!(memra_hopper_mma)
290}
291
292const fn legacy_quant_gemm_allowed(portable_cuda: bool, hopper_mma: bool, no_gemm: bool) -> bool {
297 (!portable_cuda || hopper_mma) && !no_gemm
298}
299
300const FLASH_FATBIN_VQ4: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_VQ4"));
308const FLASH_FATBIN_VF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_VF8"));
309const FLASH_FATBIN_KF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8"));
310const FLASH_FATBIN_KF8VQ4: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8VQ4"));
311const FLASH_FATBIN_KF8VF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8VF8"));
312
313pub use memra_kv::{kv_blk_bytes, kv_cache_formats};
316
317fn flash_fatbin_bytes() -> &'static [u8] {
319 match kv_cache_formats() {
320 ("q8_0", "q5_1") => FLASH_FATBIN,
321 ("q8_0", "q4_0") => FLASH_FATBIN_VQ4,
322 ("q8_0", "fp8") => FLASH_FATBIN_VF8,
323 ("fp8", "q5_1") => FLASH_FATBIN_KF8,
324 ("fp8", "q4_0") => FLASH_FATBIN_KF8VQ4,
325 ("fp8", "fp8") => FLASH_FATBIN_KF8VF8,
326 other => unreachable!("kv_cache_formats returned {other:?}"),
327 }
328}
329
330fn k1_launch_override() -> Option<(u32, u32, u32)> {
337 static K1: std::sync::OnceLock<Option<(u32, u32, u32)>> = std::sync::OnceLock::new();
338 *K1.get_or_init(|| {
339 let v = std::env::var("MEMRA_GEMM_K1_LAUNCH").ok()?;
340 let p: Vec<u32> = v.split(',').filter_map(|s| s.trim().parse().ok()).collect();
341 match p.as_slice() {
342 [bm, bn, w] => Some((*bm, *bn, *w)),
343 _ => None,
344 }
345 })
346}
347
348pub(crate) fn wgmma_gemm_enabled() -> bool {
355 static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
356 *V.get_or_init(|| std::env::var("MEMRA_WGMMA").as_deref() == Ok("1"))
357}
358
359pub const FA_VEC_MIN_TKV: usize = 96;
374pub fn fa_vec_min_tkv() -> usize {
378 static V: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
379 *V.get_or_init(|| {
380 std::env::var("MEMRA_FA_VEC_MIN")
381 .ok()
382 .and_then(|v| v.parse().ok())
383 .unwrap_or_else(|| FA_VEC_MIN_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
384 })
385}
386
387pub fn fa_f16pv_on() -> bool {
398 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
399 *ON.get_or_init(|| {
400 std::env::var("MEMRA_FA_F16PV")
401 .map(|v| v != "0")
402 .unwrap_or_else(|_| std::env::var("MEMRA_DRAFT").is_err())
403 })
404}
405
406pub fn fa512_hp_on() -> bool {
410 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
411 *ON.get_or_init(|| std::env::var("MEMRA_FA512_HP").as_deref() != Ok("0"))
412}
413
414pub fn faw_hp_on() -> bool {
418 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
419 *ON.get_or_init(|| std::env::var("MEMRA_FAW_HP").as_deref() != Ok("0"))
420}
421
422pub fn fa512_wide_warps() -> usize {
426 static N: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
427 *N.get_or_init(|| match std::env::var("MEMRA_FA512_W4").as_deref() {
428 Ok("1") => 4,
429 _ => 2,
430 })
431}
432
433pub fn fa512_min_tkv() -> usize {
436 static FA512_MIN: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
437 *FA512_MIN.get_or_init(|| {
438 std::env::var("MEMRA_FA512_MIN")
439 .ok()
440 .and_then(|v| v.parse().ok())
441 .unwrap_or(512)
442 })
443}
444pub static FA_VEC_MIN_DEFAULT: std::sync::atomic::AtomicUsize =
448 std::sync::atomic::AtomicUsize::new(FA_VEC_MIN_TKV);
449pub static FA_SPW_DEFAULT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(32);
453pub static FUSED_MR1_DEFAULT: std::sync::atomic::AtomicBool =
459 std::sync::atomic::AtomicBool::new(false);
460pub static ROUTER_W8_DEFAULT: std::sync::atomic::AtomicBool =
467 std::sync::atomic::AtomicBool::new(true);
468pub static FA_SP512_DEFAULT: std::sync::atomic::AtomicUsize =
469 std::sync::atomic::AtomicUsize::new(16);
470pub static RMS_BLOCK_DEFAULT: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(256);
475pub static FA_SP_GEMMA: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
477pub static MMQ_SK_FORCE: std::sync::atomic::AtomicI8 = std::sync::atomic::AtomicI8::new(-1);
482pub use memra_kv::KV_FP8_FORCE;
485pub(crate) fn rms_block() -> u32 {
486 static V: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
487 *V.get_or_init(|| {
488 std::env::var("MEMRA_RMS_BLOCK")
489 .ok()
490 .and_then(|v| v.parse().ok())
491 .unwrap_or_else(|| RMS_BLOCK_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
492 })
493}
494
495pub(crate) fn fa_split_keys(t_kv: usize, n_head_kv: usize) -> usize {
496 static S: std::sync::OnceLock<Option<usize>> = std::sync::OnceLock::new();
497 if let Some(forced) = *S.get_or_init(|| {
498 std::env::var("MEMRA_FA_SPLIT")
499 .ok()
500 .and_then(|v| v.parse().ok())
501 .filter(|&s: &usize| s >= 8 && s % 8 == 0)
502 }) {
503 return forced;
504 }
505 if FA_SP_GEMMA.load(std::sync::atomic::Ordering::Relaxed)
523 && std::env::var("MEMRA_FA_SP16").as_deref() == Ok("1")
524 {
525 return if t_kv <= 8192 {
526 16
527 } else if t_kv <= 16384 {
528 64
529 } else {
530 128
531 };
532 }
533 let big_rig = fa_sm_count() >= 128;
534 if big_rig {
535 let _ = n_head_kv;
536 if t_kv <= 2048 {
537 16
538 } else if t_kv <= 16384 {
539 64
540 } else {
541 128
542 }
543 } else if n_head_kv <= 4 {
544 if t_kv <= 512 {
565 8
566 } else if t_kv <= 16384 {
567 64
568 } else {
569 128
570 }
571 } else {
572 if t_kv <= 8192 {
573 32
574 } else if t_kv <= 16384 {
575 64
576 } else {
577 128
578 }
579 }
580}
581
582fn fa_sm_count() -> i32 {
585 static N: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
586 *N.get_or_init(|| {
587 cudarc::driver::result::init().ok();
588 cudarc::driver::result::device::get(0)
589 .and_then(|d| unsafe { cudarc::driver::result::device::get_attribute(
590 d, cudarc::driver::sys::CUdevice_attribute_enum::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT) })
591 .unwrap_or(82)
592 })
593}
594
595fn fa_hd_suffix(head_dim: usize) -> Result<&'static str, Box<dyn std::error::Error>> {
599 match head_dim {
600 256 => Ok(""),
601 128 => Ok("_hd128"),
602 d => Err(format!(
603 "fa_prefill: no kernel stamped for head_dim={d} (only 256/128); \
604 callers must gate to sdpa_naive"
605 )
606 .into()),
607 }
608}
609
610pub const QT_Q8_0: i32 = 0;
612pub const QT_Q4_K: i32 = 1;
613pub const QT_Q6_K: i32 = 2;
614pub const QT_Q5_K: i32 = 3;
615pub const QT_Q3_K: i32 = 4;
616pub const QT_IQ4_XS: i32 = 5;
617pub const QT_IQ3_S: i32 = 6;
618pub const QT_NVFP4: i32 = 7;
619pub const QT_F8_E4M3: i32 = 10;
625pub const QT_NVFP4_RP: i32 = 9;
628pub const QT_F32: i32 = 8;
630pub const QT_BF16: i32 = 11;
631pub const QT_Q4_0: i32 = 12; pub const QT_Q2_K: i32 = 13;
636pub const QT_F8_E4M3_BLK: i32 = 14;
652
653pub struct Engine {
655 pub gpu: memra_runtime::Gpu,
656 module: Arc<CudaModule>,
657 hybrid: Arc<CudaModule>,
658 qmatvec: Arc<CudaModule>,
659 flash: Arc<CudaModule>,
660 flash_g: std::sync::OnceLock<Arc<CudaModule>>,
664 gemm: Arc<CudaModule>,
665 router: Arc<CudaModule>,
666 sample: Arc<CudaModule>,
668 moe_cache: Mutex<Option<crate::moe_cache::MoeSlotCache>>,
672 moe_cache_layout: Mutex<Option<Vec<usize>>>,
676 capture_keep_on: std::sync::atomic::AtomicBool,
682 verify_exact: std::sync::atomic::AtomicBool,
687 capture_keep: Mutex<Vec<Box<dyn std::any::Any + Send>>>,
688 pub copy_stream: Arc<CudaStream>,
690 #[cfg(memra_cutlass)]
697 cutlass_scratch: Mutex<Option<crate::cutlass_ffi::CutlassScratch>>,
698 fp8_scratch: Mutex<Option<crate::fp8_ffi::Fp8Scratch>>,
702 fa_vf16_scratch: Mutex<Option<CudaSlice<u8>>>,
705 fa_part_pool: Mutex<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>>,
709 fa_part_retired: Mutex<Vec<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>>,
713 fn_cache: Mutex<std::collections::HashMap<String, CudaFunction>>,
715 f16_scratch: Mutex<Option<crate::f16_ffi::F16Scratch>>,
716 argmax_partials: Mutex<Option<(CudaSlice<f32>, CudaSlice<i32>)>>,
721 prime_deqw_ws: Mutex<Option<(CudaSlice<u8>, CudaSlice<u8>)>>,
726 router_stage: Mutex<Option<PinnedStage>>,
730}
731
732fn fa_v2_on() -> bool {
742 std::env::var("MEMRA_FA_V2")
748 .map(|v| v != "0")
749 .unwrap_or(true)
750}
751
752fn fa_v3_on() -> bool {
760 std::env::var("MEMRA_FA_V3")
764 .map(|v| v != "0")
765 .unwrap_or(true)
766}
767
768fn fa_v4_mode() -> &'static str {
773 static M: std::sync::OnceLock<String> = std::sync::OnceLock::new();
774 M.get_or_init(|| std::env::var("MEMRA_FA_V4").unwrap_or_default())
775}
776fn fa_v4_on() -> bool {
777 fa_v4_mode() != "0"
778} pub static FA_SMEM_TKV_DEFAULT: std::sync::atomic::AtomicUsize =
787 std::sync::atomic::AtomicUsize::new(1024);
788pub static FA_V4_MAX_DEFAULT: std::sync::atomic::AtomicUsize =
789 std::sync::atomic::AtomicUsize::new(usize::MAX);
790pub fn fa_v4_at_pub(t_kv: usize) -> bool {
791 fa_v4_at(t_kv)
792}
793fn fa_v4_at(t_kv: usize) -> bool {
794 static M: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
795 let mx = *M.get_or_init(|| {
796 std::env::var("MEMRA_FA_V4_MAX")
797 .ok()
798 .and_then(|v| v.parse().ok())
799 .unwrap_or_else(|| FA_V4_MAX_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
800 });
801 fa_v4_on() && t_kv < mx
802}
803pub const FA_DEEP_MIN_DEFAULT: usize = 0;
817fn fa_deep_at(t_kv: usize) -> bool {
818 if std::env::var("MEMRA_FA_DEEP").as_deref() == Ok("0") {
819 return false;
820 }
821 let min = std::env::var("MEMRA_FA_DEEP_MIN")
822 .ok()
823 .and_then(|v| v.parse().ok())
824 .unwrap_or(FA_DEEP_MIN_DEFAULT);
825 t_kv >= min
826}
827pub fn fa_deep_at_pub(t_kv: usize) -> bool {
829 fa_deep_at(t_kv)
830}
831
832fn fa_v3_active(head_dim: usize) -> bool {
833 fa_v3_on()
836 && head_dim % 128 == 0
837 && kv_cache_formats() == ("q8_0", "q5_1")
838 && !Engine::kv_fp8_on()
839}
840
841pub fn fa_seqs_eligible(t_kv: usize, head_dim: usize) -> bool {
849 std::env::var("MEMRA_NO_FA_VEC").is_err()
850 && t_kv >= fa_vec_min_tkv()
851 && head_dim == 256
852 && fa_v4_at(t_kv)
853 && !matches!(fa_v4_mode(), "noB3" | "stage")
854 && !Engine::kv_fp8_on()
855}
856pub fn fa_split_keys_pub(t_kv: usize, n_head_kv: usize) -> usize {
858 fa_split_keys(t_kv, n_head_kv)
859}
860
861struct PinnedStage {
866 ptr: *mut u8,
867 cap: usize,
868}
869unsafe impl Send for PinnedStage {}
870impl PinnedStage {
871 fn new(cap: usize) -> Result<Self, Box<dyn std::error::Error>> {
872 let ptr = unsafe { cudarc::driver::result::malloc_host(cap, 0)? } as *mut u8;
873 Ok(PinnedStage { ptr, cap })
874 }
875}
876impl Drop for PinnedStage {
877 fn drop(&mut self) {
878 let _ = unsafe { cudarc::driver::result::free_host(self.ptr as _) };
879 }
880}
881
882pub const ARGMAX_NB: usize = 256;
885
886pub(crate) use memra_fa3_vl as fa3_vl_raw;
888
889unsafe extern "C" {
890 fn memra_fa3_prefill(
892 q16: *const core::ffi::c_void,
893 k16: *const core::ffi::c_void,
894 v16: *const core::ffi::c_void,
895 o: *mut f32,
896 t: i32,
897 h: i32,
898 hkv: i32,
899 d: i32,
900 scale: f32,
901 stream: *mut core::ffi::c_void,
902 ) -> i32;
903 pub(crate) fn memra_fa3_vl(
905 q16s: *const *const core::ffi::c_void,
906 k16s: *const *const core::ffi::c_void,
907 v16s: *const *const core::ffi::c_void,
908 os: *const *mut f32,
909 ts: *const i32,
910 b: i32,
911 h: i32,
912 hkv: i32,
913 d: i32,
914 scale: f32,
915 stream: *mut core::ffi::c_void,
916 ) -> i32;
917}
918
919#[repr(C)]
924#[derive(Clone, Copy)]
925pub struct WPtr8(pub [u64; 8]);
926unsafe impl cudarc::driver::DeviceRepr for WPtr8 {}
927
928#[repr(C)]
933#[derive(Clone, Copy, Default)]
934pub struct GdnSeqVl {
935 pub kb16: u64,
936 pub gcum: u64,
937 pub beta: u64,
938 pub u: u64,
939 pub wb16: u64,
940 pub y: u64,
941 pub ssnap: u64,
942 pub state_in: u64,
943 pub state_out: u64,
944 pub q: u64,
945 pub p: u64,
946 pub o: u64,
947 pub k: u64,
948 pub v: u64,
949 pub g: u64,
950 pub a: u64,
951 pub w: u64,
952 pub t: i32,
953 pub nc: i32,
954}
955unsafe impl cudarc::driver::DeviceRepr for GdnSeqVl {}
956#[repr(C)]
957#[derive(Clone, Copy)]
958pub struct GdnVl8(pub [GdnSeqVl; 8]);
959unsafe impl cudarc::driver::DeviceRepr for GdnVl8 {}
960
961#[repr(C)]
964#[derive(Clone, Copy, Default)]
965pub struct GdnWVl {
966 pub qb16: u64,
967 pub pb16: u64,
968}
969unsafe impl cudarc::driver::DeviceRepr for GdnWVl {}
970#[repr(C)]
971#[derive(Clone, Copy)]
972pub struct GdnWVl8(pub [GdnWVl; 8]);
973unsafe impl cudarc::driver::DeviceRepr for GdnWVl8 {}
974
975#[repr(C)]
977#[derive(Clone, Copy, Default)]
978pub struct GdnPrepVl {
979 pub qkv: u64,
980 pub conv_state: u64,
981 pub conv_out: u64,
982 pub q_g: u64,
983 pub k_g: u64,
984 pub v_g: u64,
985 pub q_l2: u64,
986 pub k_l2: u64,
987 pub beta_raw: u64,
988 pub alpha: u64,
989 pub beta: u64,
990 pub g_log: u64,
991 pub o: u64,
992 pub z: u64,
993 pub gn: u64,
994 pub gn16: u64,
995 pub kb16: u64,
996 pub qb16: u64,
997 pub t: i32,
998 pub pad: i32,
999}
1000unsafe impl cudarc::driver::DeviceRepr for GdnPrepVl {}
1001#[repr(C)]
1002#[derive(Clone, Copy)]
1003pub struct GdnPrepVl8(pub [GdnPrepVl; 8]);
1004unsafe impl cudarc::driver::DeviceRepr for GdnPrepVl8 {}
1005
1006#[repr(C)]
1008#[derive(Clone, Copy, Default)]
1009pub struct FaSeqVl {
1010 pub q: u64,
1011 pub k16: u64,
1012 pub v16: u64,
1013 pub o: u64,
1014 pub kf: u64,
1015 pub vf: u64,
1016 pub t: i32,
1017 pub pad: i32,
1018}
1019unsafe impl cudarc::driver::DeviceRepr for FaSeqVl {}
1020#[repr(C)]
1021#[derive(Clone, Copy)]
1022pub struct FaVl8(pub [FaSeqVl; 8]);
1023unsafe impl cudarc::driver::DeviceRepr for FaVl8 {}
1024
1025#[repr(C)]
1027#[derive(Clone, Copy, Default)]
1028pub struct AttnPreVl {
1029 pub qf: u64,
1030 pub kf: u64,
1031 pub vf: u64,
1032 pub q: u64,
1033 pub gate: u64,
1034 pub qn: u64,
1035 pub kn: u64,
1036 pub kc: u64,
1037 pub vc: u64,
1038 pub t: i32,
1039 pub pad: i32,
1040}
1041unsafe impl cudarc::driver::DeviceRepr for AttnPreVl {}
1042#[repr(C)]
1043#[derive(Clone, Copy)]
1044pub struct AttnPreVl8(pub [AttnPreVl; 8]);
1045unsafe impl cudarc::driver::DeviceRepr for AttnPreVl8 {}
1046
1047pub struct GdnChunkBufs {
1050 pub gcum: CudaSlice<f32>,
1051 pub a: CudaSlice<f32>,
1052 pub p: CudaSlice<f32>,
1053 pub u: CudaSlice<f32>,
1054 pub w: CudaSlice<f32>,
1055 pub kb16: CudaSlice<u8>,
1056 pub wb16: CudaSlice<u8>,
1057 pub y16: CudaSlice<u8>,
1058 pub ssnap16: CudaSlice<u8>,
1059 pub qb16: CudaSlice<u8>,
1060 pub pb16: CudaSlice<u8>,
1061 pub o: CudaSlice<f32>,
1062 pub t: usize,
1063 pub nc: usize,
1064}
1065
1066#[repr(C)]
1068#[derive(Clone, Copy)]
1069pub struct F32x8(pub [f32; 8]);
1070unsafe impl cudarc::driver::DeviceRepr for F32x8 {}
1071
1072pub static PRIME_NANOS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1076
1077impl Engine {
1078 pub fn new(ordinal: usize) -> Result<Self, Box<dyn std::error::Error>> {
1079 let gpu = memra_runtime::Gpu::new(ordinal)?;
1080 if std::env::var("MEMRA_ARCH_CHECK").as_deref() != Ok("0") {
1084 use cudarc::driver::sys::CUdevice_attribute_enum as A;
1085 let (maj, min) = cudarc::driver::result::device::get(ordinal as i32)
1086 .and_then(|d| unsafe {
1087 Ok((
1088 cudarc::driver::result::device::get_attribute(
1089 d,
1090 A::CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR,
1091 )?,
1092 cudarc::driver::result::device::get_attribute(
1093 d,
1094 A::CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR,
1095 )?,
1096 ))
1097 })
1098 .unwrap_or((0, 0));
1099 let built = env!("MEMRA_BUILT_CUDA_ARCH");
1100 let ok = matches!(
1101 (built, maj, min),
1102 ("120a", 12, 0) | ("120a", 12, 1) | ("100a", 10, 0) | ("90a", 9, 0) | ("89", 8, 9)
1103 );
1104 if !ok {
1105 return Err(format!(
1106 "memra was built for sm_{built} but device {ordinal} reports compute \
1107 capability {maj}.{min}. Rebuild on this machine (MEMRA_CUDA_ARCH \
1108 auto-detects the GPU) or set MEMRA_ARCH_CHECK=0 to bypass."
1109 )
1110 .into());
1111 }
1112 }
1113 unsafe {
1118 use cudarc::driver::sys;
1119 let dev: sys::CUdevice = ordinal as sys::CUdevice;
1120 let mut pool: sys::CUmemoryPool = std::ptr::null_mut();
1121 if sys::cuDeviceGetDefaultMemPool(&mut pool, dev) == sys::CUresult::CUDA_SUCCESS {
1122 let mut thresh: u64 = u64::MAX;
1123 let _ = sys::cuMemPoolSetAttribute(
1124 pool,
1125 sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_RELEASE_THRESHOLD,
1126 &mut thresh as *mut u64 as *mut core::ffi::c_void,
1127 );
1128 }
1129 }
1130 let module = gpu.ctx.load_module(Ptx::from_binary(FATBIN.to_vec()))?;
1131 let hybrid = gpu
1132 .ctx
1133 .load_module(Ptx::from_binary(HYBRID_FATBIN.to_vec()))?;
1134 let qmatvec = gpu
1135 .ctx
1136 .load_module(Ptx::from_binary(QMATVEC_FATBIN.to_vec()))?;
1137 let flash = gpu
1138 .ctx
1139 .load_module(Ptx::from_binary(flash_fatbin_bytes().to_vec()))?;
1140 let gemm = gpu
1141 .ctx
1142 .load_module(Ptx::from_binary(gemm_fatbin_bytes().into_owned()))?;
1143 let router = gpu
1144 .ctx
1145 .load_module(Ptx::from_binary(ROUTER_FATBIN.to_vec()))?;
1146 let sample = gpu
1147 .ctx
1148 .load_module(Ptx::from_binary(SAMPLE_FATBIN.to_vec()))?;
1149 let copy_stream = gpu.ctx.new_stream()?;
1150 if std::env::var("MEMRA_EVT")
1166 .map(|v| v == "1")
1167 .unwrap_or(false)
1168 {
1169 } else {
1171 unsafe {
1172 gpu.ctx.disable_event_tracking();
1173 }
1174 }
1175 Ok(Self {
1176 gpu,
1177 module,
1178 hybrid,
1179 qmatvec,
1180 flash,
1181 flash_g: std::sync::OnceLock::new(),
1182 gemm,
1183 router,
1184 sample,
1185 moe_cache: Mutex::new(None),
1186 moe_cache_layout: Mutex::new(None),
1187 copy_stream,
1188 capture_keep_on: std::sync::atomic::AtomicBool::new(false),
1189 verify_exact: std::sync::atomic::AtomicBool::new(false),
1190 capture_keep: Mutex::new(Vec::new()),
1191 argmax_partials: Mutex::new(None),
1192 prime_deqw_ws: Mutex::new(None),
1193 router_stage: Mutex::new(None),
1194 fp8_scratch: Mutex::new(None),
1195 fa_vf16_scratch: Mutex::new(None),
1196 fa_part_pool: Mutex::new(None),
1197 fa_part_retired: Mutex::new(Vec::new()),
1198 fn_cache: Mutex::new(Default::default()),
1199 f16_scratch: Mutex::new(None),
1200 #[cfg(memra_cutlass)]
1201 cutlass_scratch: Mutex::new(None),
1202 })
1203 }
1204
1205 pub fn ctx(&self) -> &Arc<CudaContext> {
1206 &self.gpu.ctx
1207 }
1208
1209 pub fn pool_cached_bytes(&self) -> usize {
1227 let (reserved, used) = self.pool_reserved_used();
1228 reserved.saturating_sub(used)
1229 }
1230
1231 pub fn pool_reserved_used(&self) -> (usize, usize) {
1238 use cudarc::driver::sys;
1239 unsafe {
1240 let mut pool: sys::CUmemoryPool = std::ptr::null_mut();
1241 if sys::cuDeviceGetDefaultMemPool(&mut pool, self.gpu.ctx.ordinal() as sys::CUdevice)
1242 != sys::CUresult::CUDA_SUCCESS
1243 {
1244 return (0, 0);
1245 }
1246 let (mut reserved, mut used) = (0u64, 0u64);
1247 if sys::cuMemPoolGetAttribute(
1248 pool,
1249 sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_RESERVED_MEM_CURRENT,
1250 &mut reserved as *mut u64 as *mut core::ffi::c_void,
1251 ) != sys::CUresult::CUDA_SUCCESS
1252 {
1253 return (0, 0);
1254 }
1255 if sys::cuMemPoolGetAttribute(
1256 pool,
1257 sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_USED_MEM_CURRENT,
1258 &mut used as *mut u64 as *mut core::ffi::c_void,
1259 ) != sys::CUresult::CUDA_SUCCESS
1260 {
1261 return (0, 0);
1262 }
1263 (reserved as usize, used as usize)
1264 }
1265 }
1266
1267 pub fn stream(&self) -> Arc<CudaStream> {
1270 self.gpu.stream()
1271 }
1272 pub fn gkv_on() -> bool {
1275 memra_kv::gkv_on()
1276 }
1277
1278 pub fn wkv_on() -> bool {
1290 memra_kv::wkv_on()
1291 }
1292
1293 pub fn kv_fp8_on() -> bool {
1299 memra_kv::kv_fp8_on()
1300 }
1301
1302 fn fa_func(&self, name: &str, head_dim: usize) -> CudaFunction {
1305 if head_dim == 512 && Self::gkv_on() {
1306 self.func_g(name)
1307 } else {
1308 self.func(name)
1309 }
1310 }
1311
1312 fn func_g(&self, name: &str) -> CudaFunction {
1316 let m = self.flash_g.get_or_init(|| {
1317 self.gpu
1318 .ctx
1319 .load_module(cudarc::nvrtc::Ptx::from_binary(
1320 FLASH_FATBIN_KF8VF8.to_vec(),
1321 ))
1322 .expect("load kf8vf8 flash fatbin (fp8-globals arm)")
1323 });
1324 let key = format!("g:{name}");
1325 if let Some(f) = self.fn_cache.lock().unwrap().get(&key) {
1326 return f.clone();
1327 }
1328 let f = match m.load_function(name) {
1329 Ok(f) => f,
1330 Err(_) => self.func(name),
1331 };
1332 self.fn_cache.lock().unwrap().insert(key, f.clone());
1333 f
1334 }
1335
1336 fn func(&self, name: &str) -> CudaFunction {
1337 if let Some(f) = self.fn_cache.lock().unwrap().get(name) {
1340 return f.clone();
1341 }
1342 let f = self
1343 .module
1344 .load_function(name)
1345 .or_else(|_| self.hybrid.load_function(name))
1346 .or_else(|_| self.qmatvec.load_function(name))
1347 .or_else(|_| self.flash.load_function(name))
1348 .or_else(|_| self.gemm.load_function(name))
1349 .or_else(|_| self.router.load_function(name))
1350 .or_else(|_| self.sample.load_function(name))
1351 .unwrap_or_else(|_| panic!("kernel {name} not in any fatbin"));
1352 self.fn_cache
1353 .lock()
1354 .unwrap()
1355 .insert(name.to_string(), f.clone());
1356 f
1357 }
1358
1359 pub fn scatter_trim_logits(
1362 &self,
1363 src: &CudaSlice<f32>,
1364 d2t: &CudaSlice<u32>,
1365 dst: &mut CudaSlice<f32>,
1366 d_vocab: usize,
1367 n_vocab: usize,
1368 ) -> Result<(), Box<dyn std::error::Error>> {
1369 let f1 = self.func("scatter_trim_logits_f32");
1370 let f2 = self.func("scatter_trim_logits_pass2_f32");
1371 let (dv, nv) = (d_vocab as i32, n_vocab as i32);
1372 let cfg1 = LaunchConfig {
1373 grid_dim: (256, 1, 1),
1374 block_dim: (256, 1, 1),
1375 shared_mem_bytes: 0,
1376 };
1377 let __s_b1 = self.gpu.stream();
1378 let mut b1 = __s_b1.launch_builder(&f1);
1379 b1.arg(src).arg(d2t).arg(&mut *dst).arg(&dv).arg(&nv);
1380 unsafe {
1381 b1.launch(cfg1)?;
1382 }
1383 let cfg2 = LaunchConfig {
1384 grid_dim: (d_vocab.div_ceil(256) as u32, 1, 1),
1385 block_dim: (256, 1, 1),
1386 shared_mem_bytes: 0,
1387 };
1388 let __s_b2 = self.gpu.stream();
1389 let mut b2 = __s_b2.launch_builder(&f2);
1390 b2.arg(src).arg(d2t).arg(&mut *dst).arg(&dv);
1391 unsafe {
1392 b2.launch(cfg2)?;
1393 }
1394 Ok(())
1395 }
1396
1397 #[allow(clippy::too_many_arguments)]
1403 pub fn filter_stats(
1404 &self,
1405 x: &CudaSlice<f32>,
1406 row_stride: usize,
1407 rows: &CudaSlice<i32>,
1408 out_th: &mut CudaSlice<f32>,
1409 out_z: &mut CudaSlice<f32>,
1410 out_max: &mut CudaSlice<f32>,
1411 n: usize,
1412 nrow: usize,
1413 temp: f32,
1414 top_k: i32,
1415 top_p: f32,
1416 min_p: f32,
1417 ) -> Result<(), Box<dyn std::error::Error>> {
1418 let f = self.func("filter_stats_f32");
1419 let (ni, nr, rs) = (n as i32, nrow as i32, row_stride as i64);
1420 let cfg = LaunchConfig {
1421 grid_dim: (nrow as u32, 1, 1),
1422 block_dim: (1024, 1, 1),
1423 shared_mem_bytes: 0,
1424 };
1425 let __s_b = self.gpu.stream();
1426 let mut b = __s_b.launch_builder(&f);
1427 b.arg(x)
1428 .arg(&rs)
1429 .arg(rows)
1430 .arg(&mut *out_th)
1431 .arg(&mut *out_z)
1432 .arg(&mut *out_max)
1433 .arg(&ni)
1434 .arg(&nr)
1435 .arg(&temp)
1436 .arg(&top_k)
1437 .arg(&top_p)
1438 .arg(&min_p);
1439 unsafe {
1440 b.launch(cfg)?;
1441 }
1442 Ok(())
1443 }
1444
1445 #[allow(clippy::too_many_arguments)]
1447 pub fn softmax_gather_filtered(
1448 &self,
1449 x: &CudaSlice<f32>,
1450 row_stride: usize,
1451 ids: &CudaSlice<u32>,
1452 rows: &CudaSlice<i32>,
1453 th: &CudaSlice<f32>,
1454 z: &CudaSlice<f32>,
1455 out: &mut CudaSlice<f32>,
1456 n: usize,
1457 npair: usize,
1458 temp: f32,
1459 ) -> Result<(), Box<dyn std::error::Error>> {
1460 let f = self.func("softmax_gather_filtered_f32");
1461 let (ni, np, rs) = (n as i32, npair as i32, row_stride as i64);
1462 let cfg = LaunchConfig {
1463 grid_dim: (npair as u32, 1, 1),
1464 block_dim: (256, 1, 1),
1465 shared_mem_bytes: 0,
1466 };
1467 let __s_b = self.gpu.stream();
1468 let mut b = __s_b.launch_builder(&f);
1469 b.arg(x)
1470 .arg(&rs)
1471 .arg(ids)
1472 .arg(rows)
1473 .arg(th)
1474 .arg(z)
1475 .arg(&mut *out)
1476 .arg(&ni)
1477 .arg(&np)
1478 .arg(&temp);
1479 unsafe {
1480 b.launch(cfg)?;
1481 }
1482 Ok(())
1483 }
1484
1485 #[allow(clippy::too_many_arguments)]
1487 pub fn residual_sample_filtered(
1488 &self,
1489 p: &CudaSlice<f32>,
1490 q: Option<&CudaSlice<f32>>,
1491 n: usize,
1492 temp: f32,
1493 seed: u64,
1494 stream_pos: u32,
1495 p_stats: (f32, f32, f32),
1496 q_stats: (f32, f32, f32),
1497 out_tok: &mut CudaSlice<u32>,
1498 ) -> Result<(), Box<dyn std::error::Error>> {
1499 let f = self.func("residual_sample_filtered_f32");
1500 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
1501 let has_q: i32 = q.is_some() as i32;
1502 let qbuf = q.unwrap_or(p);
1503 let (pm, pth, pz) = p_stats;
1504 let (qm, qth, qz) = q_stats;
1505 let cfg = LaunchConfig {
1506 grid_dim: (1, 1, 1),
1507 block_dim: (1024, 1, 1),
1508 shared_mem_bytes: 0,
1509 };
1510 let __s_b = self.gpu.stream();
1511 let mut b = __s_b.launch_builder(&f);
1512 b.arg(p)
1513 .arg(qbuf)
1514 .arg(&has_q)
1515 .arg(&ni)
1516 .arg(&temp)
1517 .arg(&slo)
1518 .arg(&shi)
1519 .arg(&stream_pos)
1520 .arg(&pm)
1521 .arg(&pth)
1522 .arg(&pz)
1523 .arg(&qm)
1524 .arg(&qth)
1525 .arg(&qz)
1526 .arg(&mut *out_tok);
1527 unsafe {
1528 b.launch(cfg)?;
1529 }
1530 Ok(())
1531 }
1532
1533 #[allow(clippy::too_many_arguments)]
1535 pub fn gumbel_perturb_filtered(
1536 &self,
1537 x: &CudaSlice<f32>,
1538 y: &mut CudaSlice<f32>,
1539 n: usize,
1540 seed: u64,
1541 stream_pos: u32,
1542 temp: f32,
1543 row_max: f32,
1544 th: f32,
1545 ) -> Result<(), Box<dyn std::error::Error>> {
1546 let f = self.func("gumbel_perturb_filtered_f32");
1547 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
1548 let cfg = LaunchConfig {
1549 grid_dim: (n.div_ceil(256) as u32, 1, 1),
1550 block_dim: (256, 1, 1),
1551 shared_mem_bytes: 0,
1552 };
1553 let __s_b = self.gpu.stream();
1554 let mut b = __s_b.launch_builder(&f);
1555 b.arg(x)
1556 .arg(&mut *y)
1557 .arg(&ni)
1558 .arg(&slo)
1559 .arg(&shi)
1560 .arg(&stream_pos)
1561 .arg(&temp)
1562 .arg(&row_max)
1563 .arg(&th);
1564 unsafe {
1565 b.launch(cfg)?;
1566 }
1567 Ok(())
1568 }
1569
1570 #[allow(clippy::too_many_arguments)]
1574 pub fn penalize_logits(
1575 &self,
1576 x: &mut CudaSlice<f32>,
1577 hist: &CudaSlice<u32>,
1578 n_hist: usize,
1579 rep: f32,
1580 freq: f32,
1581 present: f32,
1582 n: usize,
1583 ) -> Result<(), Box<dyn std::error::Error>> {
1584 if n_hist == 0 {
1585 return Ok(());
1586 }
1587 let f = self.func("penalize_logits_f32");
1588 let (nh, ni) = (n_hist as i32, n as i32);
1589 let cfg = LaunchConfig {
1590 grid_dim: (n_hist.div_ceil(128) as u32, 1, 1),
1591 block_dim: (128, 1, 1),
1592 shared_mem_bytes: 0,
1593 };
1594 let __s_b = self.gpu.stream();
1595 let mut b = __s_b.launch_builder(&f);
1596 b.arg(&mut *x)
1597 .arg(hist)
1598 .arg(&nh)
1599 .arg(&rep)
1600 .arg(&freq)
1601 .arg(&present)
1602 .arg(&ni);
1603 unsafe {
1604 b.launch(cfg)?;
1605 }
1606 Ok(())
1607 }
1608
1609 #[allow(clippy::too_many_arguments)]
1611 pub fn penalize_logits_rows(
1612 &self,
1613 x: &mut CudaSlice<f32>,
1614 hist: &CudaSlice<u32>,
1615 n_hist: usize,
1616 rep: f32,
1617 freq: f32,
1618 present: f32,
1619 n: usize,
1620 nrow: usize,
1621 ) -> Result<(), Box<dyn std::error::Error>> {
1622 if n_hist == 0 || nrow == 0 {
1623 return Ok(());
1624 }
1625 let f = self.func("penalize_logits_rows_f32");
1626 let (nh, ni, nr) = (n_hist as i32, n as i32, nrow as i32);
1627 let cfg = LaunchConfig {
1628 grid_dim: (n_hist.div_ceil(128) as u32, nrow as u32, 1),
1629 block_dim: (128, 1, 1),
1630 shared_mem_bytes: 0,
1631 };
1632 let __s_b = self.gpu.stream();
1633 let mut b = __s_b.launch_builder(&f);
1634 b.arg(&mut *x)
1635 .arg(hist)
1636 .arg(&nh)
1637 .arg(&rep)
1638 .arg(&freq)
1639 .arg(&present)
1640 .arg(&ni)
1641 .arg(&nr);
1642 unsafe {
1643 b.launch(cfg)?;
1644 }
1645 Ok(())
1646 }
1647
1648 pub fn wpf_level() -> u32 {
1656 static ON: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
1657 *ON.get_or_init(|| {
1658 std::env::var("MEMRA_WPF")
1659 .ok()
1660 .and_then(|v| v.parse().ok())
1661 .unwrap_or(1)
1662 })
1663 }
1664
1665 pub fn set_verify_exact(&self, on: bool) {
1677 self.verify_exact
1678 .store(on, std::sync::atomic::Ordering::Relaxed);
1679 }
1680 pub(crate) fn verify_exact_on(&self) -> bool {
1681 self.verify_exact.load(std::sync::atomic::Ordering::Relaxed)
1682 }
1683
1684 pub fn qkv_append_on() -> bool {
1687 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1688 *ON.get_or_init(|| {
1689 std::env::var("MEMRA_QKV_APPEND")
1690 .map(|v| v != "0")
1691 .unwrap_or(true)
1692 })
1693 }
1694
1695 pub fn pdl_wb_on() -> bool {
1698 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1699 *ON.get_or_init(|| {
1700 std::env::var("MEMRA_PDL_WB")
1701 .map(|v| v != "0")
1702 .unwrap_or(true)
1703 })
1704 }
1705
1706 pub fn pdl_mmvq_on() -> bool {
1710 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1711 *ON.get_or_init(|| {
1712 std::env::var("MEMRA_PDL_MMVQ")
1713 .map(|v| v != "0")
1714 .unwrap_or(true)
1715 })
1716 }
1717
1718 pub fn pdl_on() -> bool {
1719 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1720 *ON.get_or_init(|| std::env::var("MEMRA_PDL").map(|v| v != "0").unwrap_or(true))
1721 }
1722
1723 pub fn pdl_nvfp4q8_on() -> bool {
1729 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1730 *ON.get_or_init(|| {
1731 std::env::var("MEMRA_PDL_NVFP4")
1732 .map(|v| v != "0")
1733 .unwrap_or(true)
1734 })
1735 }
1736
1737 fn q40_mr1_on() -> bool {
1743 static Q40MR: std::sync::OnceLock<Option<u32>> = std::sync::OnceLock::new();
1744 match *Q40MR.get_or_init(|| {
1745 std::env::var("MEMRA_Q40_MR")
1746 .ok()
1747 .and_then(|v| v.parse().ok())
1748 }) {
1749 Some(v) => v == 1,
1750 None => crate::FUSED_MR1_DEFAULT.load(std::sync::atomic::Ordering::Relaxed),
1751 }
1752 }
1753
1754 fn pdl_func_flash(
1759 &self,
1760 g: bool,
1761 name: &'static str,
1762 ) -> Result<cudarc::driver::sys::CUfunction, Box<dyn std::error::Error>> {
1763 use cudarc::driver::sys as cu;
1764 static MODS: std::sync::Mutex<Option<std::collections::HashMap<(usize, bool), usize>>> =
1771 std::sync::Mutex::new(None);
1772 static FNS: std::sync::Mutex<
1773 Option<std::collections::HashMap<(usize, bool, &'static str), usize>>,
1774 > = std::sync::Mutex::new(None);
1775 let ctx_key = self.ctx().cu_ctx() as usize;
1776 if let Some(&f) = FNS
1777 .lock()
1778 .unwrap()
1779 .get_or_insert_with(Default::default)
1780 .get(&(ctx_key, g, name))
1781 {
1782 return Ok(f as cu::CUfunction);
1783 }
1784 let module = {
1785 let mut mods = MODS.lock().unwrap();
1786 let map = mods.get_or_insert_with(Default::default);
1787 match map.get(&(ctx_key, g)) {
1788 Some(&m) => m,
1789 None => {
1790 let m = self.pdl_load_module_in_ctx(if g {
1791 FLASH_FATBIN_KF8VF8
1792 } else {
1793 FLASH_FATBIN
1794 })?;
1795 map.insert((ctx_key, g), m);
1796 m
1797 }
1798 }
1799 };
1800 let cname = std::ffi::CString::new(name)?;
1801 let mut f: cu::CUfunction = std::ptr::null_mut();
1802 let r = unsafe { cu::cuModuleGetFunction(&mut f, module as cu::CUmodule, cname.as_ptr()) };
1803 if r != cu::CUresult::CUDA_SUCCESS {
1804 return Err(format!("pdl_func_flash {name} (g={g}): {r:?}").into());
1805 }
1806 FNS.lock()
1807 .unwrap()
1808 .get_or_insert_with(Default::default)
1809 .insert((ctx_key, g, name), f as usize);
1810 Ok(f)
1811 }
1812
1813 fn pdl_load_module_in_ctx(&self, bytes: &[u8]) -> Result<usize, Box<dyn std::error::Error>> {
1818 use cudarc::driver::sys as cu;
1819 let mut prev: cu::CUcontext = std::ptr::null_mut();
1820 unsafe {
1821 cu::cuCtxGetCurrent(&mut prev).result()?;
1822 }
1823 self.ctx().bind_to_thread()?;
1824 let mut m: cu::CUmodule = std::ptr::null_mut();
1825 let r = unsafe { cu::cuModuleLoadData(&mut m, bytes.as_ptr() as *const std::ffi::c_void) };
1826 let restore = if prev.is_null() {
1827 cu::CUresult::CUDA_SUCCESS
1828 } else {
1829 unsafe { cu::cuCtxSetCurrent(prev) }
1830 };
1831 if r != cu::CUresult::CUDA_SUCCESS {
1832 return Err(format!("pdl module load: {r:?}").into());
1833 }
1834 if restore != cu::CUresult::CUDA_SUCCESS {
1835 return Err(format!("pdl module load: ctx restore {restore:?}").into());
1836 }
1837 Ok(m as usize)
1838 }
1839
1840 fn pdl_func(
1841 &self,
1842 name: &'static str,
1843 ) -> Result<cudarc::driver::sys::CUfunction, Box<dyn std::error::Error>> {
1844 use cudarc::driver::sys as cu;
1845 static MODULES: std::sync::Mutex<Option<std::collections::HashMap<usize, usize>>> =
1848 std::sync::Mutex::new(None);
1849 static QMODULES: std::sync::Mutex<Option<std::collections::HashMap<usize, usize>>> =
1852 std::sync::Mutex::new(None);
1853 static FNS: std::sync::Mutex<
1854 Option<std::collections::HashMap<(usize, &'static str), usize>>,
1855 > = std::sync::Mutex::new(None);
1856 let ctx_key = self.ctx().cu_ctx() as usize;
1857 if let Some(&f) = FNS
1858 .lock()
1859 .unwrap()
1860 .get_or_insert_with(Default::default)
1861 .get(&(ctx_key, name))
1862 {
1863 return Ok(f as cu::CUfunction);
1864 }
1865 let module = {
1866 let mut mods = MODULES.lock().unwrap();
1867 let map = mods.get_or_insert_with(Default::default);
1868 match map.get(&ctx_key) {
1869 Some(&m) => m,
1870 None => {
1871 let m = self.pdl_load_module_in_ctx(FATBIN)?;
1872 map.insert(ctx_key, m);
1873 m
1874 }
1875 }
1876 };
1877 let cname = std::ffi::CString::new(name)?;
1878 let mut f: cu::CUfunction = std::ptr::null_mut();
1879 let mut r =
1880 unsafe { cu::cuModuleGetFunction(&mut f, module as cu::CUmodule, cname.as_ptr()) };
1881 if r == cu::CUresult::CUDA_ERROR_NOT_FOUND {
1882 let qmodule = {
1883 let mut mods = QMODULES.lock().unwrap();
1884 let map = mods.get_or_insert_with(Default::default);
1885 match map.get(&ctx_key) {
1886 Some(&m) => m,
1887 None => {
1888 let m = self.pdl_load_module_in_ctx(QMATVEC_FATBIN)?;
1889 map.insert(ctx_key, m);
1890 m
1891 }
1892 }
1893 };
1894 r = unsafe { cu::cuModuleGetFunction(&mut f, qmodule as cu::CUmodule, cname.as_ptr()) };
1895 }
1896 if r != cu::CUresult::CUDA_SUCCESS {
1897 return Err(format!("pdl_func {name}: {r:?}").into());
1898 }
1899 FNS.lock()
1900 .unwrap()
1901 .get_or_insert_with(Default::default)
1902 .insert((ctx_key, name), f as usize);
1903 Ok(f)
1904 }
1905
1906 unsafe fn launch_pdl_flash(
1918 &self,
1919 g: bool,
1920 name: &'static str,
1921 grid: (u32, u32, u32),
1922 block: (u32, u32, u32),
1923 smem: u32,
1924 params: &mut [*mut std::ffi::c_void],
1925 ) -> Result<(), Box<dyn std::error::Error>> {
1926 use cudarc::driver::sys as cu;
1927 let f = self.pdl_func_flash(g, name)?;
1928 if smem > 0 {
1929 let r =
1931 unsafe {
1932 cu::cuFuncSetAttribute(f,
1933 cu::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
1934 smem as i32)
1935 };
1936 if r != cu::CUresult::CUDA_SUCCESS {
1937 return Err(format!("pdl smem attr {name}: {r:?}").into());
1938 }
1939 }
1940 let mut attr = cu::CUlaunchAttribute {
1941 id: cu::CUlaunchAttributeID::CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION,
1942 pad: [0; 4],
1943 value: cu::CUlaunchAttributeValue {
1944 programmaticStreamSerializationAllowed: 1,
1945 },
1946 };
1947 let cfg = cu::CUlaunchConfig {
1948 gridDimX: grid.0,
1949 gridDimY: grid.1,
1950 gridDimZ: grid.2,
1951 blockDimX: block.0,
1952 blockDimY: block.1,
1953 blockDimZ: block.2,
1954 sharedMemBytes: smem,
1955 hStream: self.gpu.stream().cu_stream(),
1956 attrs: &mut attr,
1957 numAttrs: 1,
1958 };
1959 let r = unsafe { cu::cuLaunchKernelEx(&cfg, f, params.as_mut_ptr(), std::ptr::null_mut()) };
1960 if r != cu::CUresult::CUDA_SUCCESS {
1961 return Err(format!("launch_pdl_flash {name}: {r:?}").into());
1962 }
1963 Ok(())
1964 }
1965
1966 unsafe fn launch_pdl(
1967 &self,
1968 name: &'static str,
1969 grid: (u32, u32, u32),
1970 block: (u32, u32, u32),
1971 params: &mut [*mut std::ffi::c_void],
1972 ) -> Result<(), Box<dyn std::error::Error>> {
1973 use cudarc::driver::sys as cu;
1974 let f = self.pdl_func(name)?;
1975 let mut attr = cu::CUlaunchAttribute {
1976 id: cu::CUlaunchAttributeID::CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION,
1977 pad: [0; 4],
1978 value: cu::CUlaunchAttributeValue {
1979 programmaticStreamSerializationAllowed: 1,
1980 },
1981 };
1982 let cfg = cu::CUlaunchConfig {
1983 gridDimX: grid.0,
1984 gridDimY: grid.1,
1985 gridDimZ: grid.2,
1986 blockDimX: block.0,
1987 blockDimY: block.1,
1988 blockDimZ: block.2,
1989 sharedMemBytes: 0,
1990 hStream: self.gpu.stream().cu_stream(),
1991 attrs: &mut attr,
1992 numAttrs: 1,
1993 };
1994 let r = unsafe { cu::cuLaunchKernelEx(&cfg, f, params.as_mut_ptr(), std::ptr::null_mut()) };
1995 if r != cu::CUresult::CUDA_SUCCESS {
1996 return Err(format!("launch_pdl {name}: {r:?}").into());
1997 }
1998 Ok(())
1999 }
2000
2001 pub fn prefetch_weight_l2(
2004 &self,
2005 w: &crate::model::GpuTensor,
2006 ) -> Result<(), Box<dyn std::error::Error>> {
2007 if let crate::model::GpuTensor::Quant { bytes, rp4, .. } = w {
2008 let p = rp4.as_ref().unwrap_or(bytes);
2009 self.prefetch_l2(p, p.len())?;
2010 }
2011 Ok(())
2012 }
2013
2014 pub fn gather_row_bf16(
2017 &self,
2018 table: &CudaSlice<u8>,
2019 tok: &CudaSlice<u32>,
2020 idx: usize,
2021 dst: &mut CudaSlice<f32>,
2022 ncols: usize,
2023 ) -> Result<(), Box<dyn std::error::Error>> {
2024 let f = self.func("gather_row_bf16_f32");
2025 let cfg = LaunchConfig {
2026 grid_dim: (ncols.div_ceil(256) as u32, 1, 1),
2027 block_dim: (256, 1, 1),
2028 shared_mem_bytes: 0,
2029 };
2030 let (nc, ix) = (ncols as i32, idx as i32);
2031 let __s_b = self.gpu.stream();
2032 let mut b = __s_b.launch_builder(&f);
2033 b.arg(table).arg(tok).arg(&ix).arg(dst).arg(&nc);
2034 unsafe {
2035 b.launch(cfg)?;
2036 }
2037 Ok(())
2038 }
2039
2040 pub fn add_row_inplace(
2042 &self,
2043 logits: &mut CudaSlice<f32>,
2044 bias: &CudaSlice<f32>,
2045 n: usize,
2046 row_off: usize,
2047 ) -> Result<(), Box<dyn std::error::Error>> {
2048 let f = self.func("add_row_inplace_f32");
2049 let cfg = LaunchConfig {
2050 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2051 block_dim: (256, 1, 1),
2052 shared_mem_bytes: 0,
2053 };
2054 let (ni, off) = (n as i32, row_off as i64);
2055 let __s_b = self.gpu.stream();
2056 let mut b = __s_b.launch_builder(&f);
2057 b.arg(logits).arg(bias).arg(&ni).arg(&off);
2058 unsafe {
2059 b.launch(cfg)?;
2060 }
2061 Ok(())
2062 }
2063
2064 pub fn prefetch_l2(
2066 &self,
2067 p: &CudaSlice<u8>,
2068 n: usize,
2069 ) -> Result<(), Box<dyn std::error::Error>> {
2070 let f = self.func("prefetch_l2_bytes");
2071 let lines = n.div_ceil(128);
2072 let ni = n as i64;
2073 let cfg = LaunchConfig {
2074 grid_dim: (lines.div_ceil(256) as u32, 1, 1),
2075 block_dim: (256, 1, 1),
2076 shared_mem_bytes: 0,
2077 };
2078 let __s_b = self.gpu.stream();
2079 let mut b = __s_b.launch_builder(&f);
2080 b.arg(p).arg(&ni);
2081 unsafe {
2082 b.launch(cfg)?;
2083 }
2084 Ok(())
2085 }
2086
2087 pub fn router_gemv(
2090 &self,
2091 w: &CudaSlice<f32>,
2092 x: &CudaSlice<f32>,
2093 n_embd: usize,
2094 n_experts: usize,
2095 t: usize,
2096 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2097 let w8 = match std::env::var("MEMRA_ROUTER_V2").as_deref() {
2103 Ok("0") => false,
2104 Ok(_) => true,
2105 Err(_) => ROUTER_W8_DEFAULT.load(std::sync::atomic::Ordering::Relaxed),
2106 };
2107 let batch = w8 && t >= ROUTER_BATCH_MIN_T && router_batch_on();
2117 self.router_gemv_form(w, x, n_embd, n_experts, t, w8, batch)
2118 }
2119
2120 pub fn router_gemv_form(
2123 &self,
2124 w: &CudaSlice<f32>,
2125 x: &CudaSlice<f32>,
2126 n_embd: usize,
2127 n_experts: usize,
2128 t: usize,
2129 w8: bool,
2130 batch: bool,
2131 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2132 debug_assert!(!batch || w8, "batch twin exists for the w8 form only");
2133 let mut y = self.alloc_uninit::<f32>(t * n_experts)?;
2134 let f = if batch {
2135 self.func("router_gemv_f32_w8_batch")
2136 } else if w8 {
2137 self.func("router_gemv_f32_w8")
2138 } else {
2139 self.func("router_gemv_f32")
2140 };
2141 let (ne, nx, ti) = (n_embd as i32, n_experts as i32, t as i32);
2142 let cfg = if batch {
2143 LaunchConfig {
2144 grid_dim: (n_experts.div_ceil(8) as u32, t.div_ceil(8) as u32, 1),
2145 block_dim: (32, 8, 1),
2146 shared_mem_bytes: 0,
2147 }
2148 } else {
2149 LaunchConfig {
2150 grid_dim: (n_experts as u32, t as u32, 1),
2151 block_dim: (32, if w8 { 8 } else { 1 }, 1),
2152 shared_mem_bytes: 0,
2153 }
2154 };
2155 let __s_b = self.gpu.stream();
2156 let mut b = __s_b.launch_builder(&f);
2157 b.arg(w).arg(x).arg(&mut y).arg(&ne).arg(&nx).arg(&ti);
2158 unsafe {
2159 b.launch(cfg)?;
2160 }
2161 Ok(y)
2162 }
2163
2164 pub fn rows_permute(
2166 &self,
2167 src: &CudaSlice<f32>,
2168 idx: &CudaSlice<i32>,
2169 nrows: usize,
2170 ncols: usize,
2171 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2172 let mut dst = self.alloc_uninit::<f32>(nrows * ncols)?;
2173 let f = self.func("rows_permute_f32");
2174 let (nc, nr) = (ncols as i32, nrows as i32);
2175 let cfg = LaunchConfig {
2176 grid_dim: (nrows as u32, 1, 1),
2177 block_dim: (256, 1, 1),
2178 shared_mem_bytes: 0,
2179 };
2180 let __s_b = self.gpu.stream();
2181 let mut b = __s_b.launch_builder(&f);
2182 b.arg(src).arg(idx).arg(&mut dst).arg(&nc).arg(&nr);
2183 unsafe {
2184 b.launch(cfg)?;
2185 }
2186 Ok(dst)
2187 }
2188
2189 pub fn sigmoid_dot_rows(
2194 &self,
2195 x: &CudaSlice<f32>,
2196 w: &CudaSlice<f32>,
2197 n_embd: usize,
2198 t: usize,
2199 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2200 static OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2203 if *OFF.get_or_init(|| std::env::var("MEMRA_SHEXP_DOT").as_deref() == Ok("0")) {
2204 let gs = self.linear(x, w, t, n_embd, 1)?;
2205 let mut g = self.uninit(t)?;
2206 self.sigmoid(&gs, &mut g, t)?;
2207 return Ok(g);
2208 }
2209 let mut g = self.alloc_uninit::<f32>(t)?;
2215 let f = self.func("sigmoid_dot_rows_f32");
2216 let (ne, ti) = (n_embd as i32, t as i32);
2217 let cfg = LaunchConfig {
2218 grid_dim: (t as u32, 1, 1),
2219 block_dim: (32, 8, 1),
2220 shared_mem_bytes: 0,
2221 };
2222 let __s_b = self.gpu.stream();
2223 let mut b = __s_b.launch_builder(&f);
2224 b.arg(x).arg(w).arg(&mut g).arg(&ne).arg(&ti);
2225 unsafe {
2226 b.launch(cfg)?;
2227 }
2228 Ok(g)
2229 }
2230
2231 pub fn spec_rollback_stream(
2233 &self,
2234 len_ptrs: &CudaSlice<u64>,
2235 pos_start: &CudaSlice<i32>,
2236 acc: &CudaSlice<u32>,
2237 base: usize,
2238 n_rows: usize,
2239 ) -> Result<(), Box<dyn std::error::Error>> {
2240 let f = self.func("spec_rollback_stream");
2241 let (b, nr) = (base as i32, n_rows as i32);
2242 let cfg = LaunchConfig {
2243 grid_dim: (n_rows.div_ceil(64) as u32, 1, 1),
2244 block_dim: (64, 1, 1),
2245 shared_mem_bytes: 0,
2246 };
2247 let __s_bl = self.gpu.stream();
2248 let mut bl = __s_bl.launch_builder(&f);
2249 bl.arg(len_ptrs).arg(pos_start).arg(acc).arg(&b).arg(&nr);
2250 unsafe {
2251 bl.launch(cfg)?;
2252 }
2253 Ok(())
2254 }
2255
2256 pub fn plain_tok_ring(
2258 &self,
2259 vam: &CudaSlice<u32>,
2260 pos_start: &CudaSlice<i32>,
2261 base: usize,
2262 ring: &mut CudaSlice<u32>,
2263 ) -> Result<(), Box<dyn std::error::Error>> {
2264 let f = self.func("plain_tok_ring");
2265 let (b, cap) = (base as i32, ring.len() as i32);
2266 let cfg = LaunchConfig {
2267 grid_dim: (1, 1, 1),
2268 block_dim: (32, 1, 1),
2269 shared_mem_bytes: 0,
2270 };
2271 let __s_bl = self.gpu.stream();
2272 let mut bl = __s_bl.launch_builder(&f);
2273 bl.arg(vam).arg(pos_start).arg(&b).arg(&mut *ring).arg(&cap);
2274 unsafe {
2275 bl.launch(cfg)?;
2276 }
2277 Ok(())
2278 }
2279
2280 pub fn spec_ring_commit(
2282 &self,
2283 vtok: &CudaSlice<u32>,
2284 acc: &CudaSlice<u32>,
2285 brk: &CudaSlice<u32>,
2286 ring: &mut CudaSlice<u32>,
2287 pend: &mut CudaSlice<u32>,
2288 ) -> Result<(), Box<dyn std::error::Error>> {
2289 let f = self.func("spec_ring_commit");
2290 let cfg = LaunchConfig {
2291 grid_dim: (1, 1, 1),
2292 block_dim: (32, 1, 1),
2293 shared_mem_bytes: 0,
2294 };
2295 let __s_b = self.gpu.stream();
2296 let mut b = __s_b.launch_builder(&f);
2297 b.arg(vtok).arg(acc).arg(brk).arg(ring).arg(pend);
2298 unsafe {
2299 b.launch(cfg)?;
2300 }
2301 Ok(())
2302 }
2303 pub fn i32_copy_add(
2304 &self,
2305 src: &CudaSlice<i32>,
2306 dst: &mut CudaSlice<i32>,
2307 delta: i32,
2308 ) -> Result<(), Box<dyn std::error::Error>> {
2309 let f = self.func("i32_copy_add");
2310 let cfg = LaunchConfig {
2311 grid_dim: (1, 1, 1),
2312 block_dim: (32, 1, 1),
2313 shared_mem_bytes: 0,
2314 };
2315 let __s_b = self.gpu.stream();
2316 let mut b = __s_b.launch_builder(&f);
2317 b.arg(src).arg(dst).arg(&delta);
2318 unsafe {
2319 b.launch(cfg)?;
2320 }
2321 Ok(())
2322 }
2323 pub fn u32_copy(
2324 &self,
2325 src: &CudaSlice<u32>,
2326 dst: &mut CudaSlice<u32>,
2327 ) -> Result<(), Box<dyn std::error::Error>> {
2328 let f = self.func("u32_copy");
2329 let cfg = LaunchConfig {
2330 grid_dim: (1, 1, 1),
2331 block_dim: (32, 1, 1),
2332 shared_mem_bytes: 0,
2333 };
2334 let __s_b = self.gpu.stream();
2335 let mut b = __s_b.launch_builder(&f);
2336 b.arg(src).arg(dst);
2337 unsafe {
2338 b.launch(cfg)?;
2339 }
2340 Ok(())
2341 }
2342
2343 pub fn spec_adapt_k(
2347 &self,
2348 acc: &CudaSlice<u32>,
2349 brk: &mut CudaSlice<u32>,
2350 floor: usize,
2351 cap: usize,
2352 ) -> Result<(), Box<dyn std::error::Error>> {
2353 let f = self.func("spec_adapt_k");
2354 let (fl, cp) = (floor as i32, cap as i32);
2355 let cfg = LaunchConfig {
2356 grid_dim: (1, 1, 1),
2357 block_dim: (32, 1, 1),
2358 shared_mem_bytes: 0,
2359 };
2360 let __s_b = self.gpu.stream();
2361 let mut b = __s_b.launch_builder(&f);
2362 b.arg(acc).arg(brk).arg(&fl).arg(&cp);
2363 unsafe {
2364 b.launch(cfg)?;
2365 }
2366 Ok(())
2367 }
2368
2369 pub fn spec_accept_greedy_dc(
2371 &self,
2372 preds: &CudaSlice<u32>,
2373 vtok: &CudaSlice<u32>,
2374 last_pred: &CudaSlice<u32>,
2375 brk: &CudaSlice<u32>,
2376 out: &mut CudaSlice<u32>,
2377 ) -> Result<(), Box<dyn std::error::Error>> {
2378 let f = self.func("spec_accept_greedy_dc");
2379 let cfg = LaunchConfig {
2380 grid_dim: (1, 1, 1),
2381 block_dim: (32, 1, 1),
2382 shared_mem_bytes: 0,
2383 };
2384 let __s_b = self.gpu.stream();
2385 let mut b = __s_b.launch_builder(&f);
2386 b.arg(preds).arg(vtok).arg(last_pred).arg(brk).arg(out);
2387 unsafe {
2388 b.launch(cfg)?;
2389 }
2390 Ok(())
2391 }
2392
2393 pub fn pos_iota(
2395 &self,
2396 pos0: &CudaSlice<i32>,
2397 out: &mut CudaSlice<i32>,
2398 t: usize,
2399 ) -> Result<(), Box<dyn std::error::Error>> {
2400 let f = self.func("pos_iota_i32");
2401 let ti = t as i32;
2402 let cfg = LaunchConfig {
2403 grid_dim: (1, 1, 1),
2404 block_dim: (t.max(1) as u32, 1, 1),
2405 shared_mem_bytes: 0,
2406 };
2407 let __s_b = self.gpu.stream();
2408 let mut b = __s_b.launch_builder(&f);
2409 b.arg(pos0).arg(out).arg(&ti);
2410 unsafe {
2411 b.launch(cfg)?;
2412 }
2413 Ok(())
2414 }
2415 #[allow(clippy::too_many_arguments)]
2416 pub fn append_kv_quantized_rows_dc(
2417 &self,
2418 k_rows: &CudaSlice<f32>,
2419 v_rows: &CudaSlice<f32>,
2420 kc: &mut CudaSlice<u8>,
2421 vc: &mut CudaSlice<u8>,
2422 t0_dev: &CudaSlice<i32>,
2423 t: usize,
2424 kv_dim_k: usize,
2425 kv_dim_v: usize,
2426 k_tok_bytes: usize,
2427 v_tok_bytes: usize,
2428 g: bool,
2429 ) -> Result<(), Box<dyn std::error::Error>> {
2430 let f = if g {
2431 self.func_g("append_quantize_kv_q8_0_q5_1_rows_dc")
2432 } else {
2433 self.func("append_quantize_kv_q8_0_q5_1_rows_dc")
2434 };
2435 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
2436 let cfg = LaunchConfig {
2437 grid_dim: (nblk, t as u32, 1),
2438 block_dim: (32, 1, 1),
2439 shared_mem_bytes: 0,
2440 };
2441 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
2442 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
2443 let __s_b = self.gpu.stream();
2444 let mut b = __s_b.launch_builder(&f);
2445 b.arg(k_rows)
2446 .arg(v_rows)
2447 .arg(kc)
2448 .arg(vc)
2449 .arg(t0_dev)
2450 .arg(&kdk)
2451 .arg(&kdv)
2452 .arg(&ktb)
2453 .arg(&vtb);
2454 unsafe {
2455 b.launch(cfg)?;
2456 }
2457 Ok(())
2458 }
2459
2460 #[allow(clippy::too_many_arguments)]
2463 pub fn append_kv_quantized_row_dc_inc(
2464 &self,
2465 k_row: &CudaSlice<f32>,
2466 v_row: &CudaSlice<f32>,
2467 kc: &mut CudaSlice<u8>,
2468 vc: &mut CudaSlice<u8>,
2469 t0_dev: &mut CudaSlice<i32>,
2470 kv_dim_k: usize,
2471 kv_dim_v: usize,
2472 k_tok_bytes: usize,
2473 v_tok_bytes: usize,
2474 g: bool,
2475 ) -> Result<(), Box<dyn std::error::Error>> {
2476 let f = if g {
2477 self.func_g("append_quantize_kv_q8_0_q5_1_dc_inc")
2478 } else {
2479 self.func("append_quantize_kv_q8_0_q5_1_dc_inc")
2480 };
2481 let nthreads = ((kv_dim_k.max(kv_dim_v) / 32) * 32).min(1024) as u32;
2482 let cfg = LaunchConfig {
2483 grid_dim: (1, 1, 1),
2484 block_dim: (nthreads, 1, 1),
2485 shared_mem_bytes: 0,
2486 };
2487 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
2488 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
2489 let __s_b = self.gpu.stream();
2490 let mut b = __s_b.launch_builder(&f);
2491 b.arg(k_row)
2492 .arg(v_row)
2493 .arg(kc)
2494 .arg(vc)
2495 .arg(t0_dev)
2496 .arg(&kdk)
2497 .arg(&kdv)
2498 .arg(&ktb)
2499 .arg(&vtb);
2500 unsafe {
2501 b.launch(cfg)?;
2502 }
2503 Ok(())
2504 }
2505
2506 pub fn pack_tok_p(
2508 &self,
2509 tok: &CudaSlice<u32>,
2510 p: &CudaSlice<f32>,
2511 out: &mut CudaSlice<u32>,
2512 slot: usize,
2513 ) -> Result<(), Box<dyn std::error::Error>> {
2514 let f = self.func("pack_tok_p");
2515 let sl = slot as i32;
2516 let cfg = LaunchConfig {
2517 grid_dim: (1, 1, 1),
2518 block_dim: (32, 1, 1),
2519 shared_mem_bytes: 0,
2520 };
2521 let __s_b = self.gpu.stream();
2522 let mut b = __s_b.launch_builder(&f);
2523 b.arg(tok).arg(p).arg(out).arg(&sl);
2524 unsafe {
2525 b.launch(cfg)?;
2526 }
2527 Ok(())
2528 }
2529 pub fn tok_map_u32(
2530 &self,
2531 tok: &mut CudaSlice<u32>,
2532 map: &CudaSlice<u32>,
2533 ) -> Result<(), Box<dyn std::error::Error>> {
2534 let f = self.func("tok_map_u32");
2535 let cfg = LaunchConfig {
2536 grid_dim: (1, 1, 1),
2537 block_dim: (32, 1, 1),
2538 shared_mem_bytes: 0,
2539 };
2540 let __s_b = self.gpu.stream();
2541 let mut b = __s_b.launch_builder(&f);
2542 b.arg(tok).arg(map);
2543 unsafe {
2544 b.launch(cfg)?;
2545 }
2546 Ok(())
2547 }
2548
2549 #[allow(clippy::too_many_arguments)]
2551 pub fn spec_assemble_verify(
2552 &self,
2553 tokp: &CudaSlice<u32>,
2554 pend: &CudaSlice<u32>,
2555 d2t: Option<&CudaSlice<u32>>,
2556 vtok: &mut CudaSlice<u32>,
2557 brk: &mut CudaSlice<u32>,
2558 p_min: f32,
2559 k: usize,
2560 pmin0: bool,
2561 ) -> Result<(), Box<dyn std::error::Error>> {
2562 let f = self.func("spec_assemble_verify");
2563 let (ki, pm) = (k as i32, if pmin0 { 1i32 } else { 0i32 });
2564 let cfg = LaunchConfig {
2565 grid_dim: (1, 1, 1),
2566 block_dim: (32, 1, 1),
2567 shared_mem_bytes: 0,
2568 };
2569 let __s_b = self.gpu.stream();
2570 let mut b = __s_b.launch_builder(&f);
2571 match d2t {
2572 Some(m) => {
2573 b.arg(tokp)
2574 .arg(pend)
2575 .arg(m)
2576 .arg(vtok)
2577 .arg(brk)
2578 .arg(&p_min)
2579 .arg(&ki)
2580 .arg(&pm);
2581 unsafe {
2582 b.launch(cfg)?;
2583 }
2584 }
2585 None => {
2586 let null: u64 = 0;
2587 b.arg(tokp)
2588 .arg(pend)
2589 .arg(&null)
2590 .arg(vtok)
2591 .arg(brk)
2592 .arg(&p_min)
2593 .arg(&ki)
2594 .arg(&pm);
2595 unsafe {
2596 b.launch(cfg)?;
2597 }
2598 }
2599 }
2600 Ok(())
2601 }
2602
2603 #[allow(clippy::too_many_arguments)]
2605 pub fn ssm_conv_ring_rebuild_dc(
2606 &self,
2607 qkv_tm: &CudaSlice<f32>,
2608 ring_old: &CudaSlice<f32>,
2609 conv_state: &mut CudaSlice<f32>,
2610 conv_dim: usize,
2611 acc: &CudaSlice<u32>,
2612 base: usize,
2613 t_v: usize,
2614 d_conv: usize,
2615 ) -> Result<(), Box<dyn std::error::Error>> {
2616 let f = self.func("ssm_conv_ring_rebuild_f32_dc");
2617 let n = conv_dim * (d_conv - 1);
2618 let cfg = LaunchConfig::for_num_elems(n as u32);
2619 let (cd, b0, tv, dc) = (conv_dim as i32, base as i32, t_v as i32, d_conv as i32);
2620 let __s_b = self.gpu.stream();
2621 let mut b = __s_b.launch_builder(&f);
2622 b.arg(qkv_tm)
2623 .arg(ring_old)
2624 .arg(conv_state)
2625 .arg(&cd)
2626 .arg(acc)
2627 .arg(&b0)
2628 .arg(&tv)
2629 .arg(&dc);
2630 unsafe {
2631 b.launch(cfg)?;
2632 }
2633 Ok(())
2634 }
2635 #[allow(clippy::too_many_arguments)]
2636 pub fn gdn_scan_s128_dc(
2637 &self,
2638 q: &CudaSlice<f32>,
2639 k: &CudaSlice<f32>,
2640 v: &CudaSlice<f32>,
2641 g: &CudaSlice<f32>,
2642 beta: &CudaSlice<f32>,
2643 state_in: &CudaSlice<f32>,
2644 state_out: &mut CudaSlice<f32>,
2645 o: &mut CudaSlice<f32>,
2646 n_head: usize,
2647 acc: &CudaSlice<u32>,
2648 base: usize,
2649 t_v: usize,
2650 scale: f32,
2651 ) -> Result<(), Box<dyn std::error::Error>> {
2652 let f = self.func("gdn_scan_s128_dc");
2653 const S_V: u32 = 128;
2654 const WARP: u32 = 32;
2655 const COLS_PER_BLOCK: u32 = 4;
2656 let cfg = LaunchConfig {
2657 grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
2658 block_dim: (WARP, COLS_PER_BLOCK, 1),
2659 shared_mem_bytes: 0,
2660 };
2661 let (h, b0, tv) = (n_head as i32, base as i32, t_v as i32);
2662 let __s_b = self.gpu.stream();
2663 let mut b = __s_b.launch_builder(&f);
2664 b.arg(q)
2665 .arg(k)
2666 .arg(v)
2667 .arg(g)
2668 .arg(beta)
2669 .arg(state_in)
2670 .arg(state_out)
2671 .arg(o)
2672 .arg(&h)
2673 .arg(acc)
2674 .arg(&b0)
2675 .arg(&tv)
2676 .arg(&scale);
2677 unsafe {
2678 b.launch(cfg)?;
2679 }
2680 Ok(())
2681 }
2682
2683 pub fn spec_rollback_kv(
2685 &self,
2686 len_ptrs: &CudaSlice<u64>,
2687 saved: &CudaSlice<i32>,
2688 acc: &CudaSlice<u32>,
2689 base: usize,
2690 n_layer: usize,
2691 ) -> Result<(), Box<dyn std::error::Error>> {
2692 let f = self.func("spec_rollback_kv");
2693 let (b, nl) = (base as i32, n_layer as i32);
2694 let cfg = LaunchConfig {
2695 grid_dim: (n_layer.div_ceil(64) as u32, 1, 1),
2696 block_dim: (64, 1, 1),
2697 shared_mem_bytes: 0,
2698 };
2699 let __s_bl = self.gpu.stream();
2700 let mut bl = __s_bl.launch_builder(&f);
2701 bl.arg(len_ptrs).arg(saved).arg(acc).arg(&b).arg(&nl);
2702 unsafe {
2703 bl.launch(cfg)?;
2704 }
2705 Ok(())
2706 }
2707
2708 pub fn spec_fork_valid(
2710 &self,
2711 acc: &CudaSlice<u32>,
2712 optimistic_pending: u32,
2713 valid: &mut CudaSlice<u32>,
2714 ) -> Result<(), Box<dyn std::error::Error>> {
2715 let f = self.func("spec_fork_valid");
2716 let cfg = LaunchConfig {
2717 grid_dim: (1, 1, 1),
2718 block_dim: (1, 1, 1),
2719 shared_mem_bytes: 0,
2720 };
2721 let __s_bl = self.gpu.stream();
2722 let mut bl = __s_bl.launch_builder(&f);
2723 bl.arg(acc).arg(&optimistic_pending).arg(valid);
2724 unsafe {
2725 bl.launch(cfg)?;
2726 }
2727 Ok(())
2728 }
2729
2730 pub fn spec_fork_reconcile_kv(
2732 &self,
2733 len_ptrs: &CudaSlice<u64>,
2734 saved: &CudaSlice<i32>,
2735 acc: &CudaSlice<u32>,
2736 valid: &CudaSlice<u32>,
2737 base: usize,
2738 n_layer: usize,
2739 ) -> Result<(), Box<dyn std::error::Error>> {
2740 let f = self.func("spec_fork_reconcile_kv");
2741 let (b, nl) = (base as i32, n_layer as i32);
2742 let cfg = LaunchConfig {
2743 grid_dim: (n_layer.div_ceil(64) as u32, 1, 1),
2744 block_dim: (64, 1, 1),
2745 shared_mem_bytes: 0,
2746 };
2747 let __s_bl = self.gpu.stream();
2748 let mut bl = __s_bl.launch_builder(&f);
2749 bl.arg(len_ptrs)
2750 .arg(saved)
2751 .arg(acc)
2752 .arg(valid)
2753 .arg(&b)
2754 .arg(&nl);
2755 unsafe {
2756 bl.launch(cfg)?;
2757 }
2758 Ok(())
2759 }
2760
2761 pub fn spec_fork_restore_f32(
2763 &self,
2764 snapshot: &CudaSlice<f32>,
2765 state: &mut CudaSlice<f32>,
2766 valid: &CudaSlice<u32>,
2767 ) -> Result<(), Box<dyn std::error::Error>> {
2768 assert_eq!(
2769 snapshot.len(),
2770 state.len(),
2771 "fork recurrent snapshot shape mismatch"
2772 );
2773 let f = self.func("spec_fork_restore_f32");
2774 let n = state.len() as i32;
2775 let blocks = state.len().div_ceil(256).min(65535).max(1) as u32;
2776 let cfg = LaunchConfig {
2777 grid_dim: (blocks, 1, 1),
2778 block_dim: (256, 1, 1),
2779 shared_mem_bytes: 0,
2780 };
2781 let __s_bl = self.gpu.stream();
2782 let mut bl = __s_bl.launch_builder(&f);
2783 bl.arg(snapshot).arg(state).arg(valid).arg(&n);
2784 unsafe {
2785 bl.launch(cfg)?;
2786 }
2787 Ok(())
2788 }
2789
2790 pub fn spec_seed_gather(
2793 &self,
2794 vx: &CudaSlice<f32>,
2795 fill_prev: &CudaSlice<f32>,
2796 acc: &CudaSlice<u32>,
2797 h_seed: &mut CudaSlice<f32>,
2798 base: usize,
2799 n_embd: usize,
2800 ) -> Result<(), Box<dyn std::error::Error>> {
2801 let f = self.func("spec_seed_gather");
2802 let (b, ne) = (base as i32, n_embd as i32);
2803 let cfg = LaunchConfig {
2804 grid_dim: (n_embd.div_ceil(256) as u32, 1, 1),
2805 block_dim: (256, 1, 1),
2806 shared_mem_bytes: 0,
2807 };
2808 let __s_bl = self.gpu.stream();
2809 let mut bl = __s_bl.launch_builder(&f);
2810 bl.arg(vx)
2811 .arg(fill_prev)
2812 .arg(acc)
2813 .arg(h_seed)
2814 .arg(&b)
2815 .arg(&ne);
2816 unsafe {
2817 bl.launch(cfg)?;
2818 }
2819 Ok(())
2820 }
2821
2822 pub fn spec_accept_greedy(
2824 &self,
2825 preds: &CudaSlice<u32>,
2826 draft: &CudaSlice<u32>,
2827 last_pred: u32,
2828 base: usize,
2829 k_round: usize,
2830 out: &mut CudaSlice<u32>,
2831 ) -> Result<(), Box<dyn std::error::Error>> {
2832 let f = self.func("spec_accept_greedy");
2833 let (b, k) = (base as i32, k_round as i32);
2834 let cfg = LaunchConfig {
2835 grid_dim: (1, 1, 1),
2836 block_dim: (32, 1, 1),
2837 shared_mem_bytes: 0,
2838 };
2839 let __s_bl = self.gpu.stream();
2840 let mut bl = __s_bl.launch_builder(&f);
2841 bl.arg(preds)
2842 .arg(draft)
2843 .arg(&last_pred)
2844 .arg(&b)
2845 .arg(&k)
2846 .arg(out);
2847 unsafe {
2848 bl.launch(cfg)?;
2849 }
2850 Ok(())
2851 }
2852
2853 pub fn gumbel_perturb(
2860 &self,
2861 x: &CudaSlice<f32>,
2862 y: &mut CudaSlice<f32>,
2863 n: usize,
2864 seed: u64,
2865 stream_pos: u32,
2866 temp: f32,
2867 ) -> Result<(), Box<dyn std::error::Error>> {
2868 let f = self.func("gumbel_perturb_f32");
2869 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2870 let cfg = LaunchConfig {
2871 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2872 block_dim: (256, 1, 1),
2873 shared_mem_bytes: 0,
2874 };
2875 let __s_b = self.gpu.stream();
2876 let mut b = __s_b.launch_builder(&f);
2877 b.arg(x)
2878 .arg(&mut *y)
2879 .arg(&ni)
2880 .arg(&slo)
2881 .arg(&shi)
2882 .arg(&stream_pos)
2883 .arg(&temp);
2884 unsafe {
2885 b.launch(cfg)?;
2886 }
2887 Ok(())
2888 }
2889
2890 pub fn mask_logits_col(
2898 &self,
2899 logits: &mut CudaSlice<f32>,
2900 mask: &CudaSlice<u32>,
2901 col: usize,
2902 n: usize,
2903 mask_words: usize,
2904 ) -> Result<(), Box<dyn std::error::Error>> {
2905 let f = self.func("mask_logits_f32");
2906 let (ci, ni, mw) = (col as i32, n as i32, mask_words as i32);
2907 let cfg = LaunchConfig {
2908 grid_dim: (n.div_ceil(256).min(1024) as u32, 1, 1),
2909 block_dim: (256, 1, 1),
2910 shared_mem_bytes: 0,
2911 };
2912 let __s_b = self.gpu.stream();
2913 let mut b = __s_b.launch_builder(&f);
2914 b.arg(&mut *logits).arg(mask).arg(&ci).arg(&ni).arg(&mw);
2915 unsafe {
2916 b.launch(cfg)?;
2917 }
2918 Ok(())
2919 }
2920
2921 pub fn gumbel_perturb_col(
2928 &self,
2929 x: &CudaSlice<f32>,
2930 col: usize,
2931 y: &mut CudaSlice<f32>,
2932 n: usize,
2933 seed: u64,
2934 stream_pos: u32,
2935 temp: f32,
2936 ) -> Result<(), Box<dyn std::error::Error>> {
2937 let f = self.func("gumbel_perturb_f32");
2938 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2939 let col_view = x.slice(col * n..(col + 1) * n);
2940 let cfg = LaunchConfig {
2941 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2942 block_dim: (256, 1, 1),
2943 shared_mem_bytes: 0,
2944 };
2945 let __s_b = self.gpu.stream();
2946 let mut b = __s_b.launch_builder(&f);
2947 b.arg(&col_view)
2948 .arg(&mut *y)
2949 .arg(&ni)
2950 .arg(&slo)
2951 .arg(&shi)
2952 .arg(&stream_pos)
2953 .arg(&temp);
2954 unsafe {
2955 b.launch(cfg)?;
2956 }
2957 Ok(())
2958 }
2959
2960 #[allow(clippy::too_many_arguments)]
2966 pub fn gumbel_perturb_filtered_col(
2967 &self,
2968 x: &CudaSlice<f32>,
2969 col: usize,
2970 y: &mut CudaSlice<f32>,
2971 n: usize,
2972 seed: u64,
2973 stream_pos: u32,
2974 temp: f32,
2975 stat_max: &CudaSlice<f32>,
2976 stat_th: &CudaSlice<f32>,
2977 stat_idx: usize,
2978 ) -> Result<(), Box<dyn std::error::Error>> {
2979 let f = self.func("gumbel_perturb_filtered_col_f32");
2980 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2981 let (ci, si) = (col as i32, stat_idx as i32);
2982 let cfg = LaunchConfig {
2983 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2984 block_dim: (256, 1, 1),
2985 shared_mem_bytes: 0,
2986 };
2987 let __s_b = self.gpu.stream();
2988 let mut b = __s_b.launch_builder(&f);
2989 b.arg(x)
2990 .arg(&ci)
2991 .arg(&mut *y)
2992 .arg(&ni)
2993 .arg(&slo)
2994 .arg(&shi)
2995 .arg(&stream_pos)
2996 .arg(&temp)
2997 .arg(stat_max)
2998 .arg(stat_th)
2999 .arg(&si);
3000 unsafe {
3001 b.launch(cfg)?;
3002 }
3003 Ok(())
3004 }
3005
3006 pub fn sctr_inc(&self, ctr: &mut CudaSlice<u32>) -> Result<(), Box<dyn std::error::Error>> {
3011 let f = self.func("memra_sctr_inc");
3012 let cfg = LaunchConfig {
3013 grid_dim: (1, 1, 1),
3014 block_dim: (1, 1, 1),
3015 shared_mem_bytes: 0,
3016 };
3017 let __s_b = self.gpu.stream();
3018 let mut b = __s_b.launch_builder(&f);
3019 b.arg(&mut *ctr);
3020 unsafe {
3021 b.launch(cfg)?;
3022 }
3023 Ok(())
3024 }
3025
3026 pub fn gumbel_perturb_ctr(
3031 &self,
3032 x: &CudaSlice<f32>,
3033 y: &mut CudaSlice<f32>,
3034 n: usize,
3035 seed: u64,
3036 ctr: &CudaSlice<u32>,
3037 temp: f32,
3038 ) -> Result<(), Box<dyn std::error::Error>> {
3039 let f = self.func("gumbel_perturb_ctr_f32");
3040 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3041 let cfg = LaunchConfig {
3042 grid_dim: (n.div_ceil(256) as u32, 1, 1),
3043 block_dim: (256, 1, 1),
3044 shared_mem_bytes: 0,
3045 };
3046 let __s_b = self.gpu.stream();
3047 let mut b = __s_b.launch_builder(&f);
3048 b.arg(x)
3049 .arg(&mut *y)
3050 .arg(&ni)
3051 .arg(&slo)
3052 .arg(&shi)
3053 .arg(ctr)
3054 .arg(&temp);
3055 unsafe {
3056 b.launch(cfg)?;
3057 }
3058 Ok(())
3059 }
3060
3061 pub fn softmax_gather(
3065 &self,
3066 x: &CudaSlice<f32>,
3067 row_stride: usize,
3068 ids: &CudaSlice<u32>,
3069 rows: &CudaSlice<i32>,
3070 out: &mut CudaSlice<f32>,
3071 n: usize,
3072 npair: usize,
3073 temp: f32,
3074 ) -> Result<(), Box<dyn std::error::Error>> {
3075 let f = self.func("softmax_gather_f32");
3076 let (ni, rs) = (n as i32, row_stride as i64);
3077 let np = npair as i32;
3078 let cfg = LaunchConfig {
3079 grid_dim: (npair as u32, 1, 1),
3080 block_dim: (256, 1, 1),
3081 shared_mem_bytes: 0,
3082 };
3083 let __s_b = self.gpu.stream();
3084 let mut b = __s_b.launch_builder(&f);
3085 b.arg(x)
3086 .arg(&rs)
3087 .arg(ids)
3088 .arg(rows)
3089 .arg(&mut *out)
3090 .arg(&ni)
3091 .arg(&np)
3092 .arg(&temp);
3093 unsafe {
3094 b.launch(cfg)?;
3095 }
3096 Ok(())
3097 }
3098
3099 pub fn residual_sample(
3103 &self,
3104 p: &CudaSlice<f32>,
3105 q: Option<&CudaSlice<f32>>,
3106 n: usize,
3107 temp: f32,
3108 seed: u64,
3109 stream_pos: u32,
3110 out_tok: &mut CudaSlice<u32>,
3111 ) -> Result<(), Box<dyn std::error::Error>> {
3112 let f = self.func("residual_sample_f32");
3113 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3114 let nth = 1024u32;
3115 let cfg = LaunchConfig {
3116 grid_dim: (1, 1, 1),
3117 block_dim: (nth, 1, 1),
3118 shared_mem_bytes: 0,
3119 };
3120 let has_q: i32 = q.is_some() as i32;
3121 let qbuf = q.unwrap_or(p); let __s_b = self.gpu.stream();
3123 let mut b = __s_b.launch_builder(&f);
3124 b.arg(p)
3125 .arg(qbuf)
3126 .arg(&has_q)
3127 .arg(&ni)
3128 .arg(&temp)
3129 .arg(&slo)
3130 .arg(&shi)
3131 .arg(&stream_pos)
3132 .arg(&mut *out_tok);
3133 unsafe {
3134 b.launch(cfg)?;
3135 }
3136 Ok(())
3137 }
3138
3139 pub fn with_moe_cache<R>(
3144 &self,
3145 max_block_bytes: usize,
3146 f: impl FnOnce(
3147 &mut crate::moe_cache::MoeSlotCache,
3148 &Engine,
3149 ) -> Result<R, Box<dyn std::error::Error>>,
3150 ) -> Result<R, Box<dyn std::error::Error>> {
3151 let mut guard = self.moe_cache.lock().unwrap();
3152 if guard.is_none() {
3153 *guard = Some(crate::moe_cache::MoeSlotCache::new(self, max_block_bytes)?);
3154 }
3155 let cache = guard.as_mut().unwrap();
3156 f(cache, self)
3157 }
3158
3159 pub fn freeze_moe_cache(&self) {
3162 if let Some(cache) = self.moe_cache.lock().unwrap().as_mut() {
3163 cache.freeze();
3164 }
3165 }
3166
3167 pub fn export_moe_residency(&self) -> Option<Vec<(u16, u8, u16)>> {
3170 self.moe_cache
3171 .lock()
3172 .unwrap()
3173 .as_ref()
3174 .map(crate::moe_cache::MoeSlotCache::export_residency)
3175 }
3176
3177 pub(crate) fn moe_cache_frozen(&self) -> bool {
3178 self.moe_cache
3179 .lock()
3180 .unwrap()
3181 .as_ref()
3182 .is_some_and(crate::moe_cache::MoeSlotCache::is_frozen)
3183 }
3184
3185 pub fn frozen_cpu_experts_prefer_tokenwise_prime(&self) -> bool {
3192 crate::cpu_experts::configured()
3193 && self.moe_cache_frozen()
3194 && std::env::var("MEMRA_CPU_EXPERT_BATCHED_PRIME").as_deref() != Ok("1")
3195 }
3196
3197 pub(crate) fn configure_moe_cache_layout(&self, block_bytes: Vec<usize>) {
3199 assert!(
3200 self.moe_cache.lock().unwrap().is_none(),
3201 "MoE cache layout configured after cache construction"
3202 );
3203 *self.moe_cache_layout.lock().unwrap() = Some(block_bytes);
3204 }
3205
3206 pub(crate) fn moe_cache_layout(&self) -> Option<Vec<usize>> {
3207 self.moe_cache_layout.lock().unwrap().clone()
3208 }
3209
3210 pub fn moe_cache_enabled() -> bool {
3212 std::env::var("MEMRA_MOE_CACHE").as_deref() != Ok("0")
3213 }
3214
3215 pub fn moe_cache_stats(&self) -> Option<(u64, u64, u64, usize)> {
3218 let guard = self.moe_cache.lock().unwrap();
3219 guard
3220 .as_ref()
3221 .map(|c| (c.hits, c.misses, c.staged_bytes, c.n_slots()))
3222 }
3223
3224 pub fn cpu_expert_stats(
3228 &self,
3229 ) -> Option<(u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64)> {
3230 crate::cpu_experts::configured().then(crate::cpu_experts::stats)
3231 }
3232
3233 pub fn cpu_expert_predictor_stats(&self) -> (u64, u64) {
3236 crate::cpu_experts::predictor_stats()
3237 }
3238
3239 pub fn cpu_expert_exposed_wait_ns(&self) -> Option<u64> {
3240 crate::cpu_experts::configured().then(crate::cpu_experts::exposed_wait_ns)
3241 }
3242
3243 pub fn cpu_expert_gpu_residency_stats(&self) -> Option<(u64, u64, u64)> {
3246 crate::cpu_experts::configured().then(crate::cpu_experts::incomplete_gpu_residency_stats)
3247 }
3248
3249 pub fn moe_pread_stats(&self) -> Option<(u64, u64, u64, u64, u64, u64, u64)> {
3252 let guard = self.moe_cache.lock().unwrap();
3253 guard
3254 .as_ref()
3255 .and_then(|cache| cache.pread_stats())
3256 .map(|stats| {
3257 (
3258 stats.reads,
3259 stats.bytes,
3260 stats.read_errors,
3261 stats.short_reads,
3262 stats.fallbacks,
3263 stats.buffer_waits,
3264 stats.ring_full,
3265 )
3266 })
3267 }
3268
3269 pub fn spill_config_fallbacks(&self) -> u64 {
3271 crate::spill_pread::config_fallbacks()
3272 }
3273
3274 pub fn moe_cache_reset_counters(&self) {
3276 if let Some(c) = self.moe_cache.lock().unwrap().as_mut() {
3277 c.reset_counters();
3278 }
3279 }
3280
3281 pub fn htod_bytes(&self, v: &[u8]) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3282 Ok(self.gpu.stream().clone_htod(v)?)
3283 }
3284
3285 pub fn htod_bytes_padded(
3289 &self,
3290 v: &[u8],
3291 pad: usize,
3292 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3293 let mut d = self.alloc_u8_uninit(v.len() + pad)?;
3294 {
3295 let mut view = d.slice_mut(0..v.len());
3296 self.gpu.stream().memcpy_htod(v, &mut view)?;
3297 }
3298 Ok(d)
3299 }
3300
3301 pub fn copy_into(
3303 &self,
3304 dst: &mut CudaSlice<f32>,
3305 off: usize,
3306 src: &CudaSlice<f32>,
3307 len: usize,
3308 ) -> Result<(), Box<dyn std::error::Error>> {
3309 let mut view = dst.slice_mut(off..off + len);
3310 self.gpu
3311 .stream()
3312 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3313 Ok(())
3314 }
3315
3316 pub fn copy_u8_into(
3319 &self,
3320 dst: &mut CudaSlice<u8>,
3321 off: usize,
3322 src: &CudaSlice<u8>,
3323 len: usize,
3324 ) -> Result<(), Box<dyn std::error::Error>> {
3325 let mut view = dst.slice_mut(off..off + len);
3326 self.gpu
3327 .stream()
3328 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3329 Ok(())
3330 }
3331
3332 pub fn copy_u8_range_into(
3334 &self,
3335 dst: &mut CudaSlice<u8>,
3336 dst_off: usize,
3337 src: &CudaSlice<u8>,
3338 src_off: usize,
3339 len: usize,
3340 ) -> Result<(), Box<dyn std::error::Error>> {
3341 let mut dst_view = dst.slice_mut(dst_off..dst_off + len);
3342 self.gpu
3343 .stream()
3344 .memcpy_dtod(&src.slice(src_off..src_off + len), &mut dst_view)?;
3345 Ok(())
3346 }
3347
3348 pub fn prepare_kv_append(
3352 &self,
3353 kv: &mut crate::cache::KvLayer,
3354 retain_from: usize,
3355 append_rows: usize,
3356 ) -> Result<usize, Box<dyn std::error::Error>> {
3357 let Some(plan) = kv
3358 .ring
3359 .as_ref()
3360 .map(|ring| ring.append_plan(kv.len, retain_from, append_rows))
3361 .transpose()?
3362 else {
3363 return Ok(kv.len);
3364 };
3365 match plan {
3366 crate::cache::KvRingAppend::Contiguous { write_row } => Ok(write_row),
3367 crate::cache::KvRingAppend::Rebase {
3368 src_row,
3369 keep_rows,
3370 new_base,
3371 write_row,
3372 } => {
3373 if keep_rows > 0 {
3374 let k_len = keep_rows * kv.k_tok_bytes;
3375 let v_len = keep_rows * kv.v_tok_bytes;
3376 let mut k_tmp = self.alloc_u8_uninit(k_len)?;
3377 let mut v_tmp = self.alloc_u8_uninit(v_len)?;
3378 self.copy_u8_range_into(&mut k_tmp, 0, &kv.k, src_row * kv.k_tok_bytes, k_len)?;
3379 self.copy_u8_range_into(&mut v_tmp, 0, &kv.v, src_row * kv.v_tok_bytes, v_len)?;
3380 self.copy_u8_into(&mut kv.k, 0, &k_tmp, k_len)?;
3381 self.copy_u8_into(&mut kv.v, 0, &v_tmp, v_len)?;
3382 }
3383 kv.ring.as_mut().unwrap().apply_rebase(new_base);
3384 Ok(write_row)
3385 }
3386 }
3387 }
3388
3389 pub fn htod_u8_into(
3392 &self,
3393 dst: &mut CudaSlice<u8>,
3394 off: usize,
3395 src: &[u8],
3396 ) -> Result<(), Box<dyn std::error::Error>> {
3397 let mut view = dst.slice_mut(off..off + src.len());
3398 self.gpu.stream().memcpy_htod(src, &mut view)?;
3399 Ok(())
3400 }
3401
3402 pub fn view<'a>(&self, b: &'a CudaSlice<f32>, len: usize) -> cudarc::driver::CudaView<'a, f32> {
3403 b.slice(0..len)
3404 }
3405
3406 pub fn view_u8_range<'a>(
3409 &self,
3410 b: &'a CudaSlice<u8>,
3411 start: usize,
3412 end: usize,
3413 ) -> cudarc::driver::CudaView<'a, u8> {
3414 b.slice(start..end)
3415 }
3416 pub fn view_u8<'a>(
3417 &self,
3418 b: &'a CudaSlice<u8>,
3419 len: usize,
3420 ) -> cudarc::driver::CudaView<'a, u8> {
3421 b.slice(0..len)
3422 }
3423
3424 pub fn append_kv_quantized(
3428 &self,
3429 k_row: &CudaSlice<f32>,
3430 v_row: &CudaSlice<f32>,
3431 kc: &mut CudaSlice<u8>,
3432 vc: &mut CudaSlice<u8>,
3433 t: usize,
3434 kv_dim_k: usize,
3435 kv_dim_v: usize,
3436 k_tok_bytes: usize,
3437 v_tok_bytes: usize,
3438 g: bool,
3439 ) -> Result<(), Box<dyn std::error::Error>> {
3440 let f = if g {
3441 self.func_g("append_quantize_kv_q8_0_q5_1")
3442 } else {
3443 self.func("append_quantize_kv_q8_0_q5_1")
3444 };
3445 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3446 let cfg = LaunchConfig {
3447 grid_dim: (nblk, 1, 1),
3448 block_dim: (32, 1, 1),
3449 shared_mem_bytes: 0,
3450 };
3451 let (ti, kdk, kdv) = (t as i32, kv_dim_k as i32, kv_dim_v as i32);
3452 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3453 let __s_b = self.gpu.stream();
3454 let mut b = __s_b.launch_builder(&f);
3455 b.arg(k_row)
3456 .arg(v_row)
3457 .arg(kc)
3458 .arg(vc)
3459 .arg(&ti)
3460 .arg(&kdk)
3461 .arg(&kdv)
3462 .arg(&ktb)
3463 .arg(&vtb);
3464 unsafe {
3465 b.launch(cfg)?;
3466 }
3467 Ok(())
3468 }
3469
3470 pub fn append_kv_quantized_dc(
3474 &self,
3475 k_row: &CudaSlice<f32>,
3476 v_row: &CudaSlice<f32>,
3477 kc: &mut CudaSlice<u8>,
3478 vc: &mut CudaSlice<u8>,
3479 t_dev: &CudaSlice<i32>,
3480 kv_dim_k: usize,
3481 kv_dim_v: usize,
3482 k_tok_bytes: usize,
3483 v_tok_bytes: usize,
3484 g: bool,
3485 ) -> Result<(), Box<dyn std::error::Error>> {
3486 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3487 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
3488 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3489 if Self::pdl_on() && Self::pdl_wb_on() {
3491 use cudarc::driver::{DevicePtr, DevicePtrMut};
3492 let s = &self.gpu.stream();
3493 let (pk, _g0) = k_row.device_ptr(s);
3494 let (pv, _g1) = v_row.device_ptr(s);
3495 let (pkc, _g2) = kc.device_ptr_mut(s);
3496 let (pvc, _g3) = vc.device_ptr_mut(s);
3497 let (pt, _g4) = t_dev.device_ptr(s);
3498 let mut ps = [
3499 &pk as *const _ as *mut std::ffi::c_void,
3500 &pv as *const _ as *mut _,
3501 &pkc as *const _ as *mut _,
3502 &pvc as *const _ as *mut _,
3503 &pt as *const _ as *mut _,
3504 &kdk as *const _ as *mut _,
3505 &kdv as *const _ as *mut _,
3506 &ktb as *const _ as *mut _,
3507 &vtb as *const _ as *mut _,
3508 ];
3509 unsafe {
3510 self.launch_pdl_flash(
3511 g,
3512 "append_quantize_kv_q8_0_q5_1_dc",
3513 (nblk, 1, 1),
3514 (32, 1, 1),
3515 0,
3516 &mut ps,
3517 )?;
3518 }
3519 return Ok(());
3520 }
3521 let f = if g {
3522 self.func_g("append_quantize_kv_q8_0_q5_1_dc")
3523 } else {
3524 self.func("append_quantize_kv_q8_0_q5_1_dc")
3525 };
3526 let cfg = LaunchConfig {
3527 grid_dim: (nblk, 1, 1),
3528 block_dim: (32, 1, 1),
3529 shared_mem_bytes: 0,
3530 };
3531 let __s_b = self.gpu.stream();
3532 let mut b = __s_b.launch_builder(&f);
3533 b.arg(k_row)
3534 .arg(v_row)
3535 .arg(kc)
3536 .arg(vc)
3537 .arg(t_dev)
3538 .arg(&kdk)
3539 .arg(&kdv)
3540 .arg(&ktb)
3541 .arg(&vtb);
3542 unsafe {
3543 b.launch(cfg)?;
3544 }
3545 Ok(())
3546 }
3547
3548 #[allow(clippy::too_many_arguments)]
3555 pub fn append_kv_quantized_rows(
3556 &self,
3557 k_rows: &CudaSlice<f32>,
3558 v_rows: &CudaSlice<f32>,
3559 kc: &mut CudaSlice<u8>,
3560 vc: &mut CudaSlice<u8>,
3561 t0: usize,
3562 t: usize,
3563 kv_dim_k: usize,
3564 kv_dim_v: usize,
3565 k_tok_bytes: usize,
3566 v_tok_bytes: usize,
3567 g: bool,
3568 ) -> Result<(), Box<dyn std::error::Error>> {
3569 if std::env::var("MEMRA_PRIME_APPEND_LOOP").is_ok() {
3570 for i in 0..t {
3571 let k_row = k_rows.slice(i * kv_dim_k..(i + 1) * kv_dim_k);
3572 let v_row = v_rows.slice(i * kv_dim_v..(i + 1) * kv_dim_v);
3573 self.append_kv_quantized_view(
3574 &k_row,
3575 &v_row,
3576 kc,
3577 vc,
3578 t0 + i,
3579 kv_dim_k,
3580 kv_dim_v,
3581 k_tok_bytes,
3582 v_tok_bytes,
3583 g,
3584 )?;
3585 }
3586 return Ok(());
3587 }
3588 let f = if g {
3589 self.func_g("append_quantize_kv_q8_0_q5_1_rows")
3590 } else {
3591 self.func("append_quantize_kv_q8_0_q5_1_rows")
3592 };
3593 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3594 let cfg = LaunchConfig {
3595 grid_dim: (nblk, t as u32, 1),
3596 block_dim: (32, 1, 1),
3597 shared_mem_bytes: 0,
3598 };
3599 let (t0i, kdk, kdv) = (t0 as i32, kv_dim_k as i32, kv_dim_v as i32);
3600 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3601 let __s_b = self.gpu.stream();
3602 let mut b = __s_b.launch_builder(&f);
3603 b.arg(k_rows)
3604 .arg(v_rows)
3605 .arg(kc)
3606 .arg(vc)
3607 .arg(&t0i)
3608 .arg(&kdk)
3609 .arg(&kdv)
3610 .arg(&ktb)
3611 .arg(&vtb);
3612 unsafe {
3613 b.launch(cfg)?;
3614 }
3615 Ok(())
3616 }
3617
3618 pub fn inc_seqlen(&self, p: &mut CudaSlice<i32>) -> Result<(), Box<dyn std::error::Error>> {
3622 let f = self.func("inc_i32");
3623 let cfg = LaunchConfig {
3624 grid_dim: (1, 1, 1),
3625 block_dim: (1, 1, 1),
3626 shared_mem_bytes: 0,
3627 };
3628 let __s_b = self.gpu.stream();
3629 let mut b = __s_b.launch_builder(&f);
3630 b.arg(p);
3631 unsafe {
3632 b.launch(cfg)?;
3633 }
3634 Ok(())
3635 }
3636
3637 pub fn append_kv_quantized_view(
3640 &self,
3641 k_row: &cudarc::driver::CudaView<f32>,
3642 v_row: &cudarc::driver::CudaView<f32>,
3643 kc: &mut CudaSlice<u8>,
3644 vc: &mut CudaSlice<u8>,
3645 t: usize,
3646 kv_dim_k: usize,
3647 kv_dim_v: usize,
3648 k_tok_bytes: usize,
3649 v_tok_bytes: usize,
3650 g: bool,
3651 ) -> Result<(), Box<dyn std::error::Error>> {
3652 let f = if g {
3653 self.func_g("append_quantize_kv_q8_0_q5_1")
3654 } else {
3655 self.func("append_quantize_kv_q8_0_q5_1")
3656 };
3657 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3658 let cfg = LaunchConfig {
3659 grid_dim: (nblk, 1, 1),
3660 block_dim: (32, 1, 1),
3661 shared_mem_bytes: 0,
3662 };
3663 let (ti, kdk, kdv) = (t as i32, kv_dim_k as i32, kv_dim_v as i32);
3664 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3665 let __s_b = self.gpu.stream();
3666 let mut b = __s_b.launch_builder(&f);
3667 b.arg(k_row)
3668 .arg(v_row)
3669 .arg(kc)
3670 .arg(vc)
3671 .arg(&ti)
3672 .arg(&kdk)
3673 .arg(&kdv)
3674 .arg(&ktb)
3675 .arg(&vtb);
3676 unsafe {
3677 b.launch(cfg)?;
3678 }
3679 Ok(())
3680 }
3681
3682 pub fn copy_view_into(
3685 &self,
3686 dst: &mut CudaSlice<f32>,
3687 off: usize,
3688 src: &cudarc::driver::CudaView<f32>,
3689 len: usize,
3690 ) -> Result<(), Box<dyn std::error::Error>> {
3691 let mut view = dst.slice_mut(off..off + len);
3692 self.gpu
3693 .stream()
3694 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3695 Ok(())
3696 }
3697
3698 pub fn clone_dtod(
3702 &self,
3703 src: &CudaSlice<f32>,
3704 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3705 let mut dst = self.gpu.stream().alloc_zeros::<f32>(src.len())?;
3706 self.gpu.stream().memcpy_dtod(src, &mut dst)?;
3707 Ok(dst)
3708 }
3709
3710 pub fn dtod_copy_view(
3713 &self,
3714 src: &cudarc::driver::CudaView<f32>,
3715 dst: &mut CudaSlice<f32>,
3716 ) -> Result<(), Box<dyn std::error::Error>> {
3717 self.gpu.stream().memcpy_dtod(src, dst)?;
3718 Ok(())
3719 }
3720
3721 pub fn dtod_copy_view_i8(
3723 &self,
3724 src: &cudarc::driver::CudaView<i8>,
3725 dst: &mut CudaSlice<i8>,
3726 ) -> Result<(), Box<dyn std::error::Error>> {
3727 self.gpu.stream().memcpy_dtod(src, dst)?;
3728 Ok(())
3729 }
3730
3731 pub fn dtod_copy_into(
3733 &self,
3734 src: &CudaSlice<f32>,
3735 dst: &mut CudaSlice<f32>,
3736 offset: usize,
3737 ) -> Result<(), Box<dyn std::error::Error>> {
3738 let n = src.len();
3739 let mut dv = dst.slice_mut(offset..offset + n);
3740 self.gpu.stream().memcpy_dtod(src, &mut dv)?;
3741 Ok(())
3742 }
3743
3744 pub fn uninit_i8(&self, n: usize) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
3746 self.alloc_uninit::<i8>(n)
3747 }
3748
3749 pub fn qmatvec(
3751 &self,
3752 w: &CudaSlice<u8>,
3753 x: &CudaSlice<f32>,
3754 m: usize,
3755 in_f: usize,
3756 out_f: usize,
3757 qtype: i32,
3758 row_bytes: usize,
3759 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3760 let f = self.func("qmatvec_f32");
3761 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
3763 grid_dim: (out_f as u32, m as u32, 1),
3764 block_dim: (256, 1, 1),
3765 shared_mem_bytes: 0,
3766 };
3767 let (inf, outf, mi, qt, rb) =
3768 (in_f as i32, out_f as i32, m as i32, qtype, row_bytes as i64);
3769 let __s_b = self.gpu.stream();
3770 let mut b = __s_b.launch_builder(&f);
3771 b.arg(w)
3772 .arg(x)
3773 .arg(&mut y)
3774 .arg(&inf)
3775 .arg(&outf)
3776 .arg(&mi)
3777 .arg(&qt)
3778 .arg(&rb);
3779 unsafe {
3780 b.launch(cfg)?;
3781 }
3782 Ok(y)
3783 }
3784
3785 pub fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3787 let s = self.gpu.stream().alloc_zeros::<u8>(n)?;
3788 self.keep_if_capturing(&s);
3789 Ok(s)
3790 }
3791
3792 pub fn alloc_u8_uninit(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3796 let s = unsafe { self.gpu.stream().alloc::<u8>(n)? };
3797 self.keep_if_capturing(&s);
3798 Ok(s)
3799 }
3800
3801 pub fn memset_zeros_view(
3804 &self,
3805 dst: &mut cudarc::driver::CudaViewMut<f32>,
3806 ) -> Result<(), Box<dyn std::error::Error>> {
3807 self.gpu.stream().memset_zeros(dst)?;
3808 Ok(())
3809 }
3810
3811 pub fn stage_expert(
3817 &self,
3818 host_bytes: &[u8],
3819 scratch: &mut CudaSlice<u8>,
3820 off: usize,
3821 ) -> Result<(), Box<dyn std::error::Error>> {
3822 let mut dst = scratch.slice_mut(off..off + host_bytes.len()); self.gpu.stream().memcpy_htod(host_bytes, &mut dst)?; Ok(())
3825 }
3826
3827 pub fn moe_router_topk(
3833 &self,
3834 logits: &CudaSlice<f32>,
3835 t: usize,
3836 n_expert: usize,
3837 n_used: usize,
3838 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
3839 let f = self.func("moe_router_topk_f32");
3840 let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?; let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?; let cfg = LaunchConfig {
3843 grid_dim: (t as u32, 1, 1),
3844 block_dim: (n_expert as u32, 1, 1),
3845 shared_mem_bytes: 0,
3846 };
3847 let (ne, nu) = (n_expert as i32, n_used as i32);
3848 let __s_b = self.gpu.stream();
3849 let mut b = __s_b.launch_builder(&f);
3850 b.arg(logits)
3851 .arg(&mut sel_idx)
3852 .arg(&mut sel_w)
3853 .arg(&ne)
3854 .arg(&nu);
3855 unsafe {
3856 b.launch(cfg)?;
3857 }
3858 Ok((sel_idx, sel_w))
3859 }
3860
3861 pub fn moe_router_topk_scaled(
3864 &self,
3865 logits: &CudaSlice<f32>,
3866 t: usize,
3867 n_expert: usize,
3868 n_used: usize,
3869 ex_scale: &CudaSlice<f32>,
3870 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
3871 let f = self.func("moe_router_topk_scaled_f32");
3876 let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?;
3877 let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?;
3878 let cfg = LaunchConfig {
3879 grid_dim: (t as u32, 1, 1),
3880 block_dim: (n_expert as u32, 1, 1),
3881 shared_mem_bytes: 0,
3882 };
3883 let (ne, nu) = (n_expert as i32, n_used as i32);
3884 let __s_b = self.gpu.stream();
3885 let mut b = __s_b.launch_builder(&f);
3886 b.arg(logits)
3887 .arg(&mut sel_idx)
3888 .arg(&mut sel_w)
3889 .arg(&ne)
3890 .arg(&nu)
3891 .arg(ex_scale);
3892 unsafe {
3893 b.launch(cfg)?;
3894 }
3895 Ok((sel_idx, sel_w))
3896 }
3897
3898 pub fn moe_router_topk_host(
3906 &self,
3907 logits: &CudaSlice<f32>,
3908 t: usize,
3909 n_expert: usize,
3910 n_used: usize,
3911 ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
3912 let f = self.func("moe_router_topk_f32");
3913 let n = t * n_used;
3914 let mut sel_idx = self.alloc_uninit::<i32>(n)?;
3915 let mut sel_w = self.alloc_uninit::<f32>(n)?;
3916 let cfg = LaunchConfig {
3917 grid_dim: (t as u32, 1, 1),
3918 block_dim: (n_expert as u32, 1, 1),
3919 shared_mem_bytes: 0,
3920 };
3921 let (ne, nu) = (n_expert as i32, n_used as i32);
3922 let __s_b = self.gpu.stream();
3923 let mut b = __s_b.launch_builder(&f);
3924 b.arg(logits)
3925 .arg(&mut sel_idx)
3926 .arg(&mut sel_w)
3927 .arg(&ne)
3928 .arg(&nu);
3929 unsafe {
3930 b.launch(cfg)?;
3931 }
3932 let bytes = n * 8;
3934 let mut guard = self.router_stage.lock().unwrap();
3935 if guard.as_ref().map(|p| p.cap < bytes).unwrap_or(true) {
3936 *guard = Some(PinnedStage::new(bytes.max(4096))?);
3937 }
3938 let stage = guard.as_mut().unwrap();
3939 let (si, sw) = unsafe {
3940 (
3941 std::slice::from_raw_parts_mut(stage.ptr as *mut i32, n),
3942 std::slice::from_raw_parts_mut(stage.ptr.add(n * 4) as *mut f32, n),
3943 )
3944 };
3945 self.gpu.stream().memcpy_dtoh(&sel_idx, si)?; self.gpu.stream().memcpy_dtoh(&sel_w, sw)?; self.gpu.stream().synchronize()?; Ok((si.iter().map(|&i| i as u32).collect(), sw.to_vec()))
3949 }
3950
3951 #[allow(clippy::too_many_arguments)]
3955 pub fn moe_router_sigmoid_topk(
3956 &self,
3957 logits: &CudaSlice<f32>,
3958 t: usize,
3959 n_expert: usize,
3960 n_used: usize,
3961 active_count: usize,
3962 correction_bias: &CudaSlice<f32>,
3963 active: &CudaSlice<u8>,
3964 scaling_factor: f32,
3965 route_norm: bool,
3966 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
3967 crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
3968 if n_expert == 0 || n_expert > 1024 || n_used == 0 || n_used > n_expert {
3969 return Err(format!(
3970 "sigmoid router shape unsupported: n_expert={n_expert}, n_used={n_used}",
3971 )
3972 .into());
3973 }
3974 if logits.len() < t * n_expert
3975 || correction_bias.len() != n_expert
3976 || active.len() != n_expert
3977 {
3978 return Err(format!(
3979 "sigmoid router buffer mismatch: logits={} bias={} active={} expected logits>={} row={}",
3980 logits.len(), correction_bias.len(), active.len(), t * n_expert, n_expert,
3981 ).into());
3982 }
3983 let f = self.func("moe_router_sigmoid_topk_f32");
3984 let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?;
3985 let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?;
3986 let threads = n_expert.div_ceil(32) * 32;
3987 let cfg = LaunchConfig {
3988 grid_dim: (t as u32, 1, 1),
3989 block_dim: (threads as u32, 1, 1),
3990 shared_mem_bytes: 0,
3991 };
3992 let (ne, nu, rn) = (n_expert as i32, n_used as i32, i32::from(route_norm));
3993 let __s_b = self.gpu.stream();
3994 let mut b = __s_b.launch_builder(&f);
3995 b.arg(logits)
3996 .arg(correction_bias)
3997 .arg(active)
3998 .arg(&mut sel_idx)
3999 .arg(&mut sel_w)
4000 .arg(&ne)
4001 .arg(&nu)
4002 .arg(&scaling_factor)
4003 .arg(&rn);
4004 unsafe {
4005 b.launch(cfg)?;
4006 }
4007 Ok((sel_idx, sel_w))
4008 }
4009
4010 #[allow(clippy::too_many_arguments)]
4013 pub fn moe_router_sigmoid_topk_host(
4014 &self,
4015 logits: &CudaSlice<f32>,
4016 t: usize,
4017 n_expert: usize,
4018 n_used: usize,
4019 active_count: usize,
4020 correction_bias: &CudaSlice<f32>,
4021 active: &CudaSlice<u8>,
4022 scaling_factor: f32,
4023 route_norm: bool,
4024 ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
4025 let (sel_idx, sel_w) = self.moe_router_sigmoid_topk(
4026 logits,
4027 t,
4028 n_expert,
4029 n_used,
4030 active_count,
4031 correction_bias,
4032 active,
4033 scaling_factor,
4034 route_norm,
4035 )?;
4036 let n = t * n_used;
4037 let bytes = n * 8;
4038 let mut guard = self.router_stage.lock().unwrap();
4039 if guard.as_ref().map(|p| p.cap < bytes).unwrap_or(true) {
4040 *guard = Some(PinnedStage::new(bytes.max(4096))?);
4041 }
4042 let stage = guard.as_mut().unwrap();
4043 let (si, sw) = unsafe {
4044 (
4045 std::slice::from_raw_parts_mut(stage.ptr as *mut i32, n),
4046 std::slice::from_raw_parts_mut(stage.ptr.add(n * 4) as *mut f32, n),
4047 )
4048 };
4049 self.gpu.stream().memcpy_dtoh(&sel_idx, si)?;
4050 self.gpu.stream().memcpy_dtoh(&sel_w, sw)?;
4051 self.gpu.stream().synchronize()?;
4052 Ok((si.iter().map(|&i| i as u32).collect(), sw.to_vec()))
4053 }
4054
4055 pub fn stage_expert_async(
4059 &self,
4060 host_bytes: &[u8],
4061 scratch: &mut CudaSlice<u8>,
4062 off: usize,
4063 ) -> Result<cudarc::driver::CudaEvent, Box<dyn std::error::Error>> {
4064 let mut dst = scratch.slice_mut(off..off + host_bytes.len());
4065 self.copy_stream.memcpy_htod(host_bytes, &mut dst)?;
4066 Ok(self.copy_stream.record_event(None)?)
4067 }
4068
4069 pub fn compute_wait(
4071 &self,
4072 ev: &cudarc::driver::CudaEvent,
4073 ) -> Result<(), Box<dyn std::error::Error>> {
4074 self.gpu.stream().wait(ev)?;
4075 Ok(())
4076 }
4077
4078 pub fn qmatvec_view(
4083 &self,
4084 w: &CudaSlice<u8>,
4085 range: std::ops::Range<usize>,
4086 x: &cudarc::driver::CudaView<f32>,
4087 m: usize,
4088 in_f: usize,
4089 out_f: usize,
4090 qtype: i32,
4091 row_bytes: usize,
4092 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4093 let f = self.func("qmatvec_f32");
4094 let wv = w.slice(range); let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
4097 grid_dim: (out_f as u32, m as u32, 1),
4098 block_dim: (256, 1, 1),
4099 shared_mem_bytes: 0,
4100 };
4101 let (inf, outf, mi, qt, rb) =
4102 (in_f as i32, out_f as i32, m as i32, qtype, row_bytes as i64);
4103 let __s_b = self.gpu.stream();
4104 let mut b = __s_b.launch_builder(&f);
4105 b.arg(&wv)
4106 .arg(x)
4107 .arg(&mut y)
4108 .arg(&inf)
4109 .arg(&outf)
4110 .arg(&mi)
4111 .arg(&qt)
4112 .arg(&rb);
4113 unsafe {
4114 b.launch(cfg)?;
4115 }
4116 Ok(y)
4117 }
4118
4119 #[allow(clippy::too_many_arguments)]
4126 pub fn moe_gate_up_silu8_q8(
4130 &self,
4131 gp: WPtr8,
4132 up: WPtr8,
4133 aq: &CudaSlice<i8>,
4134 ad: &CudaSlice<f32>,
4135 in_f: usize,
4136 n_ff: usize,
4137 n_used: usize,
4138 qt_g: i32,
4139 qt_u: i32,
4140 rb_g: usize,
4141 rb_u: usize,
4142 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4143 let f = self.func("moe_gate_up_silu8_q8");
4144 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
4145 let cfg = LaunchConfig {
4146 grid_dim: (n_ff as u32, n_used as u32, 1),
4147 block_dim: (32, 1, 1),
4148 shared_mem_bytes: 0,
4149 };
4150 let (inf, nff, rbg, rbu) = (in_f as i32, n_ff as i32, rb_g as i64, rb_u as i64);
4151 let __s_b = self.gpu.stream();
4152 let mut b = __s_b.launch_builder(&f);
4153 b.arg(&gp)
4154 .arg(&up)
4155 .arg(aq)
4156 .arg(ad)
4157 .arg(&mut act)
4158 .arg(&inf)
4159 .arg(&nff)
4160 .arg(&qt_g)
4161 .arg(&qt_u)
4162 .arg(&rbg)
4163 .arg(&rbu);
4164 unsafe {
4165 b.launch(cfg)?;
4166 }
4167 Ok(act)
4168 }
4169
4170 #[allow(clippy::too_many_arguments)]
4171 pub fn moe_down8_fma_q8(
4172 &self,
4173 dp: WPtr8,
4174 w: F32x8,
4175 aq2: &CudaSlice<i8>,
4176 ad2: &CudaSlice<f32>,
4177 dst: &mut cudarc::driver::CudaViewMut<f32>,
4178 in_f: usize,
4179 out_f: usize,
4180 n_used: usize,
4181 qt: i32,
4182 rb: usize,
4183 ) -> Result<(), Box<dyn std::error::Error>> {
4184 let f = self.func("moe_down8_fma_q8");
4185 let cfg = LaunchConfig {
4186 grid_dim: (out_f as u32, 1, 1),
4187 block_dim: (32, 1, 1),
4188 shared_mem_bytes: 0,
4189 };
4190 let (inf, outf, nu, rbi) = (in_f as i32, out_f as i32, n_used as i32, rb as i64);
4191 let __s_b = self.gpu.stream();
4192 let mut b = __s_b.launch_builder(&f);
4193 b.arg(&dp)
4194 .arg(&w)
4195 .arg(aq2)
4196 .arg(ad2)
4197 .arg(dst)
4198 .arg(&inf)
4199 .arg(&outf)
4200 .arg(&nu)
4201 .arg(&qt)
4202 .arg(&rbi);
4203 unsafe {
4204 b.launch(cfg)?;
4205 }
4206 Ok(())
4207 }
4208
4209 pub fn qmatvec_expert_q8(
4211 &self,
4212 w: &CudaSlice<u8>,
4213 range: std::ops::Range<usize>,
4214 aq: &CudaSlice<i8>,
4215 ad: &CudaSlice<f32>,
4216 m: usize,
4217 in_f: usize,
4218 out_f: usize,
4219 qtype: i32,
4220 row_bytes: usize,
4221 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4222 let f = self.func("qmatvec_expert_q8");
4223 let wv = w.slice(range);
4224 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
4225 const ROWS: u32 = 4; let cfg = LaunchConfig {
4227 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, m as u32, 1),
4228 block_dim: (32, ROWS, 1),
4229 shared_mem_bytes: 0,
4230 };
4231 let (inf, outf, mi, rbi) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
4232 let __s_b = self.gpu.stream();
4233 let mut b = __s_b.launch_builder(&f);
4234 b.arg(&wv)
4235 .arg(aq)
4236 .arg(ad)
4237 .arg(&mut y)
4238 .arg(&inf)
4239 .arg(&outf)
4240 .arg(&mi)
4241 .arg(&qtype)
4242 .arg(&rbi);
4243 unsafe {
4244 b.launch(cfg)?;
4245 }
4246 Ok(y)
4247 }
4248
4249 pub fn moe_gate_up_silu8(
4250 &self,
4251 gp: WPtr8,
4252 up: WPtr8,
4253 x: &cudarc::driver::CudaView<f32>,
4254 in_f: usize,
4255 n_ff: usize,
4256 n_used: usize,
4257 qt_g: i32,
4258 qt_u: i32,
4259 rb_g: usize,
4260 rb_u: usize,
4261 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4262 let f = self.func("moe_gate_up_silu8_f32");
4263 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?; let cfg = LaunchConfig {
4265 grid_dim: (n_ff as u32, n_used as u32, 1),
4266 block_dim: (256, 1, 1),
4267 shared_mem_bytes: 0,
4268 };
4269 let (inf, nff, rbg, rbu) = (in_f as i32, n_ff as i32, rb_g as i64, rb_u as i64);
4270 let __s_b = self.gpu.stream();
4271 let mut b = __s_b.launch_builder(&f);
4272 b.arg(&gp)
4273 .arg(&up)
4274 .arg(x)
4275 .arg(&mut act)
4276 .arg(&inf)
4277 .arg(&nff)
4278 .arg(&qt_g)
4279 .arg(&qt_u)
4280 .arg(&rbg)
4281 .arg(&rbu);
4282 unsafe {
4283 b.launch(cfg)?;
4284 }
4285 Ok(act)
4286 }
4287
4288 #[allow(clippy::too_many_arguments)]
4294 pub fn moe_down8_fma_into(
4295 &self,
4296 dp: WPtr8,
4297 w: F32x8,
4298 act: &CudaSlice<f32>,
4299 dst: &mut cudarc::driver::CudaViewMut<f32>,
4300 in_f: usize,
4301 out_f: usize,
4302 n_used: usize,
4303 qt: i32,
4304 rb: usize,
4305 ) -> Result<(), Box<dyn std::error::Error>> {
4306 let f = self.func("moe_down8_fma_f32");
4307 let cfg = LaunchConfig {
4308 grid_dim: (out_f as u32, 1, 1),
4309 block_dim: (256, 1, 1),
4310 shared_mem_bytes: 0,
4311 };
4312 let (inf, outf, nu, rbv) = (in_f as i32, out_f as i32, n_used as i32, rb as i64);
4313 let __s_b = self.gpu.stream();
4314 let mut b = __s_b.launch_builder(&f);
4315 b.arg(&dp)
4316 .arg(&w)
4317 .arg(act)
4318 .arg(dst)
4319 .arg(&inf)
4320 .arg(&outf)
4321 .arg(&nu)
4322 .arg(&qt)
4323 .arg(&rbv);
4324 unsafe {
4325 b.launch(cfg)?;
4326 }
4327 Ok(())
4328 }
4329
4330 #[allow(clippy::too_many_arguments)]
4335 #[allow(clippy::too_many_arguments)]
4350 #[allow(clippy::too_many_arguments)]
4352 pub fn moe_pairs_matvec_q8(
4353 &self,
4354 table: &CudaSlice<u64>,
4355 proj: i32,
4356 pair_tok: &CudaSlice<i32>,
4357 pair_ex: &CudaSlice<i32>,
4358 aq: &CudaSlice<i8>,
4359 ad: &CudaSlice<f32>,
4360 in_f: usize,
4361 out_f: usize,
4362 n_expert: usize,
4363 n_pairs: usize,
4364 qtype: i32,
4365 row_bytes: usize,
4366 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4367 let f = self.func("moe_pairs_matvec_q8");
4368 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4369 const ROWS: u32 = 4;
4370 let cfg = LaunchConfig {
4371 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_pairs as u32, 1),
4372 block_dim: (32, ROWS, 1),
4373 shared_mem_bytes: 0,
4374 };
4375 let (inf, outf, ne, np, rbi) = (
4376 in_f as i32,
4377 out_f as i32,
4378 n_expert as i32,
4379 n_pairs as i32,
4380 row_bytes as i64,
4381 );
4382 let __s_b = self.gpu.stream();
4383 let mut b = __s_b.launch_builder(&f);
4384 b.arg(table)
4385 .arg(&proj)
4386 .arg(pair_tok)
4387 .arg(pair_ex)
4388 .arg(aq)
4389 .arg(ad)
4390 .arg(&mut y)
4391 .arg(&inf)
4392 .arg(&outf)
4393 .arg(&ne)
4394 .arg(&np)
4395 .arg(&qtype)
4396 .arg(&rbi);
4397 unsafe {
4398 b.launch(cfg)?;
4399 }
4400 Ok(y)
4401 }
4402
4403 #[allow(clippy::too_many_arguments)]
4405 pub fn moe_pairs_matvec_q8_em(
4406 &self,
4407 table: &CudaSlice<u64>,
4408 proj: i32,
4409 ex_ids: &CudaSlice<i32>,
4410 ex_off: &CudaSlice<i32>,
4411 ex_pairs: &CudaSlice<i32>,
4412 pair_tok: &CudaSlice<i32>,
4413 aq: &CudaSlice<i8>,
4414 ad: &CudaSlice<f32>,
4415 in_f: usize,
4416 out_f: usize,
4417 n_expert: usize,
4418 n_active: usize,
4419 n_pairs: usize,
4420 qtype: i32,
4421 row_bytes: usize,
4422 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4423 let f = self.func("moe_pairs_matvec_q8_em");
4424 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4425 const ROWS: u32 = 4;
4426 let cfg = LaunchConfig {
4427 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_active as u32, 1),
4428 block_dim: (32, ROWS, 1),
4429 shared_mem_bytes: 0,
4430 };
4431 let (inf, outf, ne, na, rbi) = (
4432 in_f as i32,
4433 out_f as i32,
4434 n_expert as i32,
4435 n_active as i32,
4436 row_bytes as i64,
4437 );
4438 let __s_b = self.gpu.stream();
4439 let mut b = __s_b.launch_builder(&f);
4440 b.arg(table)
4441 .arg(&proj)
4442 .arg(ex_ids)
4443 .arg(ex_off)
4444 .arg(ex_pairs)
4445 .arg(pair_tok)
4446 .arg(aq)
4447 .arg(ad)
4448 .arg(&mut y)
4449 .arg(&inf)
4450 .arg(&outf)
4451 .arg(&ne)
4452 .arg(&na)
4453 .arg(&qtype)
4454 .arg(&rbi);
4455 unsafe {
4456 b.launch(cfg)?;
4457 }
4458 Ok(y)
4459 }
4460
4461 #[allow(clippy::too_many_arguments)]
4464 pub fn moe_pairs_matvec_q8_dec(
4465 &self,
4466 table: &CudaSlice<u64>,
4467 proj: i32,
4468 ex_ids: &CudaSlice<i32>,
4469 ex_off: &CudaSlice<i32>,
4470 ex_pairs: &CudaSlice<i32>,
4471 pair_tok: &CudaSlice<i32>,
4472 aq: &CudaSlice<i8>,
4473 ad: &CudaSlice<f32>,
4474 in_f: usize,
4475 out_f: usize,
4476 n_expert: usize,
4477 n_active: usize,
4478 n_pairs: usize,
4479 qtype: i32,
4480 row_bytes: usize,
4481 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4482 let f = self.func("moe_pairs_matvec_q8_dec");
4483 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4484 const ROWS: u32 = 4;
4485 let cfg = LaunchConfig {
4486 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_active as u32, 1),
4487 block_dim: (32, ROWS, 1),
4488 shared_mem_bytes: 0,
4489 };
4490 let (inf, outf, ne, na, rbi) = (
4491 in_f as i32,
4492 out_f as i32,
4493 n_expert as i32,
4494 n_active as i32,
4495 row_bytes as i64,
4496 );
4497 let __s_b = self.gpu.stream();
4498 let mut b = __s_b.launch_builder(&f);
4499 b.arg(table)
4500 .arg(&proj)
4501 .arg(ex_ids)
4502 .arg(ex_off)
4503 .arg(ex_pairs)
4504 .arg(pair_tok)
4505 .arg(aq)
4506 .arg(ad)
4507 .arg(&mut y)
4508 .arg(&inf)
4509 .arg(&outf)
4510 .arg(&ne)
4511 .arg(&na)
4512 .arg(&qtype)
4513 .arg(&rbi);
4514 unsafe {
4515 b.launch(cfg)?;
4516 }
4517 Ok(y)
4518 }
4519
4520 pub fn moe_pairs_gelu_mul(
4521 &self,
4522 gate: &CudaSlice<f32>,
4523 up: &CudaSlice<f32>,
4524 n: usize,
4525 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4526 let f = self.func("moe_pairs_gelu_mul");
4527 let mut act = self.alloc_uninit::<f32>(n)?;
4528 let cfg = LaunchConfig::for_num_elems(n as u32);
4529 let nl = n as i64;
4530 let __s_b = self.gpu.stream();
4531 let mut b = __s_b.launch_builder(&f);
4532 b.arg(gate).arg(up).arg(&mut act).arg(&nl);
4533 unsafe {
4534 b.launch(cfg)?;
4535 }
4536 Ok(act)
4537 }
4538
4539 pub fn moe_pairs_silu_mul(
4540 &self,
4541 gate: &CudaSlice<f32>,
4542 up: &CudaSlice<f32>,
4543 n: usize,
4544 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4545 let f = self.func("moe_pairs_silu_mul");
4546 let mut act = self.alloc_uninit::<f32>(n)?;
4547 let cfg = LaunchConfig::for_num_elems(n as u32);
4548 let nl = n as i64;
4549 let __s_b = self.gpu.stream();
4550 let mut b = __s_b.launch_builder(&f);
4551 b.arg(gate).arg(up).arg(&mut act).arg(&nl);
4552 unsafe {
4553 b.launch(cfg)?;
4554 }
4555 Ok(act)
4556 }
4557
4558 #[allow(clippy::too_many_arguments)]
4559 pub fn moe_pairs_scatter(
4560 &self,
4561 y_down: &CudaSlice<f32>,
4562 pair_w: &CudaSlice<f32>,
4563 tok_pair_off: &CudaSlice<i32>,
4564 tok_pair_ids: &CudaSlice<i32>,
4565 moe_out: &mut CudaSlice<f32>,
4566 t: usize,
4567 n_embd: usize,
4568 ) -> Result<(), Box<dyn std::error::Error>> {
4569 let f = self.func("moe_pairs_scatter");
4570 let cfg = LaunchConfig {
4571 grid_dim: (((n_embd + 255) / 256) as u32, t as u32, 1),
4572 block_dim: (256, 1, 1),
4573 shared_mem_bytes: 0,
4574 };
4575 let ne = n_embd as i32;
4576 let __s_b = self.gpu.stream();
4577 let mut b = __s_b.launch_builder(&f);
4578 b.arg(y_down)
4579 .arg(pair_w)
4580 .arg(tok_pair_off)
4581 .arg(tok_pair_ids)
4582 .arg(moe_out)
4583 .arg(&ne);
4584 unsafe {
4585 b.launch(cfg)?;
4586 }
4587 Ok(())
4588 }
4589
4590 #[allow(clippy::too_many_arguments)]
4594 pub fn moe_gate_up_gelu8_dev_q8(
4595 &self,
4596 table: &CudaSlice<u64>,
4597 sel: &cudarc::driver::CudaView<i32>,
4598 aq: &CudaSlice<i8>,
4599 ad: &CudaSlice<f32>,
4600 in_f: usize,
4601 n_ff: usize,
4602 n_used: usize,
4603 n_expert: usize,
4604 qt_g: i32,
4605 qt_u: i32,
4606 rb_g: usize,
4607 rb_u: usize,
4608 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4609 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
4610 let (inf, nff, ne, rbg, rbu) = (
4611 in_f as i32,
4612 n_ff as i32,
4613 n_expert as i32,
4614 rb_g as i64,
4615 rb_u as i64,
4616 );
4617 let f = self.func("moe_gate_up_gelu8_dev_q8");
4618 let cfg = LaunchConfig {
4619 grid_dim: (n_ff as u32, n_used as u32, 1),
4620 block_dim: (32, 1, 1),
4621 shared_mem_bytes: 0,
4622 };
4623 let __s_b = self.gpu.stream();
4624 let mut b = __s_b.launch_builder(&f);
4625 b.arg(table)
4626 .arg(sel)
4627 .arg(aq)
4628 .arg(ad)
4629 .arg(&mut act)
4630 .arg(&inf)
4631 .arg(&nff)
4632 .arg(&ne)
4633 .arg(&qt_g)
4634 .arg(&qt_u)
4635 .arg(&rbg)
4636 .arg(&rbu);
4637 unsafe {
4638 b.launch(cfg)?;
4639 }
4640 Ok(act)
4641 }
4642
4643 #[allow(clippy::too_many_arguments)]
4645 pub fn moe_gate_up_gelu8_dev_q8_rows(
4646 &self,
4647 table: &CudaSlice<u64>,
4648 sel: &CudaSlice<i32>,
4649 aq: &CudaSlice<i8>,
4650 ad: &CudaSlice<f32>,
4651 t: usize,
4652 in_f: usize,
4653 n_ff: usize,
4654 n_used: usize,
4655 n_expert: usize,
4656 qt_g: i32,
4657 qt_u: i32,
4658 rb_g: usize,
4659 rb_u: usize,
4660 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4661 let mut act = self.alloc_uninit::<f32>(t * n_used * n_ff)?;
4662 let (inf, nff, ne, rbg, rbu, nu) = (
4663 in_f as i32,
4664 n_ff as i32,
4665 n_expert as i32,
4666 rb_g as i64,
4667 rb_u as i64,
4668 n_used as i32,
4669 );
4670 let f = self.func("moe_gate_up_gelu8_dev_q8_rows");
4671 let cfg = LaunchConfig {
4672 grid_dim: (n_ff as u32, n_used as u32, t as u32),
4673 block_dim: (32, 1, 1),
4674 shared_mem_bytes: 0,
4675 };
4676 let __s_b = self.gpu.stream();
4677 let mut b = __s_b.launch_builder(&f);
4678 b.arg(table)
4679 .arg(sel)
4680 .arg(aq)
4681 .arg(ad)
4682 .arg(&mut act)
4683 .arg(&inf)
4684 .arg(&nff)
4685 .arg(&ne)
4686 .arg(&qt_g)
4687 .arg(&qt_u)
4688 .arg(&rbg)
4689 .arg(&rbu)
4690 .arg(&nu);
4691 unsafe {
4692 b.launch(cfg)?;
4693 }
4694 Ok(act)
4695 }
4696
4697 #[allow(clippy::too_many_arguments)]
4699 pub fn moe_gate_up_gelu8_dev_q8_csr(
4700 &self,
4701 table: &CudaSlice<u64>,
4702 sel: &CudaSlice<i32>,
4703 aq: &CudaSlice<i8>,
4704 ad: &CudaSlice<f32>,
4705 n_pairs: usize,
4706 in_f: usize,
4707 n_ff: usize,
4708 n_used: usize,
4709 n_expert: usize,
4710 qt_g: i32,
4711 qt_u: i32,
4712 rb_g: usize,
4713 rb_u: usize,
4714 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4715 let mut act = self.alloc_uninit::<f32>(n_pairs * n_ff)?;
4716 let (inf, nff, ne, rbg, rbu, nu, npi) = (
4717 in_f as i32,
4718 n_ff as i32,
4719 n_expert as i32,
4720 rb_g as i64,
4721 rb_u as i64,
4722 n_used as i32,
4723 n_pairs as i32,
4724 );
4725 let f = self.func("moe_gate_up_gelu8_dev_q8_csr");
4726 let cfg = LaunchConfig {
4727 grid_dim: (n_ff as u32, n_pairs as u32, 1),
4728 block_dim: (32, 1, 1),
4729 shared_mem_bytes: 0,
4730 };
4731 let __s_b = self.gpu.stream();
4732 let mut b = __s_b.launch_builder(&f);
4733 b.arg(table)
4734 .arg(sel)
4735 .arg(aq)
4736 .arg(ad)
4737 .arg(&mut act)
4738 .arg(&inf)
4739 .arg(&nff)
4740 .arg(&ne)
4741 .arg(&qt_g)
4742 .arg(&qt_u)
4743 .arg(&rbg)
4744 .arg(&rbu)
4745 .arg(&nu)
4746 .arg(&npi);
4747 unsafe {
4748 b.launch(cfg)?;
4749 }
4750 Ok(act)
4751 }
4752
4753 #[allow(clippy::too_many_arguments)]
4755 pub fn moe_down8_fma_dev_q8_rows_g(
4756 &self,
4757 table: &CudaSlice<u64>,
4758 sel: &CudaSlice<i32>,
4759 w: &CudaSlice<f32>,
4760 aq2: &CudaSlice<i8>,
4761 ad2: &CudaSlice<f32>,
4762 dst: &mut CudaSlice<f32>,
4763 t: usize,
4764 in_f: usize,
4765 out_f: usize,
4766 n_used: usize,
4767 n_expert: usize,
4768 qt: i32,
4769 rb: usize,
4770 ) -> Result<(), Box<dyn std::error::Error>> {
4771 let (inf, outf, nu, ne, rbi) = (
4772 in_f as i32,
4773 out_f as i32,
4774 n_used as i32,
4775 n_expert as i32,
4776 rb as i64,
4777 );
4778 let step_b1_w8 = t == 1 && in_f == 1280 && out_f == 4096 && n_used == 8 && qt == QT_IQ4_XS;
4782 let f = self.func(if step_b1_w8 {
4783 "moe_down8_fma_dev_q8_rows_w8"
4784 } else {
4785 "moe_down8_fma_dev_q8_rows_g"
4786 });
4787 let cfg = LaunchConfig {
4788 grid_dim: (out_f as u32, 1, t as u32),
4789 block_dim: (32, if step_b1_w8 { 8 } else { 1 }, 1),
4790 shared_mem_bytes: 0,
4791 };
4792 let __s_b = self.gpu.stream();
4793 let mut b = __s_b.launch_builder(&f);
4794 b.arg(table)
4795 .arg(sel)
4796 .arg(w)
4797 .arg(aq2)
4798 .arg(ad2)
4799 .arg(dst)
4800 .arg(&inf)
4801 .arg(&outf)
4802 .arg(&nu)
4803 .arg(&ne)
4804 .arg(&qt)
4805 .arg(&rbi);
4806 unsafe {
4807 b.launch(cfg)?;
4808 }
4809 Ok(())
4810 }
4811
4812 pub fn rp_probe_q4(&self, m: usize) -> Result<(f64, f64), Box<dyn std::error::Error>> {
4816 let (out_f, in_f) = (2048usize, 2816usize);
4817 let nblk = in_f / 32;
4818 let mut seed = 0x9E3779B97F4A7C15u64;
4819 let mut rng = move || {
4820 seed = seed
4821 .wrapping_mul(6364136223846793005)
4822 .wrapping_add(1442695040888963407);
4823 (seed >> 33) as u8
4824 };
4825 let mut w = vec![0u8; out_f * nblk * 18];
4826 for b in w.iter_mut() {
4827 *b = rng();
4828 }
4829 for r in 0..out_f {
4830 for g in 0..nblk {
4831 let off = (r * nblk + g) * 18;
4832 w[off] = 0x00;
4833 w[off + 1] = 0x2C; }
4835 }
4836 let qplane = out_f * nblk * 16;
4837 let mut wrp = vec![0u8; w.len()];
4838 for r in 0..out_f {
4839 for g in 0..nblk {
4840 let src = &w[(r * nblk + g) * 18..(r * nblk + g) * 18 + 18];
4841 wrp[qplane + (r * nblk + g) * 2..qplane + (r * nblk + g) * 2 + 2]
4842 .copy_from_slice(&src[0..2]);
4843 wrp[(r * nblk + g) * 16..(r * nblk + g) * 16 + 16].copy_from_slice(&src[2..18]);
4844 }
4845 }
4846 let w_d = self.htod_bytes(&w)?;
4847 let wrp_d = self.htod_bytes(&wrp)?;
4848 let mut aq = vec![0i8; m * in_f];
4849 for v in aq.iter_mut() {
4850 *v = rng() as i8;
4851 }
4852 let aq_d = self.htod_i8(&aq)?;
4853 let ad_d = self.htod(&vec![0.03125f32; m * nblk])?;
4854 let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
4855 let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
4856 const RPB: u32 = 4;
4857 let cfg = LaunchConfig {
4858 grid_dim: ((out_f as u32).div_ceil(RPB), 1, 1),
4859 block_dim: (32, RPB, 1),
4860 shared_mem_bytes: 0,
4861 };
4862 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
4863 let (rb, qp) = ((nblk * 18) as i64, qplane as i64);
4864 let fb = self.func("qmatvec_q4_0_mmvq_b4");
4865 let fr = self.func("qmatvec_q4_0_mmvq_b4_rp");
4866 {
4867 let __s_b = self.gpu.stream();
4868 let mut b = __s_b.launch_builder(&fb);
4869 b.arg(&w_d)
4870 .arg(&aq_d)
4871 .arg(&ad_d)
4872 .arg(&mut y0)
4873 .arg(&inf)
4874 .arg(&outf)
4875 .arg(&mi)
4876 .arg(&rb);
4877 unsafe {
4878 b.launch(cfg)?;
4879 }
4880 let __s_b = self.gpu.stream();
4881 let mut b = __s_b.launch_builder(&fr);
4882 b.arg(&wrp_d)
4883 .arg(&aq_d)
4884 .arg(&ad_d)
4885 .arg(&mut y1)
4886 .arg(&inf)
4887 .arg(&outf)
4888 .arg(&mi)
4889 .arg(&qp);
4890 unsafe {
4891 b.launch(cfg)?;
4892 }
4893 }
4894 self.gpu.stream().synchronize()?;
4895 let (h0, h1) = (self.dtoh(&y0)?, self.dtoh(&y1)?);
4896 let nd = h0
4897 .iter()
4898 .zip(&h1)
4899 .filter(|(a, b)| a.to_bits() != b.to_bits())
4900 .count();
4901 if nd != 0 {
4902 return Err(format!("rp twin not bitwise: {nd}/{} diffs", h0.len()).into());
4903 }
4904 let mut time = |rp: bool| -> Result<f64, Box<dyn std::error::Error>> {
4905 self.gpu.stream().synchronize()?;
4906 let t0 = std::time::Instant::now();
4907 for _ in 0..500 {
4908 if rp {
4909 let __s_b = self.gpu.stream();
4910 let mut b = __s_b.launch_builder(&fr);
4911 b.arg(&wrp_d)
4912 .arg(&aq_d)
4913 .arg(&ad_d)
4914 .arg(&mut y1)
4915 .arg(&inf)
4916 .arg(&outf)
4917 .arg(&mi)
4918 .arg(&qp);
4919 unsafe {
4920 b.launch(cfg)?;
4921 }
4922 } else {
4923 let __s_b = self.gpu.stream();
4924 let mut b = __s_b.launch_builder(&fb);
4925 b.arg(&w_d)
4926 .arg(&aq_d)
4927 .arg(&ad_d)
4928 .arg(&mut y0)
4929 .arg(&inf)
4930 .arg(&outf)
4931 .arg(&mi)
4932 .arg(&rb);
4933 unsafe {
4934 b.launch(cfg)?;
4935 }
4936 }
4937 }
4938 self.gpu.stream().synchronize()?;
4939 Ok(t0.elapsed().as_secs_f64() * 1e6 / 500.0)
4940 };
4941 let _ = time(false)?;
4942 let _ = time(true)?; Ok((time(false)?, time(true)?))
4944 }
4945
4946 pub fn build_q4_rp4(
4951 &self,
4952 t: &mut crate::model::GpuTensor,
4953 ) -> Result<(), Box<dyn std::error::Error>> {
4954 use crate::model::GpuTensor;
4955 let GpuTensor::Quant {
4956 bytes,
4957 qtype,
4958 row_bytes,
4959 ne,
4960 rp4,
4961 ..
4962 } = t
4963 else {
4964 return Ok(());
4965 };
4966 if *qtype != QT_Q4_0 || rp4.is_some() || ne.len() != 2 {
4967 return Ok(());
4968 }
4969 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
4970 if in_f % 32 != 0 || *row_bytes != (in_f / 32) * 18 {
4971 return Ok(());
4972 }
4973 let nblk = in_f / 32;
4974 let mut dst = self.alloc_uninit::<u8>(out_f * nblk * 18)?;
4975 let f = self.func("q4_0_split_rp_build");
4976 let n = (out_f * nblk) as i32;
4977 let cfg = LaunchConfig {
4978 grid_dim: (((out_f * nblk) as u32).div_ceil(256), 1, 1),
4979 block_dim: (256, 1, 1),
4980 shared_mem_bytes: 0,
4981 };
4982 let (of, nb) = (out_f as i32, nblk as i32);
4983 let _ = n;
4984 let __s_b = self.gpu.stream();
4985 let mut b = __s_b.launch_builder(&f);
4986 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
4987 unsafe {
4988 b.launch(cfg)?;
4989 }
4990 *rp4 = Some(dst);
4991 Ok(())
4992 }
4993
4994 pub fn build_q8_rp4(
4999 &self,
5000 t: &mut crate::model::GpuTensor,
5001 ) -> Result<(), Box<dyn std::error::Error>> {
5002 use crate::model::GpuTensor;
5003 let GpuTensor::Quant {
5004 bytes,
5005 qtype,
5006 row_bytes,
5007 ne,
5008 rp4,
5009 ..
5010 } = t
5011 else {
5012 return Ok(());
5013 };
5014 if *qtype != QT_Q8_0 || rp4.is_some() || ne.len() != 2 {
5015 return Ok(());
5016 }
5017 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5018 if in_f % 32 != 0 || *row_bytes != (in_f / 32) * 34 {
5019 return Ok(());
5020 }
5021 *rp4 = Some(self.build_q8_rp4_raw(bytes, in_f, out_f)?);
5022 Ok(())
5023 }
5024
5025 pub fn build_q8_rp4_raw(
5028 &self,
5029 bytes: &CudaSlice<u8>,
5030 in_f: usize,
5031 out_f: usize,
5032 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
5033 assert!(in_f % 32 == 0);
5034 let nblk = in_f / 32;
5035 let mut dst = self.alloc_uninit::<u8>(out_f * nblk * 34)?;
5036 let f = self.func("q8_0_split_rp_build");
5037 let cfg = LaunchConfig {
5038 grid_dim: (((out_f * nblk) as u32).div_ceil(256), 1, 1),
5039 block_dim: (256, 1, 1),
5040 shared_mem_bytes: 0,
5041 };
5042 let (of, nb) = (out_f as i32, nblk as i32);
5043 let __s_b = self.gpu.stream();
5044 let mut b = __s_b.launch_builder(&f);
5045 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
5046 unsafe {
5047 b.launch(cfg)?;
5048 }
5049 Ok(dst)
5050 }
5051
5052 pub fn build_q4k_rp4(
5060 &self,
5061 t: &mut crate::model::GpuTensor,
5062 ) -> Result<(), Box<dyn std::error::Error>> {
5063 use crate::model::GpuTensor;
5064 let GpuTensor::Quant {
5065 bytes,
5066 qtype,
5067 row_bytes,
5068 ne,
5069 rp4,
5070 ..
5071 } = t
5072 else {
5073 return Ok(());
5074 };
5075 if *qtype != QT_Q4_K || rp4.is_some() || ne.len() != 2 {
5076 return Ok(());
5077 }
5078 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5079 if in_f % 256 != 0 || *row_bytes != (in_f / 256) * 144 {
5080 return Ok(());
5081 }
5082 *rp4 = Some(self.build_kq_rp4_raw(bytes, in_f, out_f, QT_Q4_K)?);
5083 Ok(())
5084 }
5085
5086 pub fn build_q6k_rp4(
5087 &self,
5088 t: &mut crate::model::GpuTensor,
5089 ) -> Result<(), Box<dyn std::error::Error>> {
5090 use crate::model::GpuTensor;
5091 let GpuTensor::Quant {
5092 bytes,
5093 qtype,
5094 row_bytes,
5095 ne,
5096 rp4,
5097 ..
5098 } = t
5099 else {
5100 return Ok(());
5101 };
5102 if *qtype != QT_Q6_K || rp4.is_some() || ne.len() != 2 {
5103 return Ok(());
5104 }
5105 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5106 if in_f % 256 != 0 || *row_bytes != (in_f / 256) * 210 {
5107 return Ok(());
5108 }
5109 *rp4 = Some(self.build_kq_rp4_raw(bytes, in_f, out_f, QT_Q6_K)?);
5110 Ok(())
5111 }
5112
5113 pub fn build_kq_rp4_raw(
5115 &self,
5116 bytes: &CudaSlice<u8>,
5117 in_f: usize,
5118 out_f: usize,
5119 qtype: i32,
5120 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
5121 assert!(in_f % 256 == 0);
5122 let nsbk = in_f / 256;
5123 let (sb_bytes, kname) = match qtype {
5124 QT_Q4_K => (144usize, "q4_K_split_rp_build"),
5125 QT_Q6_K => (210usize, "q6_K_split_rp_build"),
5126 _ => return Err(format!("build_kq_rp4_raw: qtype {qtype} has no rp mirror").into()),
5127 };
5128 let mut dst = self.alloc_uninit::<u8>(out_f * nsbk * sb_bytes)?;
5129 let f = self.func(kname);
5130 let cfg = LaunchConfig {
5131 grid_dim: (((out_f * nsbk) as u32).div_ceil(256), 1, 1),
5132 block_dim: (256, 1, 1),
5133 shared_mem_bytes: 0,
5134 };
5135 let (of, nb) = (out_f as i32, nsbk as i32);
5136 let __s_b = self.gpu.stream();
5137 let mut b = __s_b.launch_builder(&f);
5138 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
5139 unsafe {
5140 b.launch(cfg)?;
5141 }
5142 Ok(dst)
5143 }
5144
5145 pub fn kqrp_enabled() -> bool {
5149 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5150 *ON.get_or_init(|| match std::env::var("MEMRA_KQRP").as_deref() {
5151 Ok("0") => false,
5152 Ok(_) => true,
5153 Err(_) => cfg!(memra_hopper_mma),
5154 })
5155 }
5156
5157 pub fn build_q4_rp_swap(
5163 &self,
5164 t: &mut crate::model::GpuTensor,
5165 ) -> Result<bool, Box<dyn std::error::Error>> {
5166 use crate::model::GpuTensor;
5167 if !matches!(t, GpuTensor::Quant { qtype, .. } if *qtype == QT_Q4_0) {
5177 return Ok(false);
5178 }
5179 self.build_q4_rp4(t)?;
5180 self.gpu.stream().synchronize()?; let GpuTensor::Quant { bytes, rp4, rp, .. } = t else {
5182 return Ok(false);
5183 };
5184 match rp4.take() {
5185 Some(split) => {
5186 *bytes = split; *rp = true;
5188 Ok(true)
5189 }
5190 None => Ok(false),
5191 }
5192 }
5193
5194 pub fn q4rp_enabled() -> bool {
5196 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5197 *ON.get_or_init(|| {
5198 std::env::var("MEMRA_Q4RP")
5199 .map(|v| v != "0")
5200 .unwrap_or(true)
5201 })
5202 }
5203
5204 pub fn copy_rows_strided(
5207 &self,
5208 src: &CudaSlice<f32>,
5209 dst: &mut CudaSlice<f32>,
5210 row_elems: usize,
5211 n_rows: usize,
5212 src_stride: usize,
5213 src_off: usize,
5214 ) -> Result<(), Box<dyn std::error::Error>> {
5215 let f = self.func("copy_rows_strided_f32");
5216 let cfg = LaunchConfig {
5217 grid_dim: (((row_elems as u32 + 255) / 256).max(1), n_rows as u32, 1),
5218 block_dim: (256, 1, 1),
5219 shared_mem_bytes: 0,
5220 };
5221 let (re, nr) = (row_elems as i32, n_rows as i32);
5222 let (st, off) = (src_stride as i64, src_off as i64);
5223 let __s_b = self.gpu.stream();
5224 let mut b = __s_b.launch_builder(&f);
5225 b.arg(src)
5226 .arg(&mut *dst)
5227 .arg(&re)
5228 .arg(&nr)
5229 .arg(&st)
5230 .arg(&off);
5231 unsafe {
5232 b.launch(cfg)?;
5233 }
5234 Ok(())
5235 }
5236
5237 pub fn u32_set_k(
5239 &self,
5240 dst: &mut CudaSlice<u32>,
5241 v: u32,
5242 idx: usize,
5243 ) -> Result<(), Box<dyn std::error::Error>> {
5244 let f = self.func("u32_set_k");
5245 let cfg = LaunchConfig {
5246 grid_dim: (1, 1, 1),
5247 block_dim: (1, 1, 1),
5248 shared_mem_bytes: 0,
5249 };
5250 let ii = idx as i32;
5251 let __s_b = self.gpu.stream();
5252 let mut b = __s_b.launch_builder(&f);
5253 b.arg(dst).arg(&v).arg(&ii);
5254 unsafe {
5255 b.launch(cfg)?;
5256 }
5257 Ok(())
5258 }
5259
5260 pub fn i32_add_k(
5262 &self,
5263 d: &mut CudaSlice<i32>,
5264 v: i32,
5265 ) -> Result<(), Box<dyn std::error::Error>> {
5266 let f = self.func("i32_add_k");
5267 let cfg = LaunchConfig {
5268 grid_dim: (1, 1, 1),
5269 block_dim: (32, 1, 1),
5270 shared_mem_bytes: 0,
5271 };
5272 let __s_b = self.gpu.stream();
5273 let mut b = __s_b.launch_builder(&f);
5274 b.arg(d).arg(&v);
5275 unsafe {
5276 b.launch(cfg)?;
5277 }
5278 Ok(())
5279 }
5280
5281 pub fn i32_iota_from(
5283 &self,
5284 ctr: &CudaSlice<i32>,
5285 dst: &mut CudaSlice<i32>,
5286 n: usize,
5287 ) -> Result<(), Box<dyn std::error::Error>> {
5288 let f = self.func("i32_iota_from");
5289 let cfg = LaunchConfig::for_num_elems(n as u32);
5290 let ni = n as i32;
5291 let __s_b = self.gpu.stream();
5292 let mut b = __s_b.launch_builder(&f);
5293 b.arg(ctr).arg(dst).arg(&ni);
5294 unsafe {
5295 b.launch(cfg)?;
5296 }
5297 Ok(())
5298 }
5299
5300 pub fn u32_map_k(
5302 &self,
5303 buf: &mut CudaSlice<u32>,
5304 map: &CudaSlice<u32>,
5305 idx: usize,
5306 ) -> Result<(), Box<dyn std::error::Error>> {
5307 let f = self.func("u32_map_k");
5308 let cfg = LaunchConfig {
5309 grid_dim: (1, 1, 1),
5310 block_dim: (1, 1, 1),
5311 shared_mem_bytes: 0,
5312 };
5313 let ii = idx as i32;
5314 let __s_b = self.gpu.stream();
5315 let mut b = __s_b.launch_builder(&f);
5316 b.arg(buf).arg(map).arg(&ii);
5317 unsafe {
5318 b.launch(cfg)?;
5319 }
5320 Ok(())
5321 }
5322
5323 #[allow(clippy::too_many_arguments)]
5325 pub fn u32_pack2(
5326 &self,
5327 a: &CudaSlice<u32>,
5328 off_a: usize,
5329 n1: usize,
5330 b_in: &CudaSlice<u32>,
5331 n2: usize,
5332 out: &mut CudaSlice<u32>,
5333 ) -> Result<(), Box<dyn std::error::Error>> {
5334 let f = self.func("u32_pack2");
5335 let cfg = LaunchConfig::for_num_elems((n1 + n2) as u32);
5336 let (oa, i1, i2) = (off_a as i32, n1 as i32, n2 as i32);
5337 let __s_b = self.gpu.stream();
5338 let mut b = __s_b.launch_builder(&f);
5339 b.arg(a).arg(&oa).arg(&i1).arg(b_in).arg(&i2).arg(out);
5340 unsafe {
5341 b.launch(cfg)?;
5342 }
5343 Ok(())
5344 }
5345
5346 pub fn moe_w_exscale(
5348 &self,
5349 w: &mut CudaSlice<f32>,
5350 sel: &CudaSlice<i32>,
5351 s: &CudaSlice<f32>,
5352 n: usize,
5353 ) -> Result<(), Box<dyn std::error::Error>> {
5354 let f = self.func("moe_w_exscale");
5355 let cfg = LaunchConfig::for_num_elems(n as u32);
5356 let ni = n as i32;
5357 let __s_b = self.gpu.stream();
5358 let mut b = __s_b.launch_builder(&f);
5359 b.arg(w).arg(sel).arg(s).arg(&ni);
5360 unsafe {
5361 b.launch(cfg)?;
5362 }
5363 Ok(())
5364 }
5365
5366 pub fn moe_w_scale_by_expert(
5369 &self,
5370 w: &mut CudaSlice<f32>,
5371 sel: &CudaSlice<i32>,
5372 macros: &CudaSlice<f32>,
5373 n_expert: usize,
5374 n: usize,
5375 ) -> Result<(), Box<dyn std::error::Error>> {
5376 let f = self.func("moe_w_scale_by_expert");
5377 let cfg = LaunchConfig {
5378 grid_dim: (n.div_ceil(64) as u32, 1, 1),
5379 block_dim: (64, 1, 1),
5380 shared_mem_bytes: 0,
5381 };
5382 let (ne, nn) = (n_expert as i32, n as i32);
5383 let __s_b = self.gpu.stream();
5384 let mut b = __s_b.launch_builder(&f);
5385 b.arg(w).arg(sel).arg(macros).arg(&ne).arg(&nn);
5386 unsafe {
5387 b.launch(cfg)?;
5388 }
5389 Ok(())
5390 }
5391
5392 pub fn moe_gate_up_silu8_dev_q8(
5393 &self,
5394 table: &CudaSlice<u64>,
5395 sel: &cudarc::driver::CudaView<i32>,
5396 aq: &CudaSlice<i8>,
5397 ad: &CudaSlice<f32>,
5398 in_f: usize,
5399 n_ff: usize,
5400 n_used: usize,
5401 n_expert: usize,
5402 qt_g: i32,
5403 qt_u: i32,
5404 rb_g: usize,
5405 rb_u: usize,
5406 macros: &CudaSlice<f32>,
5407 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5408 static GU: std::sync::OnceLock<(String, u32)> = std::sync::OnceLock::new();
5409 let (mode, wpb) = GU.get_or_init(|| {
5410 let mode = std::env::var("MEMRA_MOE_DEVQ8_GU").unwrap_or_default();
5411 let wpb = std::env::var("MEMRA_MOE_DEVQ8_WPB")
5412 .ok()
5413 .and_then(|v| v.parse().ok())
5414 .unwrap_or(4u32)
5415 .clamp(1, 16);
5416 (mode, wpb)
5417 });
5418 let (mode, wpb) = (mode.as_str(), *wpb);
5419 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
5420 let (inf, nff, ne, rbg, rbu) = (
5421 in_f as i32,
5422 n_ff as i32,
5423 n_expert as i32,
5424 rb_g as i64,
5425 rb_u as i64,
5426 );
5427 let (f, cfg) = match mode {
5428 "1" | "2" | "4" => {
5429 let rpw: u32 = mode.parse().unwrap();
5430 let f = self.func(match rpw {
5431 1 => "moe_gate_up_silu8_dev_q8_r1",
5432 2 => "moe_gate_up_silu8_dev_q8_r2",
5433 _ => "moe_gate_up_silu8_dev_q8_r4",
5434 });
5435 let rows_per_block = (rpw * wpb) as usize;
5436 let gx = n_ff.div_ceil(rows_per_block) as u32;
5437 (
5438 f,
5439 LaunchConfig {
5440 grid_dim: (gx, n_used as u32, 1),
5441 block_dim: (32, wpb, 1),
5442 shared_mem_bytes: 0,
5443 },
5444 )
5445 }
5446 "j8" if n_used <= 32 => (
5447 self.func("moe_gate_up_silu8_dev_q8_j8"),
5448 LaunchConfig {
5449 grid_dim: (n_ff as u32, 1, 1),
5450 block_dim: (32, n_used as u32, 1),
5451 shared_mem_bytes: 0,
5452 },
5453 ),
5454 "vsm2" => {
5456 let f = self.func("moe_gate_up_silu8_dev_q8_vsm2");
5457 let sh = (rb_g + rb_u) as u32;
5458 use cudarc::driver::sys::CUfunction_attribute_enum as A;
5459 f.set_attribute(
5460 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
5461 sh as i32,
5462 )?;
5463 (
5464 f,
5465 LaunchConfig {
5466 grid_dim: (n_ff as u32, n_used as u32, 1),
5467 block_dim: (32, 1, 1),
5468 shared_mem_bytes: sh,
5469 },
5470 )
5471 }
5472 "vsm" => {
5473 let f = self.func("moe_gate_up_silu8_dev_q8_vsm");
5474 let sh = (rb_g + rb_u) as u32;
5475 use cudarc::driver::sys::CUfunction_attribute_enum as A;
5476 f.set_attribute(
5477 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
5478 sh as i32,
5479 )?;
5480 (
5481 f,
5482 LaunchConfig {
5483 grid_dim: (n_ff as u32, n_used as u32, 1),
5484 block_dim: (32, 1, 1),
5485 shared_mem_bytes: sh,
5486 },
5487 )
5488 }
5489 "sg" => (
5490 self.func("moe_gate_up_silu8_dev_q8_sg"),
5491 LaunchConfig {
5492 grid_dim: (n_ff as u32, n_used as u32, 1),
5493 block_dim: (32, 1, 1),
5494 shared_mem_bytes: 0,
5495 },
5496 ),
5497 "j8sg" if n_used <= 32 => (
5498 self.func("moe_gate_up_silu8_dev_q8_j8sg"),
5499 LaunchConfig {
5500 grid_dim: (n_ff as u32, 1, 1),
5501 block_dim: (32, n_used as u32, 1),
5502 shared_mem_bytes: 0,
5503 },
5504 ),
5505 "u64" if in_f == 2048 => (
5506 self.func("moe_gate_up_silu8_dev_q8_u64"),
5507 LaunchConfig {
5508 grid_dim: (n_ff as u32, n_used as u32, 1),
5509 block_dim: (32, 1, 1),
5510 shared_mem_bytes: 0,
5511 },
5512 ),
5513 "gs4" if in_f == 2048 => (
5514 self.func("moe_gate_up_silu8_dev_q8_gs4"),
5515 LaunchConfig {
5516 grid_dim: (n_ff as u32, n_used as u32, 1),
5517 block_dim: (32, 4, 1),
5518 shared_mem_bytes: 0,
5519 },
5520 ),
5521 "v" | "" => (
5523 self.func("moe_gate_up_silu8_dev_q8_v"),
5524 LaunchConfig {
5525 grid_dim: (n_ff as u32, n_used as u32, 1),
5526 block_dim: (32, 1, 1),
5527 shared_mem_bytes: 0,
5528 },
5529 ),
5530 "s2" => (
5531 self.func("moe_gate_up_silu8_dev_q8_s2"),
5532 LaunchConfig {
5533 grid_dim: (n_ff as u32, n_used as u32, 1),
5534 block_dim: (32, 2, 1),
5535 shared_mem_bytes: 0,
5536 },
5537 ),
5538 "s2z" => {
5539 let rz = wpb.min(16); (
5541 self.func("moe_gate_up_silu8_dev_q8_s2z"),
5542 LaunchConfig {
5543 grid_dim: (n_ff.div_ceil(rz as usize) as u32, n_used as u32, 1),
5544 block_dim: (32, 2, rz),
5545 shared_mem_bytes: 0,
5546 },
5547 )
5548 }
5549 _ => (
5550 self.func("moe_gate_up_silu8_dev_q8"),
5551 LaunchConfig {
5552 grid_dim: (n_ff as u32, n_used as u32, 1),
5553 block_dim: (32, 1, 1),
5554 shared_mem_bytes: 0,
5555 },
5556 ),
5557 };
5558 let __s_b = self.gpu.stream();
5559 let mut b = __s_b.launch_builder(&f);
5560 b.arg(table)
5561 .arg(sel)
5562 .arg(aq)
5563 .arg(ad)
5564 .arg(&mut act)
5565 .arg(&inf)
5566 .arg(&nff)
5567 .arg(&ne)
5568 .arg(&qt_g)
5569 .arg(&qt_u)
5570 .arg(&rbg)
5571 .arg(&rbu)
5572 .arg(macros);
5573 unsafe {
5574 b.launch(cfg)?;
5575 }
5576 Ok(act)
5577 }
5578
5579 #[allow(clippy::too_many_arguments)]
5580 pub fn moe_down8_fma_dev_q8(
5581 &self,
5582 table: &CudaSlice<u64>,
5583 sel: &cudarc::driver::CudaView<i32>,
5584 w: &cudarc::driver::CudaView<f32>,
5585 aq2: &CudaSlice<i8>,
5586 ad2: &CudaSlice<f32>,
5587 dst: &mut cudarc::driver::CudaViewMut<f32>,
5588 in_f: usize,
5589 out_f: usize,
5590 n_used: usize,
5591 n_expert: usize,
5592 qt: i32,
5593 rb: usize,
5594 ) -> Result<(), Box<dyn std::error::Error>> {
5595 static DOWN: std::sync::OnceLock<String> = std::sync::OnceLock::new();
5596 let mode = DOWN.get_or_init(|| std::env::var("MEMRA_MOE_DEVQ8_DOWN").unwrap_or_default());
5597 let (inf, outf, nu, ne, rbi) = (
5598 in_f as i32,
5599 out_f as i32,
5600 n_used as i32,
5601 n_expert as i32,
5602 rb as i64,
5603 );
5604 let (f, cfg) = match mode.as_str() {
5607 m @ ("1" | "2" | "4") if n_used <= 8 => {
5608 let rpw: usize = m.parse().unwrap();
5609 let f = self.func(match rpw {
5610 1 => "moe_down8_fma_dev_q8_w8r1",
5611 2 => "moe_down8_fma_dev_q8_w8r2",
5612 _ => "moe_down8_fma_dev_q8_w8r4",
5613 });
5614 (
5615 f,
5616 LaunchConfig {
5617 grid_dim: (out_f.div_ceil(rpw) as u32, 1, 1),
5618 block_dim: (32, n_used as u32, 1),
5619 shared_mem_bytes: 0,
5620 },
5621 )
5622 }
5623 "h2" if in_f == 512 => (
5624 self.func("moe_down8_fma_dev_q8_h2"),
5625 LaunchConfig {
5626 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5627 block_dim: (32, 1, 1),
5628 shared_mem_bytes: 0,
5629 },
5630 ),
5631 "" if in_f == 704 && n_used <= 8 => (
5634 self.func("moe_down8_fma_dev_q8_w8r2"),
5635 LaunchConfig {
5636 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5637 block_dim: (32, n_used as u32, 1),
5638 shared_mem_bytes: 0,
5639 },
5640 ),
5641 "w8h2v" | "" if in_f == 512 && n_used <= 8 => (
5645 self.func("moe_down8_fma_dev_q8_w8h2v"),
5646 LaunchConfig {
5647 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5648 block_dim: (32, n_used as u32, 1),
5649 shared_mem_bytes: 0,
5650 },
5651 ),
5652 "w8h2r2v" if in_f == 512 && n_used <= 8 => (
5653 self.func("moe_down8_fma_dev_q8_w8h2r2v"),
5654 LaunchConfig {
5655 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
5656 block_dim: (32, n_used as u32, 1),
5657 shared_mem_bytes: 0,
5658 },
5659 ),
5660 "w8h2r2" if in_f == 512 && n_used <= 8 => (
5661 self.func("moe_down8_fma_dev_q8_w8h2r2"),
5662 LaunchConfig {
5663 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
5664 block_dim: (32, n_used as u32, 1),
5665 shared_mem_bytes: 0,
5666 },
5667 ),
5668 "w8h2" if in_f == 512 && n_used <= 8 => (
5669 self.func("moe_down8_fma_dev_q8_w8h2"),
5670 LaunchConfig {
5671 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5672 block_dim: (32, n_used as u32, 1),
5673 shared_mem_bytes: 0,
5674 },
5675 ),
5676 _ => (
5677 self.func("moe_down8_fma_dev_q8"),
5678 LaunchConfig {
5679 grid_dim: (out_f as u32, 1, 1),
5680 block_dim: (32, 1, 1),
5681 shared_mem_bytes: 0,
5682 },
5683 ),
5684 };
5685 let __s_b = self.gpu.stream();
5686 let mut b = __s_b.launch_builder(&f);
5687 b.arg(table)
5688 .arg(sel)
5689 .arg(w)
5690 .arg(aq2)
5691 .arg(ad2)
5692 .arg(dst)
5693 .arg(&inf)
5694 .arg(&outf)
5695 .arg(&nu)
5696 .arg(&ne)
5697 .arg(&qt)
5698 .arg(&rbi);
5699 unsafe {
5700 b.launch(cfg)?;
5701 }
5702 Ok(())
5703 }
5704
5705 #[allow(clippy::too_many_arguments)]
5712 pub fn moe_gate_up_silu8_dev_q8_rows(
5713 &self,
5714 table: &CudaSlice<u64>,
5715 sel: &CudaSlice<i32>,
5716 aq: &CudaSlice<i8>,
5717 ad: &CudaSlice<f32>,
5718 t: usize,
5719 in_f: usize,
5720 n_ff: usize,
5721 n_used: usize,
5722 n_expert: usize,
5723 qt_g: i32,
5724 qt_u: i32,
5725 rb_g: usize,
5726 rb_u: usize,
5727 macros: &CudaSlice<f32>,
5728 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5729 let f = self.func("moe_gate_up_silu8_dev_q8_v_rows");
5730 let mut act = self.alloc_uninit::<f32>(t * n_used * n_ff)?;
5731 let cfg = LaunchConfig {
5732 grid_dim: (n_ff as u32, n_used as u32, t as u32),
5733 block_dim: (32, 1, 1),
5734 shared_mem_bytes: 0,
5735 };
5736 let (inf, nff, ne, nu, rbg, rbu) = (
5737 in_f as i32,
5738 n_ff as i32,
5739 n_expert as i32,
5740 n_used as i32,
5741 rb_g as i64,
5742 rb_u as i64,
5743 );
5744 let __s_b = self.gpu.stream();
5745 let mut b = __s_b.launch_builder(&f);
5746 b.arg(table)
5747 .arg(sel)
5748 .arg(aq)
5749 .arg(ad)
5750 .arg(&mut act)
5751 .arg(&inf)
5752 .arg(&nff)
5753 .arg(&ne)
5754 .arg(&qt_g)
5755 .arg(&qt_u)
5756 .arg(&rbg)
5757 .arg(&rbu)
5758 .arg(&nu)
5759 .arg(macros);
5760 unsafe {
5761 b.launch(cfg)?;
5762 }
5763 Ok(act)
5764 }
5765
5766 #[allow(clippy::too_many_arguments)]
5771 pub fn moe_down8_fma_dev_q8_rows(
5772 &self,
5773 table: &CudaSlice<u64>,
5774 sel: &CudaSlice<i32>,
5775 w: &CudaSlice<f32>,
5776 aq2: &CudaSlice<i8>,
5777 ad2: &CudaSlice<f32>,
5778 dst: &mut CudaSlice<f32>,
5779 t: usize,
5780 in_f: usize,
5781 out_f: usize,
5782 n_used: usize,
5783 n_expert: usize,
5784 qt: i32,
5785 rb: usize,
5786 ) -> Result<(), Box<dyn std::error::Error>> {
5787 assert!(
5788 in_f == 512 && n_used <= 8,
5789 "down rows twin is w8h2v shape-gated"
5790 );
5791 let f = self.func("moe_down8_fma_dev_q8_w8h2v_rows");
5792 let cfg = LaunchConfig {
5793 grid_dim: (out_f.div_ceil(2) as u32, 1, t as u32),
5794 block_dim: (32, n_used as u32, 1),
5795 shared_mem_bytes: 0,
5796 };
5797 let (inf, outf, nu, ne, rbi) = (
5798 in_f as i32,
5799 out_f as i32,
5800 n_used as i32,
5801 n_expert as i32,
5802 rb as i64,
5803 );
5804 let __s_b = self.gpu.stream();
5805 let mut b = __s_b.launch_builder(&f);
5806 b.arg(table)
5807 .arg(sel)
5808 .arg(w)
5809 .arg(aq2)
5810 .arg(ad2)
5811 .arg(dst)
5812 .arg(&inf)
5813 .arg(&outf)
5814 .arg(&nu)
5815 .arg(&ne)
5816 .arg(&qt)
5817 .arg(&rbi);
5818 unsafe {
5819 b.launch(cfg)?;
5820 }
5821 Ok(())
5822 }
5823
5824 #[allow(clippy::too_many_arguments)]
5828 pub fn moe_gate_up_silu8_dev_q8_csr(
5829 &self,
5830 table: &CudaSlice<u64>,
5831 sel: &CudaSlice<i32>,
5832 aq: &CudaSlice<i8>,
5833 ad: &CudaSlice<f32>,
5834 n_pairs: usize,
5835 in_f: usize,
5836 n_ff: usize,
5837 n_used: usize,
5838 n_expert: usize,
5839 qt_g: i32,
5840 qt_u: i32,
5841 rb_g: usize,
5842 rb_u: usize,
5843 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5844 let f = self.func("moe_gate_up_silu8_dev_q8_csr_iq4");
5845 let mut act = self.alloc_uninit::<f32>(n_pairs * n_ff)?;
5846 let cfg = LaunchConfig {
5847 grid_dim: (n_ff as u32, n_pairs as u32, 1),
5848 block_dim: (32, 1, 1),
5849 shared_mem_bytes: 0,
5850 };
5851 let (inf, nff, ne, nu, npi, rbg, rbu) = (
5852 in_f as i32,
5853 n_ff as i32,
5854 n_expert as i32,
5855 n_used as i32,
5856 n_pairs as i32,
5857 rb_g as i64,
5858 rb_u as i64,
5859 );
5860 let __s_b = self.gpu.stream();
5861 let mut b = __s_b.launch_builder(&f);
5862 b.arg(table)
5863 .arg(sel)
5864 .arg(aq)
5865 .arg(ad)
5866 .arg(&mut act)
5867 .arg(&inf)
5868 .arg(&nff)
5869 .arg(&ne)
5870 .arg(&qt_g)
5871 .arg(&qt_u)
5872 .arg(&rbg)
5873 .arg(&rbu)
5874 .arg(&nu)
5875 .arg(&npi);
5876 unsafe {
5877 b.launch(cfg)?;
5878 }
5879 Ok(act)
5880 }
5881
5882 #[allow(clippy::too_many_arguments)]
5886 pub fn moe_down8_fma_dev_q8_variant(
5887 &self,
5888 variant: &str,
5889 table: &CudaSlice<u64>,
5890 sel: &cudarc::driver::CudaView<i32>,
5891 w: &cudarc::driver::CudaView<f32>,
5892 aq2: &CudaSlice<i8>,
5893 ad2: &CudaSlice<f32>,
5894 dst: &mut cudarc::driver::CudaViewMut<f32>,
5895 in_f: usize,
5896 out_f: usize,
5897 n_used: usize,
5898 n_expert: usize,
5899 qt: i32,
5900 rb: usize,
5901 ) -> Result<(), Box<dyn std::error::Error>> {
5902 let (inf, outf, nu, ne, rbi) = (
5903 in_f as i32,
5904 out_f as i32,
5905 n_used as i32,
5906 n_expert as i32,
5907 rb as i64,
5908 );
5909 let (f, cfg) = match variant {
5910 "w8h2" | "w8h2v" => (
5911 self.func(if variant == "w8h2" {
5912 "moe_down8_fma_dev_q8_w8h2"
5913 } else {
5914 "moe_down8_fma_dev_q8_w8h2v"
5915 }),
5916 LaunchConfig {
5917 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5918 block_dim: (32, n_used as u32, 1),
5919 shared_mem_bytes: 0,
5920 },
5921 ),
5922 "w8h2r2" | "w8h2r2v" => (
5923 self.func(if variant == "w8h2r2" {
5924 "moe_down8_fma_dev_q8_w8h2r2"
5925 } else {
5926 "moe_down8_fma_dev_q8_w8h2r2v"
5927 }),
5928 LaunchConfig {
5929 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
5930 block_dim: (32, n_used as u32, 1),
5931 shared_mem_bytes: 0,
5932 },
5933 ),
5934 _ => (
5935 self.func("moe_down8_fma_dev_q8"),
5936 LaunchConfig {
5937 grid_dim: (out_f as u32, 1, 1),
5938 block_dim: (32, 1, 1),
5939 shared_mem_bytes: 0,
5940 },
5941 ),
5942 };
5943 let __s_b = self.gpu.stream();
5944 let mut b = __s_b.launch_builder(&f);
5945 b.arg(table)
5946 .arg(sel)
5947 .arg(w)
5948 .arg(aq2)
5949 .arg(ad2)
5950 .arg(dst)
5951 .arg(&inf)
5952 .arg(&outf)
5953 .arg(&nu)
5954 .arg(&ne)
5955 .arg(&qt)
5956 .arg(&rbi);
5957 unsafe {
5958 b.launch(cfg)?;
5959 }
5960 Ok(())
5961 }
5962
5963 #[allow(clippy::too_many_arguments)]
5965 pub fn moe_gate_up_silu8_dev_q8_variant(
5966 &self,
5967 variant: &str,
5968 table: &CudaSlice<u64>,
5969 sel: &cudarc::driver::CudaView<i32>,
5970 aq: &CudaSlice<i8>,
5971 ad: &CudaSlice<f32>,
5972 in_f: usize,
5973 n_ff: usize,
5974 n_used: usize,
5975 n_expert: usize,
5976 qt_g: i32,
5977 qt_u: i32,
5978 rb_g: usize,
5979 rb_u: usize,
5980 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5981 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
5982 let (inf, nff, ne, rbg, rbu) = (
5983 in_f as i32,
5984 n_ff as i32,
5985 n_expert as i32,
5986 rb_g as i64,
5987 rb_u as i64,
5988 );
5989 let f = self.func(if variant == "v" {
5990 "moe_gate_up_silu8_dev_q8_v"
5991 } else {
5992 "moe_gate_up_silu8_dev_q8"
5993 });
5994 let cfg = LaunchConfig {
5995 grid_dim: (n_ff as u32, n_used as u32, 1),
5996 block_dim: (32, 1, 1),
5997 shared_mem_bytes: 0,
5998 };
5999 let __s_b = self.gpu.stream();
6000 let mut b = __s_b.launch_builder(&f);
6001 b.arg(table)
6002 .arg(sel)
6003 .arg(aq)
6004 .arg(ad)
6005 .arg(&mut act)
6006 .arg(&inf)
6007 .arg(&nff)
6008 .arg(&ne)
6009 .arg(&qt_g)
6010 .arg(&qt_u)
6011 .arg(&rbg)
6012 .arg(&rbu);
6013 unsafe {
6014 b.launch(cfg)?;
6015 }
6016 Ok(act)
6017 }
6018
6019 pub fn moe_gate_up_silu8_dev(
6020 &self,
6021 table: &CudaSlice<u64>,
6022 sel: &cudarc::driver::CudaView<i32>,
6023 x: &cudarc::driver::CudaView<f32>,
6024 in_f: usize,
6025 n_ff: usize,
6026 n_used: usize,
6027 n_expert: usize,
6028 qt_g: i32,
6029 qt_u: i32,
6030 rb_g: usize,
6031 rb_u: usize,
6032 macros: &CudaSlice<f32>,
6033 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6034 let f = self.func("moe_gate_up_silu8_dev");
6035 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?; let cfg = LaunchConfig {
6037 grid_dim: (n_ff as u32, n_used as u32, 1),
6038 block_dim: (256, 1, 1),
6039 shared_mem_bytes: 0,
6040 };
6041 let (inf, nff, ne, rbg, rbu) = (
6042 in_f as i32,
6043 n_ff as i32,
6044 n_expert as i32,
6045 rb_g as i64,
6046 rb_u as i64,
6047 );
6048 let __s_b = self.gpu.stream();
6049 let mut b = __s_b.launch_builder(&f);
6050 b.arg(table)
6051 .arg(sel)
6052 .arg(x)
6053 .arg(&mut act)
6054 .arg(&inf)
6055 .arg(&nff)
6056 .arg(&ne)
6057 .arg(&qt_g)
6058 .arg(&qt_u)
6059 .arg(&rbg)
6060 .arg(&rbu)
6061 .arg(macros);
6062 unsafe {
6063 b.launch(cfg)?;
6064 }
6065 Ok(act)
6066 }
6067
6068 #[allow(clippy::too_many_arguments)]
6071 pub fn moe_down8_fma_dev(
6072 &self,
6073 table: &CudaSlice<u64>,
6074 sel: &cudarc::driver::CudaView<i32>,
6075 w: &cudarc::driver::CudaView<f32>,
6076 act: &CudaSlice<f32>,
6077 dst: &mut cudarc::driver::CudaViewMut<f32>,
6078 in_f: usize,
6079 out_f: usize,
6080 n_used: usize,
6081 n_expert: usize,
6082 qt: i32,
6083 rb: usize,
6084 ) -> Result<(), Box<dyn std::error::Error>> {
6085 let f = self.func("moe_down8_fma_dev");
6086 let cfg = LaunchConfig {
6087 grid_dim: (out_f as u32, 1, 1),
6088 block_dim: (256, 1, 1),
6089 shared_mem_bytes: 0,
6090 };
6091 let (inf, outf, nu, ne, rbv) = (
6092 in_f as i32,
6093 out_f as i32,
6094 n_used as i32,
6095 n_expert as i32,
6096 rb as i64,
6097 );
6098 let __s_b = self.gpu.stream();
6099 let mut b = __s_b.launch_builder(&f);
6100 b.arg(table)
6101 .arg(sel)
6102 .arg(w)
6103 .arg(act)
6104 .arg(dst)
6105 .arg(&inf)
6106 .arg(&outf)
6107 .arg(&nu)
6108 .arg(&ne)
6109 .arg(&qt)
6110 .arg(&rbv);
6111 unsafe {
6112 b.launch(cfg)?;
6113 }
6114 Ok(())
6115 }
6116
6117 pub fn axpy_into(
6119 &self,
6120 src: &CudaSlice<f32>,
6121 alpha: f32,
6122 dst: &mut cudarc::driver::CudaViewMut<f32>,
6123 n: usize,
6124 ) -> Result<(), Box<dyn std::error::Error>> {
6125 let f = self.func("axpy_f32");
6126 let cfg = LaunchConfig::for_num_elems(n as u32);
6127 let (a, ni) = (alpha, n as i32);
6128 let __s_b = self.gpu.stream();
6129 let mut b = __s_b.launch_builder(&f);
6130 b.arg(src).arg(dst).arg(&a).arg(&ni);
6131 unsafe {
6132 b.launch(cfg)?;
6133 }
6134 Ok(())
6135 }
6136
6137 pub fn add_scaled_rows(
6139 &self,
6140 src: &CudaSlice<f32>,
6141 scale: &CudaSlice<f32>,
6142 dst: &mut CudaSlice<f32>,
6143 ncols: usize,
6144 nrows: usize,
6145 ) -> Result<(), Box<dyn std::error::Error>> {
6146 let f = self.func("add_scaled_rows_f32");
6147 let cfg = LaunchConfig::for_num_elems((ncols * nrows) as u32);
6148 let (nc, nr) = (ncols as i32, nrows as i32);
6149 let __s_b = self.gpu.stream();
6150 let mut b = __s_b.launch_builder(&f);
6151 b.arg(src).arg(scale).arg(dst).arg(&nc).arg(&nr);
6152 unsafe {
6153 b.launch(cfg)?;
6154 }
6155 Ok(())
6156 }
6157
6158 pub fn gather_rows(
6162 &self,
6163 src: &CudaSlice<f32>,
6164 idx: &CudaSlice<i32>,
6165 dst: &mut CudaSlice<f32>,
6166 ncols: usize,
6167 m_e: usize,
6168 ) -> Result<(), Box<dyn std::error::Error>> {
6169 let f = self.func("gather_rows_f32");
6170 let cfg = LaunchConfig::for_num_elems((m_e * ncols) as u32);
6171 let (nc, me) = (ncols as i32, m_e as i32);
6172 let __s_b = self.gpu.stream();
6173 let mut b = __s_b.launch_builder(&f);
6174 b.arg(src).arg(idx).arg(dst).arg(&nc).arg(&me);
6175 unsafe {
6176 b.launch(cfg)?;
6177 }
6178 Ok(())
6179 }
6180
6181 pub fn scatter_slot(
6186 &self,
6187 src: &CudaSlice<f32>,
6188 tok_idx: &CudaSlice<i32>,
6189 slot_idx: &CudaSlice<i32>,
6190 weight: &CudaSlice<f32>,
6191 dst: &mut CudaSlice<f32>,
6192 wbuf: &mut CudaSlice<f32>,
6193 ncols: usize,
6194 n_used: usize,
6195 m_e: usize,
6196 ) -> Result<(), Box<dyn std::error::Error>> {
6197 let f = self.func("scatter_add_slot_f32");
6198 let cfg = LaunchConfig::for_num_elems((m_e * ncols) as u32);
6199 let (nc, nu, me) = (ncols as i32, n_used as i32, m_e as i32);
6200 let __s_b = self.gpu.stream();
6201 let mut b = __s_b.launch_builder(&f);
6202 b.arg(src)
6203 .arg(tok_idx)
6204 .arg(slot_idx)
6205 .arg(weight)
6206 .arg(dst)
6207 .arg(wbuf)
6208 .arg(&nc)
6209 .arg(&nu)
6210 .arg(&me);
6211 unsafe {
6212 b.launch(cfg)?;
6213 }
6214 Ok(())
6215 }
6216
6217 pub fn reduce_slots(
6221 &self,
6222 slots: &CudaSlice<f32>,
6223 wbuf: &CudaSlice<f32>,
6224 dst: &mut CudaSlice<f32>,
6225 ncols: usize,
6226 n_used: usize,
6227 t: usize,
6228 ) -> Result<(), Box<dyn std::error::Error>> {
6229 let f = self.func("reduce_slots_f32");
6230 let cfg = LaunchConfig::for_num_elems((t * ncols) as u32);
6231 let (nc, nu, ti) = (ncols as i32, n_used as i32, t as i32);
6232 let __s_b = self.gpu.stream();
6233 let mut b = __s_b.launch_builder(&f);
6234 b.arg(slots).arg(wbuf).arg(dst).arg(&nc).arg(&nu).arg(&ti);
6235 unsafe {
6236 b.launch(cfg)?;
6237 }
6238 Ok(())
6239 }
6240
6241 pub fn quantize_q8_1_view(
6248 &self,
6249 x: &cudarc::driver::CudaView<f32>,
6250 m: usize,
6251 in_f: usize,
6252 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
6253 let f = self.func("quantize_q8_1");
6254 let nblk = in_f / 32;
6255 let mut q = self.alloc_uninit::<i8>(m * in_f)?;
6256 let mut d = self.alloc_uninit::<f32>(m * nblk)?;
6257 let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
6258 let (inf, mi) = (in_f as i32, m as i32);
6259 let __s_b = self.gpu.stream();
6260 let mut b = __s_b.launch_builder(&f);
6261 b.arg(x).arg(&mut q).arg(&mut d).arg(&inf).arg(&mi);
6262 unsafe {
6263 b.launch(cfg)?;
6264 }
6265 Ok((q, d))
6266 }
6267
6268 pub fn quantize_q8_1(
6269 &self,
6270 x: &CudaSlice<f32>,
6271 m: usize,
6272 in_f: usize,
6273 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
6274 let nblk = in_f / 32;
6275 let mut q = self.alloc_uninit::<i8>(m * in_f)?; let mut d = self.alloc_uninit::<f32>(m * nblk)?; let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
6279 let (inf, mi) = (in_f as i32, m as i32);
6280 if Self::pdl_on() && Self::pdl_wb_on() {
6281 {
6282 use cudarc::driver::{DevicePtr, DevicePtrMut};
6283 let s = &self.gpu.stream();
6284 let (px, _g0) = x.device_ptr(s);
6285 let (pq, _g1) = q.device_ptr_mut(s);
6286 let (pd, _g2) = d.device_ptr_mut(s);
6287 let mut ps = [
6288 &px as *const _ as *mut std::ffi::c_void,
6289 &pq as *const _ as *mut _,
6290 &pd as *const _ as *mut _,
6291 &inf as *const _ as *mut _,
6292 &mi as *const _ as *mut _,
6293 ];
6294 unsafe {
6295 self.launch_pdl("quantize_q8_1", cfg.grid_dim, cfg.block_dim, &mut ps)?;
6296 }
6297 }
6298 return Ok((q, d));
6299 }
6300 let f = self.func("quantize_q8_1");
6301 let __s_b = self.gpu.stream();
6302 let mut b = __s_b.launch_builder(&f);
6303 b.arg(x).arg(&mut q).arg(&mut d).arg(&inf).arg(&mi);
6304 unsafe {
6305 b.launch(cfg)?;
6306 }
6307 Ok((q, d))
6308 }
6309
6310 pub fn quantize_fp4_act(
6314 &self,
6315 x: &CudaSlice<f32>,
6316 m: usize,
6317 in_f: usize,
6318 ) -> Result<(CudaSlice<u32>, CudaSlice<u8>), Box<dyn std::error::Error>> {
6319 let f = self.func("quantize_fp4_act");
6320 let nb16 = in_f / 16;
6321 let mut aq4 = self.alloc_uninit::<u32>(m * (in_f / 8))?; let mut ad4 = self.alloc_uninit::<u8>(m * nb16)?; let cfg = LaunchConfig::for_num_elems((m * nb16) as u32);
6324 let (inf, mi) = (in_f as i32, m as i32);
6325 let __s_b = self.gpu.stream();
6326 let mut b = __s_b.launch_builder(&f);
6327 b.arg(x).arg(&mut aq4).arg(&mut ad4).arg(&inf).arg(&mi);
6328 unsafe {
6329 b.launch(cfg)?;
6330 }
6331 Ok((aq4, ad4))
6332 }
6333
6334 pub fn qmatvec_gemm_nvfp4_fp4(
6339 &self,
6340 bytes: &CudaSlice<u8>,
6341 x: &CudaSlice<f32>,
6342 m: usize,
6343 in_f: usize,
6344 out_f: usize,
6345 row_bytes: usize,
6346 scale: f32,
6347 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6348 assert!(
6349 in_f % 64 == 0,
6350 "FP4 GEMM requires in_f % 64 == 0, got {in_f}"
6351 );
6352 let (aq4, ad4) = self.quantize_fp4_act(x, m, in_f)?;
6353 let mut y = self.fp4_gemm_launch(bytes, &aq4, &ad4, m, in_f, out_f, row_bytes)?;
6354 if scale != 1.0 {
6355 self.scale_inplace(&mut y, scale, m * out_f)?;
6356 }
6357 Ok(y)
6358 }
6359
6360 fn fp4_gemm_launch(
6363 &self,
6364 bytes: &CudaSlice<u8>,
6365 aq4: &CudaSlice<u32>,
6366 ad4: &CudaSlice<u8>,
6367 m: usize,
6368 in_f: usize,
6369 out_f: usize,
6370 row_bytes: usize,
6371 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6372 let f = self.func("qmatvec_gemm_nvfp4_fp4");
6373 let mut y = self.alloc_uninit::<f32>(m * out_f)?; const BM: u32 = 64;
6375 const BN: u32 = 256;
6376 let cfg = LaunchConfig {
6377 grid_dim: ((out_f as u32 + BM - 1) / BM, (m as u32 + BN - 1) / BN, 1),
6378 block_dim: (32, 4, 1),
6379 shared_mem_bytes: 0,
6380 };
6381 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6382 let __s_b = self.gpu.stream();
6383 let mut b = __s_b.launch_builder(&f);
6384 b.arg(bytes)
6385 .arg(aq4)
6386 .arg(ad4)
6387 .arg(&mut y)
6388 .arg(&inf)
6389 .arg(&outf)
6390 .arg(&mi)
6391 .arg(&rb);
6392 unsafe {
6393 b.launch(cfg)?;
6394 }
6395 Ok(y)
6396 }
6397
6398 pub fn qmatvec_gemm_nvfp4_fp4_raw(
6400 &self,
6401 bytes: &CudaSlice<u8>,
6402 x: &CudaSlice<f32>,
6403 m: usize,
6404 in_f: usize,
6405 out_f: usize,
6406 row_bytes: usize,
6407 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6408 assert!(
6409 in_f % 64 == 0,
6410 "FP4 GEMM requires in_f % 64 == 0, got {in_f}"
6411 );
6412 let (aq4, ad4) = self.quantize_fp4_act(x, m, in_f)?;
6413 self.fp4_gemm_launch(bytes, &aq4, &ad4, m, in_f, out_f, row_bytes)
6414 }
6415
6416 pub fn qmatvec_q8_0_fast(
6418 &self,
6419 w: &CudaSlice<u8>,
6420 x: &CudaSlice<f32>,
6421 m: usize,
6422 in_f: usize,
6423 out_f: usize,
6424 row_bytes: usize,
6425 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6426 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6427 let f = self.func("qmatvec_q8_0_dp4a");
6428 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6430 grid_dim: (out_f as u32, m as u32, 1),
6431 block_dim: (128, 1, 1),
6432 shared_mem_bytes: 0,
6433 };
6434 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6435 let __s_b = self.gpu.stream();
6436 let mut b = __s_b.launch_builder(&f);
6437 b.arg(w)
6438 .arg(&aq)
6439 .arg(&ad)
6440 .arg(&mut y)
6441 .arg(&inf)
6442 .arg(&outf)
6443 .arg(&mi)
6444 .arg(&rb);
6445 unsafe {
6446 b.launch(cfg)?;
6447 }
6448 Ok(y)
6449 }
6450
6451 #[allow(non_snake_case)] pub fn qmatvec_q4_K_fast(
6454 &self,
6455 w: &CudaSlice<u8>,
6456 x: &CudaSlice<f32>,
6457 m: usize,
6458 in_f: usize,
6459 out_f: usize,
6460 row_bytes: usize,
6461 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6462 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6463 let f = self.func("qmatvec_q4_K_dp4a");
6464 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6466 grid_dim: (out_f as u32, m as u32, 1),
6467 block_dim: (128, 1, 1),
6468 shared_mem_bytes: 0,
6469 };
6470 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6471 let __s_b = self.gpu.stream();
6472 let mut b = __s_b.launch_builder(&f);
6473 b.arg(w)
6474 .arg(&aq)
6475 .arg(&ad)
6476 .arg(&mut y)
6477 .arg(&inf)
6478 .arg(&outf)
6479 .arg(&mi)
6480 .arg(&rb);
6481 unsafe {
6482 b.launch(cfg)?;
6483 }
6484 Ok(y)
6485 }
6486
6487 #[allow(non_snake_case)] pub fn qmatvec_q6_K_fast(
6490 &self,
6491 w: &CudaSlice<u8>,
6492 x: &CudaSlice<f32>,
6493 m: usize,
6494 in_f: usize,
6495 out_f: usize,
6496 row_bytes: usize,
6497 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6498 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6499 let f = self.func("qmatvec_q6_K_dp4a");
6500 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6502 grid_dim: (out_f as u32, m as u32, 1),
6503 block_dim: (128, 1, 1),
6504 shared_mem_bytes: 0,
6505 };
6506 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6507 let __s_b = self.gpu.stream();
6508 let mut b = __s_b.launch_builder(&f);
6509 b.arg(w)
6510 .arg(&aq)
6511 .arg(&ad)
6512 .arg(&mut y)
6513 .arg(&inf)
6514 .arg(&outf)
6515 .arg(&mi)
6516 .arg(&rb);
6517 unsafe {
6518 b.launch(cfg)?;
6519 }
6520 Ok(y)
6521 }
6522
6523 #[allow(non_snake_case)] pub fn qmatvec_q5_K_fast(
6526 &self,
6527 w: &CudaSlice<u8>,
6528 x: &CudaSlice<f32>,
6529 m: usize,
6530 in_f: usize,
6531 out_f: usize,
6532 row_bytes: usize,
6533 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6534 self.qmatvec_dp4a_named("qmatvec_q5_K_dp4a", w, x, m, in_f, out_f, row_bytes)
6535 }
6536 #[allow(non_snake_case)] pub fn qmatvec_q3_K_fast(
6539 &self,
6540 w: &CudaSlice<u8>,
6541 x: &CudaSlice<f32>,
6542 m: usize,
6543 in_f: usize,
6544 out_f: usize,
6545 row_bytes: usize,
6546 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6547 self.qmatvec_dp4a_named("qmatvec_q3_K_dp4a", w, x, m, in_f, out_f, row_bytes)
6548 }
6549 pub fn qmatvec_nvfp4_fast_rp(
6551 &self,
6552 w: &CudaSlice<u8>,
6553 x: &CudaSlice<f32>,
6554 m: usize,
6555 in_f: usize,
6556 out_f: usize,
6557 row_bytes: usize,
6558 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6559 assert!(
6560 in_f % 64 == 0,
6561 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
6562 );
6563 self.qmatvec_dp4a_named("qmatvec_nvfp4_dp4a_rp", w, x, m, in_f, out_f, row_bytes)
6564 }
6565 pub fn qmatvec_nvfp4_fast(
6567 &self,
6568 w: &CudaSlice<u8>,
6569 x: &CudaSlice<f32>,
6570 m: usize,
6571 in_f: usize,
6572 out_f: usize,
6573 row_bytes: usize,
6574 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6575 assert!(
6578 in_f % 64 == 0,
6579 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
6580 );
6581 self.qmatvec_dp4a_named("qmatvec_nvfp4_dp4a", w, x, m, in_f, out_f, row_bytes)
6582 }
6583 #[allow(non_snake_case)] pub fn qmatvec_iq4_XS_fast(
6586 &self,
6587 w: &CudaSlice<u8>,
6588 x: &CudaSlice<f32>,
6589 m: usize,
6590 in_f: usize,
6591 out_f: usize,
6592 row_bytes: usize,
6593 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6594 self.qmatvec_dp4a_named("qmatvec_iq4_XS_dp4a", w, x, m, in_f, out_f, row_bytes)
6595 }
6596
6597 fn qmatvec_dp4a_named(
6599 &self,
6600 name: &str,
6601 w: &CudaSlice<u8>,
6602 x: &CudaSlice<f32>,
6603 m: usize,
6604 in_f: usize,
6605 out_f: usize,
6606 row_bytes: usize,
6607 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6608 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6609 let f = self.func(name);
6610 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6612 grid_dim: (out_f as u32, m as u32, 1),
6613 block_dim: (128, 1, 1),
6614 shared_mem_bytes: 0,
6615 };
6616 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6617 let __s_b = self.gpu.stream();
6618 let mut b = __s_b.launch_builder(&f);
6619 b.arg(w)
6620 .arg(&aq)
6621 .arg(&ad)
6622 .arg(&mut y)
6623 .arg(&inf)
6624 .arg(&outf)
6625 .arg(&mi)
6626 .arg(&rb);
6627 unsafe {
6628 b.launch(cfg)?;
6629 }
6630 Ok(y)
6631 }
6632
6633 pub fn htod(&self, v: &[f32]) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6634 Ok(self.gpu.stream().clone_htod(v)?)
6635 }
6636 pub fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
6637 Ok(self.gpu.stream().clone_htod(v)?)
6638 }
6639 pub fn htod_i8(&self, v: &[i8]) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
6641 Ok(self.gpu.stream().clone_htod(v)?)
6642 }
6643 pub fn htod_u64(&self, v: &[u64]) -> Result<CudaSlice<u64>, Box<dyn std::error::Error>> {
6644 Ok(self.gpu.stream().clone_htod(v)?)
6645 }
6646 pub fn dtoh_view(
6648 &self,
6649 d: &cudarc::driver::CudaView<f32>,
6650 ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6651 let v = self.gpu.stream().clone_dtoh(d)?;
6652 self.gpu.stream().synchronize()?;
6653 Ok(v)
6654 }
6655 pub fn dtoh(&self, d: &CudaSlice<f32>) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6656 let v = self.gpu.stream().clone_dtoh(d)?;
6657 self.gpu.stream().synchronize()?;
6658 Ok(v)
6659 }
6660 pub fn dtoh_pair(
6664 &self,
6665 a: &CudaSlice<f32>,
6666 b: &CudaSlice<f32>,
6667 ) -> Result<(Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
6668 let av = self.gpu.stream().clone_dtoh(a)?;
6669 let bv = self.gpu.stream().clone_dtoh(b)?;
6670 self.gpu.stream().synchronize()?;
6671 Ok((av, bv))
6672 }
6673 pub fn dtoh_i32(&self, d: &CudaSlice<i32>) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
6675 let v = self.gpu.stream().clone_dtoh(d)?;
6676 self.gpu.stream().synchronize()?;
6677 Ok(v)
6678 }
6679 pub fn dtoh_u8(&self, d: &CudaSlice<u8>) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
6681 let v = self.gpu.stream().clone_dtoh(d)?;
6682 self.gpu.stream().synchronize()?;
6683 Ok(v)
6684 }
6685 pub fn dtoh_u8_view(
6686 &self,
6687 d: &cudarc::driver::CudaView<u8>,
6688 ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
6689 let v = self.gpu.stream().clone_dtoh(d)?;
6690 self.gpu.stream().synchronize()?;
6691 Ok(v)
6692 }
6693 pub fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6694 let s = self.gpu.stream().alloc_zeros::<f32>(n)?;
6695 self.keep_if_capturing(&s);
6696 Ok(s)
6697 }
6698
6699 pub fn prob_of_token_device(
6708 &self,
6709 logits: &CudaSlice<f32>,
6710 tok: &CudaSlice<u32>,
6711 n_vocab: usize,
6712 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6713 let nb = ARGMAX_NB;
6714 let mut part = self.alloc_uninit::<f32>(nb)?;
6715 let mut p = self.alloc_uninit::<f32>(1)?;
6716 let f1 = self.func("prob_of_token_partial_f32");
6717 let cfg1 = LaunchConfig {
6718 grid_dim: (nb as u32, 1, 1),
6719 block_dim: (256, 1, 1),
6720 shared_mem_bytes: 0,
6721 };
6722 let nv = n_vocab as i32;
6723 let __s_b1 = self.gpu.stream();
6724 let mut b1 = __s_b1.launch_builder(&f1);
6725 b1.arg(logits).arg(tok).arg(&mut part).arg(&nv);
6726 unsafe {
6727 b1.launch(cfg1)?;
6728 }
6729 let f2 = self.func("prob_of_token_final_f32");
6730 let cfg2 = LaunchConfig {
6731 grid_dim: (1, 1, 1),
6732 block_dim: (256, 1, 1),
6733 shared_mem_bytes: 0,
6734 };
6735 let nbi = nb as i32;
6736 let __s_b2 = self.gpu.stream();
6737 let mut b2 = __s_b2.launch_builder(&f2);
6738 b2.arg(&part).arg(&mut p).arg(&nbi);
6739 unsafe {
6740 b2.launch(cfg2)?;
6741 }
6742 Ok(p)
6743 }
6744
6745 pub fn prob_of_token_device_col(
6752 &self,
6753 logits: &CudaSlice<f32>,
6754 tok_all: &CudaSlice<u32>,
6755 tok_idx: usize,
6756 p_out: &mut CudaSlice<f32>,
6757 p_idx: usize,
6758 n_vocab: usize,
6759 ) -> Result<(), Box<dyn std::error::Error>> {
6760 let tok_v = tok_all.slice(tok_idx..tok_idx + 1);
6761 let mut p_v = p_out.slice_mut(p_idx..p_idx + 1);
6762 let nb = ARGMAX_NB;
6763 let mut part = self.alloc_uninit::<f32>(nb)?;
6764 let f1 = self.func("prob_of_token_partial_f32");
6765 let cfg1 = LaunchConfig {
6766 grid_dim: (nb as u32, 1, 1),
6767 block_dim: (256, 1, 1),
6768 shared_mem_bytes: 0,
6769 };
6770 let nv = n_vocab as i32;
6771 let __s_b1 = self.gpu.stream();
6772 let mut b1 = __s_b1.launch_builder(&f1);
6773 b1.arg(logits).arg(&tok_v).arg(&mut part).arg(&nv);
6774 unsafe {
6775 b1.launch(cfg1)?;
6776 }
6777 let f2 = self.func("prob_of_token_final_f32");
6778 let cfg2 = LaunchConfig {
6779 grid_dim: (1, 1, 1),
6780 block_dim: (256, 1, 1),
6781 shared_mem_bytes: 0,
6782 };
6783 let nbi = nb as i32;
6784 let __s_b2 = self.gpu.stream();
6785 let mut b2 = __s_b2.launch_builder(&f2);
6786 b2.arg(&part).arg(&mut p_v).arg(&nbi);
6787 unsafe {
6788 b2.launch(cfg2)?;
6789 }
6790 Ok(())
6791 }
6792
6793 pub fn prob_of_token_device_into(
6794 &self,
6795 logits: &CudaSlice<f32>,
6796 tok: &CudaSlice<u32>,
6797 p_out: &mut CudaSlice<f32>,
6798 n_vocab: usize,
6799 ) -> Result<(), Box<dyn std::error::Error>> {
6800 let nb = ARGMAX_NB;
6801 let mut part = self.alloc_uninit::<f32>(nb)?;
6802 let f1 = self.func("prob_of_token_partial_f32");
6803 let cfg1 = LaunchConfig {
6804 grid_dim: (nb as u32, 1, 1),
6805 block_dim: (256, 1, 1),
6806 shared_mem_bytes: 0,
6807 };
6808 let nv = n_vocab as i32;
6809 let __s_b1 = self.gpu.stream();
6810 let mut b1 = __s_b1.launch_builder(&f1);
6811 b1.arg(logits).arg(tok).arg(&mut part).arg(&nv);
6812 unsafe {
6813 b1.launch(cfg1)?;
6814 }
6815 let f2 = self.func("prob_of_token_final_f32");
6816 let cfg2 = LaunchConfig {
6817 grid_dim: (1, 1, 1),
6818 block_dim: (256, 1, 1),
6819 shared_mem_bytes: 0,
6820 };
6821 let nbi = nb as i32;
6822 let __s_b2 = self.gpu.stream();
6823 let mut b2 = __s_b2.launch_builder(&f2);
6824 b2.arg(&part).arg(p_out).arg(&nbi);
6825 unsafe {
6826 b2.launch(cfg2)?;
6827 }
6828 Ok(())
6829 }
6830
6831 pub fn argmax_token_device(
6832 &self,
6833 logits: &CudaSlice<f32>,
6834 n_vocab: usize,
6835 ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
6836 let mut tok = unsafe { self.gpu.stream().alloc::<u32>(1)? };
6837 self.argmax_token_device_into(logits, &mut tok, n_vocab)?;
6838 Ok(tok)
6839 }
6840 pub fn argmax_token_device_into(
6847 &self,
6848 logits: &CudaSlice<f32>,
6849 tok: &mut CudaSlice<u32>,
6850 n_vocab: usize,
6851 ) -> Result<(), Box<dyn std::error::Error>> {
6852 let nb = ARGMAX_NB;
6853 let f1 = self.func("argmax_partial_f32");
6854 let f2 = self.func("argmax_final_f32");
6855 let mut guard = self.argmax_partials.lock().unwrap();
6856 if guard.is_none() {
6857 let pv = self.gpu.stream().alloc_zeros::<f32>(nb)?;
6860 let pi = self.gpu.stream().alloc_zeros::<i32>(nb)?;
6861 *guard = Some((pv, pi));
6862 }
6863 let (part_v, part_i) = guard.as_mut().unwrap();
6864 let nv = n_vocab as i32;
6865 let nbi = nb as i32;
6866 let cfg1 = LaunchConfig {
6868 grid_dim: (nb as u32, 1, 1),
6869 block_dim: (256, 1, 1),
6870 shared_mem_bytes: 0,
6871 };
6872 let __s_b1 = self.gpu.stream();
6873 let mut b1 = __s_b1.launch_builder(&f1);
6874 b1.arg(logits).arg(&mut *part_v).arg(&mut *part_i).arg(&nv);
6875 unsafe {
6876 b1.launch(cfg1)?;
6877 }
6878 let cfg2 = LaunchConfig {
6880 grid_dim: (1, 1, 1),
6881 block_dim: (256, 1, 1),
6882 shared_mem_bytes: 0,
6883 };
6884 let __s_b2 = self.gpu.stream();
6885 let mut b2 = __s_b2.launch_builder(&f2);
6886 b2.arg(&*part_v).arg(&*part_i).arg(tok).arg(&nbi);
6887 unsafe {
6888 b2.launch(cfg2)?;
6889 }
6890 Ok(())
6891 }
6892 pub fn argmax_token_device_col(
6898 &self,
6899 logits: &CudaSlice<f32>,
6900 col: usize,
6901 n_vocab: usize,
6902 toks: &mut CudaSlice<u32>,
6903 out_idx: usize,
6904 ) -> Result<(), Box<dyn std::error::Error>> {
6905 let nb = ARGMAX_NB;
6906 let f1 = self.func("argmax_partial_f32");
6907 let f2 = self.func("argmax_final_f32");
6908 let mut guard = self.argmax_partials.lock().unwrap();
6909 if guard.is_none() {
6910 let pv = self.gpu.stream().alloc_zeros::<f32>(nb)?;
6911 let pi = self.gpu.stream().alloc_zeros::<i32>(nb)?;
6912 *guard = Some((pv, pi));
6913 }
6914 let (part_v, part_i) = guard.as_mut().unwrap();
6915 let col_view = logits.slice(col * n_vocab..(col + 1) * n_vocab);
6916 let nv = n_vocab as i32;
6917 let nbi = nb as i32;
6918 let cfg1 = LaunchConfig {
6919 grid_dim: (nb as u32, 1, 1),
6920 block_dim: (256, 1, 1),
6921 shared_mem_bytes: 0,
6922 };
6923 let __s_b1 = self.gpu.stream();
6924 let mut b1 = __s_b1.launch_builder(&f1);
6925 b1.arg(&col_view)
6926 .arg(&mut *part_v)
6927 .arg(&mut *part_i)
6928 .arg(&nv);
6929 unsafe {
6930 b1.launch(cfg1)?;
6931 }
6932 let mut tok_view = toks.slice_mut(out_idx..out_idx + 1);
6933 let cfg2 = LaunchConfig {
6934 grid_dim: (1, 1, 1),
6935 block_dim: (256, 1, 1),
6936 shared_mem_bytes: 0,
6937 };
6938 let __s_b2 = self.gpu.stream();
6939 let mut b2 = __s_b2.launch_builder(&f2);
6940 b2.arg(&*part_v).arg(&*part_i).arg(&mut tok_view).arg(&nbi);
6941 unsafe {
6942 b2.launch(cfg2)?;
6943 }
6944 Ok(())
6945 }
6946 pub fn htod_u32_v(&self, v: &[u32]) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
6948 Ok(self.gpu.stream().clone_htod(v)?)
6949 }
6950 pub fn dtoh_u32(&self, d: &CudaSlice<u32>) -> Result<Vec<u32>, Box<dyn std::error::Error>> {
6951 let v = self.gpu.stream().clone_dtoh(d)?;
6952 self.gpu.stream().synchronize()?;
6953 Ok(v)
6954 }
6955 pub fn htod_u32_into(
6959 &self,
6960 dst: &mut CudaSlice<u32>,
6961 src: &[u32],
6962 ) -> Result<(), Box<dyn std::error::Error>> {
6963 let mut view = dst.slice_mut(0..src.len());
6964 self.gpu.stream().memcpy_htod(src, &mut view)?;
6965 Ok(())
6966 }
6967
6968 pub fn htod_i32_into(
6971 &self,
6972 dst: &mut CudaSlice<i32>,
6973 src: &[i32],
6974 ) -> Result<(), Box<dyn std::error::Error>> {
6975 let mut view = dst.slice_mut(0..src.len());
6976 self.gpu.stream().memcpy_htod(src, &mut view)?;
6977 Ok(())
6978 }
6979
6980 pub fn alloc_u32_zeroed(&self, n: usize) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
6981 let s = self.gpu.stream().alloc_zeros::<u32>(n)?;
6982 self.keep_if_capturing(&s);
6983 Ok(s)
6984 }
6985 pub fn embed_gather_device_into(
6988 &self,
6989 embd: &CudaSlice<u8>,
6990 token_d: &CudaSlice<u32>,
6991 x_out: &mut CudaSlice<f32>,
6992 n_embd: usize,
6993 qtype: i32,
6994 row_bytes: usize,
6995 ) -> Result<(), Box<dyn std::error::Error>> {
6996 let f = self.func("embed_gather_u32");
6997 let cfg = LaunchConfig {
6998 grid_dim: (((n_embd as u32 + 255) / 256).max(1), 1, 1),
6999 block_dim: (256, 1, 1),
7000 shared_mem_bytes: 0,
7001 };
7002 let (ne, qt, rb) = (n_embd as i32, qtype, row_bytes as i64);
7003 let __s_b = self.gpu.stream();
7004 let mut b = __s_b.launch_builder(&f);
7005 b.arg(embd)
7006 .arg(token_d)
7007 .arg(x_out)
7008 .arg(&ne)
7009 .arg(&qt)
7010 .arg(&rb);
7011 unsafe {
7012 b.launch(cfg)?;
7013 }
7014 Ok(())
7015 }
7016 pub fn dtoh_i32_one(&self, d: &CudaSlice<i32>) -> Result<i32, Box<dyn std::error::Error>> {
7018 let v = self.gpu.stream().clone_dtoh(d)?;
7019 self.gpu.stream().synchronize()?;
7020 Ok(v[0])
7021 }
7022 pub fn i32_set_k(
7029 &self,
7030 dst: &mut CudaSlice<i32>,
7031 v: i32,
7032 ) -> Result<(), Box<dyn std::error::Error>> {
7033 let f = self.func("i32_set_k");
7034 let cfg = LaunchConfig {
7035 grid_dim: (1, 1, 1),
7036 block_dim: (1, 1, 1),
7037 shared_mem_bytes: 0,
7038 };
7039 let idx = 0i32;
7040 let __s_b = self.gpu.stream();
7041 let mut b = __s_b.launch_builder(&f);
7042 b.arg(dst).arg(&v).arg(&idx);
7043 unsafe {
7044 b.launch(cfg)?;
7045 }
7046 Ok(())
7047 }
7048
7049 pub fn set_i32_one(
7050 &self,
7051 d: &mut CudaSlice<i32>,
7052 v: i32,
7053 ) -> Result<(), Box<dyn std::error::Error>> {
7054 self.gpu.stream().memcpy_htod(&[v], d)?;
7055 Ok(())
7056 }
7057 pub fn set_u32_one(
7060 &self,
7061 d: &mut CudaSlice<u32>,
7062 v: u32,
7063 ) -> Result<(), Box<dyn std::error::Error>> {
7064 self.gpu.stream().memcpy_htod(&[v], d)?;
7065 Ok(())
7066 }
7067 pub fn dtoh_u32_one(&self, d: &CudaSlice<u32>) -> Result<u32, Box<dyn std::error::Error>> {
7069 let v = self.gpu.stream().clone_dtoh(d)?;
7070 self.gpu.stream().synchronize()?;
7071 Ok(v[0])
7072 }
7073 pub fn upload_u8(&self, bytes: &[u8]) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
7075 Ok(self.gpu.stream().clone_htod(bytes)?)
7076 }
7077 pub fn embed_gather_device(
7081 &self,
7082 embd: &CudaSlice<u8>,
7083 token_d: &CudaSlice<u32>,
7084 n_embd: usize,
7085 qtype: i32,
7086 row_bytes: usize,
7087 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7088 let f = self.func("embed_gather_u32");
7089 let mut x = self.alloc_uninit::<f32>(n_embd)?;
7090 let cfg = LaunchConfig {
7091 grid_dim: (((n_embd as u32 + 255) / 256).max(1), 1, 1),
7092 block_dim: (256, 1, 1),
7093 shared_mem_bytes: 0,
7094 };
7095 let (ne, qt, rb) = (n_embd as i32, qtype, row_bytes as i64);
7096 let __s_b = self.gpu.stream();
7097 let mut b = __s_b.launch_builder(&f);
7098 b.arg(embd)
7099 .arg(token_d)
7100 .arg(&mut x)
7101 .arg(&ne)
7102 .arg(&qt)
7103 .arg(&rb);
7104 unsafe {
7105 b.launch(cfg)?;
7106 }
7107 Ok(x)
7108 }
7109
7110 pub fn embed_gather_device_t(
7114 &self,
7115 embd: &CudaSlice<u8>,
7116 tokens: &[u32],
7117 n_embd: usize,
7118 qtype: i32,
7119 row_bytes: usize,
7120 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7121 let t = tokens.len();
7122 let tok_d = self.gpu.stream().clone_htod(tokens)?;
7123 let f = self.func("embed_gather_u32_t");
7124 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7125 let cfg = LaunchConfig {
7126 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7127 block_dim: (256, 1, 1),
7128 shared_mem_bytes: 0,
7129 };
7130 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7131 let __s_b = self.gpu.stream();
7132 let mut b = __s_b.launch_builder(&f);
7133 b.arg(embd)
7134 .arg(&tok_d)
7135 .arg(&mut x)
7136 .arg(&ne)
7137 .arg(&qt)
7138 .arg(&rb)
7139 .arg(&ti);
7140 unsafe {
7141 b.launch(cfg)?;
7142 }
7143 Ok(x)
7144 }
7145
7146 pub fn embed_gather_device_tv(
7151 &self,
7152 embd: &CudaSlice<u8>,
7153 tok_v: &cudarc::driver::CudaView<u32>,
7154 t: usize,
7155 n_embd: usize,
7156 qtype: i32,
7157 row_bytes: usize,
7158 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7159 let f = self.func("embed_gather_u32_t");
7160 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7161 let cfg = LaunchConfig {
7162 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7163 block_dim: (256, 1, 1),
7164 shared_mem_bytes: 0,
7165 };
7166 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7167 let __s_b = self.gpu.stream();
7168 let mut b = __s_b.launch_builder(&f);
7169 b.arg(embd)
7170 .arg(tok_v)
7171 .arg(&mut x)
7172 .arg(&ne)
7173 .arg(&qt)
7174 .arg(&rb)
7175 .arg(&ti);
7176 unsafe {
7177 b.launch(cfg)?;
7178 }
7179 Ok(x)
7180 }
7181
7182 pub fn embed_gather_device_td(
7183 &self,
7184 embd: &CudaSlice<u8>,
7185 tok_d: &CudaSlice<u32>,
7186 t: usize,
7187 n_embd: usize,
7188 qtype: i32,
7189 row_bytes: usize,
7190 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7191 let f = self.func("embed_gather_u32_t");
7192 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7193 let cfg = LaunchConfig {
7194 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7195 block_dim: (256, 1, 1),
7196 shared_mem_bytes: 0,
7197 };
7198 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7199 let __s_b = self.gpu.stream();
7200 let mut b = __s_b.launch_builder(&f);
7201 b.arg(embd)
7202 .arg(tok_d)
7203 .arg(&mut x)
7204 .arg(&ne)
7205 .arg(&qt)
7206 .arg(&rb)
7207 .arg(&ti);
7208 unsafe {
7209 b.launch(cfg)?;
7210 }
7211 Ok(x)
7212 }
7213
7214 #[inline]
7220 fn keep_if_capturing<T: cudarc::driver::DeviceRepr + Send + 'static>(&self, s: &CudaSlice<T>) {
7222 if self
7223 .capture_keep_on
7224 .load(std::sync::atomic::Ordering::Relaxed)
7225 {
7226 self.capture_keep.lock().unwrap().push(Box::new(s.clone()));
7227 }
7228 }
7229
7230 fn alloc_uninit<T: cudarc::driver::DeviceRepr + Send + 'static>(
7231 &self,
7232 n: usize,
7233 ) -> Result<CudaSlice<T>, Box<dyn std::error::Error>> {
7234 let mut s = unsafe { self.gpu.stream().alloc::<T>(n)? };
7235 {
7239 static Z: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7240 if *Z.get_or_init(|| std::env::var("MEMRA_DEBUG_ZERO_ALLOCS").as_deref() == Ok("1")) {
7241 use cudarc::driver::DevicePtrMut;
7243 let n_bytes = s.len() * std::mem::size_of::<T>();
7244 let stream = self.gpu.stream();
7245 let (p_, _g) = s.device_ptr_mut(&stream);
7246 unsafe {
7247 cudarc::driver::sys::cuMemsetD8Async(p_, 0, n_bytes, stream.cu_stream())
7248 .result()?;
7249 }
7250 }
7251 }
7252 self.keep_if_capturing(&s);
7253 Ok(s)
7254 }
7255
7256 pub fn uninit_q8_pair(
7261 &self,
7262 n: usize,
7263 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7264 Ok((
7265 self.alloc_uninit::<i8>(n)?,
7266 self.alloc_uninit::<f32>(n / 32)?,
7267 ))
7268 }
7269
7270 pub fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7271 self.alloc_uninit::<f32>(n)
7272 }
7273
7274 pub fn alloc_i8_uninit(&self, n: usize) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
7276 self.alloc_uninit::<i8>(n)
7277 }
7278
7279 #[allow(clippy::too_many_arguments)]
7283 pub fn rms_norm3(
7284 &self,
7285 x: &CudaSlice<f32>,
7286 w0: &CudaSlice<f32>,
7287 w1: &CudaSlice<f32>,
7288 w2: &CudaSlice<f32>,
7289 d0: &mut CudaSlice<f32>,
7290 d1: &mut CudaSlice<f32>,
7291 d2: &mut CudaSlice<f32>,
7292 ncols: usize,
7293 nrows: usize,
7294 eps: f32,
7295 ) -> Result<(), Box<dyn std::error::Error>> {
7296 let f = self.func("rms_norm3_f32");
7297 let cfg = LaunchConfig {
7298 grid_dim: (nrows as u32, 1, 1),
7299 block_dim: (rms_block(), 1, 1),
7300 shared_mem_bytes: 0,
7301 };
7302 let (nc, e) = (ncols as i32, eps);
7303 let __s_b = self.gpu.stream();
7304 let mut b = __s_b.launch_builder(&f);
7305 b.arg(x)
7306 .arg(w0)
7307 .arg(w1)
7308 .arg(w2)
7309 .arg(d0)
7310 .arg(d1)
7311 .arg(d2)
7312 .arg(&nc)
7313 .arg(&e);
7314 unsafe {
7315 b.launch(cfg)?;
7316 }
7317 Ok(())
7318 }
7319
7320 #[allow(clippy::too_many_arguments)]
7322 pub fn qkvnorm_w_on_prefill(rows: usize, ncols: usize) -> bool {
7325 static WARP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7326 *WARP_ON.get_or_init(|| {
7327 std::env::var("MEMRA_QKVNORM_W")
7328 .map(|v| v != "0")
7329 .unwrap_or(true)
7330 }) && ncols % 4 == 0
7331 && rows >= 64
7332 }
7333
7334 #[allow(clippy::too_many_arguments)]
7337 pub fn rms_norm_qkv_w4b(
7338 &self,
7339 q: &CudaSlice<f32>,
7340 k: &CudaSlice<f32>,
7341 v: &CudaSlice<f32>,
7342 wq: &CudaSlice<f32>,
7343 wk: &CudaSlice<f32>,
7344 wv: &CudaSlice<f32>,
7345 dq: &mut CudaSlice<f32>,
7346 dk: &mut CudaSlice<f32>,
7347 dv: &mut CudaSlice<f32>,
7348 dvb: &mut CudaSlice<u8>,
7349 ncols: usize,
7350 rq: usize,
7351 rk: usize,
7352 eps: f32,
7353 vf16: bool,
7354 ) -> Result<(), Box<dyn std::error::Error>> {
7355 assert!(ncols % 4 == 0 && rq + 2 * rk >= 64);
7356 let f = self.func("rms_norm_qkv_w4b_f32");
7357 let rows = (rq + 2 * rk) as u32;
7358 let cfg = LaunchConfig {
7359 grid_dim: (rows.div_ceil(8), 1, 1),
7360 block_dim: (256, 1, 1),
7361 shared_mem_bytes: 0,
7362 };
7363 let (nc, rqi, rki, rvi, e) = (ncols as i32, rq as i32, rk as i32, rk as i32, eps);
7364 let vf = vf16 as i32;
7365 let __s_b = self.gpu.stream();
7366 let mut b = __s_b.launch_builder(&f);
7367 b.arg(q)
7368 .arg(k)
7369 .arg(v)
7370 .arg(wq)
7371 .arg(wk)
7372 .arg(wv)
7373 .arg(dq)
7374 .arg(dk)
7375 .arg(dv)
7376 .arg(&mut *dvb)
7377 .arg(&nc)
7378 .arg(&rqi)
7379 .arg(&rki)
7380 .arg(&rvi)
7381 .arg(&e)
7382 .arg(&vf);
7383 unsafe {
7384 b.launch(cfg)?;
7385 }
7386 Ok(())
7387 }
7388
7389 pub fn rms_norm_qkv(
7390 &self,
7391 q: &CudaSlice<f32>,
7392 k: &CudaSlice<f32>,
7393 v: &CudaSlice<f32>,
7394 wq: &CudaSlice<f32>,
7395 wk: &CudaSlice<f32>,
7396 wv: &CudaSlice<f32>,
7397 dq: &mut CudaSlice<f32>,
7398 dk: &mut CudaSlice<f32>,
7399 dv: &mut CudaSlice<f32>,
7400 ncols: usize,
7401 rq: usize,
7402 rk: usize,
7403 eps: f32,
7404 ) -> Result<(), Box<dyn std::error::Error>> {
7405 static WARP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7409 let warp_on = *WARP_ON.get_or_init(|| {
7410 std::env::var("MEMRA_QKVNORM_W")
7411 .map(|v| v != "0")
7412 .unwrap_or(true)
7413 });
7414 if warp_on && ncols % 4 == 0 && rq + 2 * rk >= 64 {
7417 let f = self.func("rms_norm_qkv_w4_f32");
7418 let rows = (rq + 2 * rk) as u32;
7419 let cfg = LaunchConfig {
7420 grid_dim: (rows.div_ceil(8), 1, 1),
7421 block_dim: (256, 1, 1),
7422 shared_mem_bytes: 0,
7423 };
7424 let (nc, rqi, rki, rvi, e) = (ncols as i32, rq as i32, rk as i32, rk as i32, eps);
7425 let __s_b = self.gpu.stream();
7426 let mut b = __s_b.launch_builder(&f);
7427 b.arg(q)
7428 .arg(k)
7429 .arg(v)
7430 .arg(wq)
7431 .arg(wk)
7432 .arg(wv)
7433 .arg(dq)
7434 .arg(dk)
7435 .arg(dv)
7436 .arg(&nc)
7437 .arg(&rqi)
7438 .arg(&rki)
7439 .arg(&rvi)
7440 .arg(&e);
7441 unsafe {
7442 b.launch(cfg)?;
7443 }
7444 return Ok(());
7445 }
7446 let f = self.func("rms_norm_qkv_f32");
7447 let grid = (rq + 2 * rk) as u32;
7448 let cfg = LaunchConfig {
7449 grid_dim: (grid, 1, 1),
7450 block_dim: (rms_block(), 1, 1),
7451 shared_mem_bytes: 0,
7452 };
7453 let (nc, rqi, rki, e) = (ncols as i32, rq as i32, rk as i32, eps);
7454 let __s_b = self.gpu.stream();
7455 let mut b = __s_b.launch_builder(&f);
7456 b.arg(q)
7457 .arg(k)
7458 .arg(v)
7459 .arg(wq)
7460 .arg(wk)
7461 .arg(wv)
7462 .arg(dq)
7463 .arg(dk)
7464 .arg(dv)
7465 .arg(&nc)
7466 .arg(&rqi)
7467 .arg(&rki)
7468 .arg(&e);
7469 unsafe {
7470 b.launch(cfg)?;
7471 }
7472 Ok(())
7473 }
7474
7475 #[allow(clippy::too_many_arguments)]
7477 pub fn rms_norm2x(
7478 &self,
7479 a: &CudaSlice<f32>,
7480 bb: &CudaSlice<f32>,
7481 wa: &CudaSlice<f32>,
7482 wb: &CudaSlice<f32>,
7483 da: &mut CudaSlice<f32>,
7484 db: &mut CudaSlice<f32>,
7485 ncols: usize,
7486 nrows: usize,
7487 eps: f32,
7488 ) -> Result<(), Box<dyn std::error::Error>> {
7489 let f = self.func("rms_norm2x_f32");
7490 let cfg = LaunchConfig {
7491 grid_dim: (2 * nrows as u32, 1, 1),
7492 block_dim: (rms_block(), 1, 1),
7493 shared_mem_bytes: 0,
7494 };
7495 let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
7496 let __s_b = self.gpu.stream();
7497 let mut b = __s_b.launch_builder(&f);
7498 b.arg(a)
7499 .arg(bb)
7500 .arg(wa)
7501 .arg(wb)
7502 .arg(da)
7503 .arg(db)
7504 .arg(&nc)
7505 .arg(&nr)
7506 .arg(&e);
7507 unsafe {
7508 b.launch(cfg)?;
7509 }
7510 Ok(())
7511 }
7512
7513 pub fn softcap(
7515 &self,
7516 y: &mut CudaSlice<f32>,
7517 cap: f32,
7518 n: usize,
7519 ) -> Result<(), Box<dyn std::error::Error>> {
7520 let f = self.func("softcap_f32");
7521 let cfg = LaunchConfig::for_num_elems(n as u32);
7522 let ni = n as i32;
7523 let __s_b = self.gpu.stream();
7524 let mut b = __s_b.launch_builder(&f);
7525 b.arg(y).arg(&cap).arg(&ni);
7526 unsafe {
7527 b.launch(cfg)?;
7528 }
7529 Ok(())
7530 }
7531
7532 pub fn mask_ids_rows(
7535 &self,
7536 y: &mut CudaSlice<f32>,
7537 ids: &CudaSlice<i32>,
7538 n_ids: usize,
7539 n_vocab: usize,
7540 t: usize,
7541 ) -> Result<(), Box<dyn std::error::Error>> {
7542 let f = self.func("mask_ids_rows_f32");
7543 let cfg = LaunchConfig::for_num_elems((n_ids * t) as u32);
7544 let (ni, nv, ti) = (n_ids as i32, n_vocab as i32, t as i32);
7545 let __s_b = self.gpu.stream();
7546 let mut b = __s_b.launch_builder(&f);
7547 b.arg(y).arg(ids).arg(&ni).arg(&nv).arg(&ti);
7548 unsafe {
7549 b.launch(cfg)?;
7550 }
7551 Ok(())
7552 }
7553
7554 #[allow(clippy::too_many_arguments)]
7556 pub fn add_scale_rms_norm(
7557 &self,
7558 a: &CudaSlice<f32>,
7559 b_in: &CudaSlice<f32>,
7560 c: f32,
7561 w: &CudaSlice<f32>,
7562 res: &mut CudaSlice<f32>,
7563 dst: &mut CudaSlice<f32>,
7564 ncols: usize,
7565 nrows: usize,
7566 eps: f32,
7567 ) -> Result<(), Box<dyn std::error::Error>> {
7568 let f = self.func("add_scale_rms_norm_f32");
7569 let cfg = LaunchConfig {
7570 grid_dim: (nrows as u32, 1, 1),
7571 block_dim: (rms_block(), 1, 1),
7572 shared_mem_bytes: 0,
7573 };
7574 let (nc, e2) = (ncols as i32, eps);
7575 let __s_b = self.gpu.stream();
7576 let mut b = __s_b.launch_builder(&f);
7577 b.arg(a)
7578 .arg(b_in)
7579 .arg(&c)
7580 .arg(w)
7581 .arg(res)
7582 .arg(dst)
7583 .arg(&nc)
7584 .arg(&e2);
7585 unsafe {
7586 b.launch(cfg)?;
7587 }
7588 Ok(())
7589 }
7590
7591 #[allow(clippy::too_many_arguments)]
7594 pub fn add_scale_rms_norm_q8_1(
7595 &self,
7596 a: &CudaSlice<f32>,
7597 b_in: &CudaSlice<f32>,
7598 c: f32,
7599 w: &CudaSlice<f32>,
7600 res: &mut CudaSlice<f32>,
7601 ncols: usize,
7602 nrows: usize,
7603 eps: f32,
7604 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7605 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7606 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7607 let (nc, e2) = (ncols as i32, eps);
7608 if Self::pdl_on() && Self::pdl_wb_on() {
7609 {
7610 use cudarc::driver::{DevicePtr, DevicePtrMut};
7611 let s = &self.gpu.stream();
7612 let (pa, _g0) = a.device_ptr(s);
7613 let (pb, _g1) = b_in.device_ptr(s);
7614 let (pw, _g2) = w.device_ptr(s);
7615 let (pr, _g3) = res.device_ptr_mut(s);
7616 let (pq, _g4) = out_q.device_ptr_mut(s);
7617 let (pd, _g5) = out_d.device_ptr_mut(s);
7618 let mut ps = [
7619 &pa as *const _ as *mut std::ffi::c_void,
7620 &pb as *const _ as *mut _,
7621 &c as *const _ as *mut _,
7622 &pw as *const _ as *mut _,
7623 &pr as *const _ as *mut _,
7624 &pq as *const _ as *mut _,
7625 &pd as *const _ as *mut _,
7626 &nc as *const _ as *mut _,
7627 &e2 as *const _ as *mut _,
7628 ];
7629 unsafe {
7630 self.launch_pdl(
7631 "add_scale_rms_norm_q8_1",
7632 (nrows as u32, 1, 1),
7633 (rms_block(), 1, 1),
7634 &mut ps,
7635 )?;
7636 }
7637 }
7638 return Ok((out_q, out_d));
7639 }
7640 let f = self.func("add_scale_rms_norm_q8_1");
7641 let cfg = LaunchConfig {
7642 grid_dim: (nrows as u32, 1, 1),
7643 block_dim: (rms_block(), 1, 1),
7644 shared_mem_bytes: 0,
7645 };
7646 let __s_b = self.gpu.stream();
7647 let mut b = __s_b.launch_builder(&f);
7648 b.arg(a)
7649 .arg(b_in)
7650 .arg(&c)
7651 .arg(w)
7652 .arg(res)
7653 .arg(&mut out_q)
7654 .arg(&mut out_d)
7655 .arg(&nc)
7656 .arg(&e2);
7657 unsafe {
7658 b.launch(cfg)?;
7659 }
7660 Ok((out_q, out_d))
7661 }
7662
7663 #[allow(clippy::too_many_arguments)]
7665 pub fn add_scale_rms_norm_q8_1_into(
7666 &self,
7667 a: &CudaSlice<f32>,
7668 b_in: &CudaSlice<f32>,
7669 c: f32,
7670 w: &CudaSlice<f32>,
7671 res: &mut CudaSlice<f32>,
7672 ncols: usize,
7673 nrows: usize,
7674 eps: f32,
7675 out_q: &mut CudaSlice<i8>,
7676 out_d: &mut CudaSlice<f32>,
7677 ) -> Result<(), Box<dyn std::error::Error>> {
7678 debug_assert!(out_q.len() >= nrows * ncols && out_d.len() >= nrows * (ncols / 32));
7679 let (nc, e2) = (ncols as i32, eps);
7680 if Self::pdl_on() && Self::pdl_wb_on() {
7681 use cudarc::driver::{DevicePtr, DevicePtrMut};
7682 let s = &self.gpu.stream();
7683 let (pa, _g0) = a.device_ptr(s);
7684 let (pb, _g1) = b_in.device_ptr(s);
7685 let (pw, _g2) = w.device_ptr(s);
7686 let (pr, _g3) = res.device_ptr_mut(s);
7687 let (pq, _g4) = out_q.device_ptr_mut(s);
7688 let (pd, _g5) = out_d.device_ptr_mut(s);
7689 let mut ps = [
7690 &pa as *const _ as *mut std::ffi::c_void,
7691 &pb as *const _ as *mut _,
7692 &c as *const _ as *mut _,
7693 &pw as *const _ as *mut _,
7694 &pr as *const _ as *mut _,
7695 &pq as *const _ as *mut _,
7696 &pd as *const _ as *mut _,
7697 &nc as *const _ as *mut _,
7698 &e2 as *const _ as *mut _,
7699 ];
7700 unsafe {
7701 self.launch_pdl(
7702 "add_scale_rms_norm_q8_1",
7703 (nrows as u32, 1, 1),
7704 (rms_block(), 1, 1),
7705 &mut ps,
7706 )?;
7707 }
7708 return Ok(());
7709 }
7710 let f = self.func("add_scale_rms_norm_q8_1");
7711 let cfg = LaunchConfig {
7712 grid_dim: (nrows as u32, 1, 1),
7713 block_dim: (rms_block(), 1, 1),
7714 shared_mem_bytes: 0,
7715 };
7716 let __s_b = self.gpu.stream();
7717 let mut b = __s_b.launch_builder(&f);
7718 b.arg(a)
7719 .arg(b_in)
7720 .arg(&c)
7721 .arg(w)
7722 .arg(res)
7723 .arg(&mut *out_q)
7724 .arg(&mut *out_d)
7725 .arg(&nc)
7726 .arg(&e2);
7727 unsafe {
7728 b.launch(cfg)?;
7729 }
7730 Ok(())
7731 }
7732
7733 #[allow(clippy::too_many_arguments)]
7736 pub fn rms_pre_add_scale_rms_norm_q8_1(
7737 &self,
7738 a: &CudaSlice<f32>,
7739 wa: &CudaSlice<f32>,
7740 b_in: &CudaSlice<f32>,
7741 c: f32,
7742 w: &CudaSlice<f32>,
7743 res: &mut CudaSlice<f32>,
7744 ncols: usize,
7745 nrows: usize,
7746 eps: f32,
7747 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7748 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7749 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7750 let (nc, e2) = (ncols as i32, eps);
7751 if Self::pdl_on() {
7752 {
7753 use cudarc::driver::{DevicePtr, DevicePtrMut};
7754 let s = &self.gpu.stream();
7755 let (pa, _g0) = a.device_ptr(s);
7756 let (pwa, _g1) = wa.device_ptr(s);
7757 let (pb, _g2) = b_in.device_ptr(s);
7758 let (pw, _g3) = w.device_ptr(s);
7759 let (pr, _g4) = res.device_ptr_mut(s);
7760 let (pq, _g5) = out_q.device_ptr_mut(s);
7761 let (pd, _g6) = out_d.device_ptr_mut(s);
7762 let mut ps = [
7763 &pa as *const _ as *mut std::ffi::c_void,
7764 &pwa as *const _ as *mut _,
7765 &pb as *const _ as *mut _,
7766 &c as *const _ as *mut _,
7767 &pw as *const _ as *mut _,
7768 &pr as *const _ as *mut _,
7769 &pq as *const _ as *mut _,
7770 &pd as *const _ as *mut _,
7771 &nc as *const _ as *mut _,
7772 &e2 as *const _ as *mut _,
7773 ];
7774 unsafe {
7775 self.launch_pdl(
7776 "rms_pre_add_scale_rms_norm_q8_1",
7777 (nrows as u32, 1, 1),
7778 (rms_block(), 1, 1),
7779 &mut ps,
7780 )?;
7781 }
7782 }
7783 return Ok((out_q, out_d));
7784 }
7785 let f = self.func("rms_pre_add_scale_rms_norm_q8_1");
7786 let cfg = LaunchConfig {
7787 grid_dim: (nrows as u32, 1, 1),
7788 block_dim: (rms_block(), 1, 1),
7789 shared_mem_bytes: 0,
7790 };
7791 let __s_b = self.gpu.stream();
7792 let mut b = __s_b.launch_builder(&f);
7793 b.arg(a)
7794 .arg(wa)
7795 .arg(b_in)
7796 .arg(&c)
7797 .arg(w)
7798 .arg(res)
7799 .arg(&mut out_q)
7800 .arg(&mut out_d)
7801 .arg(&nc)
7802 .arg(&e2);
7803 unsafe {
7804 b.launch(cfg)?;
7805 }
7806 Ok((out_q, out_d))
7807 }
7808
7809 pub fn gelu_tanh_mul_q8_1(
7812 &self,
7813 gate: &CudaSlice<f32>,
7814 up: &cudarc::driver::CudaView<f32>,
7815 act: &mut CudaSlice<f32>,
7816 ncols: usize,
7817 nrows: usize,
7818 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7819 debug_assert!(ncols % 128 == 0);
7820 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7821 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7822 let nc = ncols as i32;
7823 if Self::pdl_on() {
7824 {
7825 use cudarc::driver::{DevicePtr, DevicePtrMut};
7826 let s = &self.gpu.stream();
7827 let (pg, _g0) = gate.device_ptr(s);
7828 let (pu, _g1) = up.device_ptr(s);
7829 let (pact, _g2) = act.device_ptr_mut(s);
7830 let (pq, _g3) = out_q.device_ptr_mut(s);
7831 let (pd, _g4) = out_d.device_ptr_mut(s);
7832 let mut ps = [
7833 &pg as *const _ as *mut std::ffi::c_void,
7834 &pu as *const _ as *mut _,
7835 &pact as *const _ as *mut _,
7836 &pq as *const _ as *mut _,
7837 &pd as *const _ as *mut _,
7838 &nc as *const _ as *mut _,
7839 ];
7840 unsafe {
7841 self.launch_pdl(
7842 "gelu_tanh_mul_q8_1",
7843 (nrows as u32, 1, 1),
7844 (rms_block(), 1, 1),
7845 &mut ps,
7846 )?;
7847 }
7848 }
7849 return Ok((out_q, out_d));
7850 }
7851 let f = self.func("gelu_tanh_mul_q8_1");
7852 let cfg = LaunchConfig {
7853 grid_dim: (nrows as u32, 1, 1),
7854 block_dim: (rms_block(), 1, 1),
7855 shared_mem_bytes: 0,
7856 };
7857 let __s_b = self.gpu.stream();
7858 let mut b = __s_b.launch_builder(&f);
7859 b.arg(gate)
7860 .arg(up)
7861 .arg(act)
7862 .arg(&mut out_q)
7863 .arg(&mut out_d)
7864 .arg(&nc);
7865 unsafe {
7866 b.launch(cfg)?;
7867 }
7868 Ok((out_q, out_d))
7869 }
7870
7871 #[allow(clippy::too_many_arguments)]
7873 pub fn gelu_tanh_mul_q8_1_into(
7874 &self,
7875 gate: &CudaSlice<f32>,
7876 up: &cudarc::driver::CudaView<f32>,
7877 act: &mut CudaSlice<f32>,
7878 ncols: usize,
7879 nrows: usize,
7880 out_q: &mut CudaSlice<i8>,
7881 out_d: &mut CudaSlice<f32>,
7882 ) -> Result<(), Box<dyn std::error::Error>> {
7883 debug_assert!(ncols % 128 == 0);
7884 debug_assert!(out_q.len() >= nrows * ncols && out_d.len() >= nrows * (ncols / 32));
7885 let nc = ncols as i32;
7886 if Self::pdl_on() {
7887 use cudarc::driver::{DevicePtr, DevicePtrMut};
7888 let s = &self.gpu.stream();
7889 let (pg, _g0) = gate.device_ptr(s);
7890 let (pu, _g1) = up.device_ptr(s);
7891 let (pact, _g2) = act.device_ptr_mut(s);
7892 let (pq, _g3) = out_q.device_ptr_mut(s);
7893 let (pd, _g4) = out_d.device_ptr_mut(s);
7894 let mut ps = [
7895 &pg as *const _ as *mut std::ffi::c_void,
7896 &pu as *const _ as *mut _,
7897 &pact as *const _ as *mut _,
7898 &pq as *const _ as *mut _,
7899 &pd as *const _ as *mut _,
7900 &nc as *const _ as *mut _,
7901 ];
7902 unsafe {
7903 self.launch_pdl(
7904 "gelu_tanh_mul_q8_1",
7905 (nrows as u32, 1, 1),
7906 (rms_block(), 1, 1),
7907 &mut ps,
7908 )?;
7909 }
7910 return Ok(());
7911 }
7912 let f = self.func("gelu_tanh_mul_q8_1");
7913 let cfg = LaunchConfig {
7914 grid_dim: (nrows as u32, 1, 1),
7915 block_dim: (rms_block(), 1, 1),
7916 shared_mem_bytes: 0,
7917 };
7918 let __s_b = self.gpu.stream();
7919 let mut b = __s_b.launch_builder(&f);
7920 b.arg(gate)
7921 .arg(up)
7922 .arg(&mut *act)
7923 .arg(&mut *out_q)
7924 .arg(&mut *out_d)
7925 .arg(&nc);
7926 unsafe {
7927 b.launch(cfg)?;
7928 }
7929 Ok(())
7930 }
7931
7932 #[allow(clippy::too_many_arguments)]
7934 pub fn add_rms_norm3_q8z(
7935 &self,
7936 a: &CudaSlice<f32>,
7937 b_in: &CudaSlice<f32>,
7938 w0: &CudaSlice<f32>,
7939 w1: &CudaSlice<f32>,
7940 w2: &CudaSlice<f32>,
7941 res: &mut CudaSlice<f32>,
7942 out1: &mut CudaSlice<f32>,
7943 ncols: usize,
7944 nrows: usize,
7945 eps: f32,
7946 ) -> Result<
7947 (
7948 (CudaSlice<i8>, CudaSlice<f32>),
7949 (CudaSlice<i8>, CudaSlice<f32>),
7950 ),
7951 Box<dyn std::error::Error>,
7952 > {
7953 let mut q0 = self.alloc_uninit::<i8>(nrows * ncols)?;
7954 let mut d0 = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7955 let mut q2 = self.alloc_uninit::<i8>(nrows * ncols)?;
7956 let mut d2 = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7957 let f = self.func("add_rms_norm3_q8z_f32");
7958 let cfg = LaunchConfig {
7959 grid_dim: (nrows as u32, 1, 1),
7960 block_dim: (rms_block(), 1, 1),
7961 shared_mem_bytes: 0,
7962 };
7963 let (nc, e2) = (ncols as i32, eps);
7964 let __s_b = self.gpu.stream();
7965 let mut b = __s_b.launch_builder(&f);
7966 b.arg(a)
7967 .arg(b_in)
7968 .arg(w0)
7969 .arg(w1)
7970 .arg(w2)
7971 .arg(res)
7972 .arg(&mut q0)
7973 .arg(&mut d0)
7974 .arg(out1)
7975 .arg(&mut q2)
7976 .arg(&mut d2)
7977 .arg(&nc)
7978 .arg(&e2);
7979 unsafe {
7980 b.launch(cfg)?;
7981 }
7982 Ok(((q0, d0), (q2, d2)))
7983 }
7984
7985 #[allow(clippy::too_many_arguments)]
7987 pub fn add_rms_norm3(
7988 &self,
7989 a: &CudaSlice<f32>,
7990 b_in: &CudaSlice<f32>,
7991 w0: &CudaSlice<f32>,
7992 w1: &CudaSlice<f32>,
7993 w2: &CudaSlice<f32>,
7994 res: &mut CudaSlice<f32>,
7995 d0: &mut CudaSlice<f32>,
7996 d1: &mut CudaSlice<f32>,
7997 d2: &mut CudaSlice<f32>,
7998 ncols: usize,
7999 nrows: usize,
8000 eps: f32,
8001 ) -> Result<(), Box<dyn std::error::Error>> {
8002 let f = self.func("add_rms_norm3_f32");
8003 let cfg = LaunchConfig {
8004 grid_dim: (nrows as u32, 1, 1),
8005 block_dim: (rms_block(), 1, 1),
8006 shared_mem_bytes: 0,
8007 };
8008 let (nc, e2) = (ncols as i32, eps);
8009 let __s_b = self.gpu.stream();
8010 let mut b = __s_b.launch_builder(&f);
8011 b.arg(a)
8012 .arg(b_in)
8013 .arg(w0)
8014 .arg(w1)
8015 .arg(w2)
8016 .arg(res)
8017 .arg(d0)
8018 .arg(d1)
8019 .arg(d2)
8020 .arg(&nc)
8021 .arg(&e2);
8022 unsafe {
8023 b.launch(cfg)?;
8024 }
8025 Ok(())
8026 }
8027
8028 pub fn add_scale(
8030 &self,
8031 a: &CudaSlice<f32>,
8032 b_in: &CudaSlice<f32>,
8033 c: f32,
8034 dst: &mut CudaSlice<f32>,
8035 n: usize,
8036 ) -> Result<(), Box<dyn std::error::Error>> {
8037 let f = self.func("add_scale_f32");
8038 let cfg = LaunchConfig::for_num_elems(n as u32);
8039 let ni = n as i32;
8040 let __s_b = self.gpu.stream();
8041 let mut b = __s_b.launch_builder(&f);
8042 b.arg(a).arg(b_in).arg(&c).arg(dst).arg(&ni);
8043 unsafe {
8044 b.launch(cfg)?;
8045 }
8046 Ok(())
8047 }
8048
8049 pub fn layer_norm_bias(
8051 &self,
8052 x: &CudaSlice<f32>,
8053 w: &CudaSlice<f32>,
8054 b: &CudaSlice<f32>,
8055 dst: &mut CudaSlice<f32>,
8056 ncols: usize,
8057 nrows: usize,
8058 eps: f32,
8059 ) -> Result<(), Box<dyn std::error::Error>> {
8060 let f = self.func("layer_norm_bias_f32");
8061 let (nc, e) = (ncols as i32, eps);
8062 let cfg = LaunchConfig {
8063 grid_dim: (nrows as u32, 1, 1),
8064 block_dim: (256, 1, 1),
8065 shared_mem_bytes: 0,
8066 };
8067 let __s_b = self.gpu.stream();
8068 let mut lb = __s_b.launch_builder(&f);
8069 lb.arg(x).arg(w).arg(b).arg(&mut *dst).arg(&nc).arg(&e);
8070 unsafe {
8071 lb.launch(cfg)?;
8072 }
8073 Ok(())
8074 }
8075
8076 pub fn gelu_tanh(
8078 &self,
8079 x: &CudaSlice<f32>,
8080 dst: &mut CudaSlice<f32>,
8081 n: usize,
8082 ) -> Result<(), Box<dyn std::error::Error>> {
8083 let f = self.func("gelu_tanh_f32");
8084 let ni = n as i64;
8085 let cfg = LaunchConfig {
8086 grid_dim: (n.div_ceil(256) as u32, 1, 1),
8087 block_dim: (256, 1, 1),
8088 shared_mem_bytes: 0,
8089 };
8090 let __s_b = self.gpu.stream();
8091 let mut lb = __s_b.launch_builder(&f);
8092 lb.arg(x).arg(&mut *dst).arg(&ni);
8093 unsafe {
8094 lb.launch(cfg)?;
8095 }
8096 Ok(())
8097 }
8098
8099 pub fn row_softmax(
8101 &self,
8102 x: &mut CudaSlice<f32>,
8103 ncols: usize,
8104 nrows: usize,
8105 ) -> Result<(), Box<dyn std::error::Error>> {
8106 let f = self.func("row_softmax_f32");
8107 let nc = ncols as i32;
8108 let cfg = LaunchConfig {
8109 grid_dim: (nrows as u32, 1, 1),
8110 block_dim: (256, 1, 1),
8111 shared_mem_bytes: 0,
8112 };
8113 let __s_b = self.gpu.stream();
8114 let mut lb = __s_b.launch_builder(&f);
8115 lb.arg(&mut *x).arg(&nc);
8116 unsafe {
8117 lb.launch(cfg)?;
8118 }
8119 Ok(())
8120 }
8121
8122 pub fn rms_norm(
8123 &self,
8124 x: &CudaSlice<f32>,
8125 w: &CudaSlice<f32>,
8126 dst: &mut CudaSlice<f32>,
8127 ncols: usize,
8128 nrows: usize,
8129 eps: f32,
8130 ) -> Result<(), Box<dyn std::error::Error>> {
8131 let (nc, e) = (ncols as i32, eps);
8132 if Self::pdl_on() && Self::pdl_wb_on() {
8133 use cudarc::driver::{DevicePtr, DevicePtrMut};
8134 let s = &self.gpu.stream();
8135 let (px, _g0) = x.device_ptr(s);
8136 let (pw, _g1) = w.device_ptr(s);
8137 let (pd, _g2) = dst.device_ptr_mut(s);
8138 let mut ps = [
8139 &px as *const _ as *mut std::ffi::c_void,
8140 &pw as *const _ as *mut _,
8141 &pd as *const _ as *mut _,
8142 &nc as *const _ as *mut _,
8143 &e as *const _ as *mut _,
8144 ];
8145 unsafe {
8146 self.launch_pdl(
8147 "rms_norm_f32",
8148 (nrows as u32, 1, 1),
8149 (rms_block(), 1, 1),
8150 &mut ps,
8151 )?;
8152 }
8153 return Ok(());
8154 }
8155 let f = self.func("rms_norm_f32");
8156 let cfg = LaunchConfig {
8157 grid_dim: (nrows as u32, 1, 1),
8158 block_dim: (rms_block(), 1, 1),
8159 shared_mem_bytes: 0,
8160 };
8161 let __s_b = self.gpu.stream();
8162 let mut b = __s_b.launch_builder(&f);
8163 b.arg(x).arg(w).arg(dst).arg(&nc).arg(&e);
8164 unsafe {
8165 b.launch(cfg)?;
8166 }
8167 Ok(())
8168 }
8169
8170 pub fn rms_norm_decode(
8178 &self,
8179 x: &CudaSlice<f32>,
8180 w: &CudaSlice<f32>,
8181 dst: &mut CudaSlice<f32>,
8182 ncols: usize,
8183 nrows: usize,
8184 eps: f32,
8185 ) -> Result<(), Box<dyn std::error::Error>> {
8186 let f = self.func("rms_norm_f32");
8187 let cfg = LaunchConfig {
8188 grid_dim: (nrows as u32, 1, 1),
8189 block_dim: (1024, 1, 1),
8190 shared_mem_bytes: 0,
8191 };
8192 let (nc, e) = (ncols as i32, eps);
8193 let __s_b = self.gpu.stream();
8194 let mut b = __s_b.launch_builder(&f);
8195 b.arg(x).arg(w).arg(dst).arg(&nc).arg(&e);
8196 unsafe {
8197 b.launch(cfg)?;
8198 }
8199 Ok(())
8200 }
8201
8202 pub fn rms_norm_q8_1(
8206 &self,
8207 x: &CudaSlice<f32>,
8208 w: &CudaSlice<f32>,
8209 ncols: usize,
8210 nrows: usize,
8211 eps: f32,
8212 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8213 let nblk = ncols / 32;
8214 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
8215 let mut d = self.alloc_uninit::<f32>(nrows * nblk)?;
8216 let (nc, e) = (ncols as i32, eps);
8217 if Self::pdl_on() {
8218 {
8219 use cudarc::driver::{DevicePtr, DevicePtrMut};
8220 let s = &self.gpu.stream();
8221 let (px, _g0) = x.device_ptr(s);
8222 let (pw, _g1) = w.device_ptr(s);
8223 let (pq, _g2) = q.device_ptr_mut(s);
8224 let (pd, _g3) = d.device_ptr_mut(s);
8225 let mut ps = [
8226 &px as *const _ as *mut std::ffi::c_void,
8227 &pw as *const _ as *mut _,
8228 &pq as *const _ as *mut _,
8229 &pd as *const _ as *mut _,
8230 &nc as *const _ as *mut _,
8231 &e as *const _ as *mut _,
8232 ];
8233 unsafe {
8234 self.launch_pdl("rms_norm_q8_1", (nrows as u32, 1, 1), (1024, 1, 1), &mut ps)?;
8235 }
8236 }
8237 return Ok((q, d));
8238 }
8239 let f = self.func("rms_norm_q8_1");
8240 let cfg = LaunchConfig {
8243 grid_dim: (nrows as u32, 1, 1),
8244 block_dim: (1024, 1, 1),
8245 shared_mem_bytes: 0,
8246 };
8247 let __s_b = self.gpu.stream();
8248 let mut b = __s_b.launch_builder(&f);
8249 b.arg(x).arg(w).arg(&mut q).arg(&mut d).arg(&nc).arg(&e);
8250 unsafe {
8251 b.launch(cfg)?;
8252 }
8253 Ok((q, d))
8254 }
8255
8256 pub fn rms_norm_q8_1_into(
8259 &self,
8260 x: &CudaSlice<f32>,
8261 w: &CudaSlice<f32>,
8262 ncols: usize,
8263 nrows: usize,
8264 eps: f32,
8265 q: &mut CudaSlice<i8>,
8266 d: &mut CudaSlice<f32>,
8267 ) -> Result<(), Box<dyn std::error::Error>> {
8268 let nblk = ncols / 32;
8269 debug_assert!(q.len() >= nrows * ncols && d.len() >= nrows * nblk);
8270 let (nc, e) = (ncols as i32, eps);
8271 if Self::pdl_on() {
8272 use cudarc::driver::{DevicePtr, DevicePtrMut};
8273 let s = &self.gpu.stream();
8274 let (px, _g0) = x.device_ptr(s);
8275 let (pw, _g1) = w.device_ptr(s);
8276 let (pq, _g2) = q.device_ptr_mut(s);
8277 let (pd, _g3) = d.device_ptr_mut(s);
8278 let mut ps = [
8279 &px as *const _ as *mut std::ffi::c_void,
8280 &pw as *const _ as *mut _,
8281 &pq as *const _ as *mut _,
8282 &pd as *const _ as *mut _,
8283 &nc as *const _ as *mut _,
8284 &e as *const _ as *mut _,
8285 ];
8286 unsafe {
8287 self.launch_pdl("rms_norm_q8_1", (nrows as u32, 1, 1), (1024, 1, 1), &mut ps)?;
8288 }
8289 return Ok(());
8290 }
8291 let f = self.func("rms_norm_q8_1");
8292 let cfg = LaunchConfig {
8293 grid_dim: (nrows as u32, 1, 1),
8294 block_dim: (1024, 1, 1),
8295 shared_mem_bytes: 0,
8296 };
8297 let __s_b = self.gpu.stream();
8298 let mut b = __s_b.launch_builder(&f);
8299 b.arg(x).arg(w).arg(&mut *q).arg(&mut *d).arg(&nc).arg(&e);
8300 unsafe {
8301 b.launch(cfg)?;
8302 }
8303 Ok(())
8304 }
8305
8306 pub fn quantize_q8_1_into(
8308 &self,
8309 x: &CudaSlice<f32>,
8310 m: usize,
8311 in_f: usize,
8312 q: &mut CudaSlice<i8>,
8313 d: &mut CudaSlice<f32>,
8314 ) -> Result<(), Box<dyn std::error::Error>> {
8315 let nblk = in_f / 32;
8316 debug_assert!(q.len() >= m * in_f && d.len() >= m * nblk);
8317 let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
8318 let (inf, mi) = (in_f as i32, m as i32);
8319 if Self::pdl_on() && Self::pdl_wb_on() {
8320 use cudarc::driver::{DevicePtr, DevicePtrMut};
8321 let s = &self.gpu.stream();
8322 let (px, _g0) = x.device_ptr(s);
8323 let (pq, _g1) = q.device_ptr_mut(s);
8324 let (pd, _g2) = d.device_ptr_mut(s);
8325 let mut ps = [
8326 &px as *const _ as *mut std::ffi::c_void,
8327 &pq as *const _ as *mut _,
8328 &pd as *const _ as *mut _,
8329 &inf as *const _ as *mut _,
8330 &mi as *const _ as *mut _,
8331 ];
8332 unsafe {
8333 self.launch_pdl("quantize_q8_1", cfg.grid_dim, cfg.block_dim, &mut ps)?;
8334 }
8335 return Ok(());
8336 }
8337 let f = self.func("quantize_q8_1");
8338 let __s_b = self.gpu.stream();
8339 let mut b = __s_b.launch_builder(&f);
8340 b.arg(x).arg(&mut *q).arg(&mut *d).arg(&inf).arg(&mi);
8341 unsafe {
8342 b.launch(cfg)?;
8343 }
8344 Ok(())
8345 }
8346
8347 pub fn add_rms_norm_q8_1(
8351 &self,
8352 a: &CudaSlice<f32>,
8353 b_in: &CudaSlice<f32>,
8354 w: &CudaSlice<f32>,
8355 res: &mut CudaSlice<f32>,
8356 ncols: usize,
8357 nrows: usize,
8358 eps: f32,
8359 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8360 let nblk = ncols / 32;
8361 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
8362 let mut d = self.alloc_uninit::<f32>(nrows * nblk)?;
8363 let f = self.func("add_rms_norm_q8_1");
8364 let cfg = LaunchConfig {
8366 grid_dim: (nrows as u32, 1, 1),
8367 block_dim: (1024, 1, 1),
8368 shared_mem_bytes: 0,
8369 };
8370 let (nc, e) = (ncols as i32, eps);
8371 let __s_bld = self.gpu.stream();
8372 let mut bld = __s_bld.launch_builder(&f);
8373 bld.arg(a)
8374 .arg(b_in)
8375 .arg(w)
8376 .arg(res)
8377 .arg(&mut q)
8378 .arg(&mut d)
8379 .arg(&nc)
8380 .arg(&e);
8381 unsafe {
8382 bld.launch(cfg)?;
8383 }
8384 Ok((q, d))
8385 }
8386
8387 pub fn add_rms_norm(
8391 &self,
8392 a: &CudaSlice<f32>,
8393 b: &CudaSlice<f32>,
8394 w: &CudaSlice<f32>,
8395 res: &mut CudaSlice<f32>,
8396 dst: &mut CudaSlice<f32>,
8397 ncols: usize,
8398 nrows: usize,
8399 eps: f32,
8400 ) -> Result<(), Box<dyn std::error::Error>> {
8401 let (nc, e) = (ncols as i32, eps);
8402 if Self::pdl_on() && Self::pdl_wb_on() {
8403 use cudarc::driver::{DevicePtr, DevicePtrMut};
8404 let s = &self.gpu.stream();
8405 let (pa, _g0) = a.device_ptr(s);
8406 let (pb, _g1) = b.device_ptr(s);
8407 let (pw, _g2) = w.device_ptr(s);
8408 let (pr, _g3) = res.device_ptr_mut(s);
8409 let (pd, _g4) = dst.device_ptr_mut(s);
8410 let mut ps = [
8411 &pa as *const _ as *mut std::ffi::c_void,
8412 &pb as *const _ as *mut _,
8413 &pw as *const _ as *mut _,
8414 &pr as *const _ as *mut _,
8415 &pd as *const _ as *mut _,
8416 &nc as *const _ as *mut _,
8417 &e as *const _ as *mut _,
8418 ];
8419 unsafe {
8420 self.launch_pdl(
8421 "add_rms_norm_f32",
8422 (nrows as u32, 1, 1),
8423 (rms_block(), 1, 1),
8424 &mut ps,
8425 )?;
8426 }
8427 return Ok(());
8428 }
8429 let f = self.func("add_rms_norm_f32");
8430 let cfg = LaunchConfig {
8431 grid_dim: (nrows as u32, 1, 1),
8432 block_dim: (rms_block(), 1, 1),
8433 shared_mem_bytes: 0,
8434 };
8435 let __s_b2 = self.gpu.stream();
8436 let mut b2 = __s_b2.launch_builder(&f);
8437 b2.arg(a)
8438 .arg(b)
8439 .arg(w)
8440 .arg(&mut *res)
8441 .arg(&mut *dst)
8442 .arg(&nc)
8443 .arg(&e);
8444 unsafe {
8445 b2.launch(cfg)?;
8446 }
8447 Ok(())
8448 }
8449
8450 #[allow(clippy::too_many_arguments)]
8453 pub fn rms_pre_add_rms_norm(
8454 &self,
8455 a: &CudaSlice<f32>,
8456 wa: &CudaSlice<f32>,
8457 b: &CudaSlice<f32>,
8458 w: &CudaSlice<f32>,
8459 res: &mut CudaSlice<f32>,
8460 dst: &mut CudaSlice<f32>,
8461 ncols: usize,
8462 nrows: usize,
8463 eps: f32,
8464 ) -> Result<(), Box<dyn std::error::Error>> {
8465 let f = self.func("rms_pre_add_rms_norm_f32");
8466 let cfg = LaunchConfig {
8467 grid_dim: (nrows as u32, 1, 1),
8468 block_dim: (rms_block(), 1, 1),
8469 shared_mem_bytes: 0,
8470 };
8471 let (nc, e) = (ncols as i32, eps);
8472 let __s_b2 = self.gpu.stream();
8473 let mut b2 = __s_b2.launch_builder(&f);
8474 b2.arg(a)
8475 .arg(wa)
8476 .arg(b)
8477 .arg(w)
8478 .arg(&mut *res)
8479 .arg(&mut *dst)
8480 .arg(&nc)
8481 .arg(&e);
8482 unsafe {
8483 b2.launch(cfg)?;
8484 }
8485 Ok(())
8486 }
8487
8488 #[allow(clippy::too_many_arguments)]
8490 pub fn rms_pre_add_rms_norm_q8z(
8491 &self,
8492 a: &CudaSlice<f32>,
8493 wa: &CudaSlice<f32>,
8494 b: &CudaSlice<f32>,
8495 w: &CudaSlice<f32>,
8496 res: &mut CudaSlice<f32>,
8497 dst: &mut CudaSlice<f32>,
8498 ncols: usize,
8499 nrows: usize,
8500 eps: f32,
8501 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8502 debug_assert!(ncols % 128 == 0);
8503 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
8504 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
8505 let (nc, e) = (ncols as i32, eps);
8506 if Self::pdl_on() {
8507 {
8508 use cudarc::driver::{DevicePtr, DevicePtrMut};
8509 let s = &self.gpu.stream();
8510 let (pa, _g0) = a.device_ptr(s);
8511 let (pwa, _g1) = wa.device_ptr(s);
8512 let (pb, _g2) = b.device_ptr(s);
8513 let (pw, _g3) = w.device_ptr(s);
8514 let (pr, _g4) = res.device_ptr_mut(s);
8515 let (pdst, _g5) = dst.device_ptr_mut(s);
8516 let (pq, _g6) = out_q.device_ptr_mut(s);
8517 let (pd, _g7) = out_d.device_ptr_mut(s);
8518 let mut ps = [
8519 &pa as *const _ as *mut std::ffi::c_void,
8520 &pwa as *const _ as *mut _,
8521 &pb as *const _ as *mut _,
8522 &pw as *const _ as *mut _,
8523 &pr as *const _ as *mut _,
8524 &pdst as *const _ as *mut _,
8525 &pq as *const _ as *mut _,
8526 &pd as *const _ as *mut _,
8527 &nc as *const _ as *mut _,
8528 &e as *const _ as *mut _,
8529 ];
8530 unsafe {
8531 self.launch_pdl(
8532 "rms_pre_add_rms_norm_q8z_f32",
8533 (nrows as u32, 1, 1),
8534 (rms_block(), 1, 1),
8535 &mut ps,
8536 )?;
8537 }
8538 }
8539 return Ok((out_q, out_d));
8540 }
8541 let f = self.func("rms_pre_add_rms_norm_q8z_f32");
8542 let cfg = LaunchConfig {
8543 grid_dim: (nrows as u32, 1, 1),
8544 block_dim: (rms_block(), 1, 1),
8545 shared_mem_bytes: 0,
8546 };
8547 let __s_b2 = self.gpu.stream();
8548 let mut b2 = __s_b2.launch_builder(&f);
8549 b2.arg(a)
8550 .arg(wa)
8551 .arg(b)
8552 .arg(w)
8553 .arg(&mut *res)
8554 .arg(&mut *dst)
8555 .arg(&mut out_q)
8556 .arg(&mut out_d)
8557 .arg(&nc)
8558 .arg(&e);
8559 unsafe {
8560 b2.launch(cfg)?;
8561 }
8562 Ok((out_q, out_d))
8563 }
8564
8565 #[allow(clippy::too_many_arguments)]
8569 pub fn rms_pre_add_rms_norm_q8z_into(
8570 &self,
8571 a: &CudaSlice<f32>,
8572 wa: &CudaSlice<f32>,
8573 b: &CudaSlice<f32>,
8574 w: &CudaSlice<f32>,
8575 res: &mut CudaSlice<f32>,
8576 dst: &mut CudaSlice<f32>,
8577 ncols: usize,
8578 nrows: usize,
8579 eps: f32,
8580 out_q: &mut CudaSlice<i8>,
8581 out_d: &mut CudaSlice<f32>,
8582 ) -> Result<(), Box<dyn std::error::Error>> {
8583 debug_assert!(ncols % 128 == 0);
8584 let (nc, e) = (ncols as i32, eps);
8585 let f = self.func("rms_pre_add_rms_norm_q8z_f32");
8586 let cfg = LaunchConfig {
8587 grid_dim: (nrows as u32, 1, 1),
8588 block_dim: (rms_block(), 1, 1),
8589 shared_mem_bytes: 0,
8590 };
8591 let __s_b = self.gpu.stream();
8592 let mut b2 = __s_b.launch_builder(&f);
8593 b2.arg(a)
8594 .arg(wa)
8595 .arg(b)
8596 .arg(w)
8597 .arg(&mut *res)
8598 .arg(&mut *dst)
8599 .arg(&mut *out_q)
8600 .arg(&mut *out_d)
8601 .arg(&nc)
8602 .arg(&e);
8603 unsafe {
8604 b2.launch(cfg)?;
8605 }
8606 Ok(())
8607 }
8608
8609 #[allow(clippy::too_many_arguments)]
8612 pub fn rms_pre_add_scale_rms_norm_q8_1_into(
8613 &self,
8614 a: &CudaSlice<f32>,
8615 wa: &CudaSlice<f32>,
8616 b_in: &CudaSlice<f32>,
8617 c: f32,
8618 w: &CudaSlice<f32>,
8619 res: &mut CudaSlice<f32>,
8620 ncols: usize,
8621 nrows: usize,
8622 eps: f32,
8623 out_q: &mut CudaSlice<i8>,
8624 out_d: &mut CudaSlice<f32>,
8625 ) -> Result<(), Box<dyn std::error::Error>> {
8626 debug_assert!(ncols % 128 == 0);
8627 let (nc, e2) = (ncols as i32, eps);
8628 let f = self.func("rms_pre_add_scale_rms_norm_q8_1");
8629 let cfg = LaunchConfig {
8630 grid_dim: (nrows as u32, 1, 1),
8631 block_dim: (rms_block(), 1, 1),
8632 shared_mem_bytes: 0,
8633 };
8634 let __s_b = self.gpu.stream();
8635 let mut b2 = __s_b.launch_builder(&f);
8636 b2.arg(a)
8637 .arg(wa)
8638 .arg(b_in)
8639 .arg(&c)
8640 .arg(w)
8641 .arg(&mut *res)
8642 .arg(&mut *out_q)
8643 .arg(&mut *out_d)
8644 .arg(&nc)
8645 .arg(&e2);
8646 unsafe {
8647 b2.launch(cfg)?;
8648 }
8649 Ok(())
8650 }
8651
8652 pub fn g4_pnfold_on() -> bool {
8660 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8661 *ON.get_or_init(|| {
8662 std::env::var("MEMRA_G4_PNFOLD")
8663 .map(|v| v != "0")
8664 .unwrap_or(true)
8665 })
8666 }
8667
8668 pub fn build_q4_out_concat3(
8672 &self,
8673 w0: &crate::model::GpuTensor,
8674 w1: &crate::model::GpuTensor,
8675 w2: &crate::model::GpuTensor,
8676 ) -> Result<Option<crate::model::GpuTensor>, Box<dyn std::error::Error>> {
8677 use crate::model::GpuTensor;
8678 let part = |w: &GpuTensor| -> Option<(usize, usize)> {
8679 match w {
8680 GpuTensor::Quant {
8681 qtype,
8682 row_bytes,
8683 rp,
8684 ..
8685 } if *qtype == QT_Q4_0 && !*rp => Some((*row_bytes, w.out_features())),
8686 _ => None,
8687 }
8688 };
8689 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (part(w0), part(w1), part(w2))
8690 else {
8691 return Ok(None);
8692 };
8693 if rb0 != rb1
8694 || rb0 != rb2
8695 || w0.in_features() != w1.in_features()
8696 || w0.in_features() != w2.in_features()
8697 {
8698 return Ok(None);
8699 }
8700 fn bytes_of(w: &crate::model::GpuTensor) -> &CudaSlice<u8> {
8701 match w {
8702 crate::model::GpuTensor::Quant { bytes, .. } => bytes,
8703 _ => unreachable!(),
8704 }
8705 }
8706 let (b0, b1, b2) = (bytes_of(w0), bytes_of(w1), bytes_of(w2));
8707 let total = rb0 * (o0 + o1 + o2);
8708 let mut cat = self.alloc_u8(total)?;
8709 self.copy_u8_into(&mut cat, 0, b0, rb0 * o0)?;
8710 self.copy_u8_into(&mut cat, rb0 * o0, b1, rb1 * o1)?;
8711 self.copy_u8_into(&mut cat, rb0 * (o0 + o1), b2, rb2 * o2)?;
8712 Ok(Some(GpuTensor::Quant {
8713 bytes: cat,
8714 qtype: QT_Q4_0,
8715 row_bytes: rb0,
8716 ne: vec![w0.in_features() as u64, (o0 + o1 + o2) as u64],
8717 scale: 1.0,
8718 rp: false,
8719 #[cfg(memra_cutlass)]
8720 cutlass: None,
8721 fp8: None,
8722 blk: None,
8723 rp4: None,
8724 f16: None,
8725 }))
8726 }
8727
8728 fn full_width_rope_only(
8746 kernel: &str,
8747 n_rot: usize,
8748 head_dim: usize,
8749 ) -> Result<(), Box<dyn std::error::Error>> {
8750 if n_rot == head_dim {
8751 return Ok(());
8752 }
8753 Err(format!(
8754 "{kernel}: PARTIAL ROTARY REFUSED — n_rot {n_rot} != head_dim {head_dim}. This fused \
8755 rms_norm+qkv+rope kernel carries no n_dims parameter and rotates the full head \
8756 width (half = ncols/2), so it would rotate dims {n_rot}..{head_dim} that must pass \
8757 through unrotated. Use the split path (rms_norm_qkv + rope_neox/rope_neox2 with \
8758 n_dims={n_rot}), or add an n_dims early-return to the kernel and widen this guard."
8759 )
8760 .into())
8761 }
8762
8763 #[allow(clippy::too_many_arguments)]
8767 pub fn rms_norm_qkv_rope_cat(
8768 &self,
8769 qkv: &CudaSlice<f32>,
8770 wq: &CudaSlice<f32>,
8771 wk: &CudaSlice<f32>,
8772 wv: &CudaSlice<f32>,
8773 q: &mut CudaSlice<f32>,
8774 k: &mut CudaSlice<f32>,
8775 v: &mut CudaSlice<f32>,
8776 head_dim: usize,
8777 n_rot: usize,
8778 rq: usize,
8779 rk: usize,
8780 pos: &CudaSlice<i32>,
8781 nh_q: usize,
8782 nh_k: usize,
8783 base: f32,
8784 freq_scale: f32,
8785 ff: Option<&CudaSlice<f32>>,
8786 eps: f32,
8787 ) -> Result<(), Box<dyn std::error::Error>> {
8788 Self::full_width_rope_only("rms_norm_qkv_rope_cat", n_rot, head_dim)?;
8789 let rows = rq + rk + rk;
8790 let theta_scale = base.powf(-2.0 / head_dim as f32);
8791 let (nc, rqi, rki, nhq, nhk) = (
8792 head_dim as i32,
8793 rq as i32,
8794 rk as i32,
8795 nh_q as i32,
8796 nh_k as i32,
8797 );
8798 if Self::pdl_on() {
8799 use cudarc::driver::{DevicePtr, DevicePtrMut};
8800 let s = &self.gpu.stream();
8801 let (pqkv, _g0) = qkv.device_ptr(s);
8802 let (pwq, _g1) = wq.device_ptr(s);
8803 let (pwk, _g2) = wk.device_ptr(s);
8804 let (pwv, _g3) = wv.device_ptr(s);
8805 let (pq, _g4) = q.device_ptr_mut(s);
8806 let (pk, _g5) = k.device_ptr_mut(s);
8807 let (pv, _g6) = v.device_ptr_mut(s);
8808 let (ppos, _g7) = pos.device_ptr(s);
8809 let (pff, _g8) = match ff {
8810 Some(t) => {
8811 let (p, g) = t.device_ptr(s);
8812 (p, Some(g))
8813 }
8814 None => (0, None),
8815 };
8816 let mut ps = [
8817 &pqkv as *const _ as *mut std::ffi::c_void,
8818 &pwq as *const _ as *mut _,
8819 &pwk as *const _ as *mut _,
8820 &pwv as *const _ as *mut _,
8821 &pq as *const _ as *mut _,
8822 &pk as *const _ as *mut _,
8823 &pv as *const _ as *mut _,
8824 &nc as *const _ as *mut _,
8825 &rqi as *const _ as *mut _,
8826 &rki as *const _ as *mut _,
8827 &ppos as *const _ as *mut _,
8828 &nhq as *const _ as *mut _,
8829 &nhk as *const _ as *mut _,
8830 &theta_scale as *const _ as *mut _,
8831 &freq_scale as *const _ as *mut _,
8832 &pff as *const _ as *mut _,
8833 &eps as *const _ as *mut _,
8834 ];
8835 unsafe {
8836 self.launch_pdl(
8837 "rms_norm_qkv_rope_cat_f32",
8838 (rows as u32, 1, 1),
8839 (rms_block(), 1, 1),
8840 &mut ps,
8841 )?;
8842 }
8843 return Ok(());
8844 }
8845 let f = self.func("rms_norm_qkv_rope_cat_f32");
8846 let cfg = LaunchConfig {
8847 grid_dim: (rows as u32, 1, 1),
8848 block_dim: (rms_block(), 1, 1),
8849 shared_mem_bytes: 0,
8850 };
8851 let __s_b = self.gpu.stream();
8852 let mut b = __s_b.launch_builder(&f);
8853 match ff {
8854 Some(t) => {
8855 b.arg(qkv)
8856 .arg(wq)
8857 .arg(wk)
8858 .arg(wv)
8859 .arg(&mut *q)
8860 .arg(&mut *k)
8861 .arg(&mut *v)
8862 .arg(&nc)
8863 .arg(&rqi)
8864 .arg(&rki)
8865 .arg(pos)
8866 .arg(&nhq)
8867 .arg(&nhk)
8868 .arg(&theta_scale)
8869 .arg(&freq_scale)
8870 .arg(t)
8871 .arg(&eps);
8872 unsafe {
8873 b.launch(cfg)?;
8874 }
8875 }
8876 None => {
8877 let null: u64 = 0;
8878 b.arg(qkv)
8879 .arg(wq)
8880 .arg(wk)
8881 .arg(wv)
8882 .arg(&mut *q)
8883 .arg(&mut *k)
8884 .arg(&mut *v)
8885 .arg(&nc)
8886 .arg(&rqi)
8887 .arg(&rki)
8888 .arg(pos)
8889 .arg(&nhq)
8890 .arg(&nhk)
8891 .arg(&theta_scale)
8892 .arg(&freq_scale)
8893 .arg(&null)
8894 .arg(&eps);
8895 unsafe {
8896 b.launch(cfg)?;
8897 }
8898 }
8899 }
8900 Ok(())
8901 }
8902
8903 #[allow(clippy::too_many_arguments)]
8907 pub fn rms_norm_qkv_rope(
8908 &self,
8909 q0: &CudaSlice<f32>,
8910 k0: &CudaSlice<f32>,
8911 v0: &CudaSlice<f32>,
8912 wq: &CudaSlice<f32>,
8913 wk: &CudaSlice<f32>,
8914 wv: &CudaSlice<f32>,
8915 q: &mut CudaSlice<f32>,
8916 k: &mut CudaSlice<f32>,
8917 v: &mut CudaSlice<f32>,
8918 head_dim: usize,
8919 n_rot: usize,
8920 rq: usize,
8921 rk: usize,
8922 pos: &CudaSlice<i32>,
8923 nh_q: usize,
8924 nh_k: usize,
8925 base: f32,
8926 freq_scale: f32,
8927 ff: Option<&CudaSlice<f32>>,
8928 eps: f32,
8929 ) -> Result<(), Box<dyn std::error::Error>> {
8930 Self::full_width_rope_only("rms_norm_qkv_rope", n_rot, head_dim)?;
8931 let f = self.func("rms_norm_qkv_rope_f32");
8932 let rows = rq + rk + rk; let cfg = LaunchConfig {
8934 grid_dim: (rows as u32, 1, 1),
8935 block_dim: (rms_block(), 1, 1),
8936 shared_mem_bytes: 0,
8937 };
8938 let theta_scale = base.powf(-2.0 / head_dim as f32);
8939 let (nc, rqi, rki, nhq, nhk) = (
8940 head_dim as i32,
8941 rq as i32,
8942 rk as i32,
8943 nh_q as i32,
8944 nh_k as i32,
8945 );
8946 let __s_b = self.gpu.stream();
8947 let mut b = __s_b.launch_builder(&f);
8948 match ff {
8949 Some(t) => {
8950 b.arg(q0)
8951 .arg(k0)
8952 .arg(v0)
8953 .arg(wq)
8954 .arg(wk)
8955 .arg(wv)
8956 .arg(&mut *q)
8957 .arg(&mut *k)
8958 .arg(&mut *v)
8959 .arg(&nc)
8960 .arg(&rqi)
8961 .arg(&rki)
8962 .arg(pos)
8963 .arg(&nhq)
8964 .arg(&nhk)
8965 .arg(&theta_scale)
8966 .arg(&freq_scale)
8967 .arg(t)
8968 .arg(&eps);
8969 unsafe {
8970 b.launch(cfg)?;
8971 }
8972 }
8973 None => {
8974 let null: u64 = 0;
8975 b.arg(q0)
8976 .arg(k0)
8977 .arg(v0)
8978 .arg(wq)
8979 .arg(wk)
8980 .arg(wv)
8981 .arg(&mut *q)
8982 .arg(&mut *k)
8983 .arg(&mut *v)
8984 .arg(&nc)
8985 .arg(&rqi)
8986 .arg(&rki)
8987 .arg(pos)
8988 .arg(&nhq)
8989 .arg(&nhk)
8990 .arg(&theta_scale)
8991 .arg(&freq_scale)
8992 .arg(&null)
8993 .arg(&eps);
8994 unsafe {
8995 b.launch(cfg)?;
8996 }
8997 }
8998 }
8999 Ok(())
9000 }
9001
9002 #[allow(clippy::too_many_arguments)]
9008 pub fn rms_norm_qkv_rope_append_dc(
9009 &self,
9010 q0: &CudaSlice<f32>,
9011 k0: &CudaSlice<f32>,
9012 v0: &CudaSlice<f32>,
9013 wq: &CudaSlice<f32>,
9014 wk: &CudaSlice<f32>,
9015 wv: &CudaSlice<f32>,
9016 q: &mut CudaSlice<f32>,
9017 k: &mut CudaSlice<f32>,
9018 v: &mut CudaSlice<f32>,
9019 head_dim: usize,
9020 n_rot: usize,
9021 rq: usize,
9022 rk: usize,
9023 pos: &CudaSlice<i32>,
9024 nh_q: usize,
9025 nh_k: usize,
9026 base: f32,
9027 freq_scale: f32,
9028 ff: Option<&CudaSlice<f32>>,
9029 eps: f32,
9030 kc: &mut CudaSlice<u8>,
9031 vc: &mut CudaSlice<u8>,
9032 t_dev: &CudaSlice<i32>,
9033 k_tok_bytes: usize,
9034 v_tok_bytes: usize,
9035 g: bool,
9036 ) -> Result<(), Box<dyn std::error::Error>> {
9037 Self::full_width_rope_only("rms_norm_qkv_rope_append_dc", n_rot, head_dim)?;
9038 let rows = rq + rk + rk;
9039 let theta_scale = base.powf(-2.0 / head_dim as f32);
9040 let (nc, rqi, rki, nhq, nhk) = (
9041 head_dim as i32,
9042 rq as i32,
9043 rk as i32,
9044 nh_q as i32,
9045 nh_k as i32,
9046 );
9047 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
9048 if Self::pdl_on() && Self::pdl_wb_on() {
9049 use cudarc::driver::{DevicePtr, DevicePtrMut};
9050 let s = &self.gpu.stream();
9051 let (p0, _a0) = q0.device_ptr(s);
9052 let (p1, _a1) = k0.device_ptr(s);
9053 let (p2, _a2) = v0.device_ptr(s);
9054 let (pwq, _a3) = wq.device_ptr(s);
9055 let (pwk, _a4) = wk.device_ptr(s);
9056 let (pwv, _a5) = wv.device_ptr(s);
9057 let (pq, _a6) = q.device_ptr_mut(s);
9058 let (pk, _a7) = k.device_ptr_mut(s);
9059 let (pv, _a8) = v.device_ptr_mut(s);
9060 let (pp, _a9) = pos.device_ptr(s);
9061 let pff: u64 = match ff {
9062 Some(t) => {
9063 let (p, _gg) = t.device_ptr(s);
9064 p as u64
9065 }
9066 None => 0,
9067 };
9068 let (pkc, _a10) = kc.device_ptr_mut(s);
9069 let (pvc, _a11) = vc.device_ptr_mut(s);
9070 let (pt, _a12) = t_dev.device_ptr(s);
9071 let mut ps = [
9072 &p0 as *const _ as *mut std::ffi::c_void,
9073 &p1 as *const _ as *mut _,
9074 &p2 as *const _ as *mut _,
9075 &pwq as *const _ as *mut _,
9076 &pwk as *const _ as *mut _,
9077 &pwv as *const _ as *mut _,
9078 &pq as *const _ as *mut _,
9079 &pk as *const _ as *mut _,
9080 &pv as *const _ as *mut _,
9081 &nc as *const _ as *mut _,
9082 &rqi as *const _ as *mut _,
9083 &rki as *const _ as *mut _,
9084 &pp as *const _ as *mut _,
9085 &nhq as *const _ as *mut _,
9086 &nhk as *const _ as *mut _,
9087 &theta_scale as *const _ as *mut _,
9088 &freq_scale as *const _ as *mut _,
9089 &pff as *const _ as *mut _,
9090 &eps as *const _ as *mut _,
9091 &pkc as *const _ as *mut _,
9092 &pvc as *const _ as *mut _,
9093 &pt as *const _ as *mut _,
9094 &ktb as *const _ as *mut _,
9095 &vtb as *const _ as *mut _,
9096 ];
9097 unsafe {
9098 self.launch_pdl_flash(
9099 g,
9100 "rms_norm_qkv_rope_append_dc_f32",
9101 (rows as u32, 1, 1),
9102 (rms_block(), 1, 1),
9103 0,
9104 &mut ps,
9105 )?;
9106 }
9107 return Ok(());
9108 }
9109 let f = if g {
9110 self.func_g("rms_norm_qkv_rope_append_dc_f32")
9111 } else {
9112 self.func("rms_norm_qkv_rope_append_dc_f32")
9113 };
9114 let cfg = LaunchConfig {
9115 grid_dim: (rows as u32, 1, 1),
9116 block_dim: (rms_block(), 1, 1),
9117 shared_mem_bytes: 0,
9118 };
9119 let __s_b = self.gpu.stream();
9120 let mut b = __s_b.launch_builder(&f);
9121 match ff {
9122 Some(t) => {
9123 b.arg(q0)
9124 .arg(k0)
9125 .arg(v0)
9126 .arg(wq)
9127 .arg(wk)
9128 .arg(wv)
9129 .arg(&mut *q)
9130 .arg(&mut *k)
9131 .arg(&mut *v)
9132 .arg(&nc)
9133 .arg(&rqi)
9134 .arg(&rki)
9135 .arg(pos)
9136 .arg(&nhq)
9137 .arg(&nhk)
9138 .arg(&theta_scale)
9139 .arg(&freq_scale)
9140 .arg(t)
9141 .arg(&eps)
9142 .arg(&mut *kc)
9143 .arg(&mut *vc)
9144 .arg(t_dev)
9145 .arg(&ktb)
9146 .arg(&vtb);
9147 unsafe {
9148 b.launch(cfg)?;
9149 }
9150 }
9151 None => {
9152 let null: u64 = 0;
9153 b.arg(q0)
9154 .arg(k0)
9155 .arg(v0)
9156 .arg(wq)
9157 .arg(wk)
9158 .arg(wv)
9159 .arg(&mut *q)
9160 .arg(&mut *k)
9161 .arg(&mut *v)
9162 .arg(&nc)
9163 .arg(&rqi)
9164 .arg(&rki)
9165 .arg(pos)
9166 .arg(&nhq)
9167 .arg(&nhk)
9168 .arg(&theta_scale)
9169 .arg(&freq_scale)
9170 .arg(&null)
9171 .arg(&eps)
9172 .arg(&mut *kc)
9173 .arg(&mut *vc)
9174 .arg(t_dev)
9175 .arg(&ktb)
9176 .arg(&vtb);
9177 unsafe {
9178 b.launch(cfg)?;
9179 }
9180 }
9181 }
9182 Ok(())
9183 }
9184
9185 #[allow(clippy::too_many_arguments)]
9193 pub fn rms_norm_qkv_rope_append(
9194 &self,
9195 q0: &CudaSlice<f32>,
9196 k0: &CudaSlice<f32>,
9197 v0: &CudaSlice<f32>,
9198 wq: &CudaSlice<f32>,
9199 wk: &CudaSlice<f32>,
9200 wv: &CudaSlice<f32>,
9201 q: &mut CudaSlice<f32>,
9202 k: &mut CudaSlice<f32>,
9203 v: &mut CudaSlice<f32>,
9204 head_dim: usize,
9205 n_rot: usize,
9206 rq: usize,
9207 rk: usize,
9208 pos: &CudaSlice<i32>,
9209 nh_q: usize,
9210 nh_k: usize,
9211 base: f32,
9212 freq_scale: f32,
9213 ff: Option<&CudaSlice<f32>>,
9214 eps: f32,
9215 kc: &mut CudaSlice<u8>,
9216 vc: &mut CudaSlice<u8>,
9217 t: usize,
9218 k_tok_bytes: usize,
9219 v_tok_bytes: usize,
9220 g: bool,
9221 ) -> Result<(), Box<dyn std::error::Error>> {
9222 Self::full_width_rope_only("rms_norm_qkv_rope_append", n_rot, head_dim)?;
9223 let rows = rq + rk + rk;
9224 let theta_scale = base.powf(-2.0 / head_dim as f32);
9225 let (nc, rqi, rki, nhq, nhk) = (
9226 head_dim as i32,
9227 rq as i32,
9228 rk as i32,
9229 nh_q as i32,
9230 nh_k as i32,
9231 );
9232 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
9233 let ti = t as i32;
9234 if Self::pdl_on() && Self::pdl_wb_on() {
9235 use cudarc::driver::{DevicePtr, DevicePtrMut};
9236 let s = &self.gpu.stream();
9237 let (p0, _a0) = q0.device_ptr(s);
9238 let (p1, _a1) = k0.device_ptr(s);
9239 let (p2, _a2) = v0.device_ptr(s);
9240 let (pwq, _a3) = wq.device_ptr(s);
9241 let (pwk, _a4) = wk.device_ptr(s);
9242 let (pwv, _a5) = wv.device_ptr(s);
9243 let (pq, _a6) = q.device_ptr_mut(s);
9244 let (pk, _a7) = k.device_ptr_mut(s);
9245 let (pv, _a8) = v.device_ptr_mut(s);
9246 let (pp, _a9) = pos.device_ptr(s);
9247 let pff: u64 = match ff {
9248 Some(t) => {
9249 let (p, _gg) = t.device_ptr(s);
9250 p as u64
9251 }
9252 None => 0,
9253 };
9254 let (pkc, _a10) = kc.device_ptr_mut(s);
9255 let (pvc, _a11) = vc.device_ptr_mut(s);
9256 let mut ps = [
9257 &p0 as *const _ as *mut std::ffi::c_void,
9258 &p1 as *const _ as *mut _,
9259 &p2 as *const _ as *mut _,
9260 &pwq as *const _ as *mut _,
9261 &pwk as *const _ as *mut _,
9262 &pwv as *const _ as *mut _,
9263 &pq as *const _ as *mut _,
9264 &pk as *const _ as *mut _,
9265 &pv as *const _ as *mut _,
9266 &nc as *const _ as *mut _,
9267 &rqi as *const _ as *mut _,
9268 &rki as *const _ as *mut _,
9269 &pp as *const _ as *mut _,
9270 &nhq as *const _ as *mut _,
9271 &nhk as *const _ as *mut _,
9272 &theta_scale as *const _ as *mut _,
9273 &freq_scale as *const _ as *mut _,
9274 &pff as *const _ as *mut _,
9275 &eps as *const _ as *mut _,
9276 &pkc as *const _ as *mut _,
9277 &pvc as *const _ as *mut _,
9278 &ti as *const _ as *mut _,
9279 &ktb as *const _ as *mut _,
9280 &vtb as *const _ as *mut _,
9281 ];
9282 unsafe {
9283 self.launch_pdl_flash(
9284 g,
9285 "rms_norm_qkv_rope_append_f32",
9286 (rows as u32, 1, 1),
9287 (rms_block(), 1, 1),
9288 0,
9289 &mut ps,
9290 )?;
9291 }
9292 return Ok(());
9293 }
9294 let f = if g {
9295 self.func_g("rms_norm_qkv_rope_append_f32")
9296 } else {
9297 self.func("rms_norm_qkv_rope_append_f32")
9298 };
9299 let cfg = LaunchConfig {
9300 grid_dim: (rows as u32, 1, 1),
9301 block_dim: (rms_block(), 1, 1),
9302 shared_mem_bytes: 0,
9303 };
9304 let __s_b = self.gpu.stream();
9305 let mut b = __s_b.launch_builder(&f);
9306 let null: u64 = 0;
9307 b.arg(q0)
9308 .arg(k0)
9309 .arg(v0)
9310 .arg(wq)
9311 .arg(wk)
9312 .arg(wv)
9313 .arg(&mut *q)
9314 .arg(&mut *k)
9315 .arg(&mut *v)
9316 .arg(&nc)
9317 .arg(&rqi)
9318 .arg(&rki)
9319 .arg(pos)
9320 .arg(&nhq)
9321 .arg(&nhk)
9322 .arg(&theta_scale)
9323 .arg(&freq_scale);
9324 match ff {
9325 Some(t) => {
9326 b.arg(t);
9327 }
9328 None => {
9329 b.arg(&null);
9330 }
9331 }
9332 b.arg(&eps)
9333 .arg(&mut *kc)
9334 .arg(&mut *vc)
9335 .arg(&ti)
9336 .arg(&ktb)
9337 .arg(&vtb);
9338 unsafe {
9339 b.launch(cfg)?;
9340 }
9341 Ok(())
9342 }
9343
9344 pub fn add_q8_1(
9345 &self,
9346 a: &CudaSlice<f32>,
9347 b: &CudaSlice<f32>,
9348 res: &mut CudaSlice<f32>,
9349 ncols: usize,
9350 nrows: usize,
9351 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9352 debug_assert!(ncols % 128 == 0);
9353 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
9354 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9355 let f = self.func("add_q8_1_f32");
9356 let cfg = LaunchConfig {
9357 grid_dim: (nrows as u32, 1, 1),
9358 block_dim: (rms_block(), 1, 1),
9359 shared_mem_bytes: 0,
9360 };
9361 let nc = ncols as i32;
9362 let __s_b2 = self.gpu.stream();
9363 let mut b2 = __s_b2.launch_builder(&f);
9364 b2.arg(a)
9365 .arg(b)
9366 .arg(&mut *res)
9367 .arg(&mut out_q)
9368 .arg(&mut out_d)
9369 .arg(&nc);
9370 unsafe {
9371 b2.launch(cfg)?;
9372 }
9373 Ok((out_q, out_d))
9374 }
9375
9376 pub fn rms_pre_add_q8_1(
9380 &self,
9381 a: &CudaSlice<f32>,
9382 wa: &CudaSlice<f32>,
9383 b: &CudaSlice<f32>,
9384 res: &mut CudaSlice<f32>,
9385 ncols: usize,
9386 nrows: usize,
9387 eps: f32,
9388 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9389 debug_assert!(ncols % 128 == 0);
9390 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
9391 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9392 let f = self.func("rms_pre_add_q8_1_f32");
9393 let cfg = LaunchConfig {
9394 grid_dim: (nrows as u32, 1, 1),
9395 block_dim: (rms_block(), 1, 1),
9396 shared_mem_bytes: 0,
9397 };
9398 let (nc, ep) = (ncols as i32, eps);
9399 let __s_b2 = self.gpu.stream();
9400 let mut b2 = __s_b2.launch_builder(&f);
9401 b2.arg(a)
9402 .arg(wa)
9403 .arg(b)
9404 .arg(&mut *res)
9405 .arg(&mut out_q)
9406 .arg(&mut out_d)
9407 .arg(&nc)
9408 .arg(&ep);
9409 unsafe {
9410 b2.launch(cfg)?;
9411 }
9412 Ok((out_q, out_d))
9413 }
9414
9415 pub fn l2_v2_on(ncols: usize) -> bool {
9419 ncols == 128 && std::env::var("MEMRA_L2_V2").as_deref() != Ok("0")
9420 }
9421
9422 pub fn l2_norm_pp(
9423 &self,
9424 x: &CudaSlice<f32>,
9425 dst: &mut CudaSlice<f32>,
9426 dst16: Option<&mut CudaSlice<u8>>,
9427 ncols: usize,
9428 nrows: usize,
9429 eps: f32,
9430 ) -> Result<(), Box<dyn std::error::Error>> {
9431 if Self::l2_v2_on(ncols) {
9432 let f = self.func("l2_norm_pp_v2_f32");
9433 let rows_per_block = 8u32; let cfg = LaunchConfig {
9435 grid_dim: ((nrows as u32).div_ceil(rows_per_block), 1, 1),
9436 block_dim: (256, 1, 1),
9437 shared_mem_bytes: 0,
9438 };
9439 let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
9440 let d16: u64 = match dst16 {
9442 Some(d) => self.addr_u8(d),
9443 None => 0,
9444 };
9445 let __s_b = self.gpu.stream();
9446 let mut b = __s_b.launch_builder(&f);
9447 b.arg(x).arg(dst).arg(&d16).arg(&nc).arg(&nr).arg(&e);
9448 unsafe {
9449 b.launch(cfg)?;
9450 }
9451 return Ok(());
9452 }
9453 self.l2_norm(x, dst, ncols, nrows, eps)
9454 }
9455
9456 pub fn l2_norm(
9457 &self,
9458 x: &CudaSlice<f32>,
9459 dst: &mut CudaSlice<f32>,
9460 ncols: usize,
9461 nrows: usize,
9462 eps: f32,
9463 ) -> Result<(), Box<dyn std::error::Error>> {
9464 let f = self.func("l2_norm_f32");
9465 let cfg = LaunchConfig {
9466 grid_dim: (nrows as u32, 1, 1),
9467 block_dim: (256, 1, 1),
9468 shared_mem_bytes: 0,
9469 };
9470 let (nc, e) = (ncols as i32, eps);
9471 let __s_b = self.gpu.stream();
9472 let mut b = __s_b.launch_builder(&f);
9473 b.arg(x).arg(dst).arg(&nc).arg(&e);
9474 unsafe {
9475 b.launch(cfg)?;
9476 }
9477 Ok(())
9478 }
9479
9480 pub fn l2_norm_decode(
9486 &self,
9487 x: &CudaSlice<f32>,
9488 dst: &mut CudaSlice<f32>,
9489 ncols: usize,
9490 nrows: usize,
9491 eps: f32,
9492 ) -> Result<(), Box<dyn std::error::Error>> {
9493 let f = self.func("l2_norm_f32");
9494 let cfg = LaunchConfig {
9495 grid_dim: (nrows as u32, 1, 1),
9496 block_dim: (32, 1, 1),
9497 shared_mem_bytes: 0,
9498 };
9499 let (nc, e) = (ncols as i32, eps);
9500 let __s_b = self.gpu.stream();
9501 let mut b = __s_b.launch_builder(&f);
9502 b.arg(x).arg(dst).arg(&nc).arg(&e);
9503 unsafe {
9504 b.launch(cfg)?;
9505 }
9506 Ok(())
9507 }
9508
9509 pub fn rope_neox(
9511 &self,
9512 x: &mut CudaSlice<f32>,
9513 pos: &CudaSlice<i32>,
9514 head_dim: usize,
9515 n_dims: usize,
9516 n_heads: usize,
9517 n_tokens: usize,
9518 freq_base: f32,
9519 freq_scale: f32,
9520 ) -> Result<(), Box<dyn std::error::Error>> {
9521 let f = self.func("rope_neox_f32");
9522 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9523 let grid = (n_heads * n_tokens) as u32;
9524 let cfg = LaunchConfig {
9525 grid_dim: (grid, 1, 1),
9526 block_dim: ((head_dim / 2) as u32, 1, 1),
9527 shared_mem_bytes: 0,
9528 };
9529 let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
9530 let __s_b = self.gpu.stream();
9531 let mut b = __s_b.launch_builder(&f);
9532 b.arg(x)
9533 .arg(pos)
9534 .arg(&hd)
9535 .arg(&nd)
9536 .arg(&nh)
9537 .arg(&theta_scale)
9538 .arg(&freq_scale);
9539 unsafe {
9540 b.launch(cfg)?;
9541 }
9542 Ok(())
9543 }
9544
9545 pub fn rope_neox_ff(
9547 &self,
9548 x: &mut CudaSlice<f32>,
9549 pos: &CudaSlice<i32>,
9550 head_dim: usize,
9551 n_dims: usize,
9552 n_heads: usize,
9553 n_tokens: usize,
9554 freq_base: f32,
9555 freq_scale: f32,
9556 ff: &CudaSlice<f32>,
9557 ) -> Result<(), Box<dyn std::error::Error>> {
9558 let f = self.func("rope_neox_ff_f32");
9559 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9560 let grid = (n_heads * n_tokens) as u32;
9561 let cfg = LaunchConfig {
9562 grid_dim: (grid, 1, 1),
9563 block_dim: ((head_dim / 2) as u32, 1, 1),
9564 shared_mem_bytes: 0,
9565 };
9566 let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
9567 let __s_b = self.gpu.stream();
9568 let mut b = __s_b.launch_builder(&f);
9569 b.arg(x)
9570 .arg(pos)
9571 .arg(&hd)
9572 .arg(&nd)
9573 .arg(&nh)
9574 .arg(&theta_scale)
9575 .arg(&freq_scale)
9576 .arg(ff);
9577 unsafe {
9578 b.launch(cfg)?;
9579 }
9580 Ok(())
9581 }
9582
9583 #[allow(clippy::too_many_arguments)]
9585 pub fn rope_neox2(
9586 &self,
9587 q: &mut CudaSlice<f32>,
9588 k: &mut CudaSlice<f32>,
9589 pos: &CudaSlice<i32>,
9590 head_dim: usize,
9591 n_dims: usize,
9592 nh_q: usize,
9593 nh_k: usize,
9594 n_tokens: usize,
9595 freq_base: f32,
9596 freq_scale: f32,
9597 ff: Option<&CudaSlice<f32>>,
9598 ) -> Result<(), Box<dyn std::error::Error>> {
9599 let f = self.func("rope_neox2_f32");
9600 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9601 let grid = ((nh_q + nh_k) * n_tokens) as u32;
9602 let cfg = LaunchConfig {
9603 grid_dim: (grid, 1, 1),
9604 block_dim: ((head_dim / 2) as u32, 1, 1),
9605 shared_mem_bytes: 0,
9606 };
9607 let (hd, nd, nq, nk, nt) = (
9608 head_dim as i32,
9609 n_dims as i32,
9610 nh_q as i32,
9611 nh_k as i32,
9612 n_tokens as i32,
9613 );
9614 let __s_b = self.gpu.stream();
9615 let mut b = __s_b.launch_builder(&f);
9616 b.arg(q)
9617 .arg(k)
9618 .arg(pos)
9619 .arg(&hd)
9620 .arg(&nd)
9621 .arg(&nq)
9622 .arg(&nk)
9623 .arg(&nt)
9624 .arg(&theta_scale)
9625 .arg(&freq_scale);
9626 match ff {
9627 Some(ffv) => {
9628 b.arg(ffv);
9629 unsafe {
9630 b.launch(cfg)?;
9631 }
9632 }
9633 None => {
9634 let null: u64 = 0;
9635 b.arg(&null);
9636 unsafe {
9637 b.launch(cfg)?;
9638 }
9639 }
9640 }
9641 Ok(())
9642 }
9643
9644 pub fn gelu_tanh_mul(
9646 &self,
9647 gate: &CudaSlice<f32>,
9648 up: &CudaSlice<f32>,
9649 dst: &mut CudaSlice<f32>,
9650 n: usize,
9651 ) -> Result<(), Box<dyn std::error::Error>> {
9652 let f = self.func("gelu_tanh_mul_f32");
9653 let cfg = LaunchConfig::for_num_elems(n as u32);
9654 let ni = n as i32;
9655 let __s_b = self.gpu.stream();
9656 let mut b = __s_b.launch_builder(&f);
9657 b.arg(gate).arg(up).arg(dst).arg(&ni);
9658 unsafe {
9659 b.launch(cfg)?;
9660 }
9661 Ok(())
9662 }
9663
9664 pub fn silu_mul(
9665 &self,
9666 gate: &CudaSlice<f32>,
9667 up: &CudaSlice<f32>,
9668 dst: &mut CudaSlice<f32>,
9669 n: usize,
9670 ) -> Result<(), Box<dyn std::error::Error>> {
9671 let f = self.func("silu_mul_f32");
9672 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9674 let ni = n as i32;
9675 let __s_b = self.gpu.stream();
9676 let mut b = __s_b.launch_builder(&f);
9677 b.arg(gate).arg(up).arg(dst).arg(&ni);
9678 unsafe {
9679 b.launch(cfg)?;
9680 }
9681 Ok(())
9682 }
9683
9684 pub fn silu_mul_f16out(
9687 &self,
9688 gate: &CudaSlice<f32>,
9689 up: &CudaSlice<f32>,
9690 dst: &mut CudaSlice<f32>,
9691 dst16: &mut CudaSlice<u8>,
9692 n: usize,
9693 ) -> Result<(), Box<dyn std::error::Error>> {
9694 let f = self.func("silu_mul_f16out_f32");
9695 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9696 let ni = n as i32;
9697 let __s_b = self.gpu.stream();
9698 let mut b = __s_b.launch_builder(&f);
9699 b.arg(gate).arg(up).arg(dst).arg(dst16).arg(&ni);
9700 unsafe {
9701 b.launch(cfg)?;
9702 }
9703 Ok(())
9704 }
9705
9706 pub fn silu_mul_scaled(
9713 &self,
9714 gate: &CudaSlice<f32>,
9715 up: &CudaSlice<f32>,
9716 gs: f32,
9717 us: f32,
9718 dst: &mut CudaSlice<f32>,
9719 n: usize,
9720 ) -> Result<(), Box<dyn std::error::Error>> {
9721 let f = self.func("silu_mul_scaled_f32");
9722 let cfg = LaunchConfig::for_num_elems(n as u32);
9723 let ni = n as i32;
9724 let (gsf, usf) = (gs, us);
9725 let __s_b = self.gpu.stream();
9726 let mut b = __s_b.launch_builder(&f);
9727 b.arg(gate).arg(up).arg(&gsf).arg(&usf).arg(dst).arg(&ni);
9728 unsafe {
9729 b.launch(cfg)?;
9730 }
9731 Ok(())
9732 }
9733
9734 #[allow(clippy::too_many_arguments)]
9738 pub fn swigluoai_mul_scaled(
9739 &self,
9740 gate: &CudaSlice<f32>,
9741 up: &CudaSlice<f32>,
9742 gs: f32,
9743 us: f32,
9744 alpha: f32,
9745 limit: f32,
9746 dst: &mut CudaSlice<f32>,
9747 n: usize,
9748 ) -> Result<(), Box<dyn std::error::Error>> {
9749 let f = self.func("swigluoai_mul_scaled_f32");
9750 let cfg = LaunchConfig::for_num_elems(n as u32);
9751 let ni = n as i32;
9752 let __s_b = self.gpu.stream();
9753 let mut b = __s_b.launch_builder(&f);
9754 b.arg(gate)
9755 .arg(up)
9756 .arg(&gs)
9757 .arg(&us)
9758 .arg(&alpha)
9759 .arg(&limit)
9760 .arg(dst)
9761 .arg(&ni);
9762 unsafe {
9763 b.launch(cfg)?;
9764 }
9765 Ok(())
9766 }
9767
9768 pub fn silu_mul_scaled_q8_1(
9776 &self,
9777 gate: &CudaSlice<f32>,
9778 up: &CudaSlice<f32>,
9779 gs: f32,
9780 us: f32,
9781 n: usize,
9782 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9783 let f = self.func("silu_mul_scaled_q8_1");
9784 let nblk = n / 32;
9785 let mut aq = self.alloc_uninit::<i8>(n)?; let mut ad = self.alloc_uninit::<f32>(nblk)?; let cfg = LaunchConfig::for_num_elems(n as u32);
9789 let (gsf, usf, ni) = (gs, us, n as i32);
9790 let __s_b = self.gpu.stream();
9791 let mut b = __s_b.launch_builder(&f);
9792 b.arg(gate)
9793 .arg(up)
9794 .arg(&gsf)
9795 .arg(&usf)
9796 .arg(&mut aq)
9797 .arg(&mut ad)
9798 .arg(&ni);
9799 unsafe {
9800 b.launch(cfg)?;
9801 }
9802 Ok((aq, ad))
9803 }
9804
9805 pub fn add(
9806 &self,
9807 a: &CudaSlice<f32>,
9808 b_in: &CudaSlice<f32>,
9809 dst: &mut CudaSlice<f32>,
9810 n: usize,
9811 ) -> Result<(), Box<dyn std::error::Error>> {
9812 let f = self.func("add_f32");
9813 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9815 let ni = n as i32;
9816 let __s_bld = self.gpu.stream();
9817 let mut bld = __s_bld.launch_builder(&f);
9818 bld.arg(a).arg(b_in).arg(dst).arg(&ni);
9819 unsafe {
9820 bld.launch(cfg)?;
9821 }
9822 Ok(())
9823 }
9824
9825 pub fn mul(
9826 &self,
9827 a: &CudaSlice<f32>,
9828 b_in: &CudaSlice<f32>,
9829 dst: &mut CudaSlice<f32>,
9830 n: usize,
9831 ) -> Result<(), Box<dyn std::error::Error>> {
9832 let f = self.func("mul_f32");
9833 let cfg = LaunchConfig::for_num_elems(n as u32);
9834 let ni = n as i32;
9835 let __s_bld = self.gpu.stream();
9836 let mut bld = __s_bld.launch_builder(&f);
9837 bld.arg(a).arg(b_in).arg(dst).arg(&ni);
9838 unsafe {
9839 bld.launch(cfg)?;
9840 }
9841 Ok(())
9842 }
9843
9844 pub fn matmul(
9847 &self,
9848 w: &crate::model::GpuTensor,
9849 x: &CudaSlice<f32>,
9850 m: usize,
9851 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9852 use crate::model::GpuTensor;
9853 let in_f = w.in_features();
9854 let out_f = w.out_features();
9855 #[allow(non_snake_case)]
9863 let GEMM_M_THRESHOLD = if self.verify_exact_on() {
9866 usize::MAX
9867 } else {
9868 16usize
9869 };
9870
9871 const GEMM_MIN_OUT_F: usize = 128; if m >= GEMM_M_THRESHOLD {
9896 if let Some(y) = self.try_fp8_gemm(w, x, m)? {
9897 return Ok(y);
9898 }
9899 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
9906 return Ok(y);
9907 }
9908 if let Some(y) = self.try_f16_gemm(w, x, m)? {
9911 return Ok(y);
9912 }
9913 }
9914 if let GpuTensor::Quant { qtype, .. } = w {
9929 if *qtype == QT_F8_E4M3_BLK {
9930 if m >= GEMM_M_THRESHOLD {
9931 if let Some(y) = self.try_e4m3_blk_prefill(w, x, m)? {
9932 return Ok(y);
9933 }
9934 }
9935 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9936 if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
9937 return Ok(y);
9938 }
9939 }
9940 }
9941 if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.mmq_supports(w) {
9942 return self.qmatvec_mmq(w, x, m);
9943 }
9944 if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.gemm_supports(w) {
9945 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9946 return self.qmatvec_gemm(w, &aq, &ad, m);
9947 }
9948 if m >= GEMM_M_THRESHOLD {
9951 if let Some(y) = self.try_fp4_gemm(w, x, m, in_f, out_f)? {
9952 return Ok(y);
9953 }
9954 }
9955 let fast = std::env::var("MEMRA_FAST").as_deref() != Ok("0");
9959 if m == 1 && fast {
9964 if let GpuTensor::Quant {
9965 bytes,
9966 qtype,
9967 row_bytes,
9968 rp,
9969 rp4,
9970 scale,
9971 ..
9972 } = w
9973 {
9974 if self.mmvq_supports(*qtype) {
9975 let (bytes, rp) = match rp4 {
9979 Some(m4) => (m4, true),
9980 None => (bytes, *rp),
9981 };
9982 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9983 return self.qmatvec_mmvq(
9984 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, rp,
9985 );
9986 }
9987 }
9988 }
9989 if (2..=16).contains(&m)
10005 && fast
10006 && std::env::var("MEMRA_NO_BATCHED").is_err()
10007 && (m <= 4 || Self::b8_enabled())
10008 {
10009 let m_ok = m <= 8
10019 || matches!(w, GpuTensor::Quant { qtype, .. }
10020 if *qtype == QT_Q4_0 || *qtype == QT_Q6_K || *qtype == QT_F8_E4M3
10021 || *qtype == QT_NVFP4 || *qtype == QT_Q4_K || *qtype == QT_Q5_K || *qtype == QT_Q8_0);
10022 if m_ok {
10023 if let GpuTensor::Quant {
10024 bytes,
10025 qtype,
10026 row_bytes,
10027 rp,
10028 rp4,
10029 ..
10030 } = w
10031 {
10032 if self.batched_supports(*qtype) && self.mmvq_supports(*qtype) {
10033 let (bytes, rp) = match rp4 {
10034 Some(m4) => (m4, true),
10035 None => (bytes, *rp),
10036 };
10037 let mcols = Self::batched_mcols(m);
10038 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10039 let mut y = self.qmatvec_mmvq_batched(
10040 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, mcols, 1.0, rp,
10041 )?;
10042 if let GpuTensor::Quant { scale, .. } = w {
10043 if *scale != 1.0 {
10044 self.scale_inplace(&mut y, *scale, m * out_f)?;
10045 }
10046 }
10047 return Ok(y);
10048 }
10049 }
10050 }
10051 }
10052 if fast {
10058 if let GpuTensor::Quant {
10059 bytes,
10060 qtype,
10061 row_bytes,
10062 scale,
10063 ..
10064 } = w
10065 {
10066 if *qtype == QT_F8_E4M3 {
10067 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10068 return self.qmatvec_mmvq(
10069 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, false,
10070 );
10071 }
10072 }
10073 }
10074 let mut y = match w {
10075 GpuTensor::Quant {
10076 bytes,
10077 qtype,
10078 row_bytes,
10079 ..
10080 } if fast && *qtype == QT_Q8_0 => {
10081 self.qmatvec_q8_0_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10082 }
10083 GpuTensor::Quant {
10084 bytes,
10085 qtype,
10086 row_bytes,
10087 ..
10088 } if fast && *qtype == QT_Q4_K => {
10089 self.qmatvec_q4_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10090 }
10091 GpuTensor::Quant {
10092 bytes,
10093 qtype,
10094 row_bytes,
10095 ..
10096 } if fast && *qtype == QT_Q6_K => {
10097 self.qmatvec_q6_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10098 }
10099 GpuTensor::Quant {
10100 bytes,
10101 qtype,
10102 row_bytes,
10103 ..
10104 } if fast && *qtype == QT_Q5_K => {
10105 self.qmatvec_q5_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10106 }
10107 GpuTensor::Quant {
10108 bytes,
10109 qtype,
10110 row_bytes,
10111 ..
10112 } if fast && *qtype == QT_Q3_K => {
10113 self.qmatvec_q3_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10114 }
10115 GpuTensor::Quant {
10116 bytes,
10117 qtype,
10118 row_bytes,
10119 rp,
10120 ..
10121 } if fast && *qtype == QT_NVFP4 => self.qmatvec_dp4a_named(
10122 if *rp {
10123 "qmatvec_nvfp4_dp4a_rp"
10124 } else {
10125 "qmatvec_nvfp4_dp4a"
10126 },
10127 bytes,
10128 x,
10129 m,
10130 in_f,
10131 out_f,
10132 *row_bytes,
10133 )?,
10134 GpuTensor::Quant {
10138 bytes,
10139 qtype,
10140 row_bytes,
10141 ..
10142 } if fast && *qtype == QT_IQ4_XS && Self::iq_fast_enabled() => {
10143 self.qmatvec_iq4_XS_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10144 }
10145 GpuTensor::Quant {
10150 bytes,
10151 qtype,
10152 row_bytes,
10153 rp,
10154 ..
10155 } =>
10156 {
10159 self.qmatvec(
10160 bytes,
10161 x,
10162 m,
10163 in_f,
10164 out_f,
10165 if *rp && *qtype == QT_NVFP4 {
10166 QT_NVFP4_RP
10167 } else {
10168 *qtype
10169 },
10170 *row_bytes,
10171 )?
10172 }
10173 GpuTensor::Float { data, .. } => self.linear(x, data, m, in_f, out_f)?,
10174 GpuTensor::FloatBf16 { data, .. } => {
10177 self.linear_bf16_chunked(x, data, m, in_f, out_f, false)?
10178 }
10179 };
10180 if let GpuTensor::Quant { scale, .. } = w {
10182 if *scale != 1.0 {
10183 self.scale_inplace(&mut y, *scale, m * out_f)?;
10184 }
10185 }
10186 Ok(y)
10187 }
10188
10189 pub fn stage_a_raw_needed() -> bool {
10199 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10200 *ON.get_or_init(|| std::env::var("MEMRA_FAST").as_deref() == Ok("0"))
10201 }
10202
10203 pub fn uses_q8_1_fast(&self, w: &crate::model::GpuTensor) -> bool {
10206 use crate::model::GpuTensor;
10207 if std::env::var("MEMRA_FAST").as_deref() == Ok("0") {
10208 return false;
10209 }
10210 match w {
10211 GpuTensor::Quant { qtype, .. } => {
10218 matches!(
10219 *qtype,
10220 QT_Q8_0
10221 | QT_Q4_K
10222 | QT_Q6_K
10223 | QT_Q5_K
10224 | QT_Q3_K
10225 | QT_NVFP4
10226 | QT_F8_E4M3
10227 | QT_F8_E4M3_BLK
10228 | QT_Q4_0
10229 ) || (*qtype == QT_IQ4_XS && Self::iq_fast_enabled())
10230 }
10231 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
10232 }
10233 }
10234
10235 pub fn matmul_pre(
10240 &self,
10241 w: &crate::model::GpuTensor,
10242 aq: &CudaSlice<i8>,
10243 ad: &CudaSlice<f32>,
10244 x_fallback: &CudaSlice<f32>,
10245 m: usize,
10246 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10247 use crate::model::GpuTensor;
10248 let x_raw_ok = x_fallback.len() >= m * w.in_features();
10254 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
10257 if let Some(y) = self.try_fp8_gemm(w, x_fallback, m)? {
10258 return Ok(y);
10259 }
10260 if let Some(y) = self.try_fp8_blk_mmq(w, x_fallback, m)? {
10263 return Ok(y);
10264 }
10265 if let Some(y) = self.try_f16_gemm(w, x_fallback, m)? {
10267 return Ok(y);
10268 }
10269 }
10270 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
10276 if let Some(y) = self.try_e4m3_blk_prefill(w, x_fallback, m)? {
10277 return Ok(y);
10278 }
10279 }
10280 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
10281 return Ok(y);
10282 }
10283 if m >= 16
10288 && w.out_features() >= 128
10289 && self.mmq_supports(w)
10290 && !self.verify_exact_on()
10291 && x_raw_ok
10292 {
10293 return self.qmatvec_mmq(w, x_fallback, m);
10294 }
10295 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
10298 if let Some(y) =
10299 self.try_fp4_gemm(w, x_fallback, m, w.in_features(), w.out_features())?
10300 {
10301 return Ok(y);
10302 }
10303 }
10304 if m >= 16 && self.gemm_supports(w) && !self.verify_exact_on() {
10307 return self.qmatvec_gemm(w, aq, ad, m);
10308 }
10309 if !self.uses_q8_1_fast(w) {
10328 if !x_raw_ok {
10329 return Err(format!(
10330 "matmul_pre: q8_1-fast is off for this weight but x_fallback holds {} f32 \
10331 (need m*in_f = {}*{} = {}). This call site pre-quantized its activation and \
10332 dropped the f32, so there is nothing to fall back to — pass the real f32 \
10333 activation (see Engine::rms_norm_decode, which is bit-identical to \
10334 rms_norm_q8_1's reduction) or keep the weight on the q8_1 path.",
10335 x_fallback.len(),
10336 m,
10337 w.in_features(),
10338 m * w.in_features()
10339 )
10340 .into());
10341 }
10342 return self.matmul(w, x_fallback, m);
10343 }
10344 let in_f = w.in_features();
10345 let out_f = w.out_features();
10346 let (bytes, qtype, row_bytes, scale, rp) = match w {
10347 GpuTensor::Quant {
10348 bytes,
10349 qtype,
10350 row_bytes,
10351 scale,
10352 rp,
10353 ..
10354 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10355 _ => unreachable!("uses_q8_1_fast guaranteed Quant"),
10356 };
10357 let (mbytes, mrp) = match w {
10360 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10361 _ => (bytes, rp),
10362 };
10363 if m == 1 && self.mmvq_supports(qtype) {
10367 return self.qmatvec_mmvq(mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, mrp);
10368 }
10369 if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
10382 && std::env::var("MEMRA_NO_BATCHED").is_err()
10383 && (m <= 4 || Self::b8_enabled())
10384 && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_NVFP4
10388 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_F8_E4M3 || qtype == QT_Q8_0)
10389 {
10390 let mcols = Self::batched_mcols(m);
10391 return self.qmatvec_mmvq_batched(
10392 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, mrp,
10393 );
10394 }
10395 if qtype == QT_F8_E4M3 || qtype == QT_Q4_0 {
10401 let (b2, r2) = if qtype == QT_Q4_0 {
10402 (mbytes, mrp)
10403 } else {
10404 (bytes, rp)
10405 };
10406 return self.qmatvec_mmvq(b2, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, r2);
10407 }
10408 let name = match qtype {
10409 QT_Q8_0 => "qmatvec_q8_0_dp4a",
10410 QT_Q4_K => "qmatvec_q4_K_dp4a",
10411 QT_Q6_K => "qmatvec_q6_K_dp4a",
10412 QT_Q5_K => "qmatvec_q5_K_dp4a",
10413 QT_Q3_K => "qmatvec_q3_K_dp4a",
10414 QT_NVFP4 => {
10415 if rp {
10416 "qmatvec_nvfp4_dp4a_rp"
10417 } else {
10418 "qmatvec_nvfp4_dp4a"
10419 }
10420 }
10421 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
10422 _ => unreachable!(),
10423 };
10424 let f = self.func(name);
10425 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
10427 grid_dim: (out_f as u32, m as u32, 1),
10428 block_dim: (128, 1, 1),
10429 shared_mem_bytes: 0,
10430 };
10431 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
10432 let __s_b = self.gpu.stream();
10433 let mut b = __s_b.launch_builder(&f);
10434 b.arg(bytes)
10435 .arg(aq)
10436 .arg(ad)
10437 .arg(&mut y)
10438 .arg(&inf)
10439 .arg(&outf)
10440 .arg(&mi)
10441 .arg(&rb);
10442 unsafe {
10443 b.launch(cfg)?;
10444 }
10445 if scale != 1.0 {
10446 self.scale_inplace(&mut y, scale, m * out_f)?;
10447 }
10448 Ok(y)
10449 }
10450
10451 pub fn matmul_decode_exact(
10459 &self,
10460 w: &crate::model::GpuTensor,
10461 x: &CudaSlice<f32>,
10462 m: usize,
10463 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10464 use crate::model::GpuTensor;
10465 if let GpuTensor::Float { data, .. } = w {
10473 return self.linear_decode_exact(x, data, m, w.in_features(), w.out_features());
10474 }
10475 if let GpuTensor::FloatBf16 { data, .. } = w {
10478 let (in_f, out_f) = (w.in_features(), w.out_features());
10479 return self.linear_bf16_chunked(x, data, m, in_f, out_f, true);
10480 }
10481 if !self.uses_q8_1_fast(w) {
10482 return self.matmul(w, x, m);
10483 }
10484 let in_f = w.in_features();
10485 let out_f = w.out_features();
10486 let (bytes, qtype, row_bytes, scale, rp) = match w {
10487 GpuTensor::Quant {
10488 bytes,
10489 qtype,
10490 row_bytes,
10491 scale,
10492 rp,
10493 ..
10494 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10495 _ => return self.matmul(w, x, m),
10496 };
10497 let (bytes, rp) = match w {
10500 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10501 _ => (bytes, rp),
10502 };
10503 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10504 if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
10508 return Ok(y);
10509 }
10510 if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
10519 && std::env::var("MEMRA_NO_BATCHED").is_err()
10520 && (m <= 4 || Self::b8_enabled())
10521 && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_F8_E4M3
10524 || qtype == QT_NVFP4 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_Q8_0)
10525 {
10526 let mcols = Self::batched_mcols(m);
10527 return self.qmatvec_mmvq_batched(
10528 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
10529 );
10530 }
10531 if self.mmvq_supports(qtype) {
10532 return self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
10535 }
10536 self.matmul_pre(w, &aq, &ad, x, m)
10539 }
10540
10541 pub fn matmul_decode_exact_pre(
10551 &self,
10552 w: &crate::model::GpuTensor,
10553 aq: &CudaSlice<i8>,
10554 ad: &CudaSlice<f32>,
10555 m: usize,
10556 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10557 use crate::model::GpuTensor;
10558 debug_assert!(
10559 self.uses_q8_1_fast(w),
10560 "matmul_decode_exact_pre: caller must guarantee q8_1-fast"
10561 );
10562 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
10564 return Ok(y);
10565 }
10566 let in_f = w.in_features();
10567 let out_f = w.out_features();
10568 let (bytes, qtype, row_bytes, scale, rp) = match w {
10569 GpuTensor::Quant {
10570 bytes,
10571 qtype,
10572 row_bytes,
10573 scale,
10574 rp,
10575 ..
10576 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10577 _ => {
10578 return Err(
10579 "matmul_decode_exact_pre: Quant tensor required (q8_1-fast contract)".into(),
10580 );
10581 }
10582 };
10583 let (bytes, rp) = match w {
10585 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10586 _ => (bytes, rp),
10587 };
10588 if (2..=16).contains(&m)
10590 && self.batched_supports(qtype)
10591 && self.mmvq_supports(qtype)
10592 && std::env::var("MEMRA_NO_BATCHED").is_err()
10593 && (m <= 4 || Self::b8_enabled())
10594 && (m <= 8
10595 || qtype == QT_Q4_0
10596 || qtype == QT_Q6_K
10597 || qtype == QT_F8_E4M3
10598 || qtype == QT_NVFP4
10599 || qtype == QT_Q4_K
10600 || qtype == QT_Q5_K
10601 || qtype == QT_Q8_0)
10602 {
10603 let mcols = Self::batched_mcols(m);
10604 return self.qmatvec_mmvq_batched(
10605 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
10606 );
10607 }
10608 if self.mmvq_supports(qtype) {
10609 return self.qmatvec_mmvq(bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
10610 }
10611 let x0 = self.zeros(0)?;
10614 self.matmul_pre(w, aq, ad, &x0, m)
10615 }
10616
10617 pub fn matmul_decode_exact_dual_pre(
10626 &self,
10627 w0: &crate::model::GpuTensor,
10628 w1: &crate::model::GpuTensor,
10629 aq: &CudaSlice<i8>,
10630 ad: &CudaSlice<f32>,
10631 m: usize,
10632 ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
10633 {
10634 use crate::model::GpuTensor;
10635 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10636 let on = *ON.get_or_init(|| {
10637 std::env::var("MEMRA_SPEC_DUAL_T")
10638 .map(|v| v != "0")
10639 .unwrap_or(true)
10640 });
10641 if !on
10642 || !(2..=7).contains(&m)
10643 || std::env::var("MEMRA_NO_BATCHED").is_ok()
10644 || !self.uses_q8_1_fast(w0)
10645 || !self.uses_q8_1_fast(w1)
10646 {
10647 return Ok(None);
10648 }
10649 if !self.mmvq_supports(QT_NVFP4) {
10654 return Ok(None);
10655 }
10656 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10657 if w1.in_features() != in_f || w1.out_features() != out_f {
10658 return Ok(None);
10659 }
10660 let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
10661 (
10662 GpuTensor::Quant {
10663 bytes: b0,
10664 qtype: q0,
10665 row_bytes: rb0,
10666 scale: s0,
10667 rp: rp0,
10668 rp4: None,
10669 ..
10670 },
10671 GpuTensor::Quant {
10672 bytes: b1,
10673 qtype: q1,
10674 row_bytes: rb1,
10675 scale: s1,
10676 rp: rp1,
10677 rp4: None,
10678 ..
10679 },
10680 ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
10681 (b0, b1, *rb0, *s0, *s1, *rp0)
10682 }
10683 _ => return Ok(None),
10684 };
10685 if m > 4 && !(rp && Self::b8_enabled() && std::env::var("MEMRA_B567").as_deref() != Ok("0"))
10688 {
10689 return Ok(None);
10690 }
10691 let (y0, y1) =
10692 self.qmatvec_batched_dual_raw(b0, b1, aq, ad, m, in_f, out_f, row_bytes, rp)?;
10693 Ok(Some(((y0, s0), (y1, s1))))
10694 }
10695
10696 pub fn matmul_decode_exact_dual(
10712 &self,
10713 w0: &crate::model::GpuTensor,
10714 w1: &crate::model::GpuTensor,
10715 x: &CudaSlice<f32>,
10716 m: usize,
10717 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10718 use crate::model::GpuTensor;
10719 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10720 let on = *ON.get_or_init(|| {
10721 std::env::var("MEMRA_SPEC_DUAL_T")
10722 .map(|v| v != "0")
10723 .unwrap_or(true)
10724 });
10725 if !on
10726 || !(2..=4).contains(&m)
10727 || std::env::var("MEMRA_NO_BATCHED").is_ok()
10728 || !self.uses_q8_1_fast(w0)
10729 || !self.uses_q8_1_fast(w1)
10730 {
10731 return Ok(None);
10732 }
10733 if !self.mmvq_supports(QT_NVFP4) {
10738 return Ok(None);
10739 }
10740 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10741 if w1.in_features() != in_f || w1.out_features() != out_f {
10742 return Ok(None);
10743 }
10744 let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
10745 (
10746 GpuTensor::Quant {
10747 bytes: b0,
10748 qtype: q0,
10749 row_bytes: rb0,
10750 scale: s0,
10751 rp: rp0,
10752 rp4: None,
10753 ..
10754 },
10755 GpuTensor::Quant {
10756 bytes: b1,
10757 qtype: q1,
10758 row_bytes: rb1,
10759 scale: s1,
10760 rp: rp1,
10761 rp4: None,
10762 ..
10763 },
10764 ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
10765 (b0, b1, *rb0, *s0, *s1, *rp0)
10766 }
10767 _ => return Ok(None),
10768 };
10769 if std::env::var("MEMRA_DEBUG").is_ok() {
10772 static ONCE: std::sync::Once = std::sync::Once::new();
10773 ONCE.call_once(|| eprintln!("[memra] dual gate+up batched ENGAGED (m={m} rp={rp})"));
10774 }
10775 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10776 let (y0, y1) =
10777 self.qmatvec_batched_dual_raw(b0, b1, &aq, &ad, m, in_f, out_f, row_bytes, rp)?;
10778 let mut y0 = y0;
10779 let mut y1 = y1;
10780 if s0 != 1.0 {
10781 self.scale_inplace(&mut y0, s0, m * out_f)?;
10782 }
10783 if s1 != 1.0 {
10784 self.scale_inplace(&mut y1, s1, m * out_f)?;
10785 }
10786 Ok(Some((y0, y1)))
10787 }
10788
10789 #[allow(clippy::too_many_arguments)]
10794 pub fn qmatvec_batched_dual_raw(
10795 &self,
10796 b0: &CudaSlice<u8>,
10797 b1: &CudaSlice<u8>,
10798 aq: &CudaSlice<i8>,
10799 ad: &CudaSlice<f32>,
10800 m: usize,
10801 in_f: usize,
10802 out_f: usize,
10803 row_bytes: usize,
10804 rp: bool,
10805 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10806 const ROWS_PER_BLOCK: u32 = 4;
10807 let mcols = Self::batched_mcols(m);
10808 let tiny_rp1 = rp
10811 && mcols == 4
10812 && out_f <= 128
10813 && std::env::var("MEMRA_NVFP4_AUX_DUAL").as_deref() != Ok("0");
10814 let (name, rows_per_block) = if tiny_rp1 {
10815 ("qmatvec_nvfp4_mmvq_dual_b4_rp", ROWS_PER_BLOCK)
10816 } else {
10817 match (mcols, rp, m) {
10818 (2, false, _) => ("qmatvec_nvfp4_mmvq_dual_b2", ROWS_PER_BLOCK),
10819 (4, false, _) => ("qmatvec_nvfp4_mmvq_dual_b4_r2", ROWS_PER_BLOCK * 2),
10820 (2, true, _) => ("qmatvec_nvfp4_mmvq_dual_b2_rp", ROWS_PER_BLOCK),
10821 (4, true, _) => ("qmatvec_nvfp4_mmvq_dual_b4_rpr2", ROWS_PER_BLOCK * 2),
10822 (8, true, 5) => ("qmatvec_nvfp4_mmvq_dual_b5_rpr2", ROWS_PER_BLOCK * 2),
10823 (8, true, 6) => ("qmatvec_nvfp4_mmvq_dual_b6_rpr2", ROWS_PER_BLOCK * 2),
10824 (8, true, 7) => ("qmatvec_nvfp4_mmvq_dual_b7_rpr2", ROWS_PER_BLOCK * 2),
10825 _ => {
10826 return Err(
10827 format!("qmatvec_batched_dual_raw: no dual kernel for m {m}").into(),
10828 );
10829 }
10830 }
10831 };
10832 let f = self.func(name);
10833 let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
10834 let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
10835 let cfg = LaunchConfig {
10836 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
10837 block_dim: (32, ROWS_PER_BLOCK, 1),
10838 shared_mem_bytes: 0,
10839 };
10840 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
10841 let __s_b = self.gpu.stream();
10842 let mut b = __s_b.launch_builder(&f);
10843 b.arg(b0)
10844 .arg(b1)
10845 .arg(aq)
10846 .arg(ad)
10847 .arg(&mut y0)
10848 .arg(&mut y1)
10849 .arg(&inf)
10850 .arg(&outf)
10851 .arg(&mi)
10852 .arg(&rb);
10853 unsafe {
10854 b.launch(cfg)?;
10855 }
10856 Ok((y0, y1))
10857 }
10858
10859 pub fn matmul_pre_dual_noscale(
10871 &self,
10872 w0: &crate::model::GpuTensor,
10873 w1: &crate::model::GpuTensor,
10874 aq: &CudaSlice<i8>,
10875 ad: &CudaSlice<f32>,
10876 m: usize,
10877 ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
10878 {
10879 use crate::model::GpuTensor;
10880 if m != 1 || !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
10881 return Ok(None);
10882 }
10883 if !self.mmvq_supports(QT_NVFP4) {
10893 return Ok(None);
10894 }
10895 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10896 if w1.in_features() != in_f || w1.out_features() != out_f {
10897 return Ok(None);
10898 }
10899 let no_mirror =
10912 |w: &crate::model::GpuTensor| !matches!(w, GpuTensor::Quant { rp4: Some(_), .. });
10913 if self.q8_ffn_fuse2_on()
10914 && no_mirror(w0)
10915 && no_mirror(w1)
10916 && let Some([p0, p1]) = self.q8_fused_params(&[w0, w1])
10917 {
10918 let (y0, y1) = self.q8_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2)?;
10919 return Ok(Some(((y0, 1.0), (y1, 1.0))));
10920 }
10921 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
10931 let (y0, y1) =
10932 self.e4m3_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2, 1.0, 1.0)?;
10933 return Ok(Some(((y0, p0.3), (y1, p1.3))));
10934 }
10935 let (b0, q0, rb0, s0, rp0) = match w0 {
10936 GpuTensor::Quant {
10937 bytes,
10938 qtype,
10939 row_bytes,
10940 scale,
10941 rp,
10942 ..
10943 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10944 _ => return Ok(None),
10945 };
10946 let (b1, q1, rb1, s1, rp1) = match w1 {
10947 GpuTensor::Quant {
10948 bytes,
10949 qtype,
10950 row_bytes,
10951 scale,
10952 rp,
10953 ..
10954 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10955 _ => return Ok(None),
10956 };
10957 if q0 != QT_NVFP4 || q1 != QT_NVFP4 || rb0 != rb1 || rp0 != rp1 {
10958 return Ok(None);
10959 }
10960 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
10962 let rows_per_block = ROWS_PER_BLOCK * RPW;
10963 let f = self.func(if rp0 {
10964 "qmatvec_nvfp4_mmvq_dual_mr2_rp"
10965 } else {
10966 "qmatvec_nvfp4_mmvq_dual_mr2"
10967 });
10968 let mut y0 = self.alloc_uninit::<f32>(out_f)?;
10969 let mut y1 = self.alloc_uninit::<f32>(out_f)?;
10970 let cfg = LaunchConfig {
10971 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
10972 block_dim: (32, ROWS_PER_BLOCK, 1),
10973 shared_mem_bytes: 0,
10974 };
10975 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, rb0 as i64);
10976 let one = 1.0f32;
10979 let __s_b = self.gpu.stream();
10980 let mut b = __s_b.launch_builder(&f);
10981 b.arg(b0)
10982 .arg(b1)
10983 .arg(aq)
10984 .arg(ad)
10985 .arg(&mut y0)
10986 .arg(&mut y1)
10987 .arg(&inf)
10988 .arg(&outf)
10989 .arg(&mi)
10990 .arg(&rb)
10991 .arg(&one)
10992 .arg(&one);
10993 unsafe {
10994 b.launch(cfg)?;
10995 }
10996 Ok(Some(((y0, s0), (y1, s1))))
10997 }
10998
10999 #[allow(clippy::too_many_arguments)]
11007 pub fn matmul_nvfp4_fused3(
11008 &self,
11009 w0: &crate::model::GpuTensor,
11010 w1: &crate::model::GpuTensor,
11011 w2: &crate::model::GpuTensor,
11012 aq: &CudaSlice<i8>,
11013 ad: &CudaSlice<f32>,
11014 m: usize,
11015 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11016 {
11017 use crate::model::GpuTensor;
11018 if m != 1
11022 || !self.mmvq_supports(QT_NVFP4)
11023 || !self.uses_q8_1_fast(w0)
11024 || !self.uses_q8_1_fast(w1)
11025 || !self.uses_q8_1_fast(w2)
11026 {
11027 return Ok(None);
11028 }
11029 let unpack = |w: &crate::model::GpuTensor| match w {
11030 GpuTensor::Quant {
11031 bytes,
11032 qtype,
11033 scale,
11034 rp,
11035 ..
11036 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
11037 _ => None,
11038 };
11039 let (Some(p0), Some(p1), Some(p2)) = (unpack(w0), unpack(w1), unpack(w2)) else {
11040 return Ok(None);
11041 };
11042 let in_f = w0.in_features();
11043 if w1.in_features() != in_f || w2.in_features() != in_f {
11044 return Ok(None);
11045 }
11046 let (o0, o1, o2) = (w0.out_features(), w1.out_features(), w2.out_features());
11047 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11049 let rows_pb = ROWS_PER_BLOCK * RPW;
11050 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
11051 let f = self.func("qmatvec_nvfp4_mmvq_fused3_rp");
11052 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11053 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11054 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
11055 let cfg = LaunchConfig {
11056 grid_dim: (nb(o0) + nb(o1) + nb(o2), m as u32, 1),
11057 block_dim: (32, ROWS_PER_BLOCK, 1),
11058 shared_mem_bytes: 0,
11059 };
11060 let (inf, oi0, oi1, oi2, mi) = (in_f as i32, o0 as i32, o1 as i32, o2 as i32, m as i32);
11061 let (b0, b1, b2) = unsafe { (&*p0.0, &*p1.0, &*p2.0) };
11064 let __s_b = self.gpu.stream();
11065 let mut b = __s_b.launch_builder(&f);
11066 b.arg(b0)
11067 .arg(b1)
11068 .arg(b2)
11069 .arg(aq)
11070 .arg(ad)
11071 .arg(&mut y0)
11072 .arg(&mut y1)
11073 .arg(&mut y2)
11074 .arg(&inf)
11075 .arg(&oi0)
11076 .arg(&oi1)
11077 .arg(&oi2)
11078 .arg(&mi)
11079 .arg(&p0.1)
11080 .arg(&p1.1)
11081 .arg(&p2.1);
11082 unsafe {
11083 b.launch(cfg)?;
11084 }
11085 Ok(Some((y0, y1, y2)))
11086 }
11087
11088 pub fn matmul_nvfp4_fused2(
11097 &self,
11098 w0: &crate::model::GpuTensor,
11099 w1: &crate::model::GpuTensor,
11100 aq: &CudaSlice<i8>,
11101 ad: &CudaSlice<f32>,
11102 m: usize,
11103 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11104 use crate::model::GpuTensor;
11105 static FUSED2_OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11106 let off =
11107 *FUSED2_OFF.get_or_init(|| std::env::var("MEMRA_NVFP4_FUSED2").as_deref() == Ok("0"));
11108 if off
11111 || m != 1
11112 || !self.mmvq_supports(QT_NVFP4)
11113 || !self.uses_q8_1_fast(w0)
11114 || !self.uses_q8_1_fast(w1)
11115 {
11116 return Ok(None);
11117 }
11118 let unpack = |w: &crate::model::GpuTensor| match w {
11119 GpuTensor::Quant {
11120 bytes,
11121 qtype,
11122 scale,
11123 rp,
11124 ..
11125 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
11126 _ => None,
11127 };
11128 let (Some(p0), Some(p1)) = (unpack(w0), unpack(w1)) else {
11129 return Ok(None);
11130 };
11131 let in_f = w0.in_features();
11132 if w1.in_features() != in_f {
11133 return Ok(None);
11134 }
11135 let (o0, o1) = (w0.out_features(), w1.out_features());
11136 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11138 let rows_pb = ROWS_PER_BLOCK * RPW;
11139 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
11140 let f = self.func("qmatvec_nvfp4_mmvq_fused2_rp");
11141 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11142 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11143 let cfg = LaunchConfig {
11144 grid_dim: (nb(o0) + nb(o1), m as u32, 1),
11145 block_dim: (32, ROWS_PER_BLOCK, 1),
11146 shared_mem_bytes: 0,
11147 };
11148 let (inf, oi0, oi1, mi) = (in_f as i32, o0 as i32, o1 as i32, m as i32);
11149 let (b0, b1) = unsafe { (&*p0.0, &*p1.0) };
11152 if Self::pdl_on() && Self::pdl_mmvq_on() && Self::pdl_nvfp4q8_on() {
11155 {
11156 use cudarc::driver::{DevicePtr, DevicePtrMut};
11157 let s = &self.gpu.stream();
11158 let (pw0, _g0) = b0.device_ptr(s);
11159 let (pw1, _g1) = b1.device_ptr(s);
11160 let (paq, _g2) = aq.device_ptr(s);
11161 let (pad, _g3) = ad.device_ptr(s);
11162 let (py0, _g4) = y0.device_ptr_mut(s);
11163 let (py1, _g5) = y1.device_ptr_mut(s);
11164 let (s0, s1) = (p0.1, p1.1);
11165 let mut ps = [
11166 &pw0 as *const _ as *mut std::ffi::c_void,
11167 &pw1 as *const _ as *mut _,
11168 &paq as *const _ as *mut _,
11169 &pad as *const _ as *mut _,
11170 &py0 as *const _ as *mut _,
11171 &py1 as *const _ as *mut _,
11172 &inf as *const _ as *mut _,
11173 &oi0 as *const _ as *mut _,
11174 &oi1 as *const _ as *mut _,
11175 &mi as *const _ as *mut _,
11176 &s0 as *const _ as *mut _,
11177 &s1 as *const _ as *mut _,
11178 ];
11179 unsafe {
11180 self.launch_pdl(
11181 "qmatvec_nvfp4_mmvq_fused2_rp",
11182 cfg.grid_dim,
11183 cfg.block_dim,
11184 &mut ps,
11185 )?;
11186 }
11187 }
11188 return Ok(Some((y0, y1)));
11189 }
11190 let __s_b = self.gpu.stream();
11191 let mut b = __s_b.launch_builder(&f);
11192 b.arg(b0)
11193 .arg(b1)
11194 .arg(aq)
11195 .arg(ad)
11196 .arg(&mut y0)
11197 .arg(&mut y1)
11198 .arg(&inf)
11199 .arg(&oi0)
11200 .arg(&oi1)
11201 .arg(&mi)
11202 .arg(&p0.1)
11203 .arg(&p1.1);
11204 unsafe {
11205 b.launch(cfg)?;
11206 }
11207 Ok(Some((y0, y1)))
11208 }
11209
11210 pub fn matmul_nvfp4_fused2_into(
11215 &self,
11216 w0: &crate::model::GpuTensor,
11217 w1: &crate::model::GpuTensor,
11218 aq: &CudaSlice<i8>,
11219 ad: &CudaSlice<f32>,
11220 y0: &mut CudaSlice<f32>,
11221 y1: &mut CudaSlice<f32>,
11222 ) -> Result<bool, Box<dyn std::error::Error>> {
11223 use crate::model::GpuTensor;
11224 static FUSED2_OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11225 let off =
11226 *FUSED2_OFF.get_or_init(|| std::env::var("MEMRA_NVFP4_FUSED2").as_deref() == Ok("0"));
11227 if off
11228 || !self.mmvq_supports(QT_NVFP4)
11229 || !self.uses_q8_1_fast(w0)
11230 || !self.uses_q8_1_fast(w1)
11231 {
11232 return Ok(false);
11233 }
11234 let unpack = |w: &crate::model::GpuTensor| match w {
11235 GpuTensor::Quant {
11236 bytes,
11237 qtype,
11238 scale,
11239 rp,
11240 ..
11241 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
11242 _ => None,
11243 };
11244 let (Some(p0), Some(p1)) = (unpack(w0), unpack(w1)) else {
11245 return Ok(false);
11246 };
11247 let in_f = w0.in_features();
11248 if w1.in_features() != in_f {
11249 return Ok(false);
11250 }
11251 let (o0, o1) = (w0.out_features(), w1.out_features());
11252 if y0.len() < o0 || y1.len() < o1 {
11253 return Ok(false);
11254 }
11255 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11257 let rows_pb = ROWS_PER_BLOCK * RPW;
11258 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
11259 let f = self.func("qmatvec_nvfp4_mmvq_fused2_rp");
11260 let cfg = LaunchConfig {
11261 grid_dim: (nb(o0) + nb(o1), 1, 1),
11262 block_dim: (32, ROWS_PER_BLOCK, 1),
11263 shared_mem_bytes: 0,
11264 };
11265 let (inf, oi0, oi1, mi) = (in_f as i32, o0 as i32, o1 as i32, 1i32);
11266 let (b0, b1) = unsafe { (&*p0.0, &*p1.0) };
11269 let __s_b = self.gpu.stream();
11270 let mut b = __s_b.launch_builder(&f);
11271 b.arg(b0)
11272 .arg(b1)
11273 .arg(aq)
11274 .arg(ad)
11275 .arg(&mut *y0)
11276 .arg(&mut *y1)
11277 .arg(&inf)
11278 .arg(&oi0)
11279 .arg(&oi1)
11280 .arg(&mi)
11281 .arg(&p0.1)
11282 .arg(&p1.1);
11283 unsafe {
11284 b.launch(cfg)?;
11285 }
11286 Ok(true)
11287 }
11288
11289 #[allow(clippy::type_complexity)]
11294 pub fn matmul_nvfp4_fused4(
11295 &self,
11296 w0: &crate::model::GpuTensor,
11297 w1: &crate::model::GpuTensor,
11298 w2: &crate::model::GpuTensor,
11299 w3: &crate::model::GpuTensor,
11300 aq: &CudaSlice<i8>,
11301 ad: &CudaSlice<f32>,
11302 m: usize,
11303 ) -> Result<
11304 Option<(
11305 CudaSlice<f32>,
11306 CudaSlice<f32>,
11307 CudaSlice<f32>,
11308 CudaSlice<f32>,
11309 )>,
11310 Box<dyn std::error::Error>,
11311 > {
11312 use crate::model::GpuTensor;
11313 if m != 1
11315 || std::env::var("MEMRA_NVFP4_FUSED4").as_deref() == Ok("0")
11316 || !self.mmvq_supports(QT_NVFP4)
11317 || !self.uses_q8_1_fast(w0)
11318 || !self.uses_q8_1_fast(w1)
11319 || !self.uses_q8_1_fast(w2)
11320 || !self.uses_q8_1_fast(w3)
11321 {
11322 return Ok(None);
11323 }
11324 let unpack = |w: &crate::model::GpuTensor| match w {
11325 GpuTensor::Quant {
11326 bytes,
11327 qtype,
11328 scale,
11329 rp,
11330 ..
11331 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
11332 _ => None,
11333 };
11334 let (Some(p0), Some(p1), Some(p2), Some(p3)) =
11335 (unpack(w0), unpack(w1), unpack(w2), unpack(w3))
11336 else {
11337 return Ok(None);
11338 };
11339 let in_f = w0.in_features();
11340 if w1.in_features() != in_f || w2.in_features() != in_f || w3.in_features() != in_f {
11341 return Ok(None);
11342 }
11343 let (o0, o1, o2, o3) = (
11344 w0.out_features(),
11345 w1.out_features(),
11346 w2.out_features(),
11347 w3.out_features(),
11348 );
11349 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11351 let rows_pb = ROWS_PER_BLOCK * RPW;
11352 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
11353 let f = self.func("qmatvec_nvfp4_mmvq_fused4_rp");
11354 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11355 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11356 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
11357 let mut y3 = self.alloc_uninit::<f32>(m * o3)?;
11358 let cfg = LaunchConfig {
11359 grid_dim: (nb(o0) + nb(o1) + nb(o2) + nb(o3), m as u32, 1),
11360 block_dim: (32, ROWS_PER_BLOCK, 1),
11361 shared_mem_bytes: 0,
11362 };
11363 let (inf, oi0, oi1, oi2, oi3, mi) = (
11364 in_f as i32,
11365 o0 as i32,
11366 o1 as i32,
11367 o2 as i32,
11368 o3 as i32,
11369 m as i32,
11370 );
11371 let (b0, b1, b2, b3) = unsafe { (&*p0.0, &*p1.0, &*p2.0, &*p3.0) };
11374 let __s_b = self.gpu.stream();
11375 let mut b = __s_b.launch_builder(&f);
11376 b.arg(b0)
11377 .arg(b1)
11378 .arg(b2)
11379 .arg(b3)
11380 .arg(aq)
11381 .arg(ad)
11382 .arg(&mut y0)
11383 .arg(&mut y1)
11384 .arg(&mut y2)
11385 .arg(&mut y3)
11386 .arg(&inf)
11387 .arg(&oi0)
11388 .arg(&oi1)
11389 .arg(&oi2)
11390 .arg(&oi3)
11391 .arg(&mi)
11392 .arg(&p0.1)
11393 .arg(&p1.1)
11394 .arg(&p2.1)
11395 .arg(&p3.1);
11396 unsafe {
11397 b.launch(cfg)?;
11398 }
11399 Ok(Some((y0, y1, y2, y3)))
11400 }
11401
11402 pub fn matmul_q8_fused2(
11410 &self,
11411 w0: &crate::model::GpuTensor,
11412 w1: &crate::model::GpuTensor,
11413 aq: &CudaSlice<i8>,
11414 ad: &CudaSlice<f32>,
11415 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11416 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
11422 return Ok(Some(self.e4m3_fused2_core(
11423 p0.0,
11424 p1.0,
11425 aq,
11426 ad,
11427 w0.in_features(),
11428 p0.1,
11429 p1.1,
11430 p0.2,
11431 p0.3,
11432 p1.3,
11433 )?));
11434 }
11435 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
11436 return Ok(None);
11437 };
11438 Ok(Some(self.q8_fused2_core(
11439 p0.0,
11440 p1.0,
11441 aq,
11442 ad,
11443 w0.in_features(),
11444 p0.1,
11445 p1.1,
11446 p0.2,
11447 )?))
11448 }
11449
11450 #[allow(clippy::too_many_arguments)]
11451 fn q8_fused2_core(
11452 &self,
11453 b0: &CudaSlice<u8>,
11454 b1: &CudaSlice<u8>,
11455 aq: &CudaSlice<i8>,
11456 ad: &CudaSlice<f32>,
11457 in_f: usize,
11458 out0: usize,
11459 out1: usize,
11460 row_bytes: usize,
11461 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11462 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11464 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11465 let f = self.func("qmatvec_q8_0_mmvq_fused2");
11466 let mut y0 = self.alloc_uninit::<f32>(out0)?;
11467 let mut y1 = self.alloc_uninit::<f32>(out1)?;
11468 let cfg = LaunchConfig {
11469 grid_dim: (nb0 + nb1, 1, 1),
11470 block_dim: (32, ROWS_PER_BLOCK, 1),
11471 shared_mem_bytes: 0,
11472 };
11473 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
11474 let __s_b = self.gpu.stream();
11475 let mut b = __s_b.launch_builder(&f);
11476 b.arg(b0)
11477 .arg(b1)
11478 .arg(aq)
11479 .arg(ad)
11480 .arg(&mut y0)
11481 .arg(&mut y1)
11482 .arg(&inf)
11483 .arg(&o0)
11484 .arg(&o1)
11485 .arg(&rbl);
11486 unsafe {
11487 b.launch(cfg)?;
11488 }
11489 Ok((y0, y1))
11490 }
11491
11492 pub fn matmul_q8_fused2_x(
11498 &self,
11499 w0: &crate::model::GpuTensor,
11500 w1: &crate::model::GpuTensor,
11501 x: &CudaSlice<f32>,
11502 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11503 if !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
11504 return Ok(None);
11505 }
11506 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
11507 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
11508 return Ok(Some(self.e4m3_fused2_core(
11509 p0.0,
11510 p1.0,
11511 &aq,
11512 &ad,
11513 w0.in_features(),
11514 p0.1,
11515 p1.1,
11516 p0.2,
11517 p0.3,
11518 p1.3,
11519 )?));
11520 }
11521 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
11522 return Ok(None);
11523 };
11524 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
11525 Ok(Some(self.q8_fused2_core(
11526 p0.0,
11527 p1.0,
11528 &aq,
11529 &ad,
11530 w0.in_features(),
11531 p0.1,
11532 p1.1,
11533 p0.2,
11534 )?))
11535 }
11536
11537 #[allow(clippy::too_many_arguments)]
11540 pub fn qmatvec_q8_fused2_raw(
11541 &self,
11542 b0: &CudaSlice<u8>,
11543 b1: &CudaSlice<u8>,
11544 x: &CudaSlice<f32>,
11545 in_f: usize,
11546 out0: usize,
11547 out1: usize,
11548 row_bytes: usize,
11549 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11550 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
11551 self.q8_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes)
11552 }
11553
11554 pub fn matmul_q4_fused3(
11560 &self,
11561 w0: &crate::model::GpuTensor,
11562 w1: &crate::model::GpuTensor,
11563 w2: &crate::model::GpuTensor,
11564 aq: &CudaSlice<i8>,
11565 ad: &CudaSlice<f32>,
11566 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11567 {
11568 use crate::model::GpuTensor;
11569 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11570 match w {
11571 GpuTensor::Quant {
11572 qtype, row_bytes, ..
11573 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11574 _ => None,
11575 }
11576 };
11577 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
11578 return Ok(None);
11579 };
11580 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
11581 return Ok(None);
11582 }
11583 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11587 match w {
11588 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11589 Some(m) => (m, true),
11590 None => (bytes, *rp),
11591 },
11592 _ => unreachable!(),
11593 }
11594 }
11595 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
11596 if rp0 != rp1 || rp1 != rp2 {
11597 return Ok(None);
11598 }
11599 let rp = rp0;
11600 let rpb: u32 = 4;
11601 let mr1 = rp && Self::q40_mr1_on();
11605 let nb = |o: usize| {
11606 if mr1 {
11607 (o as u32).div_ceil(rpb)
11608 } else {
11609 (o as u32).div_ceil(2).div_ceil(rpb)
11610 }
11611 };
11612 let grid = nb(o0) + nb(o1) + nb(o2);
11613 let mut y0 = self.alloc_uninit::<f32>(o0)?;
11614 let mut y1 = self.alloc_uninit::<f32>(o1)?;
11615 let mut y2 = self.alloc_uninit::<f32>(o2)?;
11616 let f = self.func(if mr1 {
11617 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
11618 } else if rp {
11619 "qmatvec_q4_0_mmvq_fused3_rp"
11620 } else {
11621 "qmatvec_q4_0_mmvq_fused3"
11622 });
11623 let cfg = LaunchConfig {
11624 grid_dim: (grid, 1, 1),
11625 block_dim: (32, rpb, 1),
11626 shared_mem_bytes: 0,
11627 };
11628 let inf = w0.in_features() as i32;
11629 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
11630 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
11631 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11634 {
11635 use cudarc::driver::{DevicePtr, DevicePtrMut};
11636 let s = &self.gpu.stream();
11637 let (p0, _g0) = b0.device_ptr(s);
11638 let (p1, _g1) = b1.device_ptr(s);
11639 let (p2, _g2) = b2.device_ptr(s);
11640 let (paq, _g3) = aq.device_ptr(s);
11641 let (pad, _g4) = ad.device_ptr(s);
11642 let (py0, _g5) = y0.device_ptr_mut(s);
11643 let (py1, _g6) = y1.device_ptr_mut(s);
11644 let (py2, _g7) = y2.device_ptr_mut(s);
11645 let mut ps = [
11646 &p0 as *const _ as *mut std::ffi::c_void,
11647 &p1 as *const _ as *mut _,
11648 &p2 as *const _ as *mut _,
11649 &paq as *const _ as *mut _,
11650 &pad as *const _ as *mut _,
11651 &py0 as *const _ as *mut _,
11652 &py1 as *const _ as *mut _,
11653 &py2 as *const _ as *mut _,
11654 &inf as *const _ as *mut _,
11655 &oo0 as *const _ as *mut _,
11656 &oo1 as *const _ as *mut _,
11657 &oo2 as *const _ as *mut _,
11658 &r0 as *const _ as *mut _,
11659 &r1 as *const _ as *mut _,
11660 &r2 as *const _ as *mut _,
11661 ];
11662 unsafe {
11663 self.launch_pdl(
11664 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
11665 (grid, 1, 1),
11666 (32, rpb, 1),
11667 &mut ps,
11668 )?;
11669 }
11670 }
11671 return Ok(Some((y0, y1, y2)));
11672 }
11673 let __s_b = self.gpu.stream();
11674 let mut b = __s_b.launch_builder(&f);
11675 b.arg(b0)
11676 .arg(b1)
11677 .arg(b2)
11678 .arg(aq)
11679 .arg(ad)
11680 .arg(&mut y0)
11681 .arg(&mut y1)
11682 .arg(&mut y2)
11683 .arg(&inf)
11684 .arg(&oo0)
11685 .arg(&oo1)
11686 .arg(&oo2)
11687 .arg(&r0)
11688 .arg(&r1)
11689 .arg(&r2);
11690 unsafe {
11691 b.launch(cfg)?;
11692 }
11693 Ok(Some((y0, y1, y2)))
11694 }
11695
11696 #[allow(clippy::too_many_arguments)]
11699 pub fn matmul_q4_fused3_into(
11700 &self,
11701 w0: &crate::model::GpuTensor,
11702 w1: &crate::model::GpuTensor,
11703 w2: &crate::model::GpuTensor,
11704 aq: &CudaSlice<i8>,
11705 ad: &CudaSlice<f32>,
11706 y0: &mut CudaSlice<f32>,
11707 y1: &mut CudaSlice<f32>,
11708 y2: &mut CudaSlice<f32>,
11709 ) -> Result<bool, Box<dyn std::error::Error>> {
11710 use crate::model::GpuTensor;
11711 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11712 match w {
11713 GpuTensor::Quant {
11714 qtype, row_bytes, ..
11715 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11716 _ => None,
11717 }
11718 };
11719 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
11720 return Ok(false);
11721 };
11722 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
11723 return Ok(false);
11724 }
11725 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11726 match w {
11727 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11728 Some(m) => (m, true),
11729 None => (bytes, *rp),
11730 },
11731 _ => unreachable!(),
11732 }
11733 }
11734 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
11735 if rp0 != rp1 || rp1 != rp2 {
11736 return Ok(false);
11737 }
11738 let rp = rp0;
11739 let rpb: u32 = 4;
11740 let mr1 = rp && Self::q40_mr1_on();
11741 let nb = |o: usize| {
11742 if mr1 {
11743 (o as u32).div_ceil(rpb)
11744 } else {
11745 (o as u32).div_ceil(2).div_ceil(rpb)
11746 }
11747 };
11748 let grid = nb(o0) + nb(o1) + nb(o2);
11749 debug_assert!(y0.len() >= o0 && y1.len() >= o1 && y2.len() >= o2);
11750 let f = self.func(if mr1 {
11751 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
11752 } else if rp {
11753 "qmatvec_q4_0_mmvq_fused3_rp"
11754 } else {
11755 "qmatvec_q4_0_mmvq_fused3"
11756 });
11757 let cfg = LaunchConfig {
11758 grid_dim: (grid, 1, 1),
11759 block_dim: (32, rpb, 1),
11760 shared_mem_bytes: 0,
11761 };
11762 let inf = w0.in_features() as i32;
11763 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
11764 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
11765 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11767 use cudarc::driver::{DevicePtr, DevicePtrMut};
11768 let s = &self.gpu.stream();
11769 let (p0, _g0) = b0.device_ptr(s);
11770 let (p1, _g1) = b1.device_ptr(s);
11771 let (p2, _g2) = b2.device_ptr(s);
11772 let (paq, _g3) = aq.device_ptr(s);
11773 let (pad, _g4) = ad.device_ptr(s);
11774 let (py0, _g5) = y0.device_ptr_mut(s);
11775 let (py1, _g6) = y1.device_ptr_mut(s);
11776 let (py2, _g7) = y2.device_ptr_mut(s);
11777 let mut ps = [
11778 &p0 as *const _ as *mut std::ffi::c_void,
11779 &p1 as *const _ as *mut _,
11780 &p2 as *const _ as *mut _,
11781 &paq as *const _ as *mut _,
11782 &pad as *const _ as *mut _,
11783 &py0 as *const _ as *mut _,
11784 &py1 as *const _ as *mut _,
11785 &py2 as *const _ as *mut _,
11786 &inf as *const _ as *mut _,
11787 &oo0 as *const _ as *mut _,
11788 &oo1 as *const _ as *mut _,
11789 &oo2 as *const _ as *mut _,
11790 &r0 as *const _ as *mut _,
11791 &r1 as *const _ as *mut _,
11792 &r2 as *const _ as *mut _,
11793 ];
11794 unsafe {
11795 self.launch_pdl(
11796 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
11797 (grid, 1, 1),
11798 (32, rpb, 1),
11799 &mut ps,
11800 )?;
11801 }
11802 return Ok(true);
11803 }
11804 let __s_b = self.gpu.stream();
11805 let mut b = __s_b.launch_builder(&f);
11806 b.arg(b0)
11807 .arg(b1)
11808 .arg(b2)
11809 .arg(aq)
11810 .arg(ad)
11811 .arg(&mut *y0)
11812 .arg(&mut *y1)
11813 .arg(&mut *y2)
11814 .arg(&inf)
11815 .arg(&oo0)
11816 .arg(&oo1)
11817 .arg(&oo2)
11818 .arg(&r0)
11819 .arg(&r1)
11820 .arg(&r2);
11821 unsafe {
11822 b.launch(cfg)?;
11823 }
11824 Ok(true)
11825 }
11826
11827 pub fn matmul_q4_fused2(
11829 &self,
11830 w0: &crate::model::GpuTensor,
11831 w1: &crate::model::GpuTensor,
11832 aq: &CudaSlice<i8>,
11833 ad: &CudaSlice<f32>,
11834 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11835 use crate::model::GpuTensor;
11836 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11837 match w {
11838 GpuTensor::Quant {
11839 qtype, row_bytes, ..
11840 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11841 _ => None,
11842 }
11843 };
11844 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
11845 return Ok(None);
11846 };
11847 if w0.in_features() != w1.in_features() {
11848 return Ok(None);
11849 }
11850 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11852 match w {
11853 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11854 Some(m) => (m, true),
11855 None => (bytes, *rp),
11856 },
11857 _ => unreachable!(),
11858 }
11859 }
11860 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11861 if rp0 != rp1 {
11862 return Ok(None);
11863 }
11864 let rp = rp0;
11865 let rpb: u32 = 4;
11866 let mr1 = rp && Self::q40_mr1_on();
11868 let nb = |o: usize| {
11869 if mr1 {
11870 (o as u32).div_ceil(rpb)
11871 } else {
11872 (o as u32).div_ceil(2).div_ceil(rpb)
11873 }
11874 };
11875 let grid = nb(o0) + nb(o1);
11876 let mut y0 = self.alloc_uninit::<f32>(o0)?;
11877 let mut y1 = self.alloc_uninit::<f32>(o1)?;
11878 let f = self.func(if mr1 {
11879 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
11880 } else if rp {
11881 "qmatvec_q4_0_mmvq_fused2_rp"
11882 } else {
11883 "qmatvec_q4_0_mmvq_fused2"
11884 });
11885 let cfg = LaunchConfig {
11886 grid_dim: (grid, 1, 1),
11887 block_dim: (32, rpb, 1),
11888 shared_mem_bytes: 0,
11889 };
11890 let inf = w0.in_features() as i32;
11891 let (oo0, oo1) = (o0 as i32, o1 as i32);
11892 let (r0, r1) = (rb0 as i64, rb1 as i64);
11893 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11895 {
11896 use cudarc::driver::{DevicePtr, DevicePtrMut};
11897 let s = &self.gpu.stream();
11898 let (p0, _g0) = b0.device_ptr(s);
11899 let (p1, _g1) = b1.device_ptr(s);
11900 let (paq, _g2) = aq.device_ptr(s);
11901 let (pad, _g3) = ad.device_ptr(s);
11902 let (py0, _g4) = y0.device_ptr_mut(s);
11903 let (py1, _g5) = y1.device_ptr_mut(s);
11904 let mut ps = [
11905 &p0 as *const _ as *mut std::ffi::c_void,
11906 &p1 as *const _ as *mut _,
11907 &paq as *const _ as *mut _,
11908 &pad as *const _ as *mut _,
11909 &py0 as *const _ as *mut _,
11910 &py1 as *const _ as *mut _,
11911 &inf as *const _ as *mut _,
11912 &oo0 as *const _ as *mut _,
11913 &oo1 as *const _ as *mut _,
11914 &r0 as *const _ as *mut _,
11915 &r1 as *const _ as *mut _,
11916 ];
11917 unsafe {
11918 self.launch_pdl(
11919 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
11920 (grid, 1, 1),
11921 (32, rpb, 1),
11922 &mut ps,
11923 )?;
11924 }
11925 }
11926 return Ok(Some((y0, y1)));
11927 }
11928 let __s_b = self.gpu.stream();
11929 let mut b = __s_b.launch_builder(&f);
11930 b.arg(b0)
11931 .arg(b1)
11932 .arg(aq)
11933 .arg(ad)
11934 .arg(&mut y0)
11935 .arg(&mut y1)
11936 .arg(&inf)
11937 .arg(&oo0)
11938 .arg(&oo1)
11939 .arg(&r0)
11940 .arg(&r1);
11941 unsafe {
11942 b.launch(cfg)?;
11943 }
11944 Ok(Some((y0, y1)))
11945 }
11946
11947 pub fn matmul_q4_fused2_into(
11949 &self,
11950 w0: &crate::model::GpuTensor,
11951 w1: &crate::model::GpuTensor,
11952 aq: &CudaSlice<i8>,
11953 ad: &CudaSlice<f32>,
11954 y0: &mut CudaSlice<f32>,
11955 y1: &mut CudaSlice<f32>,
11956 ) -> Result<bool, Box<dyn std::error::Error>> {
11957 use crate::model::GpuTensor;
11958 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11959 match w {
11960 GpuTensor::Quant {
11961 qtype, row_bytes, ..
11962 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11963 _ => None,
11964 }
11965 };
11966 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
11967 return Ok(false);
11968 };
11969 if w0.in_features() != w1.in_features() {
11970 return Ok(false);
11971 }
11972 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11973 match w {
11974 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11975 Some(m) => (m, true),
11976 None => (bytes, *rp),
11977 },
11978 _ => unreachable!(),
11979 }
11980 }
11981 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11982 if rp0 != rp1 {
11983 return Ok(false);
11984 }
11985 let rp = rp0;
11986 let rpb: u32 = 4;
11987 let mr1 = rp && Self::q40_mr1_on();
11988 let nb = |o: usize| {
11989 if mr1 {
11990 (o as u32).div_ceil(rpb)
11991 } else {
11992 (o as u32).div_ceil(2).div_ceil(rpb)
11993 }
11994 };
11995 let grid = nb(o0) + nb(o1);
11996 debug_assert!(y0.len() >= o0 && y1.len() >= o1);
11997 let f = self.func(if mr1 {
11998 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
11999 } else if rp {
12000 "qmatvec_q4_0_mmvq_fused2_rp"
12001 } else {
12002 "qmatvec_q4_0_mmvq_fused2"
12003 });
12004 let cfg = LaunchConfig {
12005 grid_dim: (grid, 1, 1),
12006 block_dim: (32, rpb, 1),
12007 shared_mem_bytes: 0,
12008 };
12009 let inf = w0.in_features() as i32;
12010 let (oo0, oo1) = (o0 as i32, o1 as i32);
12011 let (r0, r1) = (rb0 as i64, rb1 as i64);
12012 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
12014 use cudarc::driver::{DevicePtr, DevicePtrMut};
12015 let s = &self.gpu.stream();
12016 let (p0, _g0) = b0.device_ptr(s);
12017 let (p1, _g1) = b1.device_ptr(s);
12018 let (paq, _g2) = aq.device_ptr(s);
12019 let (pad, _g3) = ad.device_ptr(s);
12020 let (py0, _g4) = y0.device_ptr_mut(s);
12021 let (py1, _g5) = y1.device_ptr_mut(s);
12022 let mut ps = [
12023 &p0 as *const _ as *mut std::ffi::c_void,
12024 &p1 as *const _ as *mut _,
12025 &paq as *const _ as *mut _,
12026 &pad as *const _ as *mut _,
12027 &py0 as *const _ as *mut _,
12028 &py1 as *const _ as *mut _,
12029 &inf as *const _ as *mut _,
12030 &oo0 as *const _ as *mut _,
12031 &oo1 as *const _ as *mut _,
12032 &r0 as *const _ as *mut _,
12033 &r1 as *const _ as *mut _,
12034 ];
12035 unsafe {
12036 self.launch_pdl(
12037 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
12038 (grid, 1, 1),
12039 (32, rpb, 1),
12040 &mut ps,
12041 )?;
12042 }
12043 return Ok(true);
12044 }
12045 let __s_b = self.gpu.stream();
12046 let mut b = __s_b.launch_builder(&f);
12047 b.arg(b0)
12048 .arg(b1)
12049 .arg(aq)
12050 .arg(ad)
12051 .arg(&mut *y0)
12052 .arg(&mut *y1)
12053 .arg(&inf)
12054 .arg(&oo0)
12055 .arg(&oo1)
12056 .arg(&r0)
12057 .arg(&r1);
12058 unsafe {
12059 b.launch(cfg)?;
12060 }
12061 Ok(true)
12062 }
12063
12064 pub fn matmul_q4_fused2_batched(
12069 &self,
12070 w0: &crate::model::GpuTensor,
12071 w1: &crate::model::GpuTensor,
12072 aq: &CudaSlice<i8>,
12073 ad: &CudaSlice<f32>,
12074 m: usize,
12075 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
12076 use crate::model::GpuTensor;
12077 if m < 2 || m > 8 {
12078 return Ok(None);
12079 }
12080 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
12081 match w {
12082 GpuTensor::Quant {
12083 qtype, row_bytes, ..
12084 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
12085 _ => None,
12086 }
12087 };
12088 let (Some((rb0, o0)), Some((_rb1, o1))) = (q4(w0), q4(w1)) else {
12089 return Ok(None);
12090 };
12091 if w0.in_features() != w1.in_features() {
12092 return Ok(None);
12093 }
12094 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
12095 match w {
12096 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
12097 Some(mr) => (mr, true),
12098 None => (bytes, *rp),
12099 },
12100 _ => unreachable!(),
12101 }
12102 }
12103 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
12104 if !rp0 || !rp1 {
12105 return Ok(None);
12106 }
12107 let mcols = Self::batched_mcols(m);
12108 let rpb: u32 = 4;
12109 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
12110 let grid = nb(o0) + nb(o1);
12111 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
12112 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
12113 let f = self.func(match mcols {
12114 2 => "qmatvec_q4_0_mmvq_b2_f2_rp",
12115 4 => "qmatvec_q4_0_mmvq_b4_f2_rp",
12116 _ => "qmatvec_q4_0_mmvq_b8_f2_rp",
12117 });
12118 let cfg = LaunchConfig {
12119 grid_dim: (grid, 1, 1),
12120 block_dim: (32, rpb, 1),
12121 shared_mem_bytes: 0,
12122 };
12123 let inf = w0.in_features() as i32;
12124 let (oo0, oo1, mi) = (o0 as i32, o1 as i32, m as i32);
12125 let rb = rb0 as i64;
12126 let __s_b = self.gpu.stream();
12127 let mut b = __s_b.launch_builder(&f);
12128 b.arg(b0)
12129 .arg(b1)
12130 .arg(aq)
12131 .arg(ad)
12132 .arg(&mut y0)
12133 .arg(&mut y1)
12134 .arg(&inf)
12135 .arg(&oo0)
12136 .arg(&oo1)
12137 .arg(&mi)
12138 .arg(&rb);
12139 unsafe {
12140 b.launch(cfg)?;
12141 }
12142 Ok(Some((y0, y1)))
12143 }
12144
12145 #[allow(clippy::too_many_arguments)]
12148 pub fn matmul_q4_fused3_batched(
12149 &self,
12150 w0: &crate::model::GpuTensor,
12151 w1: &crate::model::GpuTensor,
12152 w2: &crate::model::GpuTensor,
12153 aq: &CudaSlice<i8>,
12154 ad: &CudaSlice<f32>,
12155 m: usize,
12156 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
12157 {
12158 use crate::model::GpuTensor;
12159 if m < 2 || m > 8 {
12160 return Ok(None);
12161 }
12162 let q4 = |w: &GpuTensor| -> Option<usize> {
12163 match w {
12164 GpuTensor::Quant { qtype, .. } if *qtype == QT_Q4_0 => Some(w.out_features()),
12165 _ => None,
12166 }
12167 };
12168 let (Some(o0), Some(o1), Some(o2)) = (q4(w0), q4(w1), q4(w2)) else {
12169 return Ok(None);
12170 };
12171 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
12172 return Ok(None);
12173 }
12174 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
12175 match w {
12176 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
12177 Some(mr) => (mr, true),
12178 None => (bytes, *rp),
12179 },
12180 _ => unreachable!(),
12181 }
12182 }
12183 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
12184 if !rp0 || !rp1 || !rp2 {
12185 return Ok(None);
12186 }
12187 let mcols = Self::batched_mcols(m);
12188 let rpb: u32 = 4;
12189 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
12190 let grid = nb(o0) + nb(o1) + nb(o2);
12191 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
12192 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
12193 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
12194 let f = self.func(match mcols {
12195 2 => "qmatvec_q4_0_mmvq_b2_f3_rp",
12196 4 => "qmatvec_q4_0_mmvq_b4_f3_rp",
12197 _ => "qmatvec_q4_0_mmvq_b8_f3_rp",
12198 });
12199 let cfg = LaunchConfig {
12200 grid_dim: (grid, 1, 1),
12201 block_dim: (32, rpb, 1),
12202 shared_mem_bytes: 0,
12203 };
12204 let inf = w0.in_features() as i32;
12205 let (oo0, oo1, oo2, mi) = (o0 as i32, o1 as i32, o2 as i32, m as i32);
12206 let rb = 0i64;
12207 let __s_b = self.gpu.stream();
12208 let mut b = __s_b.launch_builder(&f);
12209 b.arg(b0)
12210 .arg(b1)
12211 .arg(b2)
12212 .arg(aq)
12213 .arg(ad)
12214 .arg(&mut y0)
12215 .arg(&mut y1)
12216 .arg(&mut y2)
12217 .arg(&inf)
12218 .arg(&oo0)
12219 .arg(&oo1)
12220 .arg(&oo2)
12221 .arg(&mi)
12222 .arg(&rb);
12223 unsafe {
12224 b.launch(cfg)?;
12225 }
12226 Ok(Some((y0, y1, y2)))
12227 }
12228
12229 pub fn matmul_q8_fused3(
12230 &self,
12231 w0: &crate::model::GpuTensor,
12232 w1: &crate::model::GpuTensor,
12233 w2: &crate::model::GpuTensor,
12234 aq: &CudaSlice<i8>,
12235 ad: &CudaSlice<f32>,
12236 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
12237 {
12238 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
12241 return Ok(Some(self.e4m3_fused3_core(
12242 p0.0,
12243 p1.0,
12244 p2.0,
12245 aq,
12246 ad,
12247 w0.in_features(),
12248 p0.1,
12249 p1.1,
12250 p2.1,
12251 p0.2,
12252 p0.3,
12253 p1.3,
12254 p2.3,
12255 )?));
12256 }
12257 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
12258 return Ok(None);
12259 };
12260 Ok(Some(self.q8_fused3_core(
12261 p0.0,
12262 p1.0,
12263 p2.0,
12264 aq,
12265 ad,
12266 w0.in_features(),
12267 p0.1,
12268 p1.1,
12269 p2.1,
12270 p0.2,
12271 )?))
12272 }
12273
12274 #[allow(clippy::too_many_arguments)]
12275 fn q8_fused3_core(
12276 &self,
12277 b0: &CudaSlice<u8>,
12278 b1: &CudaSlice<u8>,
12279 b2: &CudaSlice<u8>,
12280 aq: &CudaSlice<i8>,
12281 ad: &CudaSlice<f32>,
12282 in_f: usize,
12283 out0: usize,
12284 out1: usize,
12285 out2: usize,
12286 row_bytes: usize,
12287 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12288 const ROWS_PER_BLOCK: u32 = 4;
12289 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12290 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12291 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12292 let f = self.func("qmatvec_q8_0_mmvq_fused3");
12293 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12294 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12295 let mut y2 = self.alloc_uninit::<f32>(out2)?;
12296 let cfg = LaunchConfig {
12297 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12298 block_dim: (32, ROWS_PER_BLOCK, 1),
12299 shared_mem_bytes: 0,
12300 };
12301 let (inf, o0, o1, o2, rbl) = (
12302 in_f as i32,
12303 out0 as i32,
12304 out1 as i32,
12305 out2 as i32,
12306 row_bytes as i64,
12307 );
12308 let __s_b = self.gpu.stream();
12309 let mut b = __s_b.launch_builder(&f);
12310 b.arg(b0)
12311 .arg(b1)
12312 .arg(b2)
12313 .arg(aq)
12314 .arg(ad)
12315 .arg(&mut y0)
12316 .arg(&mut y1)
12317 .arg(&mut y2)
12318 .arg(&inf)
12319 .arg(&o0)
12320 .arg(&o1)
12321 .arg(&o2)
12322 .arg(&rbl);
12323 unsafe {
12324 b.launch(cfg)?;
12325 }
12326 Ok((y0, y1, y2))
12327 }
12328
12329 #[allow(clippy::too_many_arguments)]
12331 pub fn qmatvec_q8_fused3_raw(
12332 &self,
12333 b0: &CudaSlice<u8>,
12334 b1: &CudaSlice<u8>,
12335 b2: &CudaSlice<u8>,
12336 x: &CudaSlice<f32>,
12337 in_f: usize,
12338 out0: usize,
12339 out1: usize,
12340 out2: usize,
12341 row_bytes: usize,
12342 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12343 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
12344 self.q8_fused3_core(b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes)
12345 }
12346
12347 pub fn matmul_q8_fused2_t(
12358 &self,
12359 w0: &crate::model::GpuTensor,
12360 w1: &crate::model::GpuTensor,
12361 aq: &CudaSlice<i8>,
12362 ad: &CudaSlice<f32>,
12363 m: usize,
12364 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
12365 if !(2..=8).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
12369 return Ok(None);
12370 }
12371 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
12374 if m > 4 && !Self::b8_enabled() {
12375 return Ok(None);
12376 }
12377 return Ok(Some(self.e4m3_fused2_t_core(
12378 p0.0,
12379 p1.0,
12380 aq,
12381 ad,
12382 m,
12383 w0.in_features(),
12384 p0.1,
12385 p1.1,
12386 p0.2,
12387 p0.3,
12388 p1.3,
12389 )?));
12390 }
12391 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
12392 return Ok(None);
12393 };
12394 Ok(Some(self.q8_fused2_t_core(
12395 p0.0,
12396 p1.0,
12397 aq,
12398 ad,
12399 m,
12400 w0.in_features(),
12401 p0.1,
12402 p1.1,
12403 p0.2,
12404 )?))
12405 }
12406
12407 #[allow(clippy::too_many_arguments)]
12408 fn q8_fused2_t_core(
12409 &self,
12410 b0: &CudaSlice<u8>,
12411 b1: &CudaSlice<u8>,
12412 aq: &CudaSlice<i8>,
12413 ad: &CudaSlice<f32>,
12414 m: usize,
12415 in_f: usize,
12416 out0: usize,
12417 out1: usize,
12418 row_bytes: usize,
12419 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12420 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12422 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12423 let f = self.func(match Self::batched_mcols(m) {
12424 2 => "qmatvec_q8_0_mmvq_fused2_b2",
12425 4 => "qmatvec_q8_0_mmvq_fused2_b4",
12426 _ => "qmatvec_q8_0_mmvq_fused2_b8",
12428 });
12429 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12430 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12431 let cfg = LaunchConfig {
12432 grid_dim: (nb0 + nb1, 1, 1),
12433 block_dim: (32, ROWS_PER_BLOCK, 1),
12434 shared_mem_bytes: 0,
12435 };
12436 let (inf, o0, o1, mi, rbl) = (
12437 in_f as i32,
12438 out0 as i32,
12439 out1 as i32,
12440 m as i32,
12441 row_bytes as i64,
12442 );
12443 let __s_b = self.gpu.stream();
12444 let mut b = __s_b.launch_builder(&f);
12445 b.arg(b0)
12446 .arg(b1)
12447 .arg(aq)
12448 .arg(ad)
12449 .arg(&mut y0)
12450 .arg(&mut y1)
12451 .arg(&inf)
12452 .arg(&o0)
12453 .arg(&o1)
12454 .arg(&mi)
12455 .arg(&rbl);
12456 unsafe {
12457 b.launch(cfg)?;
12458 }
12459 Ok((y0, y1))
12460 }
12461
12462 #[allow(clippy::too_many_arguments)]
12465 pub fn qmatvec_q8_fused2_t_raw(
12466 &self,
12467 b0: &CudaSlice<u8>,
12468 b1: &CudaSlice<u8>,
12469 x: &CudaSlice<f32>,
12470 m: usize,
12471 in_f: usize,
12472 out0: usize,
12473 out1: usize,
12474 row_bytes: usize,
12475 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12476 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12477 self.q8_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes)
12478 }
12479
12480 #[allow(clippy::too_many_arguments)]
12483 pub fn matmul_q8_fused3_t(
12484 &self,
12485 w0: &crate::model::GpuTensor,
12486 w1: &crate::model::GpuTensor,
12487 w2: &crate::model::GpuTensor,
12488 aq: &CudaSlice<i8>,
12489 ad: &CudaSlice<f32>,
12490 m: usize,
12491 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
12492 {
12493 if !(2..=4).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
12494 return Ok(None);
12495 }
12496 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
12497 return Ok(Some(self.e4m3_fused3_t_core(
12498 p0.0,
12499 p1.0,
12500 p2.0,
12501 aq,
12502 ad,
12503 m,
12504 w0.in_features(),
12505 p0.1,
12506 p1.1,
12507 p2.1,
12508 p0.2,
12509 p0.3,
12510 p1.3,
12511 p2.3,
12512 )?));
12513 }
12514 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
12515 return Ok(None);
12516 };
12517 Ok(Some(self.q8_fused3_t_core(
12518 p0.0,
12519 p1.0,
12520 p2.0,
12521 aq,
12522 ad,
12523 m,
12524 w0.in_features(),
12525 p0.1,
12526 p1.1,
12527 p2.1,
12528 p0.2,
12529 )?))
12530 }
12531
12532 #[allow(clippy::too_many_arguments)]
12533 fn q8_fused3_t_core(
12534 &self,
12535 b0: &CudaSlice<u8>,
12536 b1: &CudaSlice<u8>,
12537 b2: &CudaSlice<u8>,
12538 aq: &CudaSlice<i8>,
12539 ad: &CudaSlice<f32>,
12540 m: usize,
12541 in_f: usize,
12542 out0: usize,
12543 out1: usize,
12544 out2: usize,
12545 row_bytes: usize,
12546 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12547 const ROWS_PER_BLOCK: u32 = 4;
12548 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12549 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12550 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12551 let f = self.func(if Self::batched_mcols(m) == 2 {
12552 "qmatvec_q8_0_mmvq_fused3_b2"
12553 } else {
12554 "qmatvec_q8_0_mmvq_fused3_b4"
12555 });
12556 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12557 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12558 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
12559 let cfg = LaunchConfig {
12560 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12561 block_dim: (32, ROWS_PER_BLOCK, 1),
12562 shared_mem_bytes: 0,
12563 };
12564 let (inf, o0, o1, o2, mi, rbl) = (
12565 in_f as i32,
12566 out0 as i32,
12567 out1 as i32,
12568 out2 as i32,
12569 m as i32,
12570 row_bytes as i64,
12571 );
12572 let __s_b = self.gpu.stream();
12573 let mut b = __s_b.launch_builder(&f);
12574 b.arg(b0)
12575 .arg(b1)
12576 .arg(b2)
12577 .arg(aq)
12578 .arg(ad)
12579 .arg(&mut y0)
12580 .arg(&mut y1)
12581 .arg(&mut y2)
12582 .arg(&inf)
12583 .arg(&o0)
12584 .arg(&o1)
12585 .arg(&o2)
12586 .arg(&mi)
12587 .arg(&rbl);
12588 unsafe {
12589 b.launch(cfg)?;
12590 }
12591 Ok((y0, y1, y2))
12592 }
12593
12594 #[allow(clippy::too_many_arguments)]
12596 pub fn qmatvec_q8_fused3_t_raw(
12597 &self,
12598 b0: &CudaSlice<u8>,
12599 b1: &CudaSlice<u8>,
12600 b2: &CudaSlice<u8>,
12601 x: &CudaSlice<f32>,
12602 m: usize,
12603 in_f: usize,
12604 out0: usize,
12605 out1: usize,
12606 out2: usize,
12607 row_bytes: usize,
12608 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12609 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12610 self.q8_fused3_t_core(b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes)
12611 }
12612
12613 pub fn q8_ffn_fuse2_on(&self) -> bool {
12617 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12618 *ON.get_or_init(|| std::env::var("MEMRA_Q8_FFN_FUSE2").as_deref() != Ok("0"))
12619 }
12620
12621 #[allow(clippy::type_complexity)]
12627 fn q8_fused_params<'w, const N: usize>(
12628 &self,
12629 ws: &[&'w crate::model::GpuTensor; N],
12630 ) -> Option<[(&'w CudaSlice<u8>, usize, usize); N]> {
12631 use crate::model::GpuTensor;
12632 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
12633 return None;
12634 }
12635 if std::env::var("MEMRA_Q8_DUAL").is_ok_and(|v| v == "0") {
12636 return None;
12637 }
12638 let in_f = ws[0].in_features();
12639 let mut out: [Option<(&CudaSlice<u8>, usize, usize)>; N] = [None; N];
12640 for (i, w) in ws.iter().enumerate() {
12641 match w {
12642 GpuTensor::Quant {
12643 bytes,
12644 qtype,
12645 row_bytes,
12646 scale,
12647 ..
12648 } if *qtype == QT_Q8_0 && *scale == 1.0 && w.in_features() == in_f => {
12649 out[i] = Some((bytes, w.out_features(), *row_bytes))
12650 }
12651 _ => return None,
12652 }
12653 }
12654 Some(out.map(|o| o.unwrap()))
12655 }
12656
12657 pub fn e4m3_dual_on(&self) -> bool {
12660 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12661 *ON.get_or_init(|| std::env::var("MEMRA_E4M3_DUAL").as_deref() != Ok("0"))
12662 }
12663
12664 #[allow(clippy::type_complexity)]
12676 fn e4m3_fused_params<'w, const N: usize>(
12677 &self,
12678 ws: &[&'w crate::model::GpuTensor; N],
12679 ) -> Option<[(&'w CudaSlice<u8>, usize, usize, f32); N]> {
12680 use crate::model::GpuTensor;
12681 if !self.e4m3_dual_on() {
12682 return None;
12683 }
12684 let in_f = ws[0].in_features();
12685 let mut out: [Option<(&CudaSlice<u8>, usize, usize, f32)>; N] = [None; N];
12686 for (i, w) in ws.iter().enumerate() {
12687 match w {
12688 GpuTensor::Quant {
12689 bytes,
12690 qtype,
12691 row_bytes,
12692 scale,
12693 rp,
12694 rp4,
12695 ..
12696 } if *qtype == QT_F8_E4M3
12697 && w.in_features() == in_f
12698 && *row_bytes == in_f
12699 && !*rp
12700 && rp4.is_none() =>
12701 {
12702 out[i] = Some((bytes, w.out_features(), *row_bytes, *scale))
12703 }
12704 _ => return None,
12705 }
12706 }
12707 Some(out.map(|o| o.unwrap()))
12708 }
12709
12710 #[allow(clippy::too_many_arguments)]
12714 fn e4m3_fused2_core(
12715 &self,
12716 b0: &CudaSlice<u8>,
12717 b1: &CudaSlice<u8>,
12718 aq: &CudaSlice<i8>,
12719 ad: &CudaSlice<f32>,
12720 in_f: usize,
12721 out0: usize,
12722 out1: usize,
12723 row_bytes: usize,
12724 ws0: f32,
12725 ws1: f32,
12726 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12727 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12729 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12730 let f = self.func("qmatvec_e4m3_mmvq_fused2");
12731 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12732 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12733 let cfg = LaunchConfig {
12734 grid_dim: (nb0 + nb1, 1, 1),
12735 block_dim: (32, ROWS_PER_BLOCK, 1),
12736 shared_mem_bytes: 0,
12737 };
12738 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
12739 let __s_b = self.gpu.stream();
12740 let mut b = __s_b.launch_builder(&f);
12741 b.arg(b0)
12742 .arg(b1)
12743 .arg(aq)
12744 .arg(ad)
12745 .arg(&mut y0)
12746 .arg(&mut y1)
12747 .arg(&inf)
12748 .arg(&o0)
12749 .arg(&o1)
12750 .arg(&rbl)
12751 .arg(&ws0)
12752 .arg(&ws1);
12753 unsafe {
12754 b.launch(cfg)?;
12755 }
12756 Ok((y0, y1))
12757 }
12758
12759 #[allow(clippy::too_many_arguments)]
12761 fn e4m3_fused3_core(
12762 &self,
12763 b0: &CudaSlice<u8>,
12764 b1: &CudaSlice<u8>,
12765 b2: &CudaSlice<u8>,
12766 aq: &CudaSlice<i8>,
12767 ad: &CudaSlice<f32>,
12768 in_f: usize,
12769 out0: usize,
12770 out1: usize,
12771 out2: usize,
12772 row_bytes: usize,
12773 ws0: f32,
12774 ws1: f32,
12775 ws2: f32,
12776 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12777 const ROWS_PER_BLOCK: u32 = 4;
12778 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12779 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12780 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12781 let f = self.func("qmatvec_e4m3_mmvq_fused3");
12782 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12783 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12784 let mut y2 = self.alloc_uninit::<f32>(out2)?;
12785 let cfg = LaunchConfig {
12786 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12787 block_dim: (32, ROWS_PER_BLOCK, 1),
12788 shared_mem_bytes: 0,
12789 };
12790 let (inf, o0, o1, o2, rbl) = (
12791 in_f as i32,
12792 out0 as i32,
12793 out1 as i32,
12794 out2 as i32,
12795 row_bytes as i64,
12796 );
12797 let __s_b = self.gpu.stream();
12798 let mut b = __s_b.launch_builder(&f);
12799 b.arg(b0)
12800 .arg(b1)
12801 .arg(b2)
12802 .arg(aq)
12803 .arg(ad)
12804 .arg(&mut y0)
12805 .arg(&mut y1)
12806 .arg(&mut y2)
12807 .arg(&inf)
12808 .arg(&o0)
12809 .arg(&o1)
12810 .arg(&o2)
12811 .arg(&rbl)
12812 .arg(&ws0)
12813 .arg(&ws1)
12814 .arg(&ws2);
12815 unsafe {
12816 b.launch(cfg)?;
12817 }
12818 Ok((y0, y1, y2))
12819 }
12820
12821 #[allow(clippy::too_many_arguments)]
12825 fn e4m3_fused2_t_core(
12826 &self,
12827 b0: &CudaSlice<u8>,
12828 b1: &CudaSlice<u8>,
12829 aq: &CudaSlice<i8>,
12830 ad: &CudaSlice<f32>,
12831 m: usize,
12832 in_f: usize,
12833 out0: usize,
12834 out1: usize,
12835 row_bytes: usize,
12836 ws0: f32,
12837 ws1: f32,
12838 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12839 const ROWS_PER_BLOCK: u32 = 4;
12840 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12841 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12842 let f = self.func(match Self::batched_mcols(m) {
12843 2 => "qmatvec_e4m3_mmvq_fused2_b2",
12844 4 => "qmatvec_e4m3_mmvq_fused2_b4",
12845 _ => "qmatvec_e4m3_mmvq_fused2_b8",
12846 });
12847 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12848 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12849 let cfg = LaunchConfig {
12850 grid_dim: (nb0 + nb1, 1, 1),
12851 block_dim: (32, ROWS_PER_BLOCK, 1),
12852 shared_mem_bytes: 0,
12853 };
12854 let (inf, o0, o1, mi, rbl) = (
12855 in_f as i32,
12856 out0 as i32,
12857 out1 as i32,
12858 m as i32,
12859 row_bytes as i64,
12860 );
12861 let __s_b = self.gpu.stream();
12862 let mut b = __s_b.launch_builder(&f);
12863 b.arg(b0)
12864 .arg(b1)
12865 .arg(aq)
12866 .arg(ad)
12867 .arg(&mut y0)
12868 .arg(&mut y1)
12869 .arg(&inf)
12870 .arg(&o0)
12871 .arg(&o1)
12872 .arg(&mi)
12873 .arg(&rbl);
12874 unsafe {
12875 b.launch(cfg)?;
12876 }
12877 if ws0 != 1.0 {
12878 self.scale_inplace(&mut y0, ws0, m * out0)?;
12879 }
12880 if ws1 != 1.0 {
12881 self.scale_inplace(&mut y1, ws1, m * out1)?;
12882 }
12883 Ok((y0, y1))
12884 }
12885
12886 #[allow(clippy::too_many_arguments)]
12888 fn e4m3_fused3_t_core(
12889 &self,
12890 b0: &CudaSlice<u8>,
12891 b1: &CudaSlice<u8>,
12892 b2: &CudaSlice<u8>,
12893 aq: &CudaSlice<i8>,
12894 ad: &CudaSlice<f32>,
12895 m: usize,
12896 in_f: usize,
12897 out0: usize,
12898 out1: usize,
12899 out2: usize,
12900 row_bytes: usize,
12901 ws0: f32,
12902 ws1: f32,
12903 ws2: f32,
12904 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12905 const ROWS_PER_BLOCK: u32 = 4;
12906 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12907 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12908 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12909 let f = self.func(if Self::batched_mcols(m) == 2 {
12910 "qmatvec_e4m3_mmvq_fused3_b2"
12911 } else {
12912 "qmatvec_e4m3_mmvq_fused3_b4"
12913 });
12914 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12915 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12916 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
12917 let cfg = LaunchConfig {
12918 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12919 block_dim: (32, ROWS_PER_BLOCK, 1),
12920 shared_mem_bytes: 0,
12921 };
12922 let (inf, o0, o1, o2, mi, rbl) = (
12923 in_f as i32,
12924 out0 as i32,
12925 out1 as i32,
12926 out2 as i32,
12927 m as i32,
12928 row_bytes as i64,
12929 );
12930 let __s_b = self.gpu.stream();
12931 let mut b = __s_b.launch_builder(&f);
12932 b.arg(b0)
12933 .arg(b1)
12934 .arg(b2)
12935 .arg(aq)
12936 .arg(ad)
12937 .arg(&mut y0)
12938 .arg(&mut y1)
12939 .arg(&mut y2)
12940 .arg(&inf)
12941 .arg(&o0)
12942 .arg(&o1)
12943 .arg(&o2)
12944 .arg(&mi)
12945 .arg(&rbl);
12946 unsafe {
12947 b.launch(cfg)?;
12948 }
12949 if ws0 != 1.0 {
12950 self.scale_inplace(&mut y0, ws0, m * out0)?;
12951 }
12952 if ws1 != 1.0 {
12953 self.scale_inplace(&mut y1, ws1, m * out1)?;
12954 }
12955 if ws2 != 1.0 {
12956 self.scale_inplace(&mut y2, ws2, m * out2)?;
12957 }
12958 Ok((y0, y1, y2))
12959 }
12960
12961 pub fn qmatvec_e4m3_blk_mmvq(
12971 &self,
12972 bytes: &CudaSlice<u8>,
12973 aq: &CudaSlice<i8>,
12974 ad: &CudaSlice<f32>,
12975 scales: &CudaSlice<f32>,
12976 m: usize,
12977 in_f: usize,
12978 out_f: usize,
12979 row_bytes: usize,
12980 scale_cols: usize,
12981 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12982 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_e4m3_blk_mmvq_into(
12984 bytes, aq, ad, scales, m, in_f, out_f, row_bytes, scale_cols, &mut y,
12985 )?;
12986 Ok(y)
12987 }
12988
12989 #[allow(clippy::too_many_arguments)]
12991 pub fn qmatvec_e4m3_blk_mmvq_into(
12992 &self,
12993 bytes: &CudaSlice<u8>,
12994 aq: &CudaSlice<i8>,
12995 ad: &CudaSlice<f32>,
12996 scales: &CudaSlice<f32>,
12997 m: usize,
12998 in_f: usize,
12999 out_f: usize,
13000 row_bytes: usize,
13001 scale_cols: usize,
13002 y: &mut CudaSlice<f32>,
13003 ) -> Result<(), Box<dyn std::error::Error>> {
13004 const ROWS_PER_BLOCK: u32 = 4; let f = self.func("qmatvec_e4m3_blk_mmvq");
13006 let cfg = LaunchConfig {
13007 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), m as u32, 1),
13008 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
13011 let (inf, outf, mi, rb, sc) = (
13012 in_f as i32,
13013 out_f as i32,
13014 m as i32,
13015 row_bytes as i64,
13016 scale_cols as i32,
13017 );
13018 let __s_b = self.gpu.stream();
13019 let mut b = __s_b.launch_builder(&f);
13020 b.arg(bytes)
13021 .arg(aq)
13022 .arg(ad)
13023 .arg(scales)
13024 .arg(&mut *y)
13025 .arg(&inf)
13026 .arg(&outf)
13027 .arg(&mi)
13028 .arg(&rb)
13029 .arg(&sc);
13030 unsafe {
13031 b.launch(cfg)?;
13032 }
13033 Ok(())
13034 }
13035
13036 #[allow(clippy::too_many_arguments)]
13042 pub fn qmatvec_e4m3_blk_mmvq_batched(
13043 &self,
13044 bytes: &CudaSlice<u8>,
13045 aq: &CudaSlice<i8>,
13046 ad: &CudaSlice<f32>,
13047 scales: &CudaSlice<f32>,
13048 m: usize,
13049 in_f: usize,
13050 out_f: usize,
13051 row_bytes: usize,
13052 scale_cols: usize,
13053 mcols: usize,
13054 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13055 const ROWS_PER_BLOCK: u32 = 4; debug_assert!(mcols >= m, "blk batched: mcols {mcols} < m {m}");
13057 let name = match mcols {
13058 2 => "qmatvec_e4m3_blk_mmvq_b2",
13059 4 => "qmatvec_e4m3_blk_mmvq_b4",
13060 8 => "qmatvec_e4m3_blk_mmvq_b8",
13061 16 => "qmatvec_e4m3_blk_mmvq_b16",
13062 _ => {
13063 return Err(
13064 format!("qmatvec_e4m3_blk_mmvq_batched: no kernel for mcols {mcols}").into(),
13065 );
13066 }
13067 };
13068 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13069 let f = self.func(name);
13070 let cfg = LaunchConfig {
13071 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), 1, 1),
13072 block_dim: (32, ROWS_PER_BLOCK, 1),
13073 shared_mem_bytes: 0,
13074 };
13075 let (inf, outf, mi, rb, sc) = (
13076 in_f as i32,
13077 out_f as i32,
13078 m as i32,
13079 row_bytes as i64,
13080 scale_cols as i32,
13081 );
13082 let __s_b = self.gpu.stream();
13083 let mut b = __s_b.launch_builder(&f);
13084 b.arg(bytes)
13085 .arg(aq)
13086 .arg(ad)
13087 .arg(scales)
13088 .arg(&mut y)
13089 .arg(&inf)
13090 .arg(&outf)
13091 .arg(&mi)
13092 .arg(&rb)
13093 .arg(&sc);
13094 unsafe {
13095 b.launch(cfg)?;
13096 }
13097 Ok(y)
13098 }
13099
13100 #[allow(clippy::too_many_arguments)]
13103 pub fn qmatvec_e4m3_blk_batched_raw(
13104 &self,
13105 bytes: &CudaSlice<u8>,
13106 x: &CudaSlice<f32>,
13107 scales: &CudaSlice<f32>,
13108 m: usize,
13109 in_f: usize,
13110 out_f: usize,
13111 row_bytes: usize,
13112 scale_cols: usize,
13113 mcols: usize,
13114 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13115 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13116 self.qmatvec_e4m3_blk_mmvq_batched(
13117 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols, mcols,
13118 )
13119 }
13120
13121 #[allow(clippy::too_many_arguments)]
13124 pub fn qmatvec_e4m3_blk_mmvq_raw(
13125 &self,
13126 bytes: &CudaSlice<u8>,
13127 x: &CudaSlice<f32>,
13128 scales: &CudaSlice<f32>,
13129 m: usize,
13130 in_f: usize,
13131 out_f: usize,
13132 row_bytes: usize,
13133 scale_cols: usize,
13134 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13135 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13136 self.qmatvec_e4m3_blk_mmvq(
13137 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols,
13138 )
13139 }
13140
13141 #[allow(clippy::too_many_arguments)]
13144 pub fn qmatvec_e4m3_fused2_raw(
13145 &self,
13146 b0: &CudaSlice<u8>,
13147 b1: &CudaSlice<u8>,
13148 x: &CudaSlice<f32>,
13149 in_f: usize,
13150 out0: usize,
13151 out1: usize,
13152 row_bytes: usize,
13153 ws0: f32,
13154 ws1: f32,
13155 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13156 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
13157 self.e4m3_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes, ws0, ws1)
13158 }
13159
13160 #[allow(clippy::too_many_arguments)]
13161 pub fn qmatvec_e4m3_fused3_raw(
13162 &self,
13163 b0: &CudaSlice<u8>,
13164 b1: &CudaSlice<u8>,
13165 b2: &CudaSlice<u8>,
13166 x: &CudaSlice<f32>,
13167 in_f: usize,
13168 out0: usize,
13169 out1: usize,
13170 out2: usize,
13171 row_bytes: usize,
13172 ws0: f32,
13173 ws1: f32,
13174 ws2: f32,
13175 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13176 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
13177 self.e4m3_fused3_core(
13178 b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
13179 )
13180 }
13181
13182 #[allow(clippy::too_many_arguments)]
13183 pub fn qmatvec_e4m3_fused2_t_raw(
13184 &self,
13185 b0: &CudaSlice<u8>,
13186 b1: &CudaSlice<u8>,
13187 x: &CudaSlice<f32>,
13188 m: usize,
13189 in_f: usize,
13190 out0: usize,
13191 out1: usize,
13192 row_bytes: usize,
13193 ws0: f32,
13194 ws1: f32,
13195 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13196 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13197 self.e4m3_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes, ws0, ws1)
13198 }
13199
13200 #[allow(clippy::too_many_arguments)]
13201 pub fn qmatvec_e4m3_fused3_t_raw(
13202 &self,
13203 b0: &CudaSlice<u8>,
13204 b1: &CudaSlice<u8>,
13205 b2: &CudaSlice<u8>,
13206 x: &CudaSlice<f32>,
13207 m: usize,
13208 in_f: usize,
13209 out0: usize,
13210 out1: usize,
13211 out2: usize,
13212 row_bytes: usize,
13213 ws0: f32,
13214 ws1: f32,
13215 ws2: f32,
13216 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13217 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13218 self.e4m3_fused3_t_core(
13219 b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
13220 )
13221 }
13222
13223 fn try_e4m3_blk_pre(
13234 &self,
13235 w: &crate::model::GpuTensor,
13236 aq: &CudaSlice<i8>,
13237 ad: &CudaSlice<f32>,
13238 m: usize,
13239 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13240 use crate::model::GpuTensor;
13241 if let GpuTensor::Quant {
13242 bytes,
13243 qtype,
13244 row_bytes,
13245 blk: Some(g),
13246 ..
13247 } = w
13248 {
13249 if *qtype == QT_F8_E4M3_BLK {
13250 if (2..=16).contains(&m)
13256 && std::env::var("MEMRA_NO_BATCHED").is_err()
13257 && (m <= 4 || Self::b8_enabled())
13258 {
13259 let mcols = Self::batched_mcols(m);
13260 return Ok(Some(self.qmatvec_e4m3_blk_mmvq_batched(
13261 bytes,
13262 aq,
13263 ad,
13264 &g.scales,
13265 m,
13266 w.in_features(),
13267 w.out_features(),
13268 *row_bytes,
13269 g.cols,
13270 mcols,
13271 )?));
13272 }
13273 return Ok(Some(self.qmatvec_e4m3_blk_mmvq(
13274 bytes,
13275 aq,
13276 ad,
13277 &g.scales,
13278 m,
13279 w.in_features(),
13280 w.out_features(),
13281 *row_bytes,
13282 g.cols,
13283 )?));
13284 }
13285 }
13286 Ok(None)
13287 }
13288
13289 fn try_e4m3_blk_prefill(
13336 &self,
13337 w: &crate::model::GpuTensor,
13338 x: &CudaSlice<f32>,
13339 m: usize,
13340 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13341 use crate::model::GpuTensor;
13342 let GpuTensor::Quant {
13343 bytes,
13344 qtype,
13345 blk: Some(g),
13346 ..
13347 } = w
13348 else {
13349 return Ok(None);
13350 };
13351 if *qtype != QT_F8_E4M3_BLK {
13352 return Ok(None);
13353 }
13354 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
13359 return Ok(Some(y));
13360 }
13361 let (in_f, out_f) = (w.in_features(), w.out_features());
13362 let slab = self.fp8_blk_dequant_q8_0_dev(bytes, &g.scales, out_f, in_f)?;
13363 let tmp = GpuTensor::Quant {
13364 bytes: slab,
13365 qtype: QT_Q8_0,
13366 row_bytes: in_f / 32 * 34,
13367 ne: vec![in_f as u64, out_f as u64],
13368 scale: 1.0,
13369 rp: false,
13370 #[cfg(memra_cutlass)]
13371 cutlass: None,
13372 fp8: None,
13373 blk: None,
13374 f16: None,
13375 rp4: None,
13376 };
13377 Ok(Some(self.matmul(&tmp, x, m)?))
13379 }
13380
13381 pub fn matmul_pre_noscale(
13382 &self,
13383 w: &crate::model::GpuTensor,
13384 aq: &CudaSlice<i8>,
13385 ad: &CudaSlice<f32>,
13386 m: usize,
13387 ) -> Result<Option<(CudaSlice<f32>, f32)>, Box<dyn std::error::Error>> {
13388 use crate::model::GpuTensor;
13389 if m == 1 {
13393 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
13394 return Ok(Some((y, 1.0)));
13395 }
13396 }
13397 if m != 1 || !self.uses_q8_1_fast(w) {
13399 return Ok(None);
13400 }
13401 let in_f = w.in_features();
13402 let out_f = w.out_features();
13403 let (bytes, qtype, row_bytes, scale, rp) = match w {
13404 GpuTensor::Quant {
13405 bytes,
13406 qtype,
13407 row_bytes,
13408 scale,
13409 rp,
13410 ..
13411 } => (bytes, *qtype, *row_bytes, *scale, *rp),
13412 _ => return Ok(None),
13413 };
13414 if self.mmvq_supports(qtype) {
13416 let (mbytes, mrp) = match w {
13418 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
13419 _ => (bytes, rp),
13420 };
13421 let y = self.qmatvec_mmvq(
13422 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, 1.0, mrp,
13423 )?;
13424 return Ok(Some((y, scale)));
13425 }
13426 let name = match qtype {
13428 QT_Q8_0 => "qmatvec_q8_0_dp4a",
13429 QT_Q4_K => "qmatvec_q4_K_dp4a",
13430 QT_Q6_K => "qmatvec_q6_K_dp4a",
13431 QT_Q5_K => "qmatvec_q5_K_dp4a",
13432 QT_Q3_K => "qmatvec_q3_K_dp4a",
13433 QT_NVFP4 => {
13434 if rp {
13435 "qmatvec_nvfp4_dp4a_rp"
13436 } else {
13437 "qmatvec_nvfp4_dp4a"
13438 }
13439 }
13440 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
13441 _ => return Ok(None),
13442 };
13443 let f = self.func(name);
13444 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13445 let cfg = LaunchConfig {
13446 grid_dim: (out_f as u32, m as u32, 1),
13447 block_dim: (128, 1, 1),
13448 shared_mem_bytes: 0,
13449 };
13450 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13451 let __s_b = self.gpu.stream();
13452 let mut b = __s_b.launch_builder(&f);
13453 b.arg(bytes)
13454 .arg(aq)
13455 .arg(ad)
13456 .arg(&mut y)
13457 .arg(&inf)
13458 .arg(&outf)
13459 .arg(&mi)
13460 .arg(&rb);
13461 unsafe {
13462 b.launch(cfg)?;
13463 }
13464 Ok(Some((y, scale)))
13465 }
13466
13467 pub fn mmvq_supports(&self, qtype: i32) -> bool {
13470 if qtype == QT_F8_E4M3 {
13475 return true;
13476 }
13477 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
13478 return false;
13479 }
13480 matches!(
13481 qtype,
13482 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_Q4_0
13483 )
13484 }
13485
13486 pub fn qmatvec_mmvq(
13491 &self,
13492 bytes: &CudaSlice<u8>,
13493 aq: &CudaSlice<i8>,
13494 ad: &CudaSlice<f32>,
13495 m: usize,
13496 in_f: usize,
13497 out_f: usize,
13498 qtype: i32,
13499 row_bytes: usize,
13500 scale: f32,
13501 rp: bool,
13502 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13503 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_mmvq_into(
13505 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp, &mut y,
13506 )?;
13507 Ok(y)
13508 }
13509
13510 #[allow(clippy::too_many_arguments)]
13512 pub fn qmatvec_mmvq_into(
13513 &self,
13514 bytes: &CudaSlice<u8>,
13515 aq: &CudaSlice<i8>,
13516 ad: &CudaSlice<f32>,
13517 m: usize,
13518 in_f: usize,
13519 out_f: usize,
13520 qtype: i32,
13521 row_bytes: usize,
13522 scale: f32,
13523 rp: bool,
13524 y: &mut CudaSlice<f32>,
13525 ) -> Result<(), Box<dyn std::error::Error>> {
13526 debug_assert!(y.len() >= m * out_f);
13527 const ROWS_PER_BLOCK: u32 = 4; if qtype == QT_Q8_0
13533 && rp
13534 && m == 1
13535 && out_f >= 64
13536 && (out_f as u32).div_ceil(ROWS_PER_BLOCK) < 4 * self.sm_count() as u32
13537 && {
13538 static G2: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13539 *G2.get_or_init(|| std::env::var("MEMRA_Q80_G2").as_deref() != Ok("0"))
13540 }
13541 {
13542 let f = self.func("qmatvec_q8_0_mmvq_rp_g2");
13543 let cfg = LaunchConfig {
13544 grid_dim: ((out_f as u32).div_ceil(2), 1, 1),
13545 block_dim: (32, 2, 1),
13546 shared_mem_bytes: 0,
13547 };
13548 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, row_bytes as i64);
13549 let __s_b = self.gpu.stream();
13550 let mut b = __s_b.launch_builder(&f);
13551 b.arg(bytes)
13552 .arg(aq)
13553 .arg(ad)
13554 .arg(&mut *y)
13555 .arg(&inf)
13556 .arg(&outf)
13557 .arg(&mi)
13558 .arg(&rb);
13559 unsafe {
13560 b.launch(cfg)?;
13561 }
13562 if scale != 1.0 {
13563 self.scale_inplace(y, scale, out_f)?;
13564 }
13565 return Ok(());
13566 }
13567 let mut mr: u32 = if m == 1 && (qtype == QT_NVFP4 || qtype == QT_Q5_K) {
13576 2
13577 } else {
13578 1
13579 };
13580 if m == 1 && qtype == QT_Q4_0 {
13585 static Q40MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
13586 mr = *Q40MR.get_or_init(|| {
13589 std::env::var("MEMRA_Q40_MR")
13590 .ok()
13591 .and_then(|v| v.parse().ok())
13592 .unwrap_or(1)
13593 });
13594 }
13595 let q5_mode = std::env::var("MEMRA_Q5K_ISSUE").ok();
13606 let q5_force = q5_mode.as_deref() == Some("2");
13607 let q5_il = qtype == QT_Q5_K
13610 && m == 1
13611 && (q5_force || q5_mode.as_deref().map(|v| v != "0").unwrap_or(true));
13612 if q5_il && !q5_force && out_f > 65536 {
13613 mr = 1;
13614 }
13615 if qtype == QT_Q4_0 && rp && mr != 1 {
13618 mr = 2;
13619 }
13620 if qtype == QT_Q8_0 && rp {
13624 static Q80MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
13625 mr = *Q80MR.get_or_init(|| {
13626 std::env::var("MEMRA_Q80_MR")
13627 .ok()
13628 .and_then(|v| v.parse().ok())
13629 .unwrap_or(1)
13630 });
13631 }
13632 let name = match (qtype, mr, rp) {
13633 (QT_NVFP4, 2, false) => "qmatvec_nvfp4_mmvq_mr2",
13634 (QT_NVFP4, 2, true) => "qmatvec_nvfp4_mmvq_mr2_rp",
13635 (QT_NVFP4, _, true) => "qmatvec_nvfp4_mmvq_rp",
13636 (QT_Q4_0, 1, true) => "qmatvec_q4_0_mmvq_rp",
13637 (QT_Q4_0, _, true) => "qmatvec_q4_0_mmvq_mr2_rp",
13638 (QT_Q5_K, 2, _) => {
13639 if q5_il {
13640 "qmatvec_q5_K_mmvq_mr2_il"
13641 } else {
13642 "qmatvec_q5_K_mmvq_mr2"
13643 }
13644 }
13645 (QT_Q8_0, 2, true) => "qmatvec_q8_0_mmvq_mr2_rp",
13646 (QT_Q8_0, _, true)
13651 if in_f % 1024 == 0 && {
13652 static CA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13653 *CA.get_or_init(|| std::env::var("MEMRA_Q80_CA").as_deref() == Ok("1"))
13654 } =>
13655 {
13656 "qmatvec_q8_0_mmvq_rpca"
13657 }
13658 (QT_Q8_0, _, true) => "qmatvec_q8_0_mmvq_rp",
13659 (QT_Q8_0, _, _) => "qmatvec_q8_0_mmvq",
13660 (QT_Q4_K, _, true) => "qmatvec_q4_K_mmvq_rp",
13664 (QT_Q6_K, _, true) => "qmatvec_q6_K_mmvq_rp",
13665 (QT_Q4_K, _, _) => "qmatvec_q4_K_mmvq",
13666 (QT_Q4_0, 2, false) => "qmatvec_q4_0_mmvq_mr2",
13667 (QT_Q4_0, _, false) => "qmatvec_q4_0_mmvq",
13668 (QT_Q5_K, _, _) => {
13669 if q5_il {
13670 "qmatvec_q5_K_mmvq_il"
13671 } else {
13672 "qmatvec_q5_K_mmvq"
13673 }
13674 }
13675 (QT_Q6_K, _, _) => "qmatvec_q6_K_mmvq",
13676 (QT_NVFP4, _, false) => "qmatvec_nvfp4_mmvq",
13677 (QT_F8_E4M3, _, _) => "qmatvec_e4m3_mmvq",
13678 _ => panic!("qmatvec_mmvq: qtype {qtype} has no MMVQ kernel"),
13679 };
13680 let f = self.func(name);
13681 let rows_per_block = ROWS_PER_BLOCK * mr;
13683 let cfg = LaunchConfig {
13684 grid_dim: (
13685 (out_f as u32 + rows_per_block - 1) / rows_per_block,
13686 m as u32,
13687 1,
13688 ),
13689 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
13692 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13693 let __s_b = self.gpu.stream();
13694 let mut b = __s_b.launch_builder(&f);
13695 if qtype == QT_NVFP4 || qtype == QT_F8_E4M3 {
13700 if Self::pdl_on()
13703 && Self::pdl_mmvq_on()
13704 && Self::pdl_nvfp4q8_on()
13705 && name == "qmatvec_nvfp4_mmvq_mr2_rp"
13706 {
13707 use cudarc::driver::{DevicePtr, DevicePtrMut};
13708 let s = &self.gpu.stream();
13709 let (pw, _g0) = bytes.device_ptr(s);
13710 let (paq, _g1) = aq.device_ptr(s);
13711 let (pad, _g2) = ad.device_ptr(s);
13712 let (py, _g3) = y.device_ptr_mut(s);
13713 let mut ps = [
13714 &pw as *const _ as *mut std::ffi::c_void,
13715 &paq as *const _ as *mut _,
13716 &pad as *const _ as *mut _,
13717 &py as *const _ as *mut _,
13718 &inf as *const _ as *mut _,
13719 &outf as *const _ as *mut _,
13720 &mi as *const _ as *mut _,
13721 &rb as *const _ as *mut _,
13722 &scale as *const _ as *mut _,
13723 ];
13724 unsafe {
13725 self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
13726 }
13727 return Ok(());
13728 }
13729 b.arg(bytes)
13730 .arg(aq)
13731 .arg(ad)
13732 .arg(&mut *y)
13733 .arg(&inf)
13734 .arg(&outf)
13735 .arg(&mi)
13736 .arg(&rb)
13737 .arg(&scale);
13738 unsafe {
13739 b.launch(cfg)?;
13740 }
13741 } else if Self::pdl_on()
13742 && Self::pdl_mmvq_on()
13743 && (matches!(
13744 name,
13745 "qmatvec_q4_0_mmvq_rp" | "qmatvec_q6_K_mmvq" | "qmatvec_q6_K_mmvq_rp"
13746 ) || (Self::pdl_nvfp4q8_on()
13747 && matches!(name, "qmatvec_q8_0_mmvq_rp" | "qmatvec_q8_0_mmvq_mr2_rp")))
13748 {
13749 {
13753 use cudarc::driver::{DevicePtr, DevicePtrMut};
13754 let s = &self.gpu.stream();
13755 let (pw, _g0) = bytes.device_ptr(s);
13756 let (paq, _g1) = aq.device_ptr(s);
13757 let (pad, _g2) = ad.device_ptr(s);
13758 let (py, _g3) = y.device_ptr_mut(s);
13759 let mut ps = [
13760 &pw as *const _ as *mut std::ffi::c_void,
13761 &paq as *const _ as *mut _,
13762 &pad as *const _ as *mut _,
13763 &py as *const _ as *mut _,
13764 &inf as *const _ as *mut _,
13765 &outf as *const _ as *mut _,
13766 &mi as *const _ as *mut _,
13767 &rb as *const _ as *mut _,
13768 ];
13769 unsafe {
13770 self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
13771 }
13772 }
13773 if scale != 1.0 {
13774 self.scale_inplace(y, scale, m * out_f)?;
13775 }
13776 } else {
13777 b.arg(bytes)
13778 .arg(aq)
13779 .arg(ad)
13780 .arg(&mut *y)
13781 .arg(&inf)
13782 .arg(&outf)
13783 .arg(&mi)
13784 .arg(&rb);
13785 unsafe {
13786 b.launch(cfg)?;
13787 }
13788 if scale != 1.0 {
13789 self.scale_inplace(y, scale, m * out_f)?;
13790 }
13791 }
13792 Ok(())
13793 }
13794
13795 pub fn qmatvec_mmvq_raw(
13799 &self,
13800 bytes: &CudaSlice<u8>,
13801 x: &CudaSlice<f32>,
13802 m: usize,
13803 in_f: usize,
13804 out_f: usize,
13805 qtype: i32,
13806 row_bytes: usize,
13807 rp: bool,
13808 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13809 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13810 self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, 1.0, rp)
13811 }
13812
13813 pub fn batched_supports(&self, qtype: i32) -> bool {
13817 matches!(
13818 qtype,
13819 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_F8_E4M3 | QT_Q4_0
13820 )
13821 }
13822
13823 pub fn iq_fast_enabled() -> bool {
13831 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13832 *ON.get_or_init(|| {
13833 std::env::var("MEMRA_IQ_FAST")
13834 .map(|v| v != "0")
13835 .unwrap_or(true)
13836 })
13837 }
13838
13839 pub fn b8_enabled() -> bool {
13842 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13843 *ON.get_or_init(|| std::env::var("MEMRA_B8").map(|v| v != "0").unwrap_or(true))
13844 }
13845
13846 pub fn batched_mcols(m: usize) -> usize {
13848 if m == 2 {
13849 2
13850 } else if m <= 4 {
13851 4
13852 } else if m <= 8 {
13853 8
13854 } else {
13855 16
13856 }
13857 }
13858
13859 fn batched_kernel_name(qtype: i32, mcols: usize) -> Option<&'static str> {
13864 Some(match (qtype, mcols) {
13865 (QT_Q8_0, 2) => "qmatvec_q8_0_mmvq_b2",
13866 (QT_Q8_0, 4) => "qmatvec_q8_0_mmvq_b4",
13867 (QT_Q8_0, 8) => "qmatvec_q8_0_mmvq_b8",
13868 (QT_Q8_0, 16) => "qmatvec_q8_0_mmvq_b16",
13874 (QT_Q4_K, 2) => "qmatvec_q4_K_mmvq_b2",
13875 (QT_Q4_K, 4) => "qmatvec_q4_K_mmvq_b4",
13876 (QT_Q4_K, 8) => "qmatvec_q4_K_mmvq_b8",
13877 (QT_Q4_K, 16) => "qmatvec_q4_K_mmvq_b16",
13880 (QT_Q5_K, 2) => "qmatvec_q5_K_mmvq_b2",
13881 (QT_Q5_K, 4) => "qmatvec_q5_K_mmvq_b4",
13882 (QT_Q5_K, 8) => "qmatvec_q5_K_mmvq_b8",
13883 (QT_Q5_K, 16) => "qmatvec_q5_K_mmvq_b16",
13886 (QT_Q6_K, 2) => "qmatvec_q6_K_mmvq_b2",
13887 (QT_Q6_K, 4) => "qmatvec_q6_K_mmvq_b4",
13888 (QT_Q6_K, 8) => "qmatvec_q6_K_mmvq_b8",
13889 (QT_Q6_K, 16) => "qmatvec_q6_K_mmvq_b16",
13890 (QT_NVFP4, 2) => "qmatvec_nvfp4_mmvq_b2",
13891 (QT_NVFP4, 4) => "qmatvec_nvfp4_mmvq_b4",
13892 (QT_NVFP4, 8) => "qmatvec_nvfp4_mmvq_b8",
13893 (QT_NVFP4, 16) => "qmatvec_nvfp4_mmvq_b16",
13897 (QT_F8_E4M3, 2) => "qmatvec_e4m3_mmvq_b2",
13898 (QT_F8_E4M3, 4) => "qmatvec_e4m3_mmvq_b4",
13899 (QT_F8_E4M3, 8) => "qmatvec_e4m3_mmvq_b8",
13900 (QT_F8_E4M3, 16) => "qmatvec_e4m3_mmvq_b16",
13904 (QT_Q4_0, 2) => "qmatvec_q4_0_mmvq_b2",
13905 (QT_Q4_0, 4) => "qmatvec_q4_0_mmvq_b4",
13906 (QT_Q4_0, 8) => "qmatvec_q4_0_mmvq_b8",
13907 (QT_Q4_0, 16) => "qmatvec_q4_0_mmvq_b16",
13908 _ => return None,
13909 })
13910 }
13911
13912 pub fn sm_count(&self) -> i32 {
13947 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
13948 *SMS.get_or_init(|| {
13949 use cudarc::driver::sys::CUdevice_attribute_enum as A;
13950 self.gpu
13951 .ctx
13952 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
13953 .unwrap_or(82)
13954 })
13955 }
13956
13957 pub fn batched_variant(
13958 &self,
13959 _m: usize,
13960 in_f: usize,
13961 out_f: usize,
13962 qtype: i32,
13963 row_bytes: usize,
13964 mcols: usize,
13965 rp: bool,
13966 ) -> &'static str {
13967 if qtype == QT_Q8_0 {
13972 return if rp { "rp" } else { "base" };
13973 }
13974 static BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13975 let bv = *BV.get_or_init(|| match std::env::var("MEMRA_MMVQ_BV").as_deref() {
13976 Ok("base") => "base",
13977 Ok("pf") => "pf",
13978 Ok("r2") => "r2",
13979 Ok("r2w8") => "r2w8",
13980 Ok("pfr2") => "pfr2",
13981 Ok("ca") => "ca",
13982 Ok("car2") => "car2",
13983 Ok("rp") => "rp",
13986 Ok("rpr2") => "rpr2",
13987 Ok("rpr2w8") => "rpr2w8",
13988 Ok("rpca") => "rpca",
13991 Ok("rpcar2") => "rpcar2",
13992 Ok("rpsc") => "rpsc",
13999 Ok("rpms") => "rpms",
14000 Ok("rpmsc") => "rpmsc",
14001 Ok("rpks") => "rpks",
14002 Ok("rpksc") => "rpksc",
14003 _ => "auto",
14004 });
14005 let ca_ok = qtype == QT_NVFP4 && (row_bytes % 16 == 0) && (in_f % 1024 == 0);
14009 static KS_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14014 let ks_on = *KS_ON.get_or_init(|| std::env::var("MEMRA_KS").as_deref() != Ok("0"));
14015 let sc_ok = ks_on && qtype == QT_NVFP4 && (in_f % 256 == 0) && (in_f / 64 <= 272);
14016 let ks_ok = ks_on && qtype == QT_NVFP4 && (in_f % 512 == 0) && (in_f / 64 <= 272);
14017 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
14018 let sms = *SMS.get_or_init(|| {
14019 use cudarc::driver::sys::CUdevice_attribute_enum as A;
14020 self.gpu
14021 .ctx
14022 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
14023 .unwrap_or(82)
14024 });
14025 let kq_r2 = matches!(qtype, QT_Q4_K | QT_Q5_K | QT_Q6_K);
14045 static KQBV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
14048 let kq_bv = *KQBV.get_or_init(|| match std::env::var("MEMRA_KQ_BV").as_deref() {
14049 Ok("base") => "base",
14050 Ok("r2") => "r2",
14051 Ok("r2w8") => "r2w8",
14052 _ => "auto",
14053 });
14054 let variant: &'static str = if qtype == QT_Q4_0 {
14055 static Q40BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
14059 let q40 = *Q40BV.get_or_init(|| match std::env::var("MEMRA_Q40_BV").as_deref() {
14060 Ok("base") => "base",
14066 Ok("r2") => "r2",
14067 Ok("ms") => "ms",
14068 Ok("sm") => "sm",
14069 Ok("la") => "la",
14070 _ => "auto",
14071 });
14072 let v = if q40 != "auto" {
14073 q40
14074 } else if (out_f as u32).div_ceil(8) >= 4 * sms as u32 {
14075 "r2"
14076 } else {
14077 "base"
14078 };
14079 if rp {
14084 match v {
14085 "ms" => "r2ms_rp",
14086 "sm" => "r2sm_rp",
14087 "la" => "r2la_rp",
14088 "r2" => "r2_rp",
14089 _ => "rp",
14090 }
14091 } else if matches!(v, "ms" | "sm" | "la") {
14092 "r2"
14093 } else {
14094 v
14095 }
14096 } else if qtype != QT_NVFP4 && !kq_r2 {
14097 "base"
14098 } else if kq_r2 && rp {
14099 "rp"
14103 } else if kq_r2 {
14104 if kq_bv != "auto" {
14107 if kq_bv == "r2w8" && mcols != 4 {
14108 "r2"
14109 } else {
14110 kq_bv
14111 }
14112 } else if bv != "auto" {
14113 match bv {
14114 "r2" | "pfr2" | "rpr2" | "car2" => "r2",
14115 "r2w8" | "rpr2w8" => {
14116 if mcols != 4 {
14117 "r2"
14118 } else {
14119 "r2w8"
14120 }
14121 }
14122 _ => "base", }
14124 } else {
14125 let blocks = (out_f + 7) / 8;
14126 let waves = blocks as f64 / (7 * sms as usize) as f64;
14127 let filled = blocks >= 4 * sms as usize;
14128 let use_r2 = if qtype == QT_Q4_K {
14129 filled
14130 } else {
14131 waves >= 2.0
14132 };
14133 if use_r2 { "r2" } else { "base" }
14134 }
14135 } else if bv != "auto" {
14136 let v = if bv == "r2w8" && mcols == 2 {
14141 "r2"
14142 } else if bv == "ca" && (!ca_ok || mcols == 8) {
14143 "pf"
14144 } else if bv == "car2" && (!ca_ok || mcols == 8) {
14145 "r2"
14146 } else if bv == "pfr2" && mcols == 8 {
14147 "r2"
14148 } else if (bv == "rpr2w8" || bv == "rpr2") && mcols == 2 {
14149 "rpr2"
14150 }
14151 else if (bv == "rpca" || bv == "rpcar2") && (!ca_ok || mcols == 8) {
14153 if mcols == 8 { "rpr2w8" } else { "rpr2" }
14154 } else if bv == "rpcar2" && mcols == 2 {
14155 "rpca"
14156 }
14157 else if (bv == "rpsc" || bv == "rpmsc") && !sc_ok {
14160 "rpr2"
14161 } else if (bv == "rpks" || bv == "rpksc") && !ks_ok {
14162 "rpr2"
14163 } else {
14164 bv
14165 };
14166 if rp {
14167 match v {
14168 "base" | "pf" | "ca" | "rp" => "rp",
14169 "r2" | "pfr2" | "car2" | "rpr2" => "rpr2",
14170 "r2w8" | "rpr2w8" => {
14171 if mcols == 2 {
14172 "rpr2"
14173 } else {
14174 "rpr2w8"
14175 }
14176 }
14177 other => other, }
14179 } else {
14180 v
14181 }
14182 } else if mcols == 8 {
14183 if rp {
14194 if sc_ok { "rpsc" } else { "rpr2w8" }
14195 } else {
14196 "r2w8"
14197 }
14198 } else if mcols >= 4 {
14199 let blocks = (out_f + 7) / 8;
14203 let r7 = 7 * sms as usize;
14204 let r8 = 8 * sms as usize;
14205 let waves = blocks as f64 / r7 as f64;
14206 let filled = blocks >= 4 * sms as usize;
14207 if filled && blocks.div_ceil(r8) < blocks.div_ceil(r7) {
14211 if rp { "rpr2w8" } else { "r2w8" }
14215 } else if waves >= 2.0 || (waves <= 1.0 && filled) {
14216 if rp { "rpr2" } else { "r2" }
14219 } else {
14220 if rp { "rp" } else { "pf" }
14224 }
14225 } else if in_f >= 6144 {
14226 if rp { "rpr2" } else { "r2" }
14230 } else if rp {
14231 let waves = ((out_f + 7) / 8) as f64 / (7 * sms as usize) as f64;
14236 if sc_ok && waves >= 0.9 && waves <= 1.1 {
14237 "rpsc"
14238 } else {
14239 "rp"
14240 }
14241 } else {
14242 "base"
14243 };
14244 variant
14245 }
14246
14247 pub fn qmatvec_mmvq_batched(
14248 &self,
14249 bytes: &CudaSlice<u8>,
14250 aq: &CudaSlice<i8>,
14251 ad: &CudaSlice<f32>,
14252 m: usize,
14253 in_f: usize,
14254 out_f: usize,
14255 qtype: i32,
14256 row_bytes: usize,
14257 mcols: usize,
14258 scale: f32,
14259 rp: bool,
14260 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14261 const ROWS_PER_BLOCK: u32 = 4;
14262 let forced: Option<&'static str> = {
14267 static V: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
14268 V.get_or_init(|| std::env::var("MEMRA_BVAR").ok())
14269 .as_deref()
14270 .map(|s| Box::leak(s.to_string().into_boxed_str()) as &'static str)
14271 };
14272 let variant = match forced {
14273 Some(v) if !rp || v.contains("rp") => v,
14274 _ => self.batched_variant(m, in_f, out_f, qtype, row_bytes, mcols, rp),
14275 };
14276 let base_name = Self::batched_kernel_name(qtype, mcols).ok_or_else(|| {
14277 format!("qmatvec_mmvq_batched: no kernel for qtype {qtype} mcols {mcols}")
14278 })?;
14279 let variant = if mcols == 16 {
14283 if rp { "rp" } else { "base" }
14284 } else {
14285 variant
14286 };
14287 static B567: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14294 let b567 = *B567.get_or_init(|| std::env::var("MEMRA_B567").as_deref() != Ok("0"));
14295 if b567
14296 && qtype == QT_NVFP4
14297 && rp
14298 && mcols == 8
14299 && (5..=7).contains(&m)
14300 && matches!(variant, "rpsc" | "rpr2w8")
14301 {
14302 let f = self.func(&format!("qmatvec_nvfp4_mmvq_b{m}_{variant}"));
14303 let rows_per_block = ROWS_PER_BLOCK * 2; let mut y = self.alloc_uninit::<f32>(m * out_f)?;
14305 let cfg = LaunchConfig {
14306 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
14307 block_dim: (32, ROWS_PER_BLOCK, 1),
14308 shared_mem_bytes: 0,
14309 };
14310 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14311 let __s_b = self.gpu.stream();
14312 let mut b = __s_b.launch_builder(&f);
14313 b.arg(bytes)
14314 .arg(aq)
14315 .arg(ad)
14316 .arg(&mut y)
14317 .arg(&inf)
14318 .arg(&outf)
14319 .arg(&mi)
14320 .arg(&rb);
14321 unsafe {
14322 b.launch(cfg)?;
14323 }
14324 if scale != 1.0 {
14325 self.scale_inplace(&mut y, scale, m * out_f)?;
14326 }
14327 return Ok(y);
14328 }
14329 let (name, rows_per_block): (std::borrow::Cow<'static, str>, u32) = match variant {
14330 "base" => (base_name.into(), ROWS_PER_BLOCK),
14331 "pf" => (format!("{base_name}_pf").into(), ROWS_PER_BLOCK),
14332 "ca" => (format!("{base_name}_ca").into(), ROWS_PER_BLOCK),
14333 "rp" => (format!("{base_name}_rp").into(), ROWS_PER_BLOCK),
14334 "rpca" => (format!("{base_name}_rpca").into(), ROWS_PER_BLOCK), "rpks" => (format!("{base_name}_rpks").into(), ROWS_PER_BLOCK),
14338 "rpksc" => (format!("{base_name}_rpksc").into(), ROWS_PER_BLOCK),
14339 "rpms" => (format!("{base_name}_rpms").into(), ROWS_PER_BLOCK),
14340 "rpmsc" => (format!("{base_name}_rpmsc").into(), ROWS_PER_BLOCK),
14341 "r2ms_rp" => (format!("{base_name}_r2ms_rp").into(), ROWS_PER_BLOCK),
14342 "r2sm_rp" => (format!("{base_name}_r2sm_rp").into(), ROWS_PER_BLOCK * 2),
14343 "r2la_rp" => (format!("{base_name}_r2la_rp").into(), ROWS_PER_BLOCK * 2),
14344 v => (format!("{base_name}_{v}").into(), ROWS_PER_BLOCK * 2), };
14346 debug_assert!(
14347 !rp || name.contains("_rp"),
14348 "rp weight dispatched to a GGUF-layout kernel"
14349 );
14350 let f = self.func(&name);
14351 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
14352 let smem = if name.contains("_r2sm_rp") {
14354 (mcols * 32 * 9 * 4 + mcols * 32 * 4) as u32
14355 } else {
14356 0
14357 };
14358 let cfg = LaunchConfig {
14359 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
14360 block_dim: (32, ROWS_PER_BLOCK, 1),
14361 shared_mem_bytes: smem,
14362 };
14363 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14364 let __s_b = self.gpu.stream();
14365 let mut b = __s_b.launch_builder(&f);
14366 b.arg(bytes)
14367 .arg(aq)
14368 .arg(ad)
14369 .arg(&mut y)
14370 .arg(&inf)
14371 .arg(&outf)
14372 .arg(&mi)
14373 .arg(&rb);
14374 unsafe {
14375 b.launch(cfg)?;
14376 }
14377 if scale != 1.0 {
14378 self.scale_inplace(&mut y, scale, m * out_f)?;
14379 }
14380 Ok(y)
14381 }
14382
14383 pub fn qmatvec_batched_raw(
14387 &self,
14388 bytes: &CudaSlice<u8>,
14389 x: &CudaSlice<f32>,
14390 m: usize,
14391 in_f: usize,
14392 out_f: usize,
14393 qtype: i32,
14394 row_bytes: usize,
14395 mcols: usize,
14396 rp: bool,
14397 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14398 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
14399 self.qmatvec_mmvq_batched(
14400 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, 1.0, rp,
14401 )
14402 }
14403
14404 pub fn qmatvec_nvfp4_batched_raw(
14406 &self,
14407 bytes: &CudaSlice<u8>,
14408 x: &CudaSlice<f32>,
14409 m: usize,
14410 in_f: usize,
14411 out_f: usize,
14412 row_bytes: usize,
14413 mcols: usize,
14414 rp: bool,
14415 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14416 self.qmatvec_batched_raw(bytes, x, m, in_f, out_f, QT_NVFP4, row_bytes, mcols, rp)
14417 }
14418
14419 fn try_fp4_gemm(
14423 &self,
14424 w: &crate::model::GpuTensor,
14425 x: &CudaSlice<f32>,
14426 m: usize,
14427 in_f: usize,
14428 out_f: usize,
14429 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
14430 use crate::model::GpuTensor;
14431 if cfg!(memra_portable_cuda) {
14432 return Ok(None);
14433 }
14434 if std::env::var("MEMRA_FP4").is_err() {
14435 return Ok(None);
14436 }
14437 #[cfg(memra_cutlass)]
14446 if m >= 128 && std::env::var("MEMRA_FP4_CUTLASS").is_ok() {
14447 if let GpuTensor::Quant {
14448 bytes,
14449 qtype,
14450 scale,
14451 row_bytes,
14452 cutlass,
14453 ..
14454 } = w
14455 {
14456 if *qtype == QT_NVFP4 && in_f % 64 == 0 {
14457 if let Some(cw) = cutlass {
14458 let y = self.cutlass_fp4_gemm(
14460 &cw.b_packed,
14461 &cw.sfb_swizzled,
14462 x,
14463 *scale,
14464 m,
14465 out_f,
14466 in_f,
14467 )?;
14468 return Ok(Some(y));
14469 } else if std::env::var("MEMRA_FP4_CUTLASS_OTF").is_ok() {
14470 let (b_packed, sfb_sw) =
14475 self.build_cutlass_weight(bytes, out_f, in_f, *row_bytes)?;
14476 let y =
14477 self.cutlass_fp4_gemm(&b_packed, &sfb_sw, x, *scale, m, out_f, in_f)?;
14478 return Ok(Some(y));
14479 }
14480 }
14481 }
14482 }
14483 if let GpuTensor::Quant {
14484 bytes,
14485 qtype,
14486 row_bytes,
14487 scale,
14488 rp,
14489 ..
14490 } = w
14491 {
14492 if *qtype == QT_NVFP4 && in_f % 64 == 0 && !*rp {
14495 let y =
14496 self.qmatvec_gemm_nvfp4_fp4(bytes, x, m, in_f, out_f, *row_bytes, *scale)?;
14497 return Ok(Some(y));
14498 }
14499 }
14500 Ok(None)
14501 }
14502
14503 pub fn rms_norm_f16out(
14507 &self,
14508 x: &CudaSlice<f32>,
14509 w: &CudaSlice<f32>,
14510 dst: &mut CudaSlice<f32>,
14511 dst16: &mut CudaSlice<u8>,
14512 ncols: usize,
14513 nrows: usize,
14514 eps: f32,
14515 ) -> Result<(), Box<dyn std::error::Error>> {
14516 let f = self.func("rms_norm_f16out_f32");
14517 let cfg = LaunchConfig {
14518 grid_dim: (nrows as u32, 1, 1),
14519 block_dim: (rms_block(), 1, 1),
14520 shared_mem_bytes: 0,
14521 };
14522 let (nc, e) = (ncols as i32, eps);
14523 let __s_b = self.gpu.stream();
14524 let mut b = __s_b.launch_builder(&f);
14525 b.arg(x).arg(w).arg(dst).arg(dst16).arg(&nc).arg(&e);
14526 unsafe {
14527 b.launch(cfg)?;
14528 }
14529 Ok(())
14530 }
14531
14532 #[allow(clippy::too_many_arguments)]
14535 pub fn add_rms_norm_f16out(
14536 &self,
14537 a: &CudaSlice<f32>,
14538 b: &CudaSlice<f32>,
14539 w: &CudaSlice<f32>,
14540 res: &mut CudaSlice<f32>,
14541 dst: &mut CudaSlice<f32>,
14542 dst16: &mut CudaSlice<u8>,
14543 ncols: usize,
14544 nrows: usize,
14545 eps: f32,
14546 ) -> Result<(), Box<dyn std::error::Error>> {
14547 let f = self.func("add_rms_norm_f16out_f32");
14548 let cfg = LaunchConfig {
14549 grid_dim: (nrows as u32, 1, 1),
14550 block_dim: (rms_block(), 1, 1),
14551 shared_mem_bytes: 0,
14552 };
14553 let (nc, e) = (ncols as i32, eps);
14554 let __s_lb = self.gpu.stream();
14555 let mut lb = __s_lb.launch_builder(&f);
14556 lb.arg(a)
14557 .arg(b)
14558 .arg(w)
14559 .arg(res)
14560 .arg(dst)
14561 .arg(dst16)
14562 .arg(&nc)
14563 .arg(&e);
14564 unsafe {
14565 lb.launch(cfg)?;
14566 }
14567 Ok(())
14568 }
14569
14570 pub fn matmul_group_xh(
14573 &self,
14574 ws: &[&crate::model::GpuTensor],
14575 x: &CudaSlice<f32>,
14576 xh: &CudaSlice<u8>,
14577 m: usize,
14578 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
14579 let mut out = Vec::with_capacity(ws.len());
14580 let in_f = ws[0].in_features();
14581 for w in ws {
14582 if w.in_features() == in_f && m >= 16 && !self.verify_exact_on() {
14583 if let Some(y) = self.try_f16_gemm_pre(w, xh, m)? {
14584 out.push(y);
14585 continue;
14586 }
14587 }
14588 out.push(self.matmul(w, x, m)?);
14589 }
14590 Ok(out)
14591 }
14592
14593 pub fn gdn_pad_mask(
14596 &self,
14597 beta: &mut CudaSlice<f32>,
14598 g_log: &mut CudaSlice<f32>,
14599 len_d: &CudaSlice<i32>,
14600 h: usize,
14601 t: usize,
14602 ) -> Result<(), Box<dyn std::error::Error>> {
14603 let f = self.func("gdn_pad_mask_f32");
14604 let cfg = LaunchConfig::for_num_elems((t * h) as u32);
14605 let (hi, ti) = (h as i32, t as i32);
14606 let __s_b = self.gpu.stream();
14607 let mut b = __s_b.launch_builder(&f);
14608 b.arg(beta).arg(g_log).arg(len_d).arg(&hi).arg(&ti);
14609 unsafe {
14610 b.launch(cfg)?;
14611 }
14612 Ok(())
14613 }
14614
14615 pub fn row_gather_dev(
14618 &self,
14619 src: &CudaSlice<f32>,
14620 dst: &mut CudaSlice<f32>,
14621 len_d: &CudaSlice<i32>,
14622 ncols: usize,
14623 ) -> Result<(), Box<dyn std::error::Error>> {
14624 let f = self.func("row_gather_dev_f32");
14625 let cfg = LaunchConfig::for_num_elems(ncols as u32);
14626 let nc = ncols as i32;
14627 let __s_b = self.gpu.stream();
14628 let mut b = __s_b.launch_builder(&f);
14629 b.arg(src).arg(dst).arg(len_d).arg(&nc);
14630 unsafe {
14631 b.launch(cfg)?;
14632 }
14633 Ok(())
14634 }
14635
14636 pub fn matmul_group(
14643 &self,
14644 ws: &[&crate::model::GpuTensor],
14645 x: &CudaSlice<f32>,
14646 m: usize,
14647 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
14648 use crate::model::GpuTensor;
14649 let mut out = Vec::with_capacity(ws.len());
14650 let any_mirror = ws
14651 .iter()
14652 .any(|w| matches!(w, GpuTensor::Quant { f16: Some(_), .. }));
14653 if m >= 16 && any_mirror && !self.verify_exact_on() {
14654 let in_f = ws[0].in_features();
14655 let xh = self.f16_act(x, m * in_f, in_f)?;
14656 for w in ws {
14657 if w.in_features() == in_f {
14658 if let Some(y) = self.try_f16_gemm_pre(w, &xh, m)? {
14659 out.push(y);
14660 continue;
14661 }
14662 }
14663 out.push(self.matmul(w, x, m)?);
14664 }
14665 return Ok(out);
14666 }
14667 for w in ws {
14668 out.push(self.matmul(w, x, m)?);
14669 }
14670 Ok(out)
14671 }
14672
14673 pub fn matmul_group_multi(
14680 &self,
14681 ws: &[&crate::model::GpuTensor],
14682 xs: &[&CudaSlice<f32>],
14683 ms: &[usize],
14684 ) -> Result<Vec<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
14685 assert_eq!(xs.len(), ms.len());
14686 let in_f = ws[0].in_features();
14687 let total: usize = ms.iter().sum();
14688 let mut xcat = self.uninit(total * in_f)?;
14689 let mut off = 0usize;
14690 for (x, &m) in xs.iter().zip(ms) {
14691 self.copy_into(&mut xcat, off * in_f, x, m * in_f)?;
14692 off += m;
14693 }
14694 let ys = self.matmul_group(ws, &xcat, total)?;
14695 let mut out: Vec<Vec<CudaSlice<f32>>> = (0..xs.len()).map(|_| Vec::new()).collect();
14696 for (w, y) in ws.iter().zip(ys) {
14697 let out_f = w.out_features();
14698 let mut off = 0usize;
14699 for (s, &m) in ms.iter().enumerate() {
14700 let mut ys_s = self.uninit(m * out_f)?;
14701 let src = y.slice(off * out_f..(off + m) * out_f);
14702 self.gpu.stream().memcpy_dtod(&src, &mut ys_s)?;
14703 out[s].push(ys_s);
14704 off += m;
14705 }
14706 }
14707 Ok(out)
14708 }
14709
14710 pub fn gemm_supports(&self, w: &crate::model::GpuTensor) -> bool {
14720 use crate::model::GpuTensor;
14721 if !legacy_quant_gemm_allowed(
14722 cfg!(memra_portable_cuda),
14723 cfg!(memra_hopper_mma),
14724 std::env::var_os("MEMRA_NO_GEMM").is_some(),
14725 ) {
14726 return false;
14727 }
14728 match w {
14729 GpuTensor::Quant { qtype, .. } => {
14730 matches!(*qtype, QT_Q8_0 | QT_Q4_K | QT_Q6_K | QT_Q5_K | QT_Q4_0)
14731 || (*qtype == QT_NVFP4 && w.in_features() % 64 == 0)
14732 }
14733 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
14734 }
14735 }
14736
14737 pub fn qmatvec_gemm(
14744 &self,
14745 w: &crate::model::GpuTensor,
14746 aq: &CudaSlice<i8>,
14747 ad: &CudaSlice<f32>,
14748 m: usize,
14749 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14750 use crate::model::GpuTensor;
14751 let in_f = w.in_features();
14752 let out_f = w.out_features();
14753 let (bytes, qtype, row_bytes, scale, rp) = match w {
14754 GpuTensor::Quant {
14755 bytes,
14756 qtype,
14757 row_bytes,
14758 scale,
14759 rp,
14760 ..
14761 } => (bytes, *qtype, *row_bytes, *scale, *rp),
14762 _ => unreachable!("gemm_supports guaranteed Quant"),
14763 };
14764 if cfg!(memra_hopper_mma) && qtype == QT_Q8_0 && out_f % 64 == 0 && wgmma_gemm_enabled() {
14770 if let GpuTensor::Quant { rp4: Some(m4), .. } = w {
14771 let mut y = self.qmatvec_gemm_q8_0_wgmma_raw(m4, aq, ad, m, in_f, out_f)?;
14772 if scale != 1.0 {
14773 self.scale_inplace(&mut y, scale, m * out_f)?;
14774 }
14775 return Ok(y);
14776 }
14777 }
14778 let name = match qtype {
14779 QT_Q8_0 => "qmatvec_gemm_q8_0",
14780 QT_Q4_K => "qmatvec_gemm_q4_K",
14781 QT_Q4_0 => {
14782 if rp {
14783 "qmatvec_gemm_q4_0_rp"
14784 } else {
14785 "qmatvec_gemm_q4_0"
14786 }
14787 }
14788 QT_Q5_K => "qmatvec_gemm_q5_K",
14789 QT_Q6_K => "qmatvec_gemm_q6_K",
14790 QT_NVFP4 => {
14791 if rp {
14792 "qmatvec_gemm_nvfp4_rp"
14793 } else {
14794 "qmatvec_gemm_nvfp4"
14795 }
14796 }
14797 _ => unreachable!(),
14798 };
14799 let f = self.func(name);
14800 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let is_k1 = matches!(qtype, QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q4_0);
14805 let k1_tile = if is_k1 {
14807 k1_launch_override().unwrap_or((128, 128, 8))
14808 } else {
14809 (128, 128, 8)
14810 };
14811 let (bm, bn): (u32, u32) = if is_k1 {
14812 (k1_tile.0, k1_tile.1)
14813 } else {
14814 (64, 256)
14815 };
14816 let warps: u32 = if is_k1 {
14817 k1_tile.2
14818 } else {
14819 match qtype {
14820 QT_NVFP4 => 8,
14821 _ => 4,
14822 }
14823 };
14824 let cfg = LaunchConfig {
14825 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
14826 block_dim: (32, warps, 1),
14827 shared_mem_bytes: 0,
14828 };
14829 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14830 let __s_b = self.gpu.stream();
14831 let mut b = __s_b.launch_builder(&f);
14832 b.arg(bytes)
14833 .arg(aq)
14834 .arg(ad)
14835 .arg(&mut y)
14836 .arg(&inf)
14837 .arg(&outf)
14838 .arg(&mi)
14839 .arg(&rb);
14840 unsafe {
14841 b.launch(cfg)?;
14842 }
14843 if scale != 1.0 {
14844 self.scale_inplace(&mut y, scale, m * out_f)?;
14845 }
14846 Ok(y)
14847 }
14848
14849 pub fn qmatvec_gemm_raw(
14854 &self,
14855 bytes: &CudaSlice<u8>,
14856 x: &CudaSlice<f32>,
14857 m: usize,
14858 in_f: usize,
14859 out_f: usize,
14860 qtype: i32,
14861 row_bytes: usize,
14862 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14863 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
14864 let name = match qtype {
14865 QT_Q8_0 => "qmatvec_gemm_q8_0",
14866 QT_Q4_K => "qmatvec_gemm_q4_K",
14867 QT_Q4_0 => "qmatvec_gemm_q4_0",
14868 QT_Q5_K => "qmatvec_gemm_q5_K",
14869 QT_Q6_K => "qmatvec_gemm_q6_K",
14870 QT_NVFP4 => "qmatvec_gemm_nvfp4",
14871 QT_NVFP4_RP => "qmatvec_gemm_nvfp4_rp",
14872 _ => panic!("qmatvec_gemm_raw: qtype {qtype} has no GEMM kernel"),
14873 };
14874 let f = self.func(name);
14875 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let is_k1 = matches!(qtype, QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q4_0);
14879 let k1_tile = if is_k1 {
14881 k1_launch_override().unwrap_or((128, 128, 8))
14882 } else {
14883 (128, 128, 8)
14884 };
14885 let (bm, bn): (u32, u32) = if is_k1 {
14886 (k1_tile.0, k1_tile.1)
14887 } else {
14888 (64, 256)
14889 };
14890 let warps: u32 = if is_k1 {
14891 k1_tile.2
14892 } else {
14893 match qtype {
14894 QT_NVFP4 | QT_NVFP4_RP => 8,
14895 _ => 4,
14896 }
14897 };
14898 let cfg = LaunchConfig {
14899 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
14900 block_dim: (32, warps, 1),
14901 shared_mem_bytes: 0,
14902 };
14903 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14904 let __s_b = self.gpu.stream();
14905 let mut b = __s_b.launch_builder(&f);
14906 b.arg(bytes)
14907 .arg(&aq)
14908 .arg(&ad)
14909 .arg(&mut y)
14910 .arg(&inf)
14911 .arg(&outf)
14912 .arg(&mi)
14913 .arg(&rb);
14914 unsafe {
14915 b.launch(cfg)?;
14916 }
14917 Ok(y)
14918 }
14919
14920 pub fn qmatvec_gemm_q8_0_wgmma_raw(
14927 &self,
14928 rp4: &CudaSlice<u8>,
14929 aq: &CudaSlice<i8>,
14930 ad: &CudaSlice<f32>,
14931 m: usize,
14932 in_f: usize,
14933 out_f: usize,
14934 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14935 assert!(
14936 out_f % 64 == 0 && in_f % 32 == 0,
14937 "wgmma GEMM needs out_f%64==0, in_f%32==0"
14938 );
14939 let f = self.func("qmatvec_gemm_q8_0_wgmma");
14940 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
14942 grid_dim: ((out_f / 64) as u32, (m as u32).div_ceil(64), 1),
14943 block_dim: (128, 1, 1),
14944 shared_mem_bytes: 0,
14945 };
14946 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
14947 let __s_b = self.gpu.stream();
14948 let mut b = __s_b.launch_builder(&f);
14949 b.arg(rp4)
14950 .arg(aq)
14951 .arg(ad)
14952 .arg(&mut y)
14953 .arg(&inf)
14954 .arg(&outf)
14955 .arg(&mi);
14956 unsafe {
14957 b.launch(cfg)?;
14958 }
14959 Ok(y)
14960 }
14961
14962 pub fn scale_inplace(
14964 &self,
14965 y: &mut CudaSlice<f32>,
14966 s: f32,
14967 n: usize,
14968 ) -> Result<(), Box<dyn std::error::Error>> {
14969 let f = self.func("scale_f32");
14970 let cfg = LaunchConfig::for_num_elems(n as u32);
14971 let (sf, ni) = (s, n as i32);
14972 let __s_b = self.gpu.stream();
14973 let mut b = __s_b.launch_builder(&f);
14974 b.arg(y).arg(&sf).arg(&ni);
14975 unsafe {
14976 b.launch(cfg)?;
14977 }
14978 Ok(())
14979 }
14980
14981 pub fn bf16_to_f32(
14986 &self,
14987 data: &cudarc::driver::CudaView<'_, u8>,
14988 n: usize,
14989 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14990 let mut out = self.alloc_uninit::<f32>(n)?;
14991 let f = self.func("bf16_to_f32");
14992 let cfg = LaunchConfig::for_num_elems(n as u32);
14993 let ni = n as i32;
14994 let __s_b = self.gpu.stream();
14995 let mut b = __s_b.launch_builder(&f);
14996 b.arg(data).arg(&mut out).arg(&ni);
14997 unsafe {
14998 b.launch(cfg)?;
14999 }
15000 Ok(out)
15001 }
15002
15003 fn linear_bf16_chunked(
15010 &self,
15011 x: &CudaSlice<f32>,
15012 data: &CudaSlice<u8>,
15013 m: usize,
15014 in_f: usize,
15015 out_f: usize,
15016 exact: bool,
15017 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15018 const CHUNK_BYTES: usize = 256 << 20;
15019 let chunk_rows = (CHUNK_BYTES / (in_f * 4)).max(1).min(out_f);
15020 if chunk_rows >= out_f {
15021 let wf32 = self.bf16_to_f32(&data.slice(0..in_f * out_f * 2), in_f * out_f)?;
15022 return if exact {
15023 self.linear_decode_exact(x, &wf32, m, in_f, out_f)
15024 } else {
15025 self.linear(x, &wf32, m, in_f, out_f)
15026 };
15027 }
15028 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
15029 let mut r0 = 0usize;
15030 while r0 < out_f {
15031 let rows = chunk_rows.min(out_f - r0);
15032 let wslice = data.slice(r0 * in_f * 2..(r0 + rows) * in_f * 2);
15033 let wf32 = self.bf16_to_f32(&wslice, in_f * rows)?;
15034 let yc = if exact {
15035 self.linear_decode_exact(x, &wf32, m, in_f, rows)?
15036 } else {
15037 self.linear(x, &wf32, m, in_f, rows)?
15038 };
15039 for mi in 0..m {
15041 let src = yc.slice(mi * rows..(mi + 1) * rows);
15042 let mut dst = y.slice_mut(mi * out_f + r0..mi * out_f + r0 + rows);
15043 self.gpu.stream().memcpy_dtod(&src, &mut dst)?;
15044 }
15045 r0 += rows;
15046 }
15047 Ok(y)
15048 }
15049
15050 pub fn linear_decode_exact(
15057 &self,
15058 x: &CudaSlice<f32>,
15059 w: &CudaSlice<f32>,
15060 m_tokens: usize,
15061 in_f: usize,
15062 out_f: usize,
15063 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15064 if m_tokens == 1 {
15065 return self.linear(x, w, 1, in_f, out_f);
15066 }
15067 let xv = self.view(x, m_tokens * in_f);
15068 let mut y = self.alloc_uninit::<f32>(m_tokens * out_f)?;
15069 for t in 0..m_tokens {
15070 let row = xv.slice(t * in_f..(t + 1) * in_f);
15071 let mut xr = self.alloc_uninit::<f32>(in_f)?;
15072 self.copy_view_into(&mut xr, 0, &row, in_f)?;
15073 let yr = self.linear(&xr, w, 1, in_f, out_f)?;
15074 self.copy_into(&mut y, t * out_f, &yr, out_f)?;
15075 }
15076 Ok(y)
15077 }
15078
15079 pub fn linear(
15080 &self,
15081 x: &CudaSlice<f32>,
15082 w: &CudaSlice<f32>,
15083 m_tokens: usize,
15084 in_f: usize,
15085 out_f: usize,
15086 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15087 use cudarc::cublaslt::{Matmul, MatmulConfig};
15088 let mut c = self.alloc_uninit::<f32>(m_tokens * out_f)?; let cfg = MatmulConfig {
15090 transa: true,
15091 transb: false,
15092 transc: false,
15093 m: out_f as u64,
15094 n: m_tokens as u64,
15095 k: in_f as u64,
15096 alpha: 1.0,
15097 lda: in_f as i64,
15098 ldb: in_f as i64,
15099 beta: 0.0,
15100 ldc: out_f as i64,
15101 stride_a: None,
15102 stride_b: None,
15103 stride_c: None,
15104 stride_bias: None,
15105 batch_size: None,
15106 };
15107 unsafe {
15108 self.gpu.blas.matmul(cfg, w, x, &mut c, None, None)?;
15109 }
15110 Ok(c)
15111 }
15112
15113 pub fn sdpa_naive(
15115 &self,
15116 q: &CudaSlice<f32>,
15117 k: &CudaSlice<f32>,
15118 v: &CudaSlice<f32>,
15119 o: &mut CudaSlice<f32>,
15120 head_dim: usize,
15121 n_head: usize,
15122 n_head_kv: usize,
15123 t: usize,
15124 t_kv: usize,
15125 scale: f32,
15126 causal: bool,
15127 ) -> Result<(), Box<dyn std::error::Error>> {
15128 let f = self.func("sdpa_naive_f32");
15129 let cfg = LaunchConfig {
15130 grid_dim: (n_head as u32, t as u32, 1),
15131 block_dim: (128, 1, 1),
15132 shared_mem_bytes: (t_kv * 4) as u32,
15133 };
15134 let (hd, nh, nhkv, ti, tkvi, cz) = (
15135 head_dim as i32,
15136 n_head as i32,
15137 n_head_kv as i32,
15138 t as i32,
15139 t_kv as i32,
15140 causal as i32,
15141 );
15142 let __s_b = self.gpu.stream();
15143 let mut b = __s_b.launch_builder(&f);
15144 b.arg(q)
15145 .arg(k)
15146 .arg(v)
15147 .arg(o)
15148 .arg(&hd)
15149 .arg(&nh)
15150 .arg(&nhkv)
15151 .arg(&ti)
15152 .arg(&tkvi)
15153 .arg(&scale)
15154 .arg(&cz);
15155 unsafe {
15156 b.launch(cfg)?;
15157 }
15158 Ok(())
15159 }
15160
15161 #[allow(clippy::too_many_arguments)]
15166 pub fn sdpa_naive_island(
15167 &self,
15168 q: &CudaSlice<f32>,
15169 k: &CudaSlice<f32>,
15170 v: &CudaSlice<f32>,
15171 o: &mut CudaSlice<f32>,
15172 span_id: &CudaSlice<i32>,
15173 head_dim: usize,
15174 n_head: usize,
15175 n_head_kv: usize,
15176 t: usize,
15177 t_kv: usize,
15178 scale: f32,
15179 window: usize,
15180 ) -> Result<(), Box<dyn std::error::Error>> {
15181 let f = self.func("sdpa_naive_island_f32");
15182 let cfg = LaunchConfig {
15183 grid_dim: (n_head as u32, t as u32, 1),
15184 block_dim: (128, 1, 1),
15185 shared_mem_bytes: (t_kv * 4) as u32,
15186 };
15187 let (hd, nh, nhkv, ti, tkvi, wi) = (
15188 head_dim as i32,
15189 n_head as i32,
15190 n_head_kv as i32,
15191 t as i32,
15192 t_kv as i32,
15193 window as i32,
15194 );
15195 let __s_b = self.gpu.stream();
15196 let mut b = __s_b.launch_builder(&f);
15197 b.arg(q)
15198 .arg(k)
15199 .arg(v)
15200 .arg(o)
15201 .arg(span_id)
15202 .arg(&hd)
15203 .arg(&nh)
15204 .arg(&nhkv)
15205 .arg(&ti)
15206 .arg(&tkvi)
15207 .arg(&scale)
15208 .arg(&wi);
15209 unsafe {
15210 b.launch(cfg)?;
15211 }
15212 Ok(())
15213 }
15214
15215 #[allow(clippy::too_many_arguments)]
15217 pub fn sdpa_naive_w(
15218 &self,
15219 q: &CudaSlice<f32>,
15220 k: &CudaSlice<f32>,
15221 v: &CudaSlice<f32>,
15222 o: &mut CudaSlice<f32>,
15223 head_dim: usize,
15224 n_head: usize,
15225 n_head_kv: usize,
15226 t: usize,
15227 t_kv: usize,
15228 scale: f32,
15229 causal: bool,
15230 window: usize,
15231 ) -> Result<(), Box<dyn std::error::Error>> {
15232 let f = self.func("sdpa_naive_w_f32");
15233 let cfg = LaunchConfig {
15234 grid_dim: (n_head as u32, t as u32, 1),
15235 block_dim: (128, 1, 1),
15236 shared_mem_bytes: (t_kv * 4) as u32,
15237 };
15238 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15239 head_dim as i32,
15240 n_head as i32,
15241 n_head_kv as i32,
15242 t as i32,
15243 t_kv as i32,
15244 causal as i32,
15245 window as i32,
15246 );
15247 let __s_b = self.gpu.stream();
15248 let mut b = __s_b.launch_builder(&f);
15249 b.arg(q)
15250 .arg(k)
15251 .arg(v)
15252 .arg(o)
15253 .arg(&hd)
15254 .arg(&nh)
15255 .arg(&nhkv)
15256 .arg(&ti)
15257 .arg(&tkvi)
15258 .arg(&scale)
15259 .arg(&cz)
15260 .arg(&wi);
15261 unsafe {
15262 b.launch(cfg)?;
15263 }
15264 Ok(())
15265 }
15266
15267 pub fn sdpa_naive_view(
15269 &self,
15270 q: &CudaSlice<f32>,
15271 k: &cudarc::driver::CudaView<f32>,
15272 v: &cudarc::driver::CudaView<f32>,
15273 o: &mut CudaSlice<f32>,
15274 head_dim: usize,
15275 n_head: usize,
15276 n_head_kv: usize,
15277 t: usize,
15278 t_kv: usize,
15279 scale: f32,
15280 causal: bool,
15281 ) -> Result<(), Box<dyn std::error::Error>> {
15282 let f = self.func("sdpa_naive_f32");
15283 let cfg = LaunchConfig {
15284 grid_dim: (n_head as u32, t as u32, 1),
15285 block_dim: (128, 1, 1),
15286 shared_mem_bytes: (t_kv * 4) as u32,
15287 };
15288 let (hd, nh, nhkv, ti, tkvi, cz) = (
15289 head_dim as i32,
15290 n_head as i32,
15291 n_head_kv as i32,
15292 t as i32,
15293 t_kv as i32,
15294 causal as i32,
15295 );
15296 let __s_b = self.gpu.stream();
15297 let mut b = __s_b.launch_builder(&f);
15298 b.arg(q)
15299 .arg(k)
15300 .arg(v)
15301 .arg(o)
15302 .arg(&hd)
15303 .arg(&nh)
15304 .arg(&nhkv)
15305 .arg(&ti)
15306 .arg(&tkvi)
15307 .arg(&scale)
15308 .arg(&cz);
15309 unsafe {
15310 b.launch(cfg)?;
15311 }
15312 Ok(())
15313 }
15314
15315 #[allow(clippy::too_many_arguments)]
15323 pub fn fa_dequant_kv_view_f32(
15324 &self,
15325 k: &cudarc::driver::CudaView<u8>,
15326 v: &cudarc::driver::CudaView<u8>,
15327 kf: &mut CudaSlice<f32>,
15328 vf: &mut CudaSlice<f32>,
15329 kv_dim_k: usize,
15330 kv_dim_v: usize,
15331 t_kv: usize,
15332 k_tok_bytes: usize,
15333 v_tok_bytes: usize,
15334 g: bool,
15335 ) -> Result<(), Box<dyn std::error::Error>> {
15336 let f = if g {
15337 self.func_g("fa_dequant_kv_ws_f32")
15338 } else {
15339 self.func("fa_dequant_kv_ws_f32")
15340 };
15341 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
15342 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
15343 let cfg = LaunchConfig {
15344 grid_dim: (nblk.max(1), 1, 1),
15345 block_dim: (256, 1, 1),
15346 shared_mem_bytes: 0,
15347 };
15348 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
15349 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
15350 let __s_b = self.gpu.stream();
15351 let mut b = __s_b.launch_builder(&f);
15352 b.arg(k)
15353 .arg(v)
15354 .arg(&mut *kf)
15355 .arg(&mut *vf)
15356 .arg(&kdk)
15357 .arg(&kdv)
15358 .arg(&tkvi)
15359 .arg(&ktb)
15360 .arg(&vtb);
15361 unsafe {
15362 b.launch(cfg)?;
15363 }
15364 Ok(())
15365 }
15366
15367 #[allow(clippy::too_many_arguments)]
15368 pub fn sdpa_naive_quantized_view(
15369 &self,
15370 q: &CudaSlice<f32>,
15371 k: &cudarc::driver::CudaView<u8>,
15372 v: &cudarc::driver::CudaView<u8>,
15373 o: &mut CudaSlice<f32>,
15374 head_dim: usize,
15375 n_head: usize,
15376 n_head_kv: usize,
15377 t: usize,
15378 t_kv: usize,
15379 scale: f32,
15380 causal: bool,
15381 k_tok_bytes: usize,
15382 v_tok_bytes: usize,
15383 ) -> Result<(), Box<dyn std::error::Error>> {
15384 let kv_dim = n_head_kv * head_dim;
15385 let mut kf = self.uninit(t_kv * kv_dim)?;
15386 let mut vf = self.uninit(t_kv * kv_dim)?;
15387 let f = self.func("fa_dequant_kv_ws_f32");
15388 let total = (2 * t_kv * kv_dim) as u64;
15389 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
15390 let cfg = LaunchConfig {
15391 grid_dim: (nblk.max(1), 1, 1),
15392 block_dim: (256, 1, 1),
15393 shared_mem_bytes: 0,
15394 };
15395 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
15396 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
15397 let __s_b = self.gpu.stream();
15398 let mut b = __s_b.launch_builder(&f);
15399 b.arg(k)
15400 .arg(v)
15401 .arg(&mut kf)
15402 .arg(&mut vf)
15403 .arg(&kv_dim_i)
15404 .arg(&kv_dim_i)
15405 .arg(&t_kv_i)
15406 .arg(&k_tok_bytes_i)
15407 .arg(&v_tok_bytes_i);
15408 unsafe { b.launch(cfg)? };
15409 self.sdpa_naive(
15410 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
15411 )
15412 }
15413
15414 #[allow(clippy::too_many_arguments)]
15426 pub fn sdpa_naive_w_quantized_view(
15427 &self,
15428 q: &CudaSlice<f32>,
15429 k: &cudarc::driver::CudaView<u8>,
15430 v: &cudarc::driver::CudaView<u8>,
15431 o: &mut CudaSlice<f32>,
15432 head_dim: usize,
15433 n_head: usize,
15434 n_head_kv: usize,
15435 t: usize,
15436 t_kv: usize,
15437 scale: f32,
15438 causal: bool,
15439 window: usize,
15440 k_tok_bytes: usize,
15441 v_tok_bytes: usize,
15442 ) -> Result<(), Box<dyn std::error::Error>> {
15443 let kv_dim = n_head_kv * head_dim;
15444 let mut kf = self.uninit(t_kv * kv_dim)?;
15445 let mut vf = self.uninit(t_kv * kv_dim)?;
15446 let f = self.func("fa_dequant_kv_ws_f32");
15447 let total = (2 * t_kv * kv_dim) as u64;
15448 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
15449 let cfg = LaunchConfig {
15450 grid_dim: (nblk.max(1), 1, 1),
15451 block_dim: (256, 1, 1),
15452 shared_mem_bytes: 0,
15453 };
15454 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
15455 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
15456 let __s_b = self.gpu.stream();
15457 let mut b = __s_b.launch_builder(&f);
15458 b.arg(k)
15459 .arg(v)
15460 .arg(&mut kf)
15461 .arg(&mut vf)
15462 .arg(&kv_dim_i)
15463 .arg(&kv_dim_i)
15464 .arg(&t_kv_i)
15465 .arg(&k_tok_bytes_i)
15466 .arg(&v_tok_bytes_i);
15467 unsafe { b.launch(cfg)? };
15468 self.sdpa_naive_w(
15469 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
15470 )
15471 }
15472
15473 pub fn fa_prefill(
15477 &self,
15478 q: &CudaSlice<f32>,
15479 k: &CudaSlice<f32>,
15480 v: &CudaSlice<f32>,
15481 o: &mut CudaSlice<f32>,
15482 head_dim: usize,
15483 n_head: usize,
15484 n_head_kv: usize,
15485 t: usize,
15486 t_kv: usize,
15487 scale: f32,
15488 causal: bool,
15489 ) -> Result<(), Box<dyn std::error::Error>> {
15490 if portable_mma_gated() {
15491 return self.sdpa_naive(
15492 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
15493 );
15494 }
15495 let fa3_on = head_dim == 256
15503 && causal
15504 && t == t_kv
15505 && match std::env::var("MEMRA_FA3").as_deref() {
15506 Ok("0") => false,
15507 Ok("1") => true,
15508 _ => cfg!(memra_hopper_mma),
15509 };
15510 if fa3_on {
15511 let n = t * n_head * head_dim;
15512 let nkv = t * n_head_kv * head_dim;
15513 let mut q16 = self.alloc_u8_uninit(n * 2)?;
15514 let mut k16 = self.alloc_u8_uninit(nkv * 2)?;
15515 let mut v16 = self.alloc_u8_uninit(nkv * 2)?;
15516 self.f32_to_bf16_into(q, &mut q16, n)?;
15517 self.f32_to_bf16_into(k, &mut k16, nkv)?;
15518 self.f32_to_bf16_into(v, &mut v16, nkv)?;
15519 let rc = {
15520 use cudarc::driver::{DevicePtr, DevicePtrMut};
15521 let stream = self.gpu.stream();
15522 let (qp, _g1) = q16.device_ptr(&stream);
15523 let (kp, _g2) = k16.device_ptr(&stream);
15524 let (vp, _g3) = v16.device_ptr(&stream);
15525 let (op, _g4) = o.device_ptr_mut(&stream);
15526 unsafe {
15527 memra_fa3_prefill(
15528 qp as *const core::ffi::c_void,
15529 kp as *const core::ffi::c_void,
15530 vp as *const core::ffi::c_void,
15531 op as *mut f32,
15532 t as i32,
15533 n_head as i32,
15534 n_head_kv as i32,
15535 head_dim as i32,
15536 scale,
15537 stream.cu_stream() as *mut core::ffi::c_void,
15538 )
15539 }
15540 };
15541 if rc != 0 {
15542 return Err(format!("memra_fa3_prefill rc={rc}").into());
15543 }
15544 return Ok(());
15545 }
15546 static FA_P1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15551 let fa_p1 = *FA_P1.get_or_init(|| std::env::var("MEMRA_FA_P1").as_deref() == Ok("1"));
15552 if fa_p1 && head_dim == 256 && !std::env::var("MEMRA_FA_FLOOR").is_ok() {
15553 const BLOCK_Q: usize = 64;
15554 const BKX: usize = 32;
15555 let f = self.func("fa_prefill_bf16_p1");
15556 let shmem = (2 * (2 * BKX * head_dim + BLOCK_Q * BKX)
15557 + 4 * (BLOCK_Q * BKX + 2 * BLOCK_Q)) as u32;
15558 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15559 f.set_attribute(
15560 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15561 shmem as i32,
15562 )?;
15563 let cfg = LaunchConfig {
15564 grid_dim: (
15565 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15566 n_head as u32,
15567 1,
15568 ),
15569 block_dim: (32, 4, 1),
15570 shared_mem_bytes: shmem,
15571 };
15572 let (hd, nh, nhkv, ti, tkvi, cz) = (
15573 head_dim as i32,
15574 n_head as i32,
15575 n_head_kv as i32,
15576 t as i32,
15577 t_kv as i32,
15578 causal as i32,
15579 );
15580 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15581 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15582 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15583 let __s_b = self.gpu.stream();
15584 let mut b = __s_b.launch_builder(&f);
15585 b.arg(&qb)
15586 .arg(&kb)
15587 .arg(&vb)
15588 .arg(o)
15589 .arg(&hd)
15590 .arg(&nh)
15591 .arg(&nhkv)
15592 .arg(&ti)
15593 .arg(&tkvi)
15594 .arg(&scale)
15595 .arg(&cz);
15596 unsafe {
15597 b.launch(cfg)?;
15598 }
15599 return Ok(());
15600 }
15601 const BK: usize = 32;
15607 let w2 = std::env::var("MEMRA_FA_PP_W2").as_deref() == Ok("1");
15610 let (block_q, warps, w2_sfx): (usize, u32, &str) =
15611 if w2 { (32, 2, "_w2") } else { (64, 4, "") };
15612 let hd_sfx = fa_hd_suffix(head_dim)?;
15616 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
15617 let bf16kv = !floor && !w2 && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0");
15622 let (kb16, vb16) = if bf16kv {
15623 let n = t_kv * n_head_kv * head_dim;
15624 let mut kb = self.alloc_u8_uninit(n * 2)?;
15625 let mut vb = self.alloc_u8_uninit(n * 2)?;
15626 let fcv = self.func("f32_to_bf16_bulk");
15627 let ni = n as i64;
15628 let cfgc = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
15629 let __s_b = self.gpu.stream();
15630 let mut b = __s_b.launch_builder(&fcv);
15631 b.arg(k).arg(&mut kb).arg(&ni);
15632 unsafe {
15633 b.launch(cfgc)?;
15634 }
15635 let __s_b = self.gpu.stream();
15636 let mut b = __s_b.launch_builder(&fcv);
15637 b.arg(v).arg(&mut vb).arg(&ni);
15638 unsafe {
15639 b.launch(cfgc)?;
15640 }
15641 (Some(kb), Some(vb))
15642 } else {
15643 (None, None)
15644 };
15645 let f = self.func(&if bf16kv {
15646 format!("fa_prefill_bf16kv_pp{hd_sfx}")
15647 } else {
15648 format!(
15649 "fa_prefill_f32{}{}{hd_sfx}",
15650 if floor { "" } else { "_pp" },
15651 if floor { "" } else { w2_sfx }
15652 )
15653 });
15654 let kv_stages = if bf16kv { 2 } else { 1 };
15657 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
15658 + 4 * (block_q * BK + 2 * block_q)) as u32;
15659 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15660 f.set_attribute(
15661 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15662 shmem as i32,
15663 )?;
15664 let cfg = LaunchConfig {
15665 grid_dim: (
15666 (t as u32 + block_q as u32 - 1) / block_q as u32,
15667 n_head as u32,
15668 1,
15669 ),
15670 block_dim: (32, warps, 1),
15671 shared_mem_bytes: shmem,
15672 };
15673 let (hd, nh, nhkv, ti, tkvi, cz) = (
15674 head_dim as i32,
15675 n_head as i32,
15676 n_head_kv as i32,
15677 t as i32,
15678 t_kv as i32,
15679 causal as i32,
15680 );
15681 let __s_b = self.gpu.stream();
15682 let mut b = __s_b.launch_builder(&f);
15683 b.arg(q);
15684 match (&kb16, &vb16) {
15685 (Some(kb), Some(vb)) => {
15686 b.arg(kb).arg(vb);
15687 }
15688 _ => {
15689 b.arg(k).arg(v);
15690 }
15691 }
15692 b.arg(o)
15693 .arg(&hd)
15694 .arg(&nh)
15695 .arg(&nhkv)
15696 .arg(&ti)
15697 .arg(&tkvi)
15698 .arg(&scale)
15699 .arg(&cz);
15700 unsafe {
15701 b.launch(cfg)?;
15702 }
15703 Ok(())
15704 }
15705
15706 #[allow(clippy::too_many_arguments)]
15710 pub fn fa_prefill_w(
15711 &self,
15712 q: &CudaSlice<f32>,
15713 k: &CudaSlice<f32>,
15714 v: &CudaSlice<f32>,
15715 o: &mut CudaSlice<f32>,
15716 head_dim: usize,
15717 n_head: usize,
15718 n_head_kv: usize,
15719 t: usize,
15720 t_kv: usize,
15721 scale: f32,
15722 causal: bool,
15723 window: usize,
15724 ) -> Result<(), Box<dyn std::error::Error>> {
15725 if portable_mma_gated() {
15728 return self.sdpa_naive_w(
15729 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
15730 );
15731 }
15732 static FAW_F32: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15736 let faw_f32 =
15737 *FAW_F32.get_or_init(|| std::env::var("MEMRA_FAW_STAGE").as_deref() == Ok("f32"));
15738 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
15739 self.fa_prefill_w_arm(
15740 q,
15741 k,
15742 v,
15743 o,
15744 head_dim,
15745 n_head,
15746 n_head_kv,
15747 t,
15748 t_kv,
15749 scale,
15750 causal,
15751 window,
15752 floor || faw_f32,
15753 floor,
15754 )
15755 }
15756
15757 #[allow(clippy::too_many_arguments)]
15760 pub fn fa_prefill_w_pre(
15761 &self,
15762 qb: &CudaSlice<u8>,
15763 kb: &CudaSlice<u8>,
15764 vb: &CudaSlice<u8>,
15765 o: &mut CudaSlice<f32>,
15766 head_dim: usize,
15767 n_head: usize,
15768 n_head_kv: usize,
15769 t: usize,
15770 t_kv: usize,
15771 scale: f32,
15772 causal: bool,
15773 window: usize,
15774 v_f16: bool,
15775 ) -> Result<(), Box<dyn std::error::Error>> {
15776 const BLOCK_Q: usize = 64;
15777 const BK: usize = 32;
15778 debug_assert_eq!(head_dim, 256);
15779 let hp = fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15780 debug_assert!(!v_f16 || hp, "f16 V emitted but the SWA hp arm is off");
15781 if hp {
15782 const BLOCK_QH: usize = 32;
15783 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
15786 let vh: &CudaSlice<u8> = if v_f16 {
15787 vb
15788 } else {
15789 let n = t_kv * n_head_kv * head_dim;
15790 if vguard.as_ref().map(|b| b.len() < n * 2).unwrap_or(true) {
15791 *vguard = Some(self.alloc_uninit::<u8>(n * 2)?);
15792 }
15793 self.bf16_to_f16_into(vb, n, vguard.as_mut().unwrap())?;
15794 vguard.as_ref().unwrap()
15795 };
15796 let f = self.func("fa_prefill_w_bf16_p1h2");
15797 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
15798 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15799 f.set_attribute(
15800 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15801 shmem as i32,
15802 )?;
15803 let cfg = LaunchConfig {
15804 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
15805 block_dim: (32, 4, 1),
15806 shared_mem_bytes: shmem,
15807 };
15808 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15809 head_dim as i32,
15810 n_head as i32,
15811 n_head_kv as i32,
15812 t as i32,
15813 t_kv as i32,
15814 causal as i32,
15815 window as i32,
15816 );
15817 let __s_b = self.gpu.stream();
15818 let mut b = __s_b.launch_builder(&f);
15819 b.arg(qb)
15820 .arg(kb)
15821 .arg(vh)
15822 .arg(o)
15823 .arg(&hd)
15824 .arg(&nh)
15825 .arg(&nhkv)
15826 .arg(&ti)
15827 .arg(&tkvi)
15828 .arg(&scale)
15829 .arg(&cz)
15830 .arg(&wi);
15831 unsafe {
15832 b.launch(cfg)?;
15833 }
15834 return Ok(());
15835 }
15836 let f = self.func("fa_prefill_w_bf16_p1");
15837 let shmem =
15838 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15839 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15840 f.set_attribute(
15841 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15842 shmem as i32,
15843 )?;
15844 let cfg = LaunchConfig {
15845 grid_dim: (
15846 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15847 n_head as u32,
15848 1,
15849 ),
15850 block_dim: (32, 4, 1),
15851 shared_mem_bytes: shmem,
15852 };
15853 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15854 head_dim as i32,
15855 n_head as i32,
15856 n_head_kv as i32,
15857 t as i32,
15858 t_kv as i32,
15859 causal as i32,
15860 window as i32,
15861 );
15862 let __s_b = self.gpu.stream();
15863 let mut b = __s_b.launch_builder(&f);
15864 b.arg(qb)
15865 .arg(kb)
15866 .arg(vb)
15867 .arg(o)
15868 .arg(&hd)
15869 .arg(&nh)
15870 .arg(&nhkv)
15871 .arg(&ti)
15872 .arg(&tkvi)
15873 .arg(&scale)
15874 .arg(&cz)
15875 .arg(&wi);
15876 unsafe {
15877 b.launch(cfg)?;
15878 }
15879 Ok(())
15880 }
15881
15882 #[allow(clippy::too_many_arguments)]
15884 pub fn fa_prefill_w_arm(
15885 &self,
15886 q: &CudaSlice<f32>,
15887 k: &CudaSlice<f32>,
15888 v: &CudaSlice<f32>,
15889 o: &mut CudaSlice<f32>,
15890 head_dim: usize,
15891 n_head: usize,
15892 n_head_kv: usize,
15893 t: usize,
15894 t_kv: usize,
15895 scale: f32,
15896 causal: bool,
15897 window: usize,
15898 f32_stage: bool,
15899 floor: bool,
15900 ) -> Result<(), Box<dyn std::error::Error>> {
15901 const BLOCK_Q: usize = 64;
15902 const BK: usize = 32;
15903 debug_assert_eq!(head_dim, 256, "fa_prefill_w is stamped hd256 only");
15904 static P1_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15908 let p1 = !floor
15909 && !f32_stage
15910 && *P1_ON.get_or_init(|| {
15911 std::env::var("MEMRA_FAW_P1")
15912 .map(|v| v != "0")
15913 .unwrap_or(true)
15914 });
15915 let hp =
15916 p1 && fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15917 if hp {
15918 const BLOCK_QH: usize = 32;
15919 let f = self.func("fa_prefill_w_bf16_p1h2");
15920 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
15921 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15922 f.set_attribute(
15923 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15924 shmem as i32,
15925 )?;
15926 let cfg = LaunchConfig {
15927 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
15928 block_dim: (32, 4, 1),
15929 shared_mem_bytes: shmem,
15930 };
15931 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15932 head_dim as i32,
15933 n_head as i32,
15934 n_head_kv as i32,
15935 t as i32,
15936 t_kv as i32,
15937 causal as i32,
15938 window as i32,
15939 );
15940 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15941 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15942 let vh = self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?;
15943 let __s_b = self.gpu.stream();
15944 let mut b = __s_b.launch_builder(&f);
15945 b.arg(&qb)
15946 .arg(&kb)
15947 .arg(&vh)
15948 .arg(o)
15949 .arg(&hd)
15950 .arg(&nh)
15951 .arg(&nhkv)
15952 .arg(&ti)
15953 .arg(&tkvi)
15954 .arg(&scale)
15955 .arg(&cz)
15956 .arg(&wi);
15957 unsafe {
15958 b.launch(cfg)?;
15959 }
15960 return Ok(());
15961 }
15962 if p1 {
15963 let f = self.func("fa_prefill_w_bf16_p1");
15964 let shmem =
15965 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15966 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15967 f.set_attribute(
15968 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15969 shmem as i32,
15970 )?;
15971 let cfg = LaunchConfig {
15972 grid_dim: (
15973 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15974 n_head as u32,
15975 1,
15976 ),
15977 block_dim: (32, 4, 1),
15978 shared_mem_bytes: shmem,
15979 };
15980 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15981 head_dim as i32,
15982 n_head as i32,
15983 n_head_kv as i32,
15984 t as i32,
15985 t_kv as i32,
15986 causal as i32,
15987 window as i32,
15988 );
15989 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15990 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15991 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15992 let __s_b = self.gpu.stream();
15993 let mut b = __s_b.launch_builder(&f);
15994 b.arg(&qb)
15995 .arg(&kb)
15996 .arg(&vb)
15997 .arg(o)
15998 .arg(&hd)
15999 .arg(&nh)
16000 .arg(&nhkv)
16001 .arg(&ti)
16002 .arg(&tkvi)
16003 .arg(&scale)
16004 .arg(&cz)
16005 .arg(&wi);
16006 unsafe {
16007 b.launch(cfg)?;
16008 }
16009 return Ok(());
16010 }
16011 static G4_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16014 let g4 = !floor
16015 && !f32_stage
16016 && n_head_kv == 1
16017 && n_head % 4 == 0
16018 && *G4_ON.get_or_init(|| {
16019 std::env::var("MEMRA_FAW_G4")
16020 .map(|v| v != "0")
16021 .unwrap_or(true)
16022 });
16023 if g4 {
16024 const SP_M: usize = 16;
16025 static O2_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16028 let o2 = *O2_ON.get_or_init(|| {
16029 std::env::var("MEMRA_FAW_O2")
16030 .map(|v| v != "0")
16031 .unwrap_or(true)
16032 });
16033 let f = self.func(if o2 {
16034 "fa_prefill_w_bf16_g4o2"
16035 } else {
16036 "fa_prefill_w_bf16_g4"
16037 });
16038 let shmem = if o2 {
16039 (2 * (4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M)) as u32
16040 } else {
16041 (2 * (2 * BK * head_dim + 4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M))
16042 as u32
16043 };
16044 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16045 f.set_attribute(
16046 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16047 shmem as i32,
16048 )?;
16049 let cfg = LaunchConfig {
16050 grid_dim: ((t as u32).div_ceil(SP_M as u32), (n_head / 4) as u32, 1),
16051 block_dim: (32, 4, 1),
16052 shared_mem_bytes: shmem,
16053 };
16054 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
16055 head_dim as i32,
16056 n_head as i32,
16057 n_head_kv as i32,
16058 t as i32,
16059 t_kv as i32,
16060 causal as i32,
16061 window as i32,
16062 );
16063 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16064 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16065 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
16066 let __s_b = self.gpu.stream();
16067 let mut b = __s_b.launch_builder(&f);
16068 b.arg(&qb)
16069 .arg(&kb)
16070 .arg(&vb)
16071 .arg(o)
16072 .arg(&hd)
16073 .arg(&nh)
16074 .arg(&nhkv)
16075 .arg(&ti)
16076 .arg(&tkvi)
16077 .arg(&scale)
16078 .arg(&cz)
16079 .arg(&wi);
16080 unsafe {
16081 b.launch(cfg)?;
16082 }
16083 return Ok(());
16084 }
16085 let f = self.func(if floor {
16086 "fa_prefill_w_f32"
16087 } else if f32_stage {
16088 "fa_prefill_w_f32_pp"
16089 } else {
16090 "fa_prefill_w_bf16_pp"
16091 });
16092 let shmem =
16093 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
16094 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16095 f.set_attribute(
16096 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16097 shmem as i32,
16098 )?;
16099 let cfg = LaunchConfig {
16100 grid_dim: (
16101 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16102 n_head as u32,
16103 1,
16104 ),
16105 block_dim: (32, 4, 1),
16106 shared_mem_bytes: shmem,
16107 };
16108 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
16109 head_dim as i32,
16110 n_head as i32,
16111 n_head_kv as i32,
16112 t as i32,
16113 t_kv as i32,
16114 causal as i32,
16115 window as i32,
16116 );
16117 if f32_stage {
16118 let __s_b = self.gpu.stream();
16119 let mut b = __s_b.launch_builder(&f);
16120 b.arg(q)
16121 .arg(k)
16122 .arg(v)
16123 .arg(o)
16124 .arg(&hd)
16125 .arg(&nh)
16126 .arg(&nhkv)
16127 .arg(&ti)
16128 .arg(&tkvi)
16129 .arg(&scale)
16130 .arg(&cz)
16131 .arg(&wi);
16132 unsafe {
16133 b.launch(cfg)?;
16134 }
16135 } else {
16136 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16137 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16138 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
16139 let __s_b = self.gpu.stream();
16140 let mut b = __s_b.launch_builder(&f);
16141 b.arg(&qb)
16142 .arg(&kb)
16143 .arg(&vb)
16144 .arg(o)
16145 .arg(&hd)
16146 .arg(&nh)
16147 .arg(&nhkv)
16148 .arg(&ti)
16149 .arg(&tkvi)
16150 .arg(&scale)
16151 .arg(&cz)
16152 .arg(&wi);
16153 unsafe {
16154 b.launch(cfg)?;
16155 }
16156 }
16157 Ok(())
16158 }
16159
16160 #[allow(clippy::too_many_arguments)]
16164 pub fn fa_prefill_hd512(
16165 &self,
16166 q: &CudaSlice<f32>,
16167 k: &CudaSlice<f32>,
16168 v: &CudaSlice<f32>,
16169 o: &mut CudaSlice<f32>,
16170 head_dim: usize,
16171 n_head: usize,
16172 n_head_kv: usize,
16173 t: usize,
16174 t_kv: usize,
16175 scale: f32,
16176 causal: bool,
16177 ) -> Result<(), Box<dyn std::error::Error>> {
16178 if portable_mma_gated() {
16180 return self.sdpa_naive(
16181 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
16182 );
16183 }
16184 static F32_STAGE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16190 let f32_stage =
16191 *F32_STAGE.get_or_init(|| std::env::var("MEMRA_FA512_STAGE").as_deref() == Ok("f32"));
16192 static SP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16196 let sp = !f32_stage
16197 && *SP_ON.get_or_init(|| {
16198 std::env::var("MEMRA_FA512_SP")
16199 .map(|v| v != "0")
16200 .unwrap_or(true)
16201 });
16202 self.fa_prefill_hd512_arm(
16203 q,
16204 k,
16205 v,
16206 o,
16207 head_dim,
16208 n_head,
16209 n_head_kv,
16210 t,
16211 t_kv,
16212 scale,
16213 causal,
16214 f32_stage,
16215 sp,
16216 sp && fa_f16pv_on(),
16217 )
16218 }
16219
16220 #[allow(clippy::too_many_arguments)]
16222 pub fn fa_prefill_hd512_pre(
16223 &self,
16224 qb: &CudaSlice<u8>,
16225 kb: &CudaSlice<u8>,
16226 vb: &CudaSlice<u8>,
16227 o: &mut CudaSlice<f32>,
16228 head_dim: usize,
16229 n_head: usize,
16230 n_head_kv: usize,
16231 t: usize,
16232 t_kv: usize,
16233 scale: f32,
16234 causal: bool,
16235 v_f16: bool,
16236 ) -> Result<(), Box<dyn std::error::Error>> {
16237 debug_assert_eq!(head_dim, 512);
16238 const SP_M: usize = 16;
16239 const BKS: usize = 32;
16240 let f16pv = fa_f16pv_on();
16244 let nw = if f16pv { fa512_wide_warps() } else { 2 };
16245 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
16246 debug_assert!(!v_f16 || f16pv, "f16 V emitted without the door on");
16247 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
16248 let vref: &CudaSlice<u8> = if f16pv && !v_f16 {
16249 let n = t_kv * n_head_kv * head_dim;
16251 let need = n * 2;
16252 if vguard.as_ref().map(|b| b.len() < need).unwrap_or(true) {
16253 *vguard = Some(self.alloc_uninit::<u8>(need)?);
16254 }
16255 let dst = vguard.as_mut().unwrap();
16256 self.bf16_to_f16_into(vb, n, dst)?;
16257 vguard.as_ref().unwrap()
16258 } else {
16259 vb
16260 };
16261 let f = self.func(if hp {
16262 "fa_prefill_bf16_hd512_sp16h2"
16263 } else {
16264 match (f16pv, nw) {
16265 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
16266 (true, _) => "fa_prefill_bf16_hd512_sp16",
16267 _ => "fa_prefill_bf16_hd512_sp",
16268 }
16269 });
16270 let (nwarp, npart) = if hp {
16271 (4usize, 4usize)
16272 } else if nw > 2 {
16273 (nw, nw)
16274 } else {
16275 (2, 1)
16276 };
16277 let shmem = if hp {
16279 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS) + 4 * (2 * npart * SP_M * BKS + 2 * SP_M))
16280 as u32
16281 } else {
16282 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
16283 + 4 * (npart * SP_M * BKS + SP_M)) as u32
16284 };
16285 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16286 f.set_attribute(
16287 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16288 shmem as i32,
16289 )?;
16290 let grid_y = if hp {
16291 (n_head / 2) as u32
16292 } else {
16293 n_head as u32
16294 };
16295 let cfg = LaunchConfig {
16296 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
16297 block_dim: (32, nwarp as u32, 1),
16298 shared_mem_bytes: shmem,
16299 };
16300 let (hd, nh, nhkv, ti, tkvi, cz) = (
16301 head_dim as i32,
16302 n_head as i32,
16303 n_head_kv as i32,
16304 t as i32,
16305 t_kv as i32,
16306 causal as i32,
16307 );
16308 let __s_b = self.gpu.stream();
16309 let mut b = __s_b.launch_builder(&f);
16310 b.arg(qb)
16311 .arg(kb)
16312 .arg(vref)
16313 .arg(o)
16314 .arg(&hd)
16315 .arg(&nh)
16316 .arg(&nhkv)
16317 .arg(&ti)
16318 .arg(&tkvi)
16319 .arg(&scale)
16320 .arg(&cz);
16321 unsafe {
16322 b.launch(cfg)?;
16323 }
16324 Ok(())
16325 }
16326
16327 #[allow(clippy::too_many_arguments)]
16330 pub fn fa_prefill_hd512_arm(
16331 &self,
16332 q: &CudaSlice<f32>,
16333 k: &CudaSlice<f32>,
16334 v: &CudaSlice<f32>,
16335 o: &mut CudaSlice<f32>,
16336 head_dim: usize,
16337 n_head: usize,
16338 n_head_kv: usize,
16339 t: usize,
16340 t_kv: usize,
16341 scale: f32,
16342 causal: bool,
16343 f32_stage: bool,
16344 sp: bool,
16345 f16pv: bool,
16346 ) -> Result<(), Box<dyn std::error::Error>> {
16347 debug_assert_eq!(head_dim, 512, "fa_prefill_hd512 is hd512 only");
16348 if sp && !f32_stage {
16349 const SP_M: usize = 16;
16353 const BKS: usize = 32;
16354 let nw = if f16pv { fa512_wide_warps() } else { 2 };
16355 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
16356 let f = self.func(if hp {
16357 "fa_prefill_bf16_hd512_sp16h2"
16358 } else {
16359 match (f16pv, nw) {
16360 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
16361 (true, _) => "fa_prefill_bf16_hd512_sp16",
16362 _ => "fa_prefill_bf16_hd512_sp",
16363 }
16364 });
16365 let (nwarp, npart) = if hp {
16366 (4usize, 4usize)
16367 } else if nw > 2 {
16368 (nw, nw)
16369 } else {
16370 (2, 1)
16371 };
16372 let shmem = if hp {
16373 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS)
16374 + 4 * (2 * npart * SP_M * BKS + 2 * SP_M)) as u32
16375 } else {
16376 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
16377 + 4 * (npart * SP_M * BKS + SP_M)) as u32
16378 };
16379 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16380 f.set_attribute(
16381 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16382 shmem as i32,
16383 )?;
16384 let grid_y = if hp {
16385 (n_head / 2) as u32
16386 } else {
16387 n_head as u32
16388 };
16389 let cfg = LaunchConfig {
16390 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
16391 block_dim: (32, nwarp as u32, 1),
16392 shared_mem_bytes: shmem,
16393 };
16394 let (hd, nh, nhkv, ti, tkvi, cz) = (
16395 head_dim as i32,
16396 n_head as i32,
16397 n_head_kv as i32,
16398 t as i32,
16399 t_kv as i32,
16400 causal as i32,
16401 );
16402 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16403 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16404 let vb = if f16pv {
16405 self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?
16406 } else {
16407 self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?
16408 };
16409 let __s_b = self.gpu.stream();
16410 let mut b = __s_b.launch_builder(&f);
16411 b.arg(&qb)
16412 .arg(&kb)
16413 .arg(&vb)
16414 .arg(o)
16415 .arg(&hd)
16416 .arg(&nh)
16417 .arg(&nhkv)
16418 .arg(&ti)
16419 .arg(&tkvi)
16420 .arg(&scale)
16421 .arg(&cz);
16422 unsafe {
16423 b.launch(cfg)?;
16424 }
16425 return Ok(());
16426 }
16427 const BLOCK_Q: usize = 32;
16428 const BK: usize = 32;
16429 const HALF: usize = 256;
16430 let f = self.func(if f32_stage {
16431 "fa_prefill_f32_hd512"
16432 } else {
16433 "fa_prefill_bf16_hd512"
16434 });
16435 let shmem = (2 * (BLOCK_Q * head_dim + BK * head_dim + BK * HALF + BLOCK_Q * BK)
16437 + 4 * BLOCK_Q) as u32;
16438 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16439 f.set_attribute(
16440 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16441 shmem as i32,
16442 )?;
16443 let cfg = LaunchConfig {
16444 grid_dim: (
16445 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16446 n_head as u32,
16447 2,
16448 ),
16449 block_dim: (32, 2, 1),
16450 shared_mem_bytes: shmem,
16451 };
16452 let (hd, nh, nhkv, ti, tkvi, cz) = (
16453 head_dim as i32,
16454 n_head as i32,
16455 n_head_kv as i32,
16456 t as i32,
16457 t_kv as i32,
16458 causal as i32,
16459 );
16460 if f32_stage {
16461 let __s_b = self.gpu.stream();
16462 let mut b = __s_b.launch_builder(&f);
16463 b.arg(q)
16464 .arg(k)
16465 .arg(v)
16466 .arg(o)
16467 .arg(&hd)
16468 .arg(&nh)
16469 .arg(&nhkv)
16470 .arg(&ti)
16471 .arg(&tkvi)
16472 .arg(&scale)
16473 .arg(&cz);
16474 unsafe {
16475 b.launch(cfg)?;
16476 }
16477 } else {
16478 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16479 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16480 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
16481 let __s_b = self.gpu.stream();
16482 let mut b = __s_b.launch_builder(&f);
16483 b.arg(&qb)
16484 .arg(&kb)
16485 .arg(&vb)
16486 .arg(o)
16487 .arg(&hd)
16488 .arg(&nh)
16489 .arg(&nhkv)
16490 .arg(&ti)
16491 .arg(&tkvi)
16492 .arg(&scale)
16493 .arg(&cz);
16494 unsafe {
16495 b.launch(cfg)?;
16496 }
16497 }
16498 Ok(())
16499 }
16500
16501 #[allow(clippy::too_many_arguments)]
16505 pub fn rope_neox2_bf16e(
16506 &self,
16507 q: &mut CudaSlice<f32>,
16508 k: &mut CudaSlice<f32>,
16509 qb: &mut CudaSlice<u8>,
16510 kb: &mut CudaSlice<u8>,
16511 pos: &CudaSlice<i32>,
16512 head_dim: usize,
16513 n_dims: usize,
16514 nh_q: usize,
16515 nh_k: usize,
16516 n_tokens: usize,
16517 base: f32,
16518 freq_scale: f32,
16519 ff: Option<&CudaSlice<f32>>,
16520 ) -> Result<(), Box<dyn std::error::Error>> {
16521 let f = self.func("rope_neox2_bf16e_f32");
16522 let rows = ((nh_q + nh_k) * n_tokens) as u32;
16523 let cfg = LaunchConfig {
16524 grid_dim: (rows, 1, 1),
16525 block_dim: ((head_dim / 2) as u32, 1, 1),
16526 shared_mem_bytes: 0,
16527 };
16528 let theta_scale = base.powf(-2.0 / n_dims as f32);
16529 let (hd, nd, nhq, nhk, nt) = (
16530 head_dim as i32,
16531 n_dims as i32,
16532 nh_q as i32,
16533 nh_k as i32,
16534 n_tokens as i32,
16535 );
16536 let __s_b = self.gpu.stream();
16537 let mut b = __s_b.launch_builder(&f);
16538 match ff {
16539 Some(t) => {
16540 b.arg(&mut *q)
16541 .arg(&mut *k)
16542 .arg(&mut *qb)
16543 .arg(&mut *kb)
16544 .arg(pos)
16545 .arg(&hd)
16546 .arg(&nd)
16547 .arg(&nhq)
16548 .arg(&nhk)
16549 .arg(&nt)
16550 .arg(&theta_scale)
16551 .arg(&freq_scale)
16552 .arg(t);
16553 unsafe {
16554 b.launch(cfg)?;
16555 }
16556 }
16557 None => {
16558 let null: u64 = 0;
16559 b.arg(&mut *q)
16560 .arg(&mut *k)
16561 .arg(&mut *qb)
16562 .arg(&mut *kb)
16563 .arg(pos)
16564 .arg(&hd)
16565 .arg(&nd)
16566 .arg(&nhq)
16567 .arg(&nhk)
16568 .arg(&nt)
16569 .arg(&theta_scale)
16570 .arg(&freq_scale)
16571 .arg(&null);
16572 unsafe {
16573 b.launch(cfg)?;
16574 }
16575 }
16576 }
16577 Ok(())
16578 }
16579
16580 pub fn f32_to_bf16(
16583 &self,
16584 x: &CudaSlice<f32>,
16585 n: usize,
16586 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
16587 assert!(n % 4 == 0, "f32_to_bf16 requires n % 4 == 0, got {n}");
16588 let mut y = self.alloc_uninit::<u8>(n * 2)?;
16589 let f = self.func("f32_to_bf16_flat");
16590 let n_i = n as i64;
16591 let cfg = LaunchConfig {
16592 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
16593 block_dim: (256, 1, 1),
16594 shared_mem_bytes: 0,
16595 };
16596 let __s_b = self.gpu.stream();
16597 let mut b = __s_b.launch_builder(&f);
16598 b.arg(x).arg(&mut y).arg(&n_i);
16599 unsafe {
16600 b.launch(cfg)?;
16601 }
16602 Ok(y)
16603 }
16604
16605 pub fn f32_to_f16(
16606 &self,
16607 x: &CudaSlice<f32>,
16608 n: usize,
16609 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
16610 assert!(n % 4 == 0, "f32_to_f16 requires n % 4 == 0, got {n}");
16611 let mut y = self.alloc_uninit::<u8>(n * 2)?;
16612 let f = self.func("f32_to_f16_flat");
16613 let n_i = n as i64;
16614 let cfg = LaunchConfig {
16615 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
16616 block_dim: (256, 1, 1),
16617 shared_mem_bytes: 0,
16618 };
16619 let __s_b = self.gpu.stream();
16620 let mut b = __s_b.launch_builder(&f);
16621 b.arg(x).arg(&mut y).arg(&n_i);
16622 unsafe {
16623 b.launch(cfg)?;
16624 }
16625 Ok(y)
16626 }
16627
16628 pub fn bf16_to_f16(
16630 &self,
16631 xb: &CudaSlice<u8>,
16632 n: usize,
16633 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
16634 let mut y = self.alloc_uninit::<u8>(n * 2)?;
16635 self.bf16_to_f16_into(xb, n, &mut y)?;
16636 Ok(y)
16637 }
16638
16639 pub fn bf16_to_f16_into(
16641 &self,
16642 xb: &CudaSlice<u8>,
16643 n: usize,
16644 y: &mut CudaSlice<u8>,
16645 ) -> Result<(), Box<dyn std::error::Error>> {
16646 assert!(n % 2 == 0, "bf16_to_f16 requires n % 2 == 0, got {n}");
16647 assert!(y.len() >= n * 2);
16648 let f = self.func("bf16_to_f16_flat");
16649 let n2 = (n / 2) as i64;
16650 let cfg = LaunchConfig {
16651 grid_dim: (((n / 2) as u32).div_ceil(256), 1, 1),
16652 block_dim: (256, 1, 1),
16653 shared_mem_bytes: 0,
16654 };
16655 let __s_b = self.gpu.stream();
16656 let mut b = __s_b.launch_builder(&f);
16657 b.arg(xb).arg(y).arg(&n2);
16658 unsafe {
16659 b.launch(cfg)?;
16660 }
16661 Ok(())
16662 }
16663
16664 #[allow(clippy::too_many_arguments)]
16669 pub fn fa_prefill_vl8(
16670 &self,
16671 seqs: &[FaSeqVl],
16672 head_dim: usize,
16673 n_head: usize,
16674 n_head_kv: usize,
16675 scale: f32,
16676 ) -> Result<(), Box<dyn std::error::Error>> {
16677 const BK: usize = 32;
16678 let b = seqs.len();
16679 assert!(b >= 1 && b <= 8);
16680 let mut packed = [FaSeqVl::default(); 8];
16681 packed[..b].copy_from_slice(seqs);
16682 let v = FaVl8(packed);
16683 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
16684 let ept = (n_head_kv * head_dim) as i32;
16685 {
16686 let f = self.func("fa_mirror_vl");
16687 let max_n = (max_t as i64) * ept as i64;
16688 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
16689 for which in 0..2i32 {
16690 let cfg = LaunchConfig {
16691 grid_dim: (blocks, 1, b as u32),
16692 block_dim: (256, 1, 1),
16693 shared_mem_bytes: 0,
16694 };
16695 let __s_lb = self.gpu.stream();
16696 let mut lb = __s_lb.launch_builder(&f);
16697 lb.arg(&v).arg(&ept).arg(&which);
16698 unsafe {
16699 lb.launch(cfg)?;
16700 }
16701 }
16702 }
16703 let hd_sfx = fa_hd_suffix(head_dim)?;
16704 let f = self.func(&format!("fa_prefill_bf16kv_vl{hd_sfx}"));
16705 let block_q = 64usize;
16706 let kv_stages = 2usize;
16707 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
16708 + 4 * (block_q * BK + 2 * block_q)) as u32;
16709 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16710 f.set_attribute(
16711 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16712 shmem as i32,
16713 )?;
16714 let cfg = LaunchConfig {
16715 grid_dim: (max_t.div_ceil(block_q as u32), n_head as u32, b as u32),
16716 block_dim: (32, 4, 1),
16717 shared_mem_bytes: shmem,
16718 };
16719 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
16720 let __s_lb = self.gpu.stream();
16721 let mut lb = __s_lb.launch_builder(&f);
16722 lb.arg(&v).arg(&hd).arg(&nh).arg(&nhkv).arg(&scale);
16723 unsafe {
16724 lb.launch(cfg)?;
16725 }
16726 Ok(())
16727 }
16728
16729 #[allow(clippy::too_many_arguments)]
16733 pub fn attn_pre_vl8(
16734 &self,
16735 seqs: &[AttnPreVl],
16736 wq: &CudaSlice<f32>,
16737 wk: &CudaSlice<f32>,
16738 head_dim: usize,
16739 rope_dims: usize,
16740 n_head: usize,
16741 n_head_kv: usize,
16742 eps: f32,
16743 freq_base: f32,
16744 freq_scale: f32,
16745 kv_dim_k: usize,
16746 kv_dim_v: usize,
16747 k_tok_bytes: usize,
16748 v_tok_bytes: usize,
16749 ) -> Result<(), Box<dyn std::error::Error>> {
16750 let b = seqs.len();
16751 assert!(b >= 1 && b <= 8);
16752 let mut packed = [AttnPreVl::default(); 8];
16753 packed[..b].copy_from_slice(seqs);
16754 let v = AttnPreVl8(packed);
16755 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
16756 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
16757 {
16758 let f = self.func("q_gate_split_vl");
16759 let n = max_t * (n_head * head_dim) as u32;
16760 let cfg = LaunchConfig {
16761 grid_dim: (n.div_ceil(256), 1, b as u32),
16762 block_dim: (256, 1, 1),
16763 shared_mem_bytes: 0,
16764 };
16765 let __s_lb = self.gpu.stream();
16766 let mut lb = __s_lb.launch_builder(&f);
16767 lb.arg(&v).arg(&hd).arg(&nh);
16768 unsafe {
16769 lb.launch(cfg)?;
16770 }
16771 }
16772 {
16773 let f = self.func("attn_rms_vl");
16774 let cfg = LaunchConfig {
16775 grid_dim: (max_t * n_head as u32, 2, b as u32),
16776 block_dim: (rms_block(), 1, 1),
16777 shared_mem_bytes: 0,
16778 };
16779 let __s_lb = self.gpu.stream();
16780 let mut lb = __s_lb.launch_builder(&f);
16781 lb.arg(&v)
16782 .arg(wq)
16783 .arg(wk)
16784 .arg(&hd)
16785 .arg(&nh)
16786 .arg(&nhkv)
16787 .arg(&eps);
16788 unsafe {
16789 lb.launch(cfg)?;
16790 }
16791 }
16792 {
16793 let f = self.func("attn_rope_vl");
16794 let theta_scale = freq_base.powf(-2.0 / rope_dims as f32);
16795 let nd = rope_dims as i32;
16796 let cfg = LaunchConfig {
16797 grid_dim: (max_t * n_head as u32, 2, b as u32),
16798 block_dim: ((head_dim / 2) as u32, 1, 1),
16799 shared_mem_bytes: 0,
16800 };
16801 let __s_lb = self.gpu.stream();
16802 let mut lb = __s_lb.launch_builder(&f);
16803 lb.arg(&v)
16804 .arg(&hd)
16805 .arg(&nd)
16806 .arg(&nh)
16807 .arg(&nhkv)
16808 .arg(&theta_scale)
16809 .arg(&freq_scale);
16810 unsafe {
16811 lb.launch(cfg)?;
16812 }
16813 }
16814 {
16815 let f = self.func("append_kv_vl");
16816 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
16817 let cfg = LaunchConfig {
16818 grid_dim: (nblk, max_t, b as u32),
16819 block_dim: (32, 1, 1),
16820 shared_mem_bytes: 0,
16821 };
16822 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
16823 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16824 let __s_lb = self.gpu.stream();
16825 let mut lb = __s_lb.launch_builder(&f);
16826 lb.arg(&v).arg(&kdk).arg(&kdv).arg(&ktb).arg(&vtb);
16827 unsafe {
16828 lb.launch(cfg)?;
16829 }
16830 }
16831 Ok(())
16832 }
16833
16834 pub fn fa_prefill_view(
16839 &self,
16840 q: &CudaSlice<f32>,
16841 k: &cudarc::driver::CudaView<u8>,
16842 v: &cudarc::driver::CudaView<u8>,
16843 o: &mut CudaSlice<f32>,
16844 head_dim: usize,
16845 n_head: usize,
16846 n_head_kv: usize,
16847 t: usize,
16848 t_kv: usize,
16849 scale: f32,
16850 causal: bool,
16851 k_tok_bytes: usize,
16852 v_tok_bytes: usize,
16853 g: bool,
16854 ) -> Result<(), Box<dyn std::error::Error>> {
16855 if portable_mma_gated() {
16856 return self.sdpa_naive_quantized_view(
16857 q,
16858 k,
16859 v,
16860 o,
16861 head_dim,
16862 n_head,
16863 n_head_kv,
16864 t,
16865 t_kv,
16866 scale,
16867 causal,
16868 k_tok_bytes,
16869 v_tok_bytes,
16870 );
16871 }
16872 const BLOCK_Q: usize = 64;
16873 const BK: usize = 32;
16874 let name = format!("fa_prefill_q{}", fa_hd_suffix(head_dim)?);
16877 let f = if g {
16878 self.func_g(&name)
16879 } else {
16880 self.func(&name)
16881 };
16882 let shmem =
16883 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
16884 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16885 f.set_attribute(
16886 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16887 shmem as i32,
16888 )?;
16889 let cfg = LaunchConfig {
16890 grid_dim: (
16891 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16892 n_head as u32,
16893 1,
16894 ),
16895 block_dim: (32, 4, 1),
16896 shared_mem_bytes: shmem,
16897 };
16898 let (hd, nh, nhkv, ti, tkvi, cz) = (
16899 head_dim as i32,
16900 n_head as i32,
16901 n_head_kv as i32,
16902 t as i32,
16903 t_kv as i32,
16904 causal as i32,
16905 );
16906 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16907 let __s_b = self.gpu.stream();
16908 let mut b = __s_b.launch_builder(&f);
16909 b.arg(q)
16910 .arg(k)
16911 .arg(v)
16912 .arg(o)
16913 .arg(&hd)
16914 .arg(&nh)
16915 .arg(&nhkv)
16916 .arg(&ti)
16917 .arg(&tkvi)
16918 .arg(&scale)
16919 .arg(&cz)
16920 .arg(&ktb)
16921 .arg(&vtb);
16922 unsafe {
16923 b.launch(cfg)?;
16924 }
16925 Ok(())
16926 }
16927
16928 #[allow(clippy::too_many_arguments)]
16938 pub fn fa_prefill_view_ws(
16939 &self,
16940 q: &CudaSlice<f32>,
16941 k: &cudarc::driver::CudaView<u8>,
16942 v: &cudarc::driver::CudaView<u8>,
16943 o: &mut CudaSlice<f32>,
16944 head_dim: usize,
16945 n_head: usize,
16946 n_head_kv: usize,
16947 t: usize,
16948 t_kv: usize,
16949 scale: f32,
16950 causal: bool,
16951 k_tok_bytes: usize,
16952 v_tok_bytes: usize,
16953 g: bool,
16954 ) -> Result<(), Box<dyn std::error::Error>> {
16955 if portable_mma_gated() {
16956 return self.sdpa_naive_quantized_view(
16957 q,
16958 k,
16959 v,
16960 o,
16961 head_dim,
16962 n_head,
16963 n_head_kv,
16964 t,
16965 t_kv,
16966 scale,
16967 causal,
16968 k_tok_bytes,
16969 v_tok_bytes,
16970 );
16971 }
16972 const BLOCK_Q: usize = 64;
16973 const BK: usize = 32;
16974 let kv_dim_k = n_head_kv * head_dim;
16975 let kv_dim_v = n_head_kv * head_dim;
16976 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
16978 let mut guard = self.prime_deqw_ws.lock().unwrap();
16980 let need_grow = match guard.as_ref() {
16981 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
16982 None => true,
16983 };
16984 if need_grow {
16985 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
16986 let (ck, cv) = guard
16987 .as_ref()
16988 .map(|(a, b)| (a.len(), b.len()))
16989 .unwrap_or((0, 0));
16990 *guard = Some((
16991 self.alloc_u8(grow(ck, k_ws_bytes))?,
16992 self.alloc_u8(grow(cv, v_ws_bytes))?,
16993 ));
16994 }
16995 let (kw, vw) = guard.as_mut().unwrap();
16996 {
16998 let f = if g {
17000 self.func_g("fa_dequant_kv_ws_bf16")
17001 } else {
17002 self.func("fa_dequant_kv_ws_bf16")
17003 };
17004 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
17005 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
17006 let cfg = LaunchConfig {
17007 grid_dim: (nblk.max(1), 1, 1),
17008 block_dim: (256, 1, 1),
17009 shared_mem_bytes: 0,
17010 };
17011 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
17012 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17013 let __s_b = self.gpu.stream();
17014 let mut b = __s_b.launch_builder(&f);
17015 b.arg(k)
17016 .arg(v)
17017 .arg(&mut *kw)
17018 .arg(&mut *vw)
17019 .arg(&kdk)
17020 .arg(&kdv)
17021 .arg(&tkvi)
17022 .arg(&ktb)
17023 .arg(&vtb);
17024 unsafe {
17025 b.launch(cfg)?;
17026 }
17027 }
17028 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
17036 .map(|v| v != "0")
17037 .unwrap_or(true);
17038 {
17039 let hd_sfx = fa_hd_suffix(head_dim)?;
17040 let f = self.func(&format!(
17041 "fa_prefill_qw{}{hd_sfx}",
17042 if db { "_db" } else { "" }
17043 ));
17044 let shmem = if db {
17045 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
17047 } else {
17048 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
17049 };
17050 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17051 f.set_attribute(
17052 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17053 shmem as i32,
17054 )?;
17055 let cfg = LaunchConfig {
17056 grid_dim: (
17057 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
17058 n_head as u32,
17059 1,
17060 ),
17061 block_dim: (32, 4, 1),
17062 shared_mem_bytes: shmem,
17063 };
17064 let (hd, nh, nhkv, ti, tkvi, cz) = (
17065 head_dim as i32,
17066 n_head as i32,
17067 n_head_kv as i32,
17068 t as i32,
17069 t_kv as i32,
17070 causal as i32,
17071 );
17072 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
17073 let __s_b = self.gpu.stream();
17074 let mut b = __s_b.launch_builder(&f);
17075 b.arg(q)
17076 .arg(&*kw)
17077 .arg(&*vw)
17078 .arg(o)
17079 .arg(&hd)
17080 .arg(&nh)
17081 .arg(&nhkv)
17082 .arg(&ti)
17083 .arg(&tkvi)
17084 .arg(&scale)
17085 .arg(&cz)
17086 .arg(&kdk)
17087 .arg(&kdv);
17088 unsafe {
17089 b.launch(cfg)?;
17090 }
17091 }
17092 Ok(())
17093 }
17094
17095 #[allow(clippy::too_many_arguments)]
17111 pub fn fa_prefill_view_ws_w_hd128(
17112 &self,
17113 q: &CudaSlice<f32>,
17114 k: &cudarc::driver::CudaView<u8>,
17115 v: &cudarc::driver::CudaView<u8>,
17116 o: &mut CudaSlice<f32>,
17117 head_dim: usize,
17118 n_head: usize,
17119 n_head_kv: usize,
17120 t: usize,
17121 t_kv: usize,
17122 scale: f32,
17123 causal: bool,
17124 window: usize,
17125 k_tok_bytes: usize,
17126 v_tok_bytes: usize,
17127 ) -> Result<(), Box<dyn std::error::Error>> {
17128 assert_eq!(
17129 head_dim, 128,
17130 "fa_prefill_view_ws_w_hd128: only the hd128 twin is stamped"
17131 );
17132 if portable_mma_gated() {
17133 return self.sdpa_naive_w_quantized_view(
17134 q,
17135 k,
17136 v,
17137 o,
17138 head_dim,
17139 n_head,
17140 n_head_kv,
17141 t,
17142 t_kv,
17143 scale,
17144 causal,
17145 window,
17146 k_tok_bytes,
17147 v_tok_bytes,
17148 );
17149 }
17150 const BLOCK_Q: usize = 64;
17151 const BK: usize = 32;
17152 let kv_dim_k = n_head_kv * head_dim;
17153 let kv_dim_v = n_head_kv * head_dim;
17154 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
17156 let mut guard = self.prime_deqw_ws.lock().unwrap();
17157 let need_grow = match guard.as_ref() {
17158 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
17159 None => true,
17160 };
17161 if need_grow {
17162 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
17163 let (ck, cv) = guard
17164 .as_ref()
17165 .map(|(a, b)| (a.len(), b.len()))
17166 .unwrap_or((0, 0));
17167 *guard = Some((
17168 self.alloc_u8(grow(ck, k_ws_bytes))?,
17169 self.alloc_u8(grow(cv, v_ws_bytes))?,
17170 ));
17171 }
17172 let (kw, vw) = guard.as_mut().unwrap();
17173 {
17176 let f = self.func("fa_dequant_kv_ws_bf16");
17177 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
17178 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
17179 let cfg = LaunchConfig {
17180 grid_dim: (nblk.max(1), 1, 1),
17181 block_dim: (256, 1, 1),
17182 shared_mem_bytes: 0,
17183 };
17184 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
17185 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17186 let __s_b = self.gpu.stream();
17187 let mut b = __s_b.launch_builder(&f);
17188 b.arg(k)
17189 .arg(v)
17190 .arg(&mut *kw)
17191 .arg(&mut *vw)
17192 .arg(&kdk)
17193 .arg(&kdv)
17194 .arg(&tkvi)
17195 .arg(&ktb)
17196 .arg(&vtb);
17197 unsafe {
17198 b.launch(cfg)?;
17199 }
17200 }
17201 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
17203 .map(|v| v != "0")
17204 .unwrap_or(true);
17205 {
17206 let f = self.func(if db {
17207 "fa_prefill_qw_db_w_hd128"
17208 } else {
17209 "fa_prefill_qw_w_hd128"
17210 });
17211 let shmem = if db {
17212 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
17213 } else {
17214 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
17215 };
17216 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17217 f.set_attribute(
17218 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17219 shmem as i32,
17220 )?;
17221 let cfg = LaunchConfig {
17222 grid_dim: (
17223 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
17224 n_head as u32,
17225 1,
17226 ),
17227 block_dim: (32, 4, 1),
17228 shared_mem_bytes: shmem,
17229 };
17230 let (hd, nh, nhkv, ti, tkvi, cz) = (
17231 head_dim as i32,
17232 n_head as i32,
17233 n_head_kv as i32,
17234 t as i32,
17235 t_kv as i32,
17236 causal as i32,
17237 );
17238 let (kdk, kdv, wnd) = (kv_dim_k as i32, kv_dim_v as i32, window as i32);
17239 let __s_b = self.gpu.stream();
17240 let mut b = __s_b.launch_builder(&f);
17241 b.arg(q)
17242 .arg(&*kw)
17243 .arg(&*vw)
17244 .arg(o)
17245 .arg(&hd)
17246 .arg(&nh)
17247 .arg(&nhkv)
17248 .arg(&ti)
17249 .arg(&tkvi)
17250 .arg(&scale)
17251 .arg(&cz)
17252 .arg(&kdk)
17253 .arg(&kdv)
17254 .arg(&wnd);
17255 unsafe {
17256 b.launch(cfg)?;
17257 }
17258 }
17259 Ok(())
17260 }
17261
17262 pub fn fa_decode(
17266 &self,
17267 q: &CudaSlice<f32>,
17268 k: &cudarc::driver::CudaView<u8>,
17269 v: &cudarc::driver::CudaView<u8>,
17270 o: &mut CudaSlice<f32>,
17271 head_dim: usize,
17272 n_head: usize,
17273 n_head_kv: usize,
17274 t_kv: usize,
17275 scale: f32,
17276 k_tok_bytes: usize,
17277 v_tok_bytes: usize,
17278 ) -> Result<(), Box<dyn std::error::Error>> {
17279 self.fa_decode_kvmod(
17280 q,
17281 k,
17282 v,
17283 o,
17284 head_dim,
17285 n_head,
17286 n_head_kv,
17287 t_kv,
17288 scale,
17289 k_tok_bytes,
17290 v_tok_bytes,
17291 false,
17292 )
17293 }
17294
17295 #[allow(clippy::too_many_arguments)]
17299 #[allow(clippy::too_many_arguments)]
17303 #[allow(clippy::too_many_arguments)]
17304 fn fa_decode_scalar_unified(
17305 &self,
17306 q: &cudarc::driver::CudaView<f32>,
17307 k: &cudarc::driver::CudaView<u8>,
17308 v: &cudarc::driver::CudaView<u8>,
17309 o: &mut cudarc::driver::CudaViewMut<f32>,
17310 head_dim: usize,
17311 n_head: usize,
17312 n_head_kv: usize,
17313 t_kv_host: usize,
17314 t_kv_dev: Option<&CudaSlice<i32>>,
17315 scale: f32,
17316 n_splits: usize,
17317 split_keys: usize,
17318 k_tok_bytes: usize,
17319 v_tok_bytes: usize,
17320 g: bool,
17321 part_o: &mut CudaSlice<f32>,
17322 part_m: &mut CudaSlice<f32>,
17323 part_l: &mut CudaSlice<f32>,
17324 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
17325 ) -> Result<(), Box<dyn std::error::Error>> {
17326 let f = if g {
17327 self.func_g("fa_decode_f32")
17328 } else {
17329 self.fa_func("fa_decode_f32", head_dim)
17330 };
17331 let cfg = LaunchConfig {
17332 grid_dim: (n_head as u32, n_splits as u32, 1),
17333 block_dim: (head_dim as u32, 1, 1),
17334 shared_mem_bytes: (4 * (head_dim + 32)) as u32,
17335 };
17336 let (hd, nh, nhkv, nsp) = (
17337 head_dim as i32,
17338 n_head as i32,
17339 n_head_kv as i32,
17340 n_splits as i32,
17341 );
17342 let (ktb, vtb, tkvi, ski) = (
17343 k_tok_bytes as i64,
17344 v_tok_bytes as i64,
17345 t_kv_host as i32,
17346 split_keys as i32,
17347 );
17348 let __s_b = self.gpu.stream();
17349 let mut b = __s_b.launch_builder(&f);
17350 match t_kv_dev {
17351 Some(d) => {
17352 b.arg(q)
17353 .arg(k)
17354 .arg(v)
17355 .arg(&mut *part_o)
17356 .arg(&mut *part_m)
17357 .arg(&mut *part_l)
17358 .arg(&hd)
17359 .arg(&nh)
17360 .arg(&nhkv)
17361 .arg(&tkvi)
17362 .arg(d)
17363 .arg(&scale)
17364 .arg(&nsp)
17365 .arg(&ski)
17366 .arg(&ktb)
17367 .arg(&vtb);
17368 unsafe {
17369 b.launch(cfg)?;
17370 }
17371 }
17372 None => {
17373 let null: u64 = 0;
17374 b.arg(q)
17375 .arg(k)
17376 .arg(v)
17377 .arg(&mut *part_o)
17378 .arg(&mut *part_m)
17379 .arg(&mut *part_l)
17380 .arg(&hd)
17381 .arg(&nh)
17382 .arg(&nhkv)
17383 .arg(&tkvi)
17384 .arg(&null)
17385 .arg(&scale)
17386 .arg(&nsp)
17387 .arg(&ski)
17388 .arg(&ktb)
17389 .arg(&vtb);
17390 unsafe {
17391 b.launch(cfg)?;
17392 }
17393 }
17394 }
17395 let cfg2 = LaunchConfig {
17396 grid_dim: (n_head as u32, 1, 1),
17397 block_dim: (head_dim as u32, 1, 1),
17398 shared_mem_bytes: 0,
17399 };
17400 if let Some((oq, od)) = q8_out {
17401 let fc = if g {
17403 self.func_g("fa_decode_combine_q8_1")
17404 } else {
17405 self.fa_func("fa_decode_combine_q8_1", head_dim)
17406 };
17407 let __s_b2 = self.gpu.stream();
17408 let mut b2 = __s_b2.launch_builder(&fc);
17409 b2.arg(&*part_o)
17410 .arg(&*part_m)
17411 .arg(&*part_l)
17412 .arg(oq)
17413 .arg(od)
17414 .arg(&hd)
17415 .arg(&nh)
17416 .arg(&nsp);
17417 unsafe {
17418 b2.launch(cfg2)?;
17419 }
17420 return Ok(());
17421 }
17422 let fc = if g {
17423 self.func_g("fa_decode_combine_f32")
17424 } else {
17425 self.fa_func("fa_decode_combine_f32", head_dim)
17426 };
17427 let __s_b2 = self.gpu.stream();
17428 let mut b2 = __s_b2.launch_builder(&fc);
17429 b2.arg(&*part_o)
17430 .arg(&*part_m)
17431 .arg(&*part_l)
17432 .arg(o)
17433 .arg(&hd)
17434 .arg(&nh)
17435 .arg(&nsp);
17436 unsafe {
17437 b2.launch(cfg2)?;
17438 }
17439 Ok(())
17440 }
17441
17442 pub fn fa_decode_kvmod(
17443 &self,
17444 q: &CudaSlice<f32>,
17445 k: &cudarc::driver::CudaView<u8>,
17446 v: &cudarc::driver::CudaView<u8>,
17447 o: &mut CudaSlice<f32>,
17448 head_dim: usize,
17449 n_head: usize,
17450 n_head_kv: usize,
17451 t_kv: usize,
17452 scale: f32,
17453 k_tok_bytes: usize,
17454 v_tok_bytes: usize,
17455 g: bool,
17456 ) -> Result<(), Box<dyn std::error::Error>> {
17457 let q_view = q.as_view();
17458 let mut o_view = o.as_view_mut();
17459 self.fa_decode_kvmod_view(
17460 &q_view,
17461 k,
17462 v,
17463 &mut o_view,
17464 head_dim,
17465 n_head,
17466 n_head_kv,
17467 t_kv,
17468 scale,
17469 k_tok_bytes,
17470 v_tok_bytes,
17471 g,
17472 )
17473 }
17474
17475 #[allow(clippy::too_many_arguments)]
17480 pub fn fa_decode_kvmod_view(
17481 &self,
17482 q: &cudarc::driver::CudaView<f32>,
17483 k: &cudarc::driver::CudaView<u8>,
17484 v: &cudarc::driver::CudaView<u8>,
17485 o: &mut cudarc::driver::CudaViewMut<f32>,
17486 head_dim: usize,
17487 n_head: usize,
17488 n_head_kv: usize,
17489 t_kv: usize,
17490 scale: f32,
17491 k_tok_bytes: usize,
17492 v_tok_bytes: usize,
17493 g: bool,
17494 ) -> Result<(), Box<dyn std::error::Error>> {
17495 let mut fa_vec = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
17516 if g && head_dim == 256 && !fa_v4_at(t_kv) {
17520 fa_vec = false;
17521 }
17522 let sp = fa_split_keys(t_kv, n_head_kv);
17523 let n_splits = if fa_vec {
17524 ((t_kv + sp - 1) / sp).max(1)
17525 } else {
17526 ((t_kv + 255) / 256).max(1)
17527 };
17528 let o_len = n_head * n_splits * head_dim;
17529 let ml_len = n_head * n_splits;
17530 let mut part_guard = self.fa_part_pool.lock().unwrap();
17531 if part_guard
17532 .as_ref()
17533 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17534 .unwrap_or(true)
17535 {
17536 let old = part_guard.take();
17547 let (co, cm) = old
17548 .as_ref()
17549 .map(|pp| (pp.0.len(), pp.1.len()))
17550 .unwrap_or((0, 0));
17551 if let Some(old) = old {
17552 self.fa_part_retired.lock().unwrap().push(old);
17553 }
17554 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17555 eprintln!(
17556 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17557 co, o_len, cm, ml_len
17558 );
17559 }
17560 *part_guard = Some((
17561 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17562 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17563 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17564 ));
17565 }
17566 let pg = part_guard.as_mut().unwrap();
17567 self.gpu
17568 .stream()
17569 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17570 self.gpu
17571 .stream()
17572 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17573 self.gpu
17574 .stream()
17575 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17576 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17577 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
17578 let (hd, nh, nhkv, tkvi, nsp) = (
17579 head_dim as i32,
17580 n_head as i32,
17581 n_head_kv as i32,
17582 t_kv as i32,
17583 n_splits as i32,
17584 );
17585 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17586 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
17590 let fa512_min = fa512_min_tkv();
17595 let deep = fa_vec
17598 && head_dim == 256
17599 && fa_v4_at(t_kv)
17600 && !g
17601 && fa_deep_at(t_kv)
17602 && !matches!(fa_v4_mode(), "noB3" | "stage");
17603 let (f, cfg) = if fa_vec && head_dim == 512 && t_kv >= fa512_min {
17604 let gqa = (n_head / n_head_kv).max(1) as u32;
17607 let fv = self.fa_func("fa_decode_vec_q_dpl16", head_dim);
17608 (
17609 fv,
17610 LaunchConfig {
17611 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17612 block_dim: (32, gqa, 1),
17613 shared_mem_bytes: 0,
17614 },
17615 )
17616 } else if fa_vec && head_dim <= 256 {
17617 let gqa = (n_head / n_head_kv).max(1) as u32;
17618 static SMEM_TKV: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17629 let smem_tkv = *SMEM_TKV.get_or_init(|| {
17630 std::env::var("MEMRA_FA_SMEM_TKV")
17631 .ok()
17632 .and_then(|v| v.parse().ok())
17633 .unwrap_or_else(|| {
17634 FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
17635 })
17636 });
17637 if fa_v4_at(t_kv) && head_dim == 256 {
17638 let v4name = match fa_v4_mode() {
17642 "noB3" => "fa_decode_vec_q_v4_noB3", "stage" => "fa_decode_vec_q_v4_stage", _ if deep => "fa_decode_vec_q_v4_deep",
17645 _ => "fa_decode_vec_q_v4",
17646 };
17647 let fv = if g {
17648 self.func_g(v4name)
17649 } else {
17650 self.func(v4name)
17651 };
17652 let shmem = (if deep { 12160 } else { 11520 }
17655 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
17656 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17657 fv.set_attribute(
17658 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17659 shmem as i32,
17660 )?;
17661 (
17662 fv,
17663 LaunchConfig {
17664 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17665 block_dim: (32, gqa, 1),
17666 shared_mem_bytes: shmem,
17667 },
17668 )
17669 } else if fa_v3_active(head_dim) {
17670 let fv = if g {
17673 self.func_g("fa_decode_vec_q_v3")
17674 } else {
17675 self.func("fa_decode_vec_q_v3")
17676 };
17677 let shmem = (32 * head_dim * 2) as u32; (
17679 fv,
17680 LaunchConfig {
17681 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17682 block_dim: (32, gqa, 1),
17683 shared_mem_bytes: shmem,
17684 },
17685 )
17686 } else if fa_v2_on() {
17687 let fv = if g {
17691 self.func_g("fa_decode_vec_q_v2")
17692 } else {
17693 self.func("fa_decode_vec_q_v2")
17694 };
17695 let shmem = (2 * 32 * head_dim * 2) as u32; (
17697 fv,
17698 LaunchConfig {
17699 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17700 block_dim: (32, gqa, 1),
17701 shared_mem_bytes: shmem,
17702 },
17703 )
17704 } else if smem_tkv > 0 && t_kv >= smem_tkv && !g && !(head_dim == 512 && Self::gkv_on())
17705 {
17706 let fv = if g {
17710 self.func_g("fa_decode_vec_q_smem")
17711 } else {
17712 self.func("fa_decode_vec_q_smem")
17713 };
17714 let shmem = (2 * 32 * head_dim * 2) as u32; use cudarc::driver::sys::CUfunction_attribute_enum as A;
17716 fv.set_attribute(
17717 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17718 shmem as i32,
17719 )?;
17720 (
17721 fv,
17722 LaunchConfig {
17723 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17724 block_dim: (32, gqa, 1),
17725 shared_mem_bytes: shmem,
17726 },
17727 )
17728 } else {
17729 let fv = if g {
17732 self.func_g("fa_decode_vec_q")
17733 } else {
17734 self.func("fa_decode_vec_q")
17735 };
17736 (
17737 fv,
17738 LaunchConfig {
17739 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17740 block_dim: (32, gqa, 1),
17741 shared_mem_bytes: 0,
17742 },
17743 )
17744 }
17745 } else {
17746 return self.fa_decode_scalar_unified(
17749 q,
17750 k,
17751 v,
17752 o,
17753 head_dim,
17754 n_head,
17755 n_head_kv,
17756 t_kv,
17757 None,
17758 scale,
17759 n_splits,
17760 if fa_vec { sp } else { 256 },
17761 k_tok_bytes,
17762 v_tok_bytes,
17763 g,
17764 part_o,
17765 part_m,
17766 part_l,
17767 None,
17768 );
17769 };
17770 let __s_b = self.gpu.stream();
17771 let mut b = __s_b.launch_builder(&f);
17772 b.arg(q)
17773 .arg(k)
17774 .arg(v)
17775 .arg(&mut *part_o)
17776 .arg(&mut *part_m)
17777 .arg(&mut *part_l)
17778 .arg(&hd)
17779 .arg(&nh)
17780 .arg(&nhkv)
17781 .arg(&tkvi)
17782 .arg(&scale)
17783 .arg(&nsp)
17784 .arg(&ktb)
17785 .arg(&vtb);
17786 unsafe {
17787 b.launch(cfg)?;
17788 }
17789 let (fc, cfg2) = (
17792 if g {
17793 self.func_g("fa_decode_combine_f32")
17794 } else {
17795 self.fa_func("fa_decode_combine_f32", head_dim)
17796 },
17797 LaunchConfig {
17798 grid_dim: (n_head as u32, 1, 1),
17799 block_dim: (head_dim as u32, 1, 1),
17800 shared_mem_bytes: 0,
17801 },
17802 );
17803 let __s_b2 = self.gpu.stream();
17804 let mut b2 = __s_b2.launch_builder(&fc);
17805 b2.arg(&*part_o)
17806 .arg(&*part_m)
17807 .arg(&*part_l)
17808 .arg(o)
17809 .arg(&hd)
17810 .arg(&nh)
17811 .arg(&nsp);
17812 unsafe {
17813 b2.launch(cfg2)?;
17814 }
17815 Ok(())
17816 }
17817
17818 #[allow(clippy::too_many_arguments)]
17829 pub fn fa_decode_batch_seqs_v4(
17830 &self,
17831 q: &CudaSlice<f32>,
17832 kv_ptrs: &cudarc::driver::CudaView<u64>,
17833 pos_seq: &CudaSlice<i32>,
17834 o: &mut CudaSlice<f32>,
17835 head_dim: usize,
17836 n_head: usize,
17837 n_head_kv: usize,
17838 b_n: usize,
17839 t_kv_max: usize,
17840 scale: f32,
17841 split_keys: usize,
17842 k_tok_bytes: usize,
17843 v_tok_bytes: usize,
17844 ) -> Result<(), Box<dyn std::error::Error>> {
17845 debug_assert!(head_dim == 256, "seqs twin is v4-stamped (hd256 only)");
17846 let n_splits_max = (t_kv_max + split_keys - 1) / split_keys;
17847 let o_len = b_n * n_head * n_splits_max * head_dim;
17848 let ml_len = b_n * n_head * n_splits_max;
17849 let mut part_guard = self.fa_part_pool.lock().unwrap();
17850 if part_guard
17851 .as_ref()
17852 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17853 .unwrap_or(true)
17854 {
17855 let old = part_guard.take();
17866 let (co, cm) = old
17867 .as_ref()
17868 .map(|pp| (pp.0.len(), pp.1.len()))
17869 .unwrap_or((0, 0));
17870 if let Some(old) = old {
17871 self.fa_part_retired.lock().unwrap().push(old);
17872 }
17873 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17874 eprintln!(
17875 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17876 co, o_len, cm, ml_len
17877 );
17878 }
17879 *part_guard = Some((
17880 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17881 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17882 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17883 ));
17884 }
17885 let pg = part_guard.as_mut().unwrap();
17886 self.gpu
17887 .stream()
17888 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17889 self.gpu
17890 .stream()
17891 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17892 self.gpu
17893 .stream()
17894 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17895 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17896 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17897 let (nspm, spk) = (n_splits_max as i32, split_keys as i32);
17898 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17899 let gqa = (n_head / n_head_kv).max(1) as u32;
17900 let f = self.func("fa_decode_vec_q_seqs_v4");
17901 let shmem = (11520 + 32 * head_dim * 2) as u32;
17903 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17904 f.set_attribute(
17905 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17906 shmem as i32,
17907 )?;
17908 let cfg = LaunchConfig {
17909 grid_dim: (n_head_kv as u32, n_splits_max as u32, b_n as u32),
17910 block_dim: (32, gqa, 1),
17911 shared_mem_bytes: shmem,
17912 };
17913 {
17914 let __s_b = self.gpu.stream();
17915 let mut b = __s_b.launch_builder(&f);
17916 b.arg(q)
17917 .arg(kv_ptrs)
17918 .arg(pos_seq)
17919 .arg(&mut *part_o)
17920 .arg(&mut *part_m)
17921 .arg(&mut *part_l)
17922 .arg(&hd)
17923 .arg(&nh)
17924 .arg(&nhkv)
17925 .arg(&scale)
17926 .arg(&nspm)
17927 .arg(&spk)
17928 .arg(&ktb)
17929 .arg(&vtb);
17930 unsafe {
17931 b.launch(cfg)?;
17932 }
17933 }
17934 let fc = self.func("fa_decode_combine_seqs");
17935 let cfg2 = LaunchConfig {
17936 grid_dim: (n_head as u32, b_n as u32, 1),
17937 block_dim: (head_dim as u32, 1, 1),
17938 shared_mem_bytes: 0,
17939 };
17940 let __s_b2 = self.gpu.stream();
17941 let mut b2 = __s_b2.launch_builder(&fc);
17942 b2.arg(&*part_o)
17943 .arg(&*part_m)
17944 .arg(&*part_l)
17945 .arg(o)
17946 .arg(&hd)
17947 .arg(&nh)
17948 .arg(pos_seq)
17949 .arg(&nspm)
17950 .arg(&spk);
17951 unsafe {
17952 b2.launch(cfg2)?;
17953 }
17954 Ok(())
17955 }
17956
17957 #[allow(clippy::too_many_arguments)]
17964 pub fn append_kv_quantized_seqs(
17965 &self,
17966 k_rows: &CudaSlice<f32>,
17967 v_rows: &CudaSlice<f32>,
17968 kv_ptrs: &cudarc::driver::CudaView<u64>,
17969 pos_seq: &CudaSlice<i32>,
17970 b_n: usize,
17971 kv_dim_k: usize,
17972 kv_dim_v: usize,
17973 k_tok_bytes: usize,
17974 v_tok_bytes: usize,
17975 ) -> Result<(), Box<dyn std::error::Error>> {
17976 let f = self.func("append_quantize_kv_q8_0_q5_1_seqs");
17977 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
17978 let cfg = LaunchConfig {
17979 grid_dim: (nblk, b_n as u32, 1),
17980 block_dim: (32, 1, 1),
17981 shared_mem_bytes: 0,
17982 };
17983 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
17984 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17985 let __s_b = self.gpu.stream();
17986 let mut b = __s_b.launch_builder(&f);
17987 b.arg(k_rows)
17988 .arg(v_rows)
17989 .arg(kv_ptrs)
17990 .arg(pos_seq)
17991 .arg(&kdk)
17992 .arg(&kdv)
17993 .arg(&ktb)
17994 .arg(&vtb);
17995 unsafe {
17996 b.launch(cfg)?;
17997 }
17998 Ok(())
17999 }
18000
18001 pub fn fa_rows_eligible(&self, base_len: usize, head_dim: usize) -> bool {
18007 std::env::var("MEMRA_NO_FA_VEC").is_err()
18008 && std::env::var("MEMRA_FA_ROWS_OFF").is_err()
18009 && base_len + 1 >= fa_vec_min_tkv()
18010 && head_dim <= 256
18011 && head_dim % 32 == 0
18012 }
18013
18014 #[allow(clippy::too_many_arguments)]
18023 pub fn fa_decode_rows(
18024 &self,
18025 q: &CudaSlice<f32>,
18026 k: &cudarc::driver::CudaView<u8>,
18027 v: &cudarc::driver::CudaView<u8>,
18028 o: &mut CudaSlice<f32>,
18029 head_dim: usize,
18030 n_head: usize,
18031 n_head_kv: usize,
18032 base_len: usize,
18033 t: usize,
18034 scale: f32,
18035 k_tok_bytes: usize,
18036 v_tok_bytes: usize,
18037 base_dev: Option<(&CudaSlice<i32>, i32)>,
18041 kv_shared: bool,
18044 g: bool,
18048 mut q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
18051 ) -> Result<(), Box<dyn std::error::Error>> {
18052 debug_assert!(base_len + 1 >= fa_vec_min_tkv() && head_dim <= 512 && head_dim % 32 == 0);
18053 let t_kv_max = base_len + t; let mut sp = fa_split_keys(t_kv_max, n_head_kv); if head_dim == 512 {
18060 static SP512: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18061 let v = *SP512.get_or_init(|| {
18064 std::env::var("MEMRA_FA_SP512")
18065 .ok()
18066 .and_then(|x| x.parse().ok())
18067 .unwrap_or(0)
18068 });
18069 sp = if v >= 8 {
18070 v
18071 } else {
18072 FA_SP512_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
18073 };
18074 }
18075 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18076 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18077 let gqa = (n_head / n_head_kv).max(1) as u32;
18078 let mut groups: Vec<(usize, usize, usize)> = Vec::new(); if head_dim == 512 || fa_split_keys(base_len + 1, n_head_kv) == sp {
18089 groups.push((0, t, sp));
18090 } else {
18091 let mut r0 = 0usize;
18092 while r0 < t {
18093 let sp_g = fa_split_keys(base_len + r0 + 1, n_head_kv);
18094 let mut r1 = r0 + 1;
18095 while r1 < t && fa_split_keys(base_len + r1 + 1, n_head_kv) == sp_g {
18096 r1 += 1;
18097 }
18098 groups.push((r0, r1 - r0, sp_g));
18099 r0 = r1;
18100 }
18101 }
18102 static SMEM_TKV_R: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18106 let smem_tkv = *SMEM_TKV_R.get_or_init(|| {
18107 std::env::var("MEMRA_FA_SMEM_TKV")
18108 .ok()
18109 .and_then(|v| v.parse().ok())
18110 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
18111 });
18112 let v4 = fa_v4_at(base_len + t) && head_dim == 256;
18113 let v3 = fa_v3_active(head_dim);
18114 let smem_rows =
18115 head_dim <= 256 && !v3 && !fa_v2_on() && smem_tkv > 0 && t_kv_max >= smem_tkv;
18116 let _ = kv_shared;
18121 let i2 = head_dim == 512 && std::env::var("MEMRA_FA_I2").as_deref() != Ok("0");
18124 static TB512: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
18138 let tb512 = head_dim == 512
18140 && sp <= 32
18141 && n_head / n_head_kv.max(1) <= 16
18142 && *TB512.get_or_init(|| std::env::var("MEMRA_FA_TB512").as_deref() != Ok("0"));
18143 let fname = if tb512 {
18144 "fa_decode_vec_q_rows_v4_512_tb"
18145 } else if i2 {
18146 "fa_decode_vec_q_rows_dpl16_i2"
18147 } else if head_dim == 512 {
18148 "fa_decode_vec_q_rows_dpl16"
18149 }
18150 else if v4 {
18152 "fa_decode_vec_q_rows_v4"
18153 } else if v3 {
18154 "fa_decode_vec_q_rows_v3"
18155 } else if fa_v2_on() {
18156 "fa_decode_vec_q_rows_v2"
18157 } else if smem_rows {
18158 "fa_decode_vec_q_rows_smem"
18159 } else {
18160 "fa_decode_vec_q_rows"
18161 };
18162 let f = if head_dim == 512 {
18163 self.fa_func(fname, head_dim)
18164 } else if g {
18165 self.func_g(if smem_rows {
18173 "fa_decode_vec_q_rows"
18174 } else {
18175 fname
18176 })
18177 } else {
18178 self.func(fname)
18179 };
18180 let shmem = if tb512 {
18181 let gk = Self::gkv_on();
18183 let sh =
18184 (8192 + 1024 + 32 * 512 + 32 * 64 + 32 * head_dim * if gk { 1 } else { 2 }) as u32;
18185 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18186 f.set_attribute(
18187 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18188 sh as i32,
18189 )?;
18190 sh
18191 } else if v4 || v3 || smem_rows || fa_v2_on() {
18192 let sh = (if v4 {
18194 11520 + 32 * head_dim * if g { 1 } else { 2 }
18195 } else if v3 {
18196 32 * head_dim * 2
18197 } else {
18198 2 * 32 * head_dim * 2
18199 }) as u32;
18200 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18201 f.set_attribute(
18202 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18203 sh as i32,
18204 )?;
18205 sh
18206 } else {
18207 0
18208 };
18209 for &(r0, t_g, sp_g) in &groups {
18213 let n_splits_g = (base_len + r0 + t_g).div_ceil(sp_g);
18214 let (nspm, spk) = (n_splits_g as i32, sp_g as i32);
18215 let base_i = (base_len + r0) as i32;
18216 let o_len = t_g * n_head * n_splits_g * head_dim;
18217 let ml_len = t_g * n_head * n_splits_g;
18218 let mut part_guard = self.fa_part_pool.lock().unwrap();
18219 if part_guard
18220 .as_ref()
18221 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18222 .unwrap_or(true)
18223 {
18224 let old = part_guard.take();
18235 let (co, cm) = old
18236 .as_ref()
18237 .map(|pp| (pp.0.len(), pp.1.len()))
18238 .unwrap_or((0, 0));
18239 if let Some(old) = old {
18240 self.fa_part_retired.lock().unwrap().push(old);
18241 }
18242 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18243 eprintln!(
18244 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18245 co, o_len, cm, ml_len
18246 );
18247 }
18248 *part_guard = Some((
18249 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18250 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18251 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18252 ));
18253 }
18254 let pg = part_guard.as_mut().unwrap();
18255 self.gpu
18256 .stream()
18257 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18258 self.gpu
18259 .stream()
18260 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18261 self.gpu
18262 .stream()
18263 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18264 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18265 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
18266 let qv = self.view(q, t * n_head * head_dim);
18267 let q_g = qv.slice(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
18268 let cfg = LaunchConfig {
18269 grid_dim: (n_head_kv as u32, n_splits_g as u32, t_g as u32),
18270 block_dim: (32, gqa, 1),
18271 shared_mem_bytes: shmem,
18272 };
18273 {
18274 let __s_b = self.gpu.stream();
18275 let mut b = __s_b.launch_builder(&f);
18276 if tb512 {
18277 let (bd, plus) =
18279 base_dev.expect("hd512 rows twin requires a device base counter");
18280 let plus_g = plus + r0 as i32;
18281 let nr = t_g as i32;
18282 if Self::pdl_on() && Self::pdl_wb_on() {
18283 use cudarc::driver::{DevicePtr, DevicePtrMut};
18285 let s = &self.gpu.stream();
18286 let (pq, _b0) = q_g.device_ptr(s);
18287 let (pk, _b1) = k.device_ptr(s);
18288 let (pv, _b2) = v.device_ptr(s);
18289 let (po, _b3) = part_o.device_ptr_mut(s);
18290 let (pm, _b4) = part_m.device_ptr_mut(s);
18291 let (pl, _b5) = part_l.device_ptr_mut(s);
18292 let (pb, _b6) = bd.device_ptr(s);
18293 let mut ps = [
18294 &pq as *const _ as *mut std::ffi::c_void,
18295 &pk as *const _ as *mut _,
18296 &pv as *const _ as *mut _,
18297 &po as *const _ as *mut _,
18298 &pm as *const _ as *mut _,
18299 &pl as *const _ as *mut _,
18300 &hd as *const _ as *mut _,
18301 &nh as *const _ as *mut _,
18302 &nhkv as *const _ as *mut _,
18303 &pb as *const _ as *mut _,
18304 &plus_g as *const _ as *mut _,
18305 &scale as *const _ as *mut _,
18306 &nspm as *const _ as *mut _,
18307 &spk as *const _ as *mut _,
18308 &ktb as *const _ as *mut _,
18309 &vtb as *const _ as *mut _,
18310 &nr as *const _ as *mut _,
18311 ];
18312 unsafe {
18313 self.launch_pdl_flash(
18314 Self::gkv_on(),
18315 "fa_decode_vec_q_rows_v4_512_tb",
18316 (n_head_kv as u32, n_splits_g as u32, 1),
18317 (32, gqa, 1),
18318 shmem,
18319 &mut ps,
18320 )?;
18321 }
18322 } else {
18323 let cfg_tb = LaunchConfig {
18324 grid_dim: (n_head_kv as u32, n_splits_g as u32, 1),
18325 block_dim: (32, gqa, 1),
18326 shared_mem_bytes: shmem,
18327 };
18328 b.arg(&q_g)
18329 .arg(k)
18330 .arg(v)
18331 .arg(&mut *part_o)
18332 .arg(&mut *part_m)
18333 .arg(&mut *part_l)
18334 .arg(&hd)
18335 .arg(&nh)
18336 .arg(&nhkv)
18337 .arg(bd)
18338 .arg(&plus_g)
18339 .arg(&scale)
18340 .arg(&nspm)
18341 .arg(&spk)
18342 .arg(&ktb)
18343 .arg(&vtb)
18344 .arg(&nr);
18345 unsafe {
18346 b.launch(cfg_tb)?;
18347 }
18348 }
18349 } else if head_dim == 512 {
18350 let (bd, plus) =
18351 base_dev.expect("hd512 rows twin requires a device base counter");
18352 let plus_g = plus + r0 as i32;
18353 b.arg(&q_g)
18354 .arg(k)
18355 .arg(v)
18356 .arg(&mut *part_o)
18357 .arg(&mut *part_m)
18358 .arg(&mut *part_l)
18359 .arg(&hd)
18360 .arg(&nh)
18361 .arg(&nhkv)
18362 .arg(bd)
18363 .arg(&plus_g)
18364 .arg(&scale)
18365 .arg(&nspm)
18366 .arg(&spk)
18367 .arg(&ktb)
18368 .arg(&vtb);
18369 unsafe {
18370 b.launch(cfg)?;
18371 }
18372 } else {
18373 b.arg(&q_g)
18374 .arg(k)
18375 .arg(v)
18376 .arg(&mut *part_o)
18377 .arg(&mut *part_m)
18378 .arg(&mut *part_l)
18379 .arg(&hd)
18380 .arg(&nh)
18381 .arg(&nhkv)
18382 .arg(&base_i)
18383 .arg(&scale)
18384 .arg(&nspm)
18385 .arg(&spk)
18386 .arg(&ktb)
18387 .arg(&vtb);
18388 unsafe {
18389 b.launch(cfg)?;
18390 }
18391 }
18392 }
18393 let cfg2 = LaunchConfig {
18394 grid_dim: (n_head as u32, t_g as u32, 1),
18395 block_dim: (head_dim as u32, 1, 1),
18396 shared_mem_bytes: 0,
18397 };
18398 let mut o_g = o.slice_mut(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
18399 if head_dim == 512 {
18400 let (bd, plus) = base_dev.unwrap();
18403 let plus_g = plus + r0 as i32;
18404 if let Some((oq, od)) = q8_out.as_mut() {
18405 debug_assert!(t == 1, "rows q8 emit is a t=1 decode arm");
18407 if Self::pdl_on() && Self::pdl_wb_on() {
18408 use cudarc::driver::{DevicePtr, DevicePtrMut};
18410 let s = &self.gpu.stream();
18411 let (po, _g0) = part_o.device_ptr(s);
18412 let (pm, _g1) = part_m.device_ptr(s);
18413 let (pl, _g2) = part_l.device_ptr(s);
18414 let (pq, _g3) = oq.device_ptr_mut(s);
18415 let (pd, _g4) = od.device_ptr_mut(s);
18416 let (pb, _g5) = bd.device_ptr(s);
18417 let mut ps = [
18418 &po as *const _ as *mut std::ffi::c_void,
18419 &pm as *const _ as *mut _,
18420 &pl as *const _ as *mut _,
18421 &pq as *const _ as *mut _,
18422 &pd as *const _ as *mut _,
18423 &hd as *const _ as *mut _,
18424 &nh as *const _ as *mut _,
18425 &pb as *const _ as *mut _,
18426 &plus_g as *const _ as *mut _,
18427 &nspm as *const _ as *mut _,
18428 &spk as *const _ as *mut _,
18429 ];
18430 unsafe {
18431 self.launch_pdl_flash(
18432 Self::gkv_on(),
18433 "fa_decode_combine_rows_dc_q8_1",
18434 cfg2.grid_dim,
18435 cfg2.block_dim,
18436 0,
18437 &mut ps,
18438 )?;
18439 }
18440 continue;
18441 }
18442 let fc = self.fa_func("fa_decode_combine_rows_dc_q8_1", head_dim);
18443 let __s_b2 = self.gpu.stream();
18444 let mut b2 = __s_b2.launch_builder(&fc);
18445 b2.arg(&*part_o)
18446 .arg(&*part_m)
18447 .arg(&*part_l)
18448 .arg(&mut **oq)
18449 .arg(&mut **od)
18450 .arg(&hd)
18451 .arg(&nh)
18452 .arg(bd)
18453 .arg(&plus_g)
18454 .arg(&nspm)
18455 .arg(&spk);
18456 unsafe {
18457 b2.launch(cfg2)?;
18458 }
18459 continue;
18460 }
18461 let fc = self.fa_func("fa_decode_combine_rows_dc", head_dim);
18462 let __s_b2 = self.gpu.stream();
18463 let mut b2 = __s_b2.launch_builder(&fc);
18464 b2.arg(&*part_o)
18465 .arg(&*part_m)
18466 .arg(&*part_l)
18467 .arg(&mut o_g)
18468 .arg(&hd)
18469 .arg(&nh)
18470 .arg(bd)
18471 .arg(&plus_g)
18472 .arg(&nspm)
18473 .arg(&spk);
18474 unsafe {
18475 b2.launch(cfg2)?;
18476 }
18477 } else {
18478 assert!(
18481 q8_out.is_none(),
18482 "rows q8 emit requires the hd512 dc combine"
18483 );
18484 let fc = self.func("fa_decode_combine_rows");
18485 let __s_b2 = self.gpu.stream();
18486 let mut b2 = __s_b2.launch_builder(&fc);
18487 b2.arg(&*part_o)
18488 .arg(&*part_m)
18489 .arg(&*part_l)
18490 .arg(&mut o_g)
18491 .arg(&hd)
18492 .arg(&nh)
18493 .arg(&base_i)
18494 .arg(&nspm)
18495 .arg(&spk);
18496 unsafe {
18497 b2.launch(cfg2)?;
18498 }
18499 }
18500 }
18501 Ok(())
18502 }
18503
18504 #[allow(clippy::too_many_arguments)]
18508 pub fn fa_decode_rows_w(
18509 &self,
18510 q: &CudaSlice<f32>,
18511 k: &cudarc::driver::CudaView<u8>,
18512 v: &cudarc::driver::CudaView<u8>,
18513 o: &mut CudaSlice<f32>,
18514 head_dim: usize,
18515 n_head: usize,
18516 n_head_kv: usize,
18517 base_dev: &CudaSlice<i32>,
18518 base_plus: i32,
18519 t: usize,
18520 scale: f32,
18521 window: usize,
18522 k_tok_bytes: usize,
18523 v_tok_bytes: usize,
18524 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
18525 ) -> Result<(), Box<dyn std::error::Error>> {
18526 debug_assert!(head_dim == 256);
18531 let sp = {
18539 static SPW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18540 let v = *SPW.get_or_init(|| {
18541 std::env::var("MEMRA_FA_SPW")
18542 .ok()
18543 .and_then(|x| x.parse().ok())
18544 .unwrap_or(0)
18545 });
18546 if v >= 8 {
18547 v
18548 } else {
18549 FA_SPW_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
18550 }
18551 };
18552 let n_splits_max = (window + sp - 1) / sp;
18553 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18554 let (nspm, spk, wini) = (n_splits_max as i32, sp as i32, window as i32);
18555 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18556 let gqa = (n_head / n_head_kv).max(1) as u32;
18557 let o_len = t * n_head * n_splits_max * head_dim;
18558 let ml_len = t * n_head * n_splits_max;
18559 let mut part_guard = self.fa_part_pool.lock().unwrap();
18560 if part_guard
18561 .as_ref()
18562 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18563 .unwrap_or(true)
18564 {
18565 let old = part_guard.take();
18576 let (co, cm) = old
18577 .as_ref()
18578 .map(|pp| (pp.0.len(), pp.1.len()))
18579 .unwrap_or((0, 0));
18580 if let Some(old) = old {
18581 self.fa_part_retired.lock().unwrap().push(old);
18582 }
18583 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18584 eprintln!(
18585 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18586 co, o_len, cm, ml_len
18587 );
18588 }
18589 *part_guard = Some((
18590 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18591 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18592 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18593 ));
18594 }
18595 let pg = part_guard.as_mut().unwrap();
18596 self.gpu
18597 .stream()
18598 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18599 self.gpu
18600 .stream()
18601 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18602 self.gpu
18603 .stream()
18604 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18605 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18606 static SMEM_TKV_W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18612 let smem_tkv = *SMEM_TKV_W.get_or_init(|| {
18613 std::env::var("MEMRA_FA_SMEM_TKV")
18614 .ok()
18615 .and_then(|v| v.parse().ok())
18616 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
18617 });
18618 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18624 let wg = Self::wkv_on();
18629 let sp2 =
18632 gqa <= 4 && fa_v4_at(window) && std::env::var("MEMRA_FA_SPW2").as_deref() != Ok("0");
18633 if sp2 {
18634 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
18635 if Self::pdl_on() && Self::pdl_wb_on() {
18636 use cudarc::driver::{DevicePtr, DevicePtrMut};
18638 let s = &self.gpu.stream();
18639 let (pq, _b0) = q.device_ptr(s);
18640 let (pk, _b1) = k.device_ptr(s);
18641 let (pv, _b2) = v.device_ptr(s);
18642 let (po, _b3) = part_o.device_ptr_mut(s);
18643 let (pm, _b4) = part_m.device_ptr_mut(s);
18644 let (pl, _b5) = part_l.device_ptr_mut(s);
18645 let (pb, _b6) = base_dev.device_ptr(s);
18646 let mut ps = [
18647 &pq as *const _ as *mut std::ffi::c_void,
18648 &pk as *const _ as *mut _,
18649 &pv as *const _ as *mut _,
18650 &po as *const _ as *mut _,
18651 &pm as *const _ as *mut _,
18652 &pl as *const _ as *mut _,
18653 &hd as *const _ as *mut _,
18654 &nh as *const _ as *mut _,
18655 &nhkv as *const _ as *mut _,
18656 &pb as *const _ as *mut _,
18657 &base_plus as *const _ as *mut _,
18658 &scale as *const _ as *mut _,
18659 &nspm as *const _ as *mut _,
18660 &spk as *const _ as *mut _,
18661 &ktb as *const _ as *mut _,
18662 &vtb as *const _ as *mut _,
18663 &wini as *const _ as *mut _,
18664 ];
18665 unsafe {
18666 self.launch_pdl_flash(
18667 wg,
18668 "fa_decode_vec_q_rows_v4_w_sp",
18669 (n_head_kv as u32, n_splits_max as u32, t as u32),
18670 (32, gqa + 1, 1),
18671 sh,
18672 &mut ps,
18673 )?;
18674 }
18675 } else {
18676 let f = if wg {
18677 self.func_g("fa_decode_vec_q_rows_v4_w_sp")
18678 } else {
18679 self.func("fa_decode_vec_q_rows_v4_w_sp")
18680 };
18681 f.set_attribute(
18682 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18683 sh as i32,
18684 )?;
18685 let cfg = LaunchConfig {
18686 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18687 block_dim: (32, gqa + 1, 1),
18688 shared_mem_bytes: sh,
18689 };
18690 let __s_b = self.gpu.stream();
18691 let mut b = __s_b.launch_builder(&f);
18692 b.arg(q)
18693 .arg(k)
18694 .arg(v)
18695 .arg(&mut *part_o)
18696 .arg(&mut *part_m)
18697 .arg(&mut *part_l)
18698 .arg(&hd)
18699 .arg(&nh)
18700 .arg(&nhkv)
18701 .arg(base_dev)
18702 .arg(&base_plus)
18703 .arg(&scale)
18704 .arg(&nspm)
18705 .arg(&spk)
18706 .arg(&ktb)
18707 .arg(&vtb)
18708 .arg(&wini);
18709 unsafe {
18710 b.launch(cfg)?;
18711 }
18712 }
18713 } else {
18714 if fa_v4_at(window) && Self::pdl_on() && Self::pdl_wb_on() {
18715 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
18717 use cudarc::driver::{DevicePtr, DevicePtrMut};
18718 let s = &self.gpu.stream();
18719 let (pq, _b0) = q.device_ptr(s);
18720 let (pk, _b1) = k.device_ptr(s);
18721 let (pv, _b2) = v.device_ptr(s);
18722 let (po, _b3) = part_o.device_ptr_mut(s);
18723 let (pm, _b4) = part_m.device_ptr_mut(s);
18724 let (pl, _b5) = part_l.device_ptr_mut(s);
18725 let (pb, _b6) = base_dev.device_ptr(s);
18726 let mut ps = [
18727 &pq as *const _ as *mut std::ffi::c_void,
18728 &pk as *const _ as *mut _,
18729 &pv as *const _ as *mut _,
18730 &po as *const _ as *mut _,
18731 &pm as *const _ as *mut _,
18732 &pl as *const _ as *mut _,
18733 &hd as *const _ as *mut _,
18734 &nh as *const _ as *mut _,
18735 &nhkv as *const _ as *mut _,
18736 &pb as *const _ as *mut _,
18737 &base_plus as *const _ as *mut _,
18738 &scale as *const _ as *mut _,
18739 &nspm as *const _ as *mut _,
18740 &spk as *const _ as *mut _,
18741 &ktb as *const _ as *mut _,
18742 &vtb as *const _ as *mut _,
18743 &wini as *const _ as *mut _,
18744 ];
18745 unsafe {
18746 self.launch_pdl_flash(
18747 wg,
18748 "fa_decode_vec_q_rows_v4_w",
18749 (n_head_kv as u32, n_splits_max as u32, t as u32),
18750 (32, gqa, 1),
18751 sh,
18752 &mut ps,
18753 )?;
18754 }
18755 } else {
18756 let pick = |name: &str| {
18757 if wg {
18758 self.func_g(name)
18759 } else {
18760 self.func(name)
18761 }
18762 };
18763 let (f, sh) = if fa_v4_at(window) {
18764 let f = pick("fa_decode_vec_q_rows_v4_w");
18765 (f, (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32)
18766 } else if smem_tkv > 0 && window >= smem_tkv {
18767 (
18770 pick("fa_decode_vec_q_rows_smem_w"),
18771 (2 * 32 * head_dim * 2) as u32,
18772 )
18773 } else {
18774 (pick("fa_decode_vec_q_rows_reg_w"), 0u32)
18775 };
18776 f.set_attribute(
18777 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18778 sh as i32,
18779 )?;
18780 let cfg = LaunchConfig {
18781 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18782 block_dim: (32, gqa, 1),
18783 shared_mem_bytes: sh,
18784 };
18785 let __s_b = self.gpu.stream();
18786 let mut b = __s_b.launch_builder(&f);
18787 b.arg(q)
18788 .arg(k)
18789 .arg(v)
18790 .arg(&mut *part_o)
18791 .arg(&mut *part_m)
18792 .arg(&mut *part_l)
18793 .arg(&hd)
18794 .arg(&nh)
18795 .arg(&nhkv)
18796 .arg(base_dev)
18797 .arg(&base_plus)
18798 .arg(&scale)
18799 .arg(&nspm)
18800 .arg(&spk)
18801 .arg(&ktb)
18802 .arg(&vtb)
18803 .arg(&wini);
18804 unsafe {
18805 b.launch(cfg)?;
18806 }
18807 }
18808 }
18809 let cfg2 = LaunchConfig {
18810 grid_dim: (n_head as u32, t as u32, 1),
18811 block_dim: (head_dim as u32, 1, 1),
18812 shared_mem_bytes: 0,
18813 };
18814 if let Some((oq, od)) = q8_out {
18815 if Self::pdl_on() && Self::pdl_wb_on() {
18818 use cudarc::driver::{DevicePtr, DevicePtrMut};
18820 let s = &self.gpu.stream();
18821 let (po, _g0) = part_o.device_ptr(s);
18822 let (pm, _g1) = part_m.device_ptr(s);
18823 let (pl, _g2) = part_l.device_ptr(s);
18824 let (pq, _g3) = oq.device_ptr_mut(s);
18825 let (pd, _g4) = od.device_ptr_mut(s);
18826 let mut ps = [
18827 &po as *const _ as *mut std::ffi::c_void,
18828 &pm as *const _ as *mut _,
18829 &pl as *const _ as *mut _,
18830 &pq as *const _ as *mut _,
18831 &pd as *const _ as *mut _,
18832 &hd as *const _ as *mut _,
18833 &nh as *const _ as *mut _,
18834 &nspm as *const _ as *mut _,
18835 &spk as *const _ as *mut _,
18836 &wini as *const _ as *mut _,
18837 ];
18838 unsafe {
18839 self.launch_pdl_flash(
18840 wg,
18841 "fa_decode_combine_rows_w_q8_1",
18842 cfg2.grid_dim,
18843 cfg2.block_dim,
18844 0,
18845 &mut ps,
18846 )?;
18847 }
18848 return Ok(());
18849 }
18850 let fc = if wg {
18851 self.func_g("fa_decode_combine_rows_w_q8_1")
18852 } else {
18853 self.func("fa_decode_combine_rows_w_q8_1")
18854 };
18855 let __s_b2 = self.gpu.stream();
18856 let mut b2 = __s_b2.launch_builder(&fc);
18857 b2.arg(&*part_o)
18858 .arg(&*part_m)
18859 .arg(&*part_l)
18860 .arg(oq)
18861 .arg(od)
18862 .arg(&hd)
18863 .arg(&nh)
18864 .arg(&nspm)
18865 .arg(&spk)
18866 .arg(&wini);
18867 unsafe {
18868 b2.launch(cfg2)?;
18869 }
18870 return Ok(());
18871 }
18872 let fc = if wg {
18873 self.func_g("fa_decode_combine_rows_w")
18874 } else {
18875 self.func("fa_decode_combine_rows_w")
18876 };
18877 let __s_b2 = self.gpu.stream();
18878 let mut b2 = __s_b2.launch_builder(&fc);
18879 b2.arg(&*part_o)
18880 .arg(&*part_m)
18881 .arg(&*part_l)
18882 .arg(o)
18883 .arg(&hd)
18884 .arg(&nh)
18885 .arg(&nspm)
18886 .arg(&spk)
18887 .arg(&wini);
18888 unsafe {
18889 b2.launch(cfg2)?;
18890 }
18891 Ok(())
18892 }
18893
18894 #[allow(clippy::too_many_arguments)]
18900 pub fn fa_decode_rows_dc(
18901 &self,
18902 q: &CudaSlice<f32>,
18903 k: &cudarc::driver::CudaView<u8>,
18904 v: &cudarc::driver::CudaView<u8>,
18905 o: &mut CudaSlice<f32>,
18906 head_dim: usize,
18907 n_head: usize,
18908 n_head_kv: usize,
18909 base_dev: &CudaSlice<i32>,
18910 t_kv_upper: usize,
18911 t: usize,
18912 scale: f32,
18913 k_tok_bytes: usize,
18914 v_tok_bytes: usize,
18915 base_plus: i32,
18916 g: bool,
18917 ) -> Result<(), Box<dyn std::error::Error>> {
18918 let v4 = head_dim == 256 && fa_v4_at(t_kv_upper);
18919 assert!(
18920 v4 || fa_v3_active(head_dim),
18921 "stream fa rows requires the v3 or v4 lane"
18922 );
18923 assert!(v4 || base_plus == 0, "v3_dc kernel takes no plus arg");
18924 if v4 {
18925 let sp = fa_split_keys(t_kv_upper, n_head_kv);
18926 let n_splits_max = (t_kv_upper + sp - 1) / sp;
18927 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18928 let (nspm, spk) = (n_splits_max as i32, sp as i32);
18929 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18930 let gqa = (n_head / n_head_kv).max(1) as u32;
18931 let o_len = t * n_head * n_splits_max * head_dim;
18932 let ml_len = t * n_head * n_splits_max;
18933 let mut part_guard = self.fa_part_pool.lock().unwrap();
18934 if part_guard
18935 .as_ref()
18936 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18937 .unwrap_or(true)
18938 {
18939 let old = part_guard.take();
18950 let (co, cm) = old
18951 .as_ref()
18952 .map(|pp| (pp.0.len(), pp.1.len()))
18953 .unwrap_or((0, 0));
18954 if let Some(old) = old {
18955 self.fa_part_retired.lock().unwrap().push(old);
18956 }
18957 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18958 eprintln!(
18959 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18960 co, o_len, cm, ml_len
18961 );
18962 }
18963 *part_guard = Some((
18964 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18965 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18966 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18967 ));
18968 }
18969 let pg = part_guard.as_mut().unwrap();
18970 self.gpu
18971 .stream()
18972 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18973 self.gpu
18974 .stream()
18975 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18976 self.gpu
18977 .stream()
18978 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18979 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18980 let f = if g {
18981 self.func_g("fa_decode_vec_q_rows_v4_dc")
18982 } else {
18983 self.func("fa_decode_vec_q_rows_v4_dc")
18984 };
18985 let sh = (11520 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
18986 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18987 f.set_attribute(
18988 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18989 sh as i32,
18990 )?;
18991 let cfg = LaunchConfig {
18992 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18993 block_dim: (32, gqa, 1),
18994 shared_mem_bytes: sh,
18995 };
18996 let __s_b = self.gpu.stream();
18997 let mut b = __s_b.launch_builder(&f);
18998 b.arg(q)
18999 .arg(k)
19000 .arg(v)
19001 .arg(&mut *part_o)
19002 .arg(&mut *part_m)
19003 .arg(&mut *part_l)
19004 .arg(&hd)
19005 .arg(&nh)
19006 .arg(&nhkv)
19007 .arg(base_dev)
19008 .arg(&base_plus)
19009 .arg(&scale)
19010 .arg(&nspm)
19011 .arg(&spk)
19012 .arg(&ktb)
19013 .arg(&vtb);
19014 unsafe {
19015 b.launch(cfg)?;
19016 }
19017 let fc = self.func("fa_decode_combine_rows_dc");
19018 let cfg2 = LaunchConfig {
19019 grid_dim: (n_head as u32, t as u32, 1),
19020 block_dim: (head_dim as u32, 1, 1),
19021 shared_mem_bytes: 0,
19022 };
19023 let __s_b2 = self.gpu.stream();
19024 let mut b2 = __s_b2.launch_builder(&fc);
19025 b2.arg(&*part_o)
19026 .arg(&*part_m)
19027 .arg(&*part_l)
19028 .arg(o)
19029 .arg(&hd)
19030 .arg(&nh)
19031 .arg(base_dev)
19032 .arg(&base_plus)
19033 .arg(&nspm)
19034 .arg(&spk);
19035 unsafe {
19036 b2.launch(cfg2)?;
19037 }
19038 return Ok(());
19039 }
19040 let sp = fa_split_keys(t_kv_upper, n_head_kv);
19041 let n_splits_max = (t_kv_upper + sp - 1) / sp;
19042 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
19043 let (nspm, spk) = (n_splits_max as i32, sp as i32);
19044 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
19045 let gqa = (n_head / n_head_kv).max(1) as u32;
19046 let o_len = t * n_head * n_splits_max * head_dim;
19047 let ml_len = t * n_head * n_splits_max;
19048 let mut part_guard = self.fa_part_pool.lock().unwrap();
19049 if part_guard
19050 .as_ref()
19051 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
19052 .unwrap_or(true)
19053 {
19054 let old = part_guard.take();
19065 let (co, cm) = old
19066 .as_ref()
19067 .map(|pp| (pp.0.len(), pp.1.len()))
19068 .unwrap_or((0, 0));
19069 if let Some(old) = old {
19070 self.fa_part_retired.lock().unwrap().push(old);
19071 }
19072 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
19073 eprintln!(
19074 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
19075 co, o_len, cm, ml_len
19076 );
19077 }
19078 *part_guard = Some((
19079 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
19080 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19081 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19082 ));
19083 }
19084 let pg = part_guard.as_mut().unwrap();
19085 self.gpu
19086 .stream()
19087 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
19088 self.gpu
19089 .stream()
19090 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
19091 self.gpu
19092 .stream()
19093 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
19094 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
19095 let f = self.func("fa_decode_vec_q_rows_v3_dc");
19096 let sh = (32 * head_dim * 2) as u32;
19097 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19098 f.set_attribute(
19099 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19100 sh as i32,
19101 )?;
19102 let cfg = LaunchConfig {
19103 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
19104 block_dim: (32, gqa, 1),
19105 shared_mem_bytes: sh,
19106 };
19107 let __s_b = self.gpu.stream();
19108 let mut b = __s_b.launch_builder(&f);
19109 b.arg(q)
19110 .arg(k)
19111 .arg(v)
19112 .arg(&mut *part_o)
19113 .arg(&mut *part_m)
19114 .arg(&mut *part_l)
19115 .arg(&hd)
19116 .arg(&nh)
19117 .arg(&nhkv)
19118 .arg(base_dev)
19119 .arg(&scale)
19120 .arg(&nspm)
19121 .arg(&spk)
19122 .arg(&ktb)
19123 .arg(&vtb);
19124 unsafe {
19125 b.launch(cfg)?;
19126 }
19127 let fc = self.func("fa_decode_combine_rows_dc");
19128 let cfg2 = LaunchConfig {
19129 grid_dim: (n_head as u32, t as u32, 1),
19130 block_dim: (head_dim as u32, 1, 1),
19131 shared_mem_bytes: 0,
19132 };
19133 let plus0 = 0i32;
19134 let __s_b2 = self.gpu.stream();
19135 let mut b2 = __s_b2.launch_builder(&fc);
19136 b2.arg(&*part_o)
19137 .arg(&*part_m)
19138 .arg(&*part_l)
19139 .arg(o)
19140 .arg(&hd)
19141 .arg(&nh)
19142 .arg(base_dev)
19143 .arg(&plus0)
19144 .arg(&nspm)
19145 .arg(&spk);
19146 unsafe {
19147 b2.launch(cfg2)?;
19148 }
19149 Ok(())
19150 }
19151
19152 pub fn fa_decode_dc(
19163 &self,
19164 q: &CudaSlice<f32>,
19165 k: &cudarc::driver::CudaView<u8>,
19166 v: &cudarc::driver::CudaView<u8>,
19167 o: &mut CudaSlice<f32>,
19168 head_dim: usize,
19169 n_head: usize,
19170 n_head_kv: usize,
19171 t_kv_dev: &CudaSlice<i32>,
19172 bucket_max: usize,
19173 scale: f32,
19174 k_tok_bytes: usize,
19175 v_tok_bytes: usize,
19176 g: bool,
19177 ) -> Result<(), Box<dyn std::error::Error>> {
19178 self.fa_decode_dc_q8(
19179 q,
19180 k,
19181 v,
19182 o,
19183 head_dim,
19184 n_head,
19185 n_head_kv,
19186 t_kv_dev,
19187 bucket_max,
19188 scale,
19189 k_tok_bytes,
19190 v_tok_bytes,
19191 g,
19192 None,
19193 )
19194 }
19195
19196 #[allow(clippy::too_many_arguments)]
19199 pub fn fa_decode_dc_q8(
19200 &self,
19201 q: &CudaSlice<f32>,
19202 k: &cudarc::driver::CudaView<u8>,
19203 v: &cudarc::driver::CudaView<u8>,
19204 o: &mut CudaSlice<f32>,
19205 head_dim: usize,
19206 n_head: usize,
19207 n_head_kv: usize,
19208 t_kv_dev: &CudaSlice<i32>,
19209 bucket_max: usize,
19210 scale: f32,
19211 k_tok_bytes: usize,
19212 v_tok_bytes: usize,
19213 g: bool,
19214 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
19215 ) -> Result<(), Box<dyn std::error::Error>> {
19216 let mut fa_vec =
19224 std::env::var("MEMRA_NO_FA_VEC").is_err() && bucket_max >= fa_vec_min_tkv();
19225 if g && head_dim == 256 && !fa_v4_at(bucket_max) {
19226 fa_vec = false;
19227 } let sp = fa_split_keys(bucket_max, n_head_kv);
19229 let n_splits = if fa_vec {
19230 ((bucket_max + sp - 1) / sp).max(1)
19231 } else {
19232 ((bucket_max + 255) / 256).max(1)
19233 };
19234 let o_len = n_head * n_splits * head_dim;
19235 let ml_len = n_head * n_splits;
19236 let mut part_guard = self.fa_part_pool.lock().unwrap();
19237 if part_guard
19238 .as_ref()
19239 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
19240 .unwrap_or(true)
19241 {
19242 let old = part_guard.take();
19253 let (co, cm) = old
19254 .as_ref()
19255 .map(|pp| (pp.0.len(), pp.1.len()))
19256 .unwrap_or((0, 0));
19257 if let Some(old) = old {
19258 self.fa_part_retired.lock().unwrap().push(old);
19259 }
19260 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
19261 eprintln!(
19262 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
19263 co, o_len, cm, ml_len
19264 );
19265 }
19266 *part_guard = Some((
19267 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
19268 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19269 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19270 ));
19271 }
19272 let pg = part_guard.as_mut().unwrap();
19273 self.gpu
19274 .stream()
19275 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
19276 self.gpu
19277 .stream()
19278 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
19279 self.gpu
19280 .stream()
19281 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
19282 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
19283 let (hd, nh, nhkv, nsp) = (
19284 head_dim as i32,
19285 n_head as i32,
19286 n_head_kv as i32,
19287 n_splits as i32,
19288 );
19289 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
19290 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
19291 let deep = fa_vec
19294 && head_dim == 256
19295 && fa_v4_at(bucket_max)
19296 && !g
19297 && fa_deep_at(bucket_max)
19298 && !matches!(fa_v4_mode(), "noB3" | "stage");
19299 let (f, cfg) = if fa_vec
19300 && head_dim == 512
19301 && bucket_max >= {
19302 static FA512_MIN_DC: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
19303 *FA512_MIN_DC.get_or_init(|| {
19304 std::env::var("MEMRA_FA512_MIN")
19305 .ok()
19306 .and_then(|v| v.parse().ok())
19307 .unwrap_or(512)
19308 })
19309 } {
19310 let gqa = (n_head / n_head_kv).max(1) as u32;
19312 (
19313 self.fa_func("fa_decode_vec_q_dpl16_dc", head_dim),
19314 LaunchConfig {
19315 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19316 block_dim: (32, gqa, 1),
19317 shared_mem_bytes: 0,
19318 },
19319 )
19320 } else if fa_vec && head_dim == 512 {
19321 let q_view = q.as_view();
19324 let mut o_view = o.as_view_mut();
19325 return self.fa_decode_scalar_unified(
19326 &q_view,
19327 k,
19328 v,
19329 &mut o_view,
19330 head_dim,
19331 n_head,
19332 n_head_kv,
19333 0,
19334 Some(t_kv_dev),
19335 scale,
19336 n_splits,
19337 sp,
19338 k_tok_bytes,
19339 v_tok_bytes,
19340 g,
19341 &mut *part_o,
19342 &mut *part_m,
19343 &mut *part_l,
19344 q8_out,
19345 );
19346 } else if fa_vec && head_dim == 256 && fa_v4_at(bucket_max) {
19347 let gqa = (n_head / n_head_kv).max(1) as u32;
19350 let fv = if g {
19351 self.func_g("fa_decode_vec_q_v4_dc")
19352 } else if deep {
19353 self.func("fa_decode_vec_q_v4_deep_dc")
19354 } else {
19355 self.func("fa_decode_vec_q_v4_dc")
19356 };
19357 let shmem =
19358 (if deep { 12160 } else { 11520 } + 32 * head_dim * if g { 1 } else { 2 }) as u32;
19359 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19360 fv.set_attribute(
19361 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19362 shmem as i32,
19363 )?;
19364 (
19365 fv,
19366 LaunchConfig {
19367 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19368 block_dim: (32, gqa, 1),
19369 shared_mem_bytes: shmem,
19370 },
19371 )
19372 } else if fa_vec && fa_v3_active(head_dim) {
19373 let gqa = (n_head / n_head_kv).max(1) as u32;
19376 let fv = if g {
19377 self.func_g("fa_decode_vec_q_v3_dc")
19378 } else {
19379 self.func("fa_decode_vec_q_v3_dc")
19380 };
19381 let shmem = (32 * head_dim * 2) as u32; (
19383 fv,
19384 LaunchConfig {
19385 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19386 block_dim: (32, gqa, 1),
19387 shared_mem_bytes: shmem,
19388 },
19389 )
19390 } else if fa_vec && fa_v2_on() {
19391 let gqa = (n_head / n_head_kv).max(1) as u32;
19395 let fv = if g {
19396 self.func_g("fa_decode_vec_q_v2_dc")
19397 } else {
19398 self.func("fa_decode_vec_q_v2_dc")
19399 };
19400 let shmem = (2 * 32 * head_dim * 2) as u32; (
19402 fv,
19403 LaunchConfig {
19404 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19405 block_dim: (32, gqa, 1),
19406 shared_mem_bytes: shmem,
19407 },
19408 )
19409 } else if fa_vec {
19410 let gqa = (n_head / n_head_kv).max(1) as u32;
19411 let fv = if g {
19413 self.func_g("fa_decode_vec_q_dc")
19414 } else {
19415 self.func("fa_decode_vec_q_dc")
19416 };
19417 (
19418 fv,
19419 LaunchConfig {
19420 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19421 block_dim: (32, gqa, 1),
19422 shared_mem_bytes: 0,
19423 },
19424 )
19425 } else {
19426 let q_view = q.as_view();
19427 let mut o_view = o.as_view_mut();
19428 return self.fa_decode_scalar_unified(
19429 &q_view,
19430 k,
19431 v,
19432 &mut o_view,
19433 head_dim,
19434 n_head,
19435 n_head_kv,
19436 0,
19437 Some(t_kv_dev),
19438 scale,
19439 n_splits,
19440 if fa_vec { sp } else { 256 },
19441 k_tok_bytes,
19442 v_tok_bytes,
19443 g,
19444 &mut *part_o,
19445 &mut *part_m,
19446 &mut *part_l,
19447 q8_out,
19448 );
19449 };
19450 let ski = sp as i32; let __s_b = self.gpu.stream();
19452 let mut b = __s_b.launch_builder(&f);
19453 b.arg(q)
19454 .arg(k)
19455 .arg(v)
19456 .arg(&mut *part_o)
19457 .arg(&mut *part_m)
19458 .arg(&mut *part_l)
19459 .arg(&hd)
19460 .arg(&nh)
19461 .arg(&nhkv)
19462 .arg(t_kv_dev)
19463 .arg(&scale)
19464 .arg(&nsp)
19465 .arg(&ski)
19466 .arg(&ktb)
19467 .arg(&vtb);
19468 unsafe {
19469 b.launch(cfg)?;
19470 }
19471 let cfg2 = LaunchConfig {
19472 grid_dim: (n_head as u32, 1, 1),
19473 block_dim: (head_dim as u32, 1, 1),
19474 shared_mem_bytes: 0,
19475 };
19476 if let Some((oq, od)) = q8_out {
19477 let fc = if g {
19478 self.func_g("fa_decode_combine_q8_1")
19479 } else {
19480 self.fa_func("fa_decode_combine_q8_1", head_dim)
19481 };
19482 let __s_b2 = self.gpu.stream();
19483 let mut b2 = __s_b2.launch_builder(&fc);
19484 b2.arg(&*part_o)
19485 .arg(&*part_m)
19486 .arg(&*part_l)
19487 .arg(oq)
19488 .arg(od)
19489 .arg(&hd)
19490 .arg(&nh)
19491 .arg(&nsp);
19492 unsafe {
19493 b2.launch(cfg2)?;
19494 }
19495 return Ok(());
19496 }
19497 let fc = if g {
19498 self.func_g("fa_decode_combine_f32")
19499 } else {
19500 self.fa_func("fa_decode_combine_f32", head_dim)
19501 };
19502 let __s_b2 = self.gpu.stream();
19503 let mut b2 = __s_b2.launch_builder(&fc);
19504 b2.arg(&*part_o)
19505 .arg(&*part_m)
19506 .arg(&*part_l)
19507 .arg(o)
19508 .arg(&hd)
19509 .arg(&nh)
19510 .arg(&nsp);
19511 unsafe {
19512 b2.launch(cfg2)?;
19513 }
19514 Ok(())
19515 }
19516
19517 pub fn fa_geom_eager(
19523 &self,
19524 t_kv: usize,
19525 head_dim: usize,
19526 n_head_kv: usize,
19527 g: bool,
19528 ) -> (bool, usize) {
19529 let fa_ok = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
19533 let vec512 = fa_ok && head_dim == 512 && t_kv >= fa512_min_tkv();
19539 let mut fa_vec = vec512 || (fa_ok && head_dim <= 256 && head_dim % 32 == 0);
19540 if g && head_dim == 256 && !fa_v4_at(t_kv) {
19546 fa_vec = false;
19547 }
19548 let sp = fa_split_keys(t_kv, n_head_kv);
19549 let n_splits = if fa_vec {
19550 ((t_kv + sp - 1) / sp).max(1)
19551 } else {
19552 ((t_kv + 255) / 256).max(1)
19553 };
19554 (fa_vec, n_splits)
19555 }
19556
19557 pub fn fa_bucket_key(
19563 &self,
19564 t_kv: usize,
19565 head_dim: usize,
19566 n_head_kv: usize,
19567 g: bool,
19568 ) -> (bool, usize) {
19569 self.fa_geom_eager(t_kv, head_dim, n_head_kv, g)
19570 }
19571
19572 pub fn capture_graph_retained<F>(
19584 &self,
19585 step: F,
19586 ) -> Result<
19587 (
19588 cudarc::driver::CudaGraph,
19589 Vec<Box<dyn std::any::Any + Send>>,
19590 ),
19591 Box<dyn std::error::Error>,
19592 >
19593 where
19594 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
19595 {
19596 use cudarc::driver::sys::CUgraphInstantiate_flags;
19597 self.capture_graph_retained_flags(
19598 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
19599 step,
19600 )
19601 }
19602
19603 pub fn capture_graph_retained_flags<F>(
19608 &self,
19609 flags: cudarc::driver::sys::CUgraphInstantiate_flags,
19610 mut step: F,
19611 ) -> Result<
19612 (
19613 cudarc::driver::CudaGraph,
19614 Vec<Box<dyn std::any::Any + Send>>,
19615 ),
19616 Box<dyn std::error::Error>,
19617 >
19618 where
19619 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
19620 {
19621 use cudarc::driver::sys::CUstreamCaptureMode;
19622 self.capture_keep.lock().unwrap().clear();
19630 let was_tracking = self.gpu.ctx.is_event_tracking();
19631 if was_tracking {
19632 unsafe {
19633 self.gpu.ctx.disable_event_tracking();
19634 }
19635 }
19636 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
19637 self.capture_keep_on
19638 .store(true, std::sync::atomic::Ordering::Relaxed);
19639 let w = (|| {
19640 step(self)?;
19641 step(self)
19642 })();
19643 self.capture_keep_on
19644 .store(false, std::sync::atomic::Ordering::Relaxed);
19645 w?;
19646 self.gpu.stream().synchronize()?;
19647 self.gpu
19648 .stream()
19649 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
19650 let r = step(self);
19651 let g = self.gpu.stream().end_capture(flags);
19652 r?;
19653 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
19654 graph.upload()?;
19655 Ok(graph)
19656 };
19657 let result = run();
19658 self.capture_keep_on
19659 .store(false, std::sync::atomic::Ordering::Relaxed);
19660 if was_tracking {
19661 unsafe {
19662 self.gpu.ctx.enable_event_tracking();
19663 }
19664 }
19665 let keeper = std::mem::take(&mut *self.capture_keep.lock().unwrap());
19666 Ok((result?, keeper))
19667 }
19668
19669 pub fn capture_graph<F>(
19670 &self,
19671 mut step: F,
19672 ) -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>>
19673 where
19674 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
19675 {
19676 use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
19677 let was_tracking = self.gpu.ctx.is_event_tracking();
19685 if was_tracking {
19686 unsafe {
19687 self.gpu.ctx.disable_event_tracking();
19688 }
19689 }
19690 let iflag = {
19697 static F: std::sync::OnceLock<CUgraphInstantiate_flags> = std::sync::OnceLock::new();
19698 *F.get_or_init(|| match std::env::var("MEMRA_GRAPH_IFLAG").as_deref() {
19699 Ok("upload") => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_UPLOAD,
19702 Ok("priority") => {
19703 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY
19704 }
19705 _ => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
19706 })
19707 };
19708 let ct = {
19715 static T: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19716 *T.get_or_init(|| std::env::var("MEMRA_GRAPH_CAPTIME").as_deref() == Ok("1"))
19717 };
19718 let warmups = {
19741 static W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
19742 *W.get_or_init(|| {
19743 std::env::var("MEMRA_GRAPH_WARMUPS")
19744 .ok()
19745 .and_then(|v| v.parse().ok())
19746 .filter(|n| *n >= 1)
19747 .unwrap_or(1)
19748 })
19749 };
19750 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
19751 let t_w = std::time::Instant::now();
19752 for _ in 0..warmups {
19754 step(self)?;
19755 }
19756 self.gpu.stream().synchronize()?;
19757 let ms_warm = t_w.elapsed().as_secs_f64() * 1e3;
19758 let t_c = std::time::Instant::now();
19760 self.gpu
19761 .stream()
19762 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
19763 let r = step(self);
19766 let ms_body = t_c.elapsed().as_secs_f64() * 1e3;
19767 let t_i = std::time::Instant::now();
19768 let g = self.gpu.stream().end_capture(iflag);
19769 let ms_inst = t_i.elapsed().as_secs_f64() * 1e3;
19770 r?;
19771 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
19772 let t_u = std::time::Instant::now();
19773 graph.upload()?;
19774 if ct {
19775 println!(
19776 "[graph-captime] warmup2x {ms_warm:.2} ms capture-body {ms_body:.2} ms \
19777 instantiate {ms_inst:.2} ms upload {:.2} ms",
19778 t_u.elapsed().as_secs_f64() * 1e3
19779 );
19780 }
19781 Ok(graph)
19782 };
19783 let result = run();
19784 if was_tracking {
19785 unsafe {
19786 self.gpu.ctx.enable_event_tracking();
19787 }
19788 }
19789 result
19790 }
19791
19792 pub fn gdn_scan_s128_view(
19794 &self,
19795 q: &CudaSlice<f32>,
19796 k: &CudaSlice<f32>,
19797 v: &CudaSlice<f32>,
19798 g: &CudaSlice<f32>,
19799 beta: &CudaSlice<f32>,
19800 state_in: &cudarc::driver::CudaView<f32>,
19801 state_out: &mut cudarc::driver::CudaViewMut<f32>,
19802 o: &mut CudaSlice<f32>,
19803 n_head: usize,
19804 t: usize,
19805 scale: f32,
19806 ) -> Result<(), Box<dyn std::error::Error>> {
19807 let f = self.func("gdn_scan_s128");
19808 const S_V: u32 = 128;
19809 const WARP: u32 = 32;
19810 const COLS: u32 = 4;
19811 let cfg = LaunchConfig {
19812 grid_dim: (n_head as u32, 1, S_V / COLS),
19813 block_dim: (WARP, COLS, 1),
19814 shared_mem_bytes: 0,
19815 };
19816 let (h, ti) = (n_head as i32, t as i32);
19817 let __s_b = self.gpu.stream();
19818 let mut b = __s_b.launch_builder(&f);
19819 b.arg(q)
19820 .arg(k)
19821 .arg(v)
19822 .arg(g)
19823 .arg(beta)
19824 .arg(state_in)
19825 .arg(state_out)
19826 .arg(o)
19827 .arg(&h)
19828 .arg(&ti)
19829 .arg(&scale);
19830 unsafe {
19831 b.launch(cfg)?;
19832 }
19833 Ok(())
19834 }
19835
19836 pub fn ssm_conv1d_view(
19838 &self,
19839 x: &cudarc::driver::CudaView<f32>,
19840 w: &CudaSlice<f32>,
19841 y: &mut CudaSlice<f32>,
19842 conv_dim: usize,
19843 t: usize,
19844 d_conv: usize,
19845 silu: bool,
19846 ) -> Result<(), Box<dyn std::error::Error>> {
19847 let f = self.func("ssm_conv1d_silu_f32");
19848 let cfg = LaunchConfig {
19850 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
19851 block_dim: (256, 1, 1),
19852 shared_mem_bytes: 0,
19853 };
19854 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
19855 let __s_b = self.gpu.stream();
19856 let mut b = __s_b.launch_builder(&f);
19857 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
19858 unsafe {
19859 b.launch(cfg)?;
19860 }
19861 Ok(())
19862 }
19863
19864 pub fn ssm_conv1d_tm(
19871 &self,
19872 qkv_tm: &CudaSlice<f32>,
19873 w: &CudaSlice<f32>,
19874 y: &mut CudaSlice<f32>,
19875 conv_dim: usize,
19876 t: usize,
19877 d_conv: usize,
19878 ) -> Result<(), Box<dyn std::error::Error>> {
19879 let f = self.func("ssm_conv1d_tm_f32");
19880 let cfg = LaunchConfig {
19881 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19882 block_dim: (256, 1, 1),
19883 shared_mem_bytes: 0,
19884 };
19885 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19886 let __s_b = self.gpu.stream();
19887 let mut b = __s_b.launch_builder(&f);
19888 b.arg(qkv_tm).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc);
19889 unsafe {
19890 b.launch(cfg)?;
19891 }
19892 Ok(())
19893 }
19894
19895 pub fn ssm_conv1d_tm_state(
19903 &self,
19904 qkv_tm: &CudaSlice<f32>,
19905 conv_state: &mut CudaSlice<f32>,
19906 w: &CudaSlice<f32>,
19907 y: &mut CudaSlice<f32>,
19908 conv_dim: usize,
19909 t: usize,
19910 d_conv: usize,
19911 ) -> Result<(), Box<dyn std::error::Error>> {
19912 self.ssm_conv1d_tm_state_pad(qkv_tm, conv_state, w, y, conv_dim, t, d_conv, None)
19913 }
19914
19915 #[allow(clippy::too_many_arguments)]
19918 pub fn ssm_conv1d_tm_state_pad(
19919 &self,
19920 qkv_tm: &CudaSlice<f32>,
19921 conv_state: &mut CudaSlice<f32>,
19922 w: &CudaSlice<f32>,
19923 y: &mut CudaSlice<f32>,
19924 conv_dim: usize,
19925 t: usize,
19926 d_conv: usize,
19927 pad_len: Option<&CudaSlice<i32>>,
19928 ) -> Result<(), Box<dyn std::error::Error>> {
19929 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
19930 let ring_old = if t < d_conv - 1 {
19934 Some(self.clone_dtod(conv_state)?)
19935 } else {
19936 None
19937 };
19938 {
19939 let f = self.func("ssm_conv1d_tm_state_f32");
19940 let cfg = LaunchConfig {
19941 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19942 block_dim: (256, 1, 1),
19943 shared_mem_bytes: 0,
19944 };
19945 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19946 let __s_b = self.gpu.stream();
19947 let mut b = __s_b.launch_builder(&f);
19948 b.arg(qkv_tm)
19949 .arg(&*conv_state)
19950 .arg(w)
19951 .arg(y)
19952 .arg(&cd)
19953 .arg(&ti)
19954 .arg(&dc);
19955 unsafe {
19956 b.launch(cfg)?;
19957 }
19958 }
19959 match (ring_old, pad_len) {
19960 (None, Some(len_d)) => {
19961 let f = self.func("ssm_conv_ring_update_dev_f32");
19962 let n = conv_dim * (d_conv - 1);
19963 let cfg = LaunchConfig::for_num_elems(n as u32);
19964 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19965 let __s_b = self.gpu.stream();
19966 let mut b = __s_b.launch_builder(&f);
19967 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
19968 unsafe {
19969 b.launch(cfg)?;
19970 }
19971 }
19972 (None, None) => {
19973 let f = self.func("ssm_conv_ring_update_f32");
19974 let n = conv_dim * (d_conv - 1);
19975 let cfg = LaunchConfig::for_num_elems(n as u32);
19976 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19977 let __s_b = self.gpu.stream();
19978 let mut b = __s_b.launch_builder(&f);
19979 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
19980 unsafe {
19981 b.launch(cfg)?;
19982 }
19983 }
19984 (Some(old), _) => {
19985 self.ssm_conv_ring_rebuild(qkv_tm, &old, conv_state, conv_dim, t, d_conv)?
19986 }
19987 }
19988 Ok(())
19989 }
19990
19991 pub fn ssm_conv1d_tm_state_pad_v(
19993 &self,
19994 qkv_tm: &cudarc::driver::CudaView<f32>,
19995 conv_state: &mut CudaSlice<f32>,
19996 w: &CudaSlice<f32>,
19997 y: &mut CudaSlice<f32>,
19998 conv_dim: usize,
19999 t: usize,
20000 d_conv: usize,
20001 pad_len: Option<&CudaSlice<i32>>,
20002 ) -> Result<(), Box<dyn std::error::Error>> {
20003 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
20004 let ring_old = if t < d_conv - 1 {
20008 Some(self.clone_dtod(conv_state)?)
20009 } else {
20010 None
20011 };
20012 {
20013 let f = self.func("ssm_conv1d_tm_state_f32");
20014 let cfg = LaunchConfig {
20015 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
20016 block_dim: (256, 1, 1),
20017 shared_mem_bytes: 0,
20018 };
20019 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20020 let __s_b = self.gpu.stream();
20021 let mut b = __s_b.launch_builder(&f);
20022 b.arg(qkv_tm)
20023 .arg(&*conv_state)
20024 .arg(w)
20025 .arg(y)
20026 .arg(&cd)
20027 .arg(&ti)
20028 .arg(&dc);
20029 unsafe {
20030 b.launch(cfg)?;
20031 }
20032 }
20033 match (ring_old, pad_len) {
20034 (None, Some(len_d)) => {
20035 let f = self.func("ssm_conv_ring_update_dev_f32");
20036 let n = conv_dim * (d_conv - 1);
20037 let cfg = LaunchConfig::for_num_elems(n as u32);
20038 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20039 let __s_b = self.gpu.stream();
20040 let mut b = __s_b.launch_builder(&f);
20041 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
20042 unsafe {
20043 b.launch(cfg)?;
20044 }
20045 }
20046 (None, None) => {
20047 let f = self.func("ssm_conv_ring_update_f32");
20048 let n = conv_dim * (d_conv - 1);
20049 let cfg = LaunchConfig::for_num_elems(n as u32);
20050 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20051 let __s_b = self.gpu.stream();
20052 let mut b = __s_b.launch_builder(&f);
20053 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
20054 unsafe {
20055 b.launch(cfg)?;
20056 }
20057 }
20058 (Some(_), _) => unreachable!(
20059 "ssm_conv1d_tm_state_pad_v: T < d_conv-1 has no view path (PRIME_MIN_T gates it)"
20060 ),
20061 }
20062 Ok(())
20063 }
20064
20065 pub fn ssm_conv_ring_rebuild(
20070 &self,
20071 qkv_tm: &CudaSlice<f32>,
20072 ring_old: &CudaSlice<f32>,
20073 conv_state: &mut CudaSlice<f32>,
20074 conv_dim: usize,
20075 tc: usize,
20076 d_conv: usize,
20077 ) -> Result<(), Box<dyn std::error::Error>> {
20078 let f = self.func("ssm_conv_ring_rebuild_f32");
20079 let n = conv_dim * (d_conv - 1);
20080 let cfg = LaunchConfig::for_num_elems(n as u32);
20081 let (cd, ti, dc) = (conv_dim as i32, tc as i32, d_conv as i32);
20082 let __s_b = self.gpu.stream();
20083 let mut b = __s_b.launch_builder(&f);
20084 b.arg(qkv_tm)
20085 .arg(ring_old)
20086 .arg(conv_state)
20087 .arg(&cd)
20088 .arg(&ti)
20089 .arg(&dc);
20090 unsafe {
20091 b.launch(cfg)?;
20092 }
20093 Ok(())
20094 }
20095
20096 #[allow(clippy::too_many_arguments)]
20101 pub fn gdn_prep_decode(
20102 &self,
20103 conv_out: &CudaSlice<f32>,
20104 beta_raw: &CudaSlice<f32>,
20105 alpha: &CudaSlice<f32>,
20106 dt_bias: &CudaSlice<f32>,
20107 a: &CudaSlice<f32>,
20108 q_l2: &mut CudaSlice<f32>,
20109 k_l2: &mut CudaSlice<f32>,
20110 v_g: &mut CudaSlice<f32>,
20111 beta: &mut CudaSlice<f32>,
20112 g_log: &mut CudaSlice<f32>,
20113 d_state: usize,
20114 num_v: usize,
20115 num_k: usize,
20116 key_dim: usize,
20117 eps: f32,
20118 ) -> Result<(), Box<dyn std::error::Error>> {
20119 let f = self.func("gdn_prep_decode_f32");
20120 let cfg = LaunchConfig {
20121 grid_dim: (num_v as u32, 1, 1),
20122 block_dim: (32, 4, 1),
20123 shared_mem_bytes: 0,
20124 };
20125 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
20126 let __s_b = self.gpu.stream();
20127 let mut b = __s_b.launch_builder(&f);
20128 b.arg(conv_out)
20129 .arg(beta_raw)
20130 .arg(alpha)
20131 .arg(dt_bias)
20132 .arg(a)
20133 .arg(q_l2)
20134 .arg(k_l2)
20135 .arg(v_g)
20136 .arg(beta)
20137 .arg(g_log)
20138 .arg(&ds)
20139 .arg(&nv)
20140 .arg(&nk)
20141 .arg(&kd)
20142 .arg(&eps);
20143 unsafe {
20144 b.launch(cfg)?;
20145 }
20146 Ok(())
20147 }
20148
20149 #[allow(clippy::too_many_arguments)]
20153 pub fn ssm_conv1d_gdn(
20154 &self,
20155 qkv_tm: &CudaSlice<f32>,
20156 w: &CudaSlice<f32>,
20157 q_g: &mut CudaSlice<f32>,
20158 k_g: &mut CudaSlice<f32>,
20159 v_g: &mut CudaSlice<f32>,
20160 conv_dim: usize,
20161 t: usize,
20162 d_conv: usize,
20163 d_state: usize,
20164 num_v: usize,
20165 num_k: usize,
20166 key_dim: usize,
20167 ) -> Result<(), Box<dyn std::error::Error>> {
20168 let f = self.func("ssm_conv1d_gdn_f32");
20169 let cfg = LaunchConfig {
20170 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
20171 block_dim: (256, 1, 1),
20172 shared_mem_bytes: 0,
20173 };
20174 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20175 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
20176 let __s_b = self.gpu.stream();
20177 let mut b = __s_b.launch_builder(&f);
20178 b.arg(qkv_tm)
20179 .arg(w)
20180 .arg(q_g)
20181 .arg(k_g)
20182 .arg(v_g)
20183 .arg(&cd)
20184 .arg(&ti)
20185 .arg(&dc)
20186 .arg(&ds)
20187 .arg(&nv)
20188 .arg(&nk)
20189 .arg(&kd);
20190 unsafe {
20191 b.launch(cfg)?;
20192 }
20193 Ok(())
20194 }
20195
20196 pub fn ssm_conv1d(
20197 &self,
20198 x: &CudaSlice<f32>,
20199 w: &CudaSlice<f32>,
20200 y: &mut CudaSlice<f32>,
20201 conv_dim: usize,
20202 t: usize,
20203 d_conv: usize,
20204 silu: bool,
20205 ) -> Result<(), Box<dyn std::error::Error>> {
20206 let f = self.func("ssm_conv1d_silu_f32");
20207 let cfg = LaunchConfig {
20208 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
20209 block_dim: (256, 1, 1),
20210 shared_mem_bytes: 0,
20211 };
20212 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
20213 let __s_b = self.gpu.stream();
20214 let mut b = __s_b.launch_builder(&f);
20215 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
20216 unsafe {
20217 b.launch(cfg)?;
20218 }
20219 Ok(())
20220 }
20221
20222 pub fn gdn_scan_s128(
20225 &self,
20226 q: &CudaSlice<f32>,
20227 k: &CudaSlice<f32>,
20228 v: &CudaSlice<f32>,
20229 g: &CudaSlice<f32>,
20230 beta: &CudaSlice<f32>,
20231 state_in: &CudaSlice<f32>,
20232 state_out: &mut CudaSlice<f32>,
20233 o: &mut CudaSlice<f32>,
20234 n_head: usize,
20235 t: usize,
20236 scale: f32,
20237 ) -> Result<(), Box<dyn std::error::Error>> {
20238 let f = self.func("gdn_scan_s128");
20239 const S_V: u32 = 128;
20240 const WARP: u32 = 32;
20241 const COLS_PER_BLOCK: u32 = 4;
20242 let cfg = LaunchConfig {
20243 grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
20244 block_dim: (WARP, COLS_PER_BLOCK, 1),
20245 shared_mem_bytes: 0,
20246 };
20247 let (h, ti) = (n_head as i32, t as i32);
20248 let __s_b = self.gpu.stream();
20249 let mut b = __s_b.launch_builder(&f);
20250 b.arg(q)
20251 .arg(k)
20252 .arg(v)
20253 .arg(g)
20254 .arg(beta)
20255 .arg(state_in)
20256 .arg(state_out)
20257 .arg(o)
20258 .arg(&h)
20259 .arg(&ti)
20260 .arg(&scale);
20261 unsafe {
20262 b.launch(cfg)?;
20263 }
20264 Ok(())
20265 }
20266
20267 #[allow(clippy::too_many_arguments)]
20272 pub fn ssm_conv1d_fused_decode_b(
20273 &self,
20274 qkv_cols: &CudaSlice<f32>,
20275 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
20276 w: &CudaSlice<f32>,
20277 conv_outs: &mut CudaSlice<f32>,
20278 conv_dim: usize,
20279 d_conv: usize,
20280 b_n: usize,
20281 ) -> Result<(), Box<dyn std::error::Error>> {
20282 let f = self.func("ssm_conv1d_fused_decode_b_f32");
20283 let cfg = LaunchConfig {
20284 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
20285 block_dim: (256, 1, 1),
20286 shared_mem_bytes: 0,
20287 };
20288 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20289 let __s_b = self.gpu.stream();
20290 let mut b = __s_b.launch_builder(&f);
20291 b.arg(qkv_cols)
20292 .arg(conv_state_ptrs)
20293 .arg(w)
20294 .arg(conv_outs)
20295 .arg(&cd)
20296 .arg(&dc);
20297 unsafe {
20298 b.launch(cfg)?;
20299 }
20300 Ok(())
20301 }
20302
20303 #[allow(clippy::too_many_arguments)]
20304 pub fn gdn_prep_decode_b(
20305 &self,
20306 conv_outs: &CudaSlice<f32>,
20307 beta_raws: &CudaSlice<f32>,
20308 alphas: &CudaSlice<f32>,
20309 dt_bias: &CudaSlice<f32>,
20310 a: &CudaSlice<f32>,
20311 q_l2: &mut CudaSlice<f32>,
20312 k_l2: &mut CudaSlice<f32>,
20313 v_g: &mut CudaSlice<f32>,
20314 beta: &mut CudaSlice<f32>,
20315 g_log: &mut CudaSlice<f32>,
20316 d_state: usize,
20317 num_v: usize,
20318 num_k: usize,
20319 key_dim: usize,
20320 eps: f32,
20321 conv_dim: usize,
20322 b_n: usize,
20323 ) -> Result<(), Box<dyn std::error::Error>> {
20324 let f = self.func("gdn_prep_decode_b_f32");
20325 let cfg = LaunchConfig {
20326 grid_dim: (num_v as u32, 1, b_n as u32),
20327 block_dim: (32, 4, 1),
20328 shared_mem_bytes: 0,
20329 };
20330 let (ds, nv, nk, kd, cd) = (
20331 d_state as i32,
20332 num_v as i32,
20333 num_k as i32,
20334 key_dim as i32,
20335 conv_dim as i32,
20336 );
20337 let __s_b = self.gpu.stream();
20338 let mut b = __s_b.launch_builder(&f);
20339 b.arg(conv_outs)
20340 .arg(beta_raws)
20341 .arg(alphas)
20342 .arg(dt_bias)
20343 .arg(a)
20344 .arg(q_l2)
20345 .arg(k_l2)
20346 .arg(v_g)
20347 .arg(beta)
20348 .arg(g_log)
20349 .arg(&ds)
20350 .arg(&nv)
20351 .arg(&nk)
20352 .arg(&kd)
20353 .arg(&eps)
20354 .arg(&cd);
20355 unsafe {
20356 b.launch(cfg)?;
20357 }
20358 Ok(())
20359 }
20360
20361 #[allow(clippy::too_many_arguments)]
20362 pub fn gdn_scan_s128_batched(
20363 &self,
20364 q: &CudaSlice<f32>,
20365 k: &CudaSlice<f32>,
20366 v: &CudaSlice<f32>,
20367 g: &CudaSlice<f32>,
20368 beta: &CudaSlice<f32>,
20369 state_in_ptrs: &cudarc::driver::CudaView<u64>,
20370 state_out_ptrs: &cudarc::driver::CudaView<u64>,
20371 o: &mut CudaSlice<f32>,
20372 n_head: usize,
20373 b_n: usize,
20374 scale: f32,
20375 ) -> Result<(), Box<dyn std::error::Error>> {
20376 let f = self.func("gdn_scan_s128_b");
20377 const S_V: u32 = 128;
20378 const WARP: u32 = 32;
20379 const COLS_PER_BLOCK: u32 = 4;
20380 let cfg = LaunchConfig {
20381 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
20382 block_dim: (WARP, COLS_PER_BLOCK, 1),
20383 shared_mem_bytes: 0,
20384 };
20385 let h = n_head as i32;
20386 let __s_b = self.gpu.stream();
20387 let mut b = __s_b.launch_builder(&f);
20388 b.arg(q)
20389 .arg(k)
20390 .arg(v)
20391 .arg(g)
20392 .arg(beta)
20393 .arg(state_in_ptrs)
20394 .arg(state_out_ptrs)
20395 .arg(o)
20396 .arg(&h)
20397 .arg(&scale);
20398 unsafe {
20399 b.launch(cfg)?;
20400 }
20401 Ok(())
20402 }
20403
20404 #[allow(clippy::too_many_arguments)]
20410 pub fn ssm_conv1d_fused_decode_b_view(
20411 &self,
20412 qkv_cols: &cudarc::driver::CudaView<f32>,
20413 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
20414 w: &CudaSlice<f32>,
20415 conv_outs: &mut CudaSlice<f32>,
20416 conv_dim: usize,
20417 d_conv: usize,
20418 b_n: usize,
20419 ) -> Result<(), Box<dyn std::error::Error>> {
20420 let f = self.func("ssm_conv1d_fused_decode_b_f32");
20421 let cfg = LaunchConfig {
20422 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
20423 block_dim: (256, 1, 1),
20424 shared_mem_bytes: 0,
20425 };
20426 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20427 let __s_b = self.gpu.stream();
20428 let mut b = __s_b.launch_builder(&f);
20429 b.arg(qkv_cols)
20430 .arg(conv_state_ptrs)
20431 .arg(w)
20432 .arg(conv_outs)
20433 .arg(&cd)
20434 .arg(&dc);
20435 unsafe {
20436 b.launch(cfg)?;
20437 }
20438 Ok(())
20439 }
20440
20441 #[allow(clippy::too_many_arguments)]
20442 pub fn gdn_prep_decode_b_view(
20443 &self,
20444 conv_outs: &CudaSlice<f32>,
20445 beta_raws: &cudarc::driver::CudaView<f32>,
20446 alphas: &cudarc::driver::CudaView<f32>,
20447 dt_bias: &CudaSlice<f32>,
20448 a: &CudaSlice<f32>,
20449 q_l2: &mut CudaSlice<f32>,
20450 k_l2: &mut CudaSlice<f32>,
20451 v_g: &mut CudaSlice<f32>,
20452 beta: &mut CudaSlice<f32>,
20453 g_log: &mut CudaSlice<f32>,
20454 d_state: usize,
20455 num_v: usize,
20456 num_k: usize,
20457 key_dim: usize,
20458 eps: f32,
20459 conv_dim: usize,
20460 b_n: usize,
20461 ) -> Result<(), Box<dyn std::error::Error>> {
20462 let f = self.func("gdn_prep_decode_b_f32");
20463 let cfg = LaunchConfig {
20464 grid_dim: (num_v as u32, 1, b_n as u32),
20465 block_dim: (32, 4, 1),
20466 shared_mem_bytes: 0,
20467 };
20468 let (ds, nv, nk, kd, cd) = (
20469 d_state as i32,
20470 num_v as i32,
20471 num_k as i32,
20472 key_dim as i32,
20473 conv_dim as i32,
20474 );
20475 let __s_b = self.gpu.stream();
20476 let mut b = __s_b.launch_builder(&f);
20477 b.arg(conv_outs)
20478 .arg(beta_raws)
20479 .arg(alphas)
20480 .arg(dt_bias)
20481 .arg(a)
20482 .arg(q_l2)
20483 .arg(k_l2)
20484 .arg(v_g)
20485 .arg(beta)
20486 .arg(g_log)
20487 .arg(&ds)
20488 .arg(&nv)
20489 .arg(&nk)
20490 .arg(&kd)
20491 .arg(&eps)
20492 .arg(&cd);
20493 unsafe {
20494 b.launch(cfg)?;
20495 }
20496 Ok(())
20497 }
20498
20499 #[allow(clippy::too_many_arguments)]
20500 pub fn gdn_scan_s128_batched_view(
20501 &self,
20502 q: &CudaSlice<f32>,
20503 k: &CudaSlice<f32>,
20504 v: &CudaSlice<f32>,
20505 g: &CudaSlice<f32>,
20506 beta: &CudaSlice<f32>,
20507 state_in_ptrs: &cudarc::driver::CudaView<u64>,
20508 state_out_ptrs: &cudarc::driver::CudaView<u64>,
20509 o: &mut cudarc::driver::CudaViewMut<f32>,
20510 n_head: usize,
20511 b_n: usize,
20512 scale: f32,
20513 ) -> Result<(), Box<dyn std::error::Error>> {
20514 let f = self.func("gdn_scan_s128_b");
20515 const S_V: u32 = 128;
20516 const WARP: u32 = 32;
20517 const COLS_PER_BLOCK: u32 = 4;
20518 let cfg = LaunchConfig {
20519 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
20520 block_dim: (WARP, COLS_PER_BLOCK, 1),
20521 shared_mem_bytes: 0,
20522 };
20523 let h = n_head as i32;
20524 let __s_b = self.gpu.stream();
20525 let mut b = __s_b.launch_builder(&f);
20526 b.arg(q)
20527 .arg(k)
20528 .arg(v)
20529 .arg(g)
20530 .arg(beta)
20531 .arg(state_in_ptrs)
20532 .arg(state_out_ptrs)
20533 .arg(o)
20534 .arg(&h)
20535 .arg(&scale);
20536 unsafe {
20537 b.launch(cfg)?;
20538 }
20539 Ok(())
20540 }
20541
20542 pub fn gdn_chunked_enabled() -> bool {
20551 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
20552 *E.get_or_init(|| {
20553 std::env::var("MEMRA_GDN_CHUNKED")
20554 .map(|v| v != "0")
20555 .unwrap_or(true)
20556 })
20557 }
20558
20559 pub fn gdn_chunk_size() -> usize {
20564 static C: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
20565 *C.get_or_init(|| {
20566 let c: usize = std::env::var("MEMRA_GDN_CHUNK")
20567 .ok()
20568 .and_then(|v| v.parse().ok())
20569 .unwrap_or(32);
20570 c.clamp(32, 128) / 32 * 32
20571 })
20572 }
20573
20574 #[allow(clippy::too_many_arguments)]
20579 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
20582 #[allow(clippy::too_many_arguments)]
20583 pub fn gdn_chunk_k123(
20584 &self,
20585 q: &CudaSlice<f32>,
20586 k: &CudaSlice<f32>,
20587 v: &CudaSlice<f32>,
20588 g: &CudaSlice<f32>,
20589 beta: &CudaSlice<f32>,
20590 wb16: Option<&mut CudaSlice<u8>>,
20591 n_head: usize,
20592 t: usize,
20593 c: usize,
20594 hk: usize,
20595 k2w: Option<(&CudaSlice<u8>, &CudaSlice<u8>, &mut CudaSlice<u8>)>,
20596 ) -> Result<
20597 (
20598 CudaSlice<f32>,
20599 CudaSlice<f32>,
20600 CudaSlice<f32>,
20601 CudaSlice<f32>,
20602 ),
20603 Box<dyn std::error::Error>,
20604 > {
20605 const D: usize = 128;
20606 let h = n_head;
20607 let nc = (t + c - 1) / c;
20608 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
20609 let mut gcum = self.uninit(t * h)?;
20610 let mut a = self.uninit(nc * h * c * c)?;
20611 let mut p = self.uninit(nc * h * c * c)?;
20612 let mut u = self.uninit(nc * h * c * D)?;
20613 let mut w = self.uninit(nc * h * c * D)?;
20614 {
20615 let f = self.func("gdn_chunk_cumgate_f32");
20617 let cfg = LaunchConfig {
20618 grid_dim: (nc as u32, h as u32, 1),
20619 block_dim: (32, 1, 1),
20620 shared_mem_bytes: 0,
20621 };
20622 let __s_b = self.gpu.stream();
20623 let mut b = __s_b.launch_builder(&f);
20624 b.arg(g).arg(&mut gcum).arg(&hi).arg(&ti).arg(&ci);
20625 unsafe {
20626 b.launch(cfg)?;
20627 }
20628 }
20629 if let Some((qb, kb, pb)) = k2w {
20630 assert!(c == 32, "gdn_k2_wgmma is a C==32 tile");
20633 let f = self.func("gdn_k2_wgmma");
20634 let cfg = LaunchConfig {
20635 grid_dim: (nc as u32, h as u32, 1),
20636 block_dim: (128, 1, 1),
20637 shared_mem_bytes: 0,
20638 };
20639 let hki = hk as i32;
20640 let __s_b = self.gpu.stream();
20641 let mut b = __s_b.launch_builder(&f);
20642 b.arg(qb)
20643 .arg(kb)
20644 .arg(&gcum)
20645 .arg(beta)
20646 .arg(&mut a)
20647 .arg(&mut *pb)
20648 .arg(&hi)
20649 .arg(&ti)
20650 .arg(&ci)
20651 .arg(&hki);
20652 unsafe {
20653 b.launch(cfg)?;
20654 }
20655 } else if c <= 64 && !portable_mma_gated() {
20656 let f = self.func("gdn_chunk_attn_f32");
20658 let jt = ((c + 31) / 32) as u32;
20659 let cfg = LaunchConfig {
20660 grid_dim: (nc as u32, h as u32, jt),
20661 block_dim: (256, 1, 1),
20662 shared_mem_bytes: 0,
20663 };
20664 let hki = hk as i32;
20665 let __s_b = self.gpu.stream();
20666 let mut b = __s_b.launch_builder(&f);
20667 b.arg(q)
20668 .arg(k)
20669 .arg(&gcum)
20670 .arg(beta)
20671 .arg(&mut a)
20672 .arg(&mut p)
20673 .arg(&hi)
20674 .arg(&ti)
20675 .arg(&ci)
20676 .arg(&hki);
20677 unsafe {
20678 b.launch(cfg)?;
20679 }
20680 } else {
20681 assert!(
20683 hk == h,
20684 "generic K2 is broadcast-only (de-broadcast rides C==32)"
20685 );
20686 let f = self.func("gdn_chunk_attn_g_f32");
20687 let cfg = LaunchConfig {
20688 grid_dim: (nc as u32, h as u32, 1),
20689 block_dim: (32, 8, 1),
20690 shared_mem_bytes: 0,
20691 };
20692 let __s_b = self.gpu.stream();
20693 let mut b = __s_b.launch_builder(&f);
20694 b.arg(q)
20695 .arg(k)
20696 .arg(&gcum)
20697 .arg(beta)
20698 .arg(&mut a)
20699 .arg(&mut p)
20700 .arg(&hi)
20701 .arg(&ti)
20702 .arg(&ci);
20703 unsafe {
20704 b.launch(cfg)?;
20705 }
20706 }
20707 {
20708 let cfg = LaunchConfig {
20710 grid_dim: (nc as u32, h as u32, 1),
20711 block_dim: (256, 1, 1),
20712 shared_mem_bytes: 0,
20713 };
20714 match c {
20715 32 | 64 => {
20716 let f = self.func(if c == 32 {
20717 "gdn_chunk_solve32_f32"
20718 } else {
20719 "gdn_chunk_solve64_f32"
20720 });
20721 let wb: u64 = match wb16 {
20723 Some(d) => self.addr_u8(d),
20724 None => 0,
20725 };
20726 let hki = hk as i32;
20727 let __s_b = self.gpu.stream();
20728 let mut b = __s_b.launch_builder(&f);
20729 b.arg(v)
20730 .arg(k)
20731 .arg(&a)
20732 .arg(&gcum)
20733 .arg(&mut u)
20734 .arg(&mut w)
20735 .arg(&wb)
20736 .arg(&hi)
20737 .arg(&ti)
20738 .arg(&hki);
20739 unsafe {
20740 b.launch(cfg)?;
20741 }
20742 }
20743 _ => {
20744 assert!(hk == h, "generic K3 is broadcast-only");
20745 let f = self.func("gdn_chunk_solve_f32");
20746 let __s_b = self.gpu.stream();
20747 let mut b = __s_b.launch_builder(&f);
20748 b.arg(v)
20749 .arg(k)
20750 .arg(&a)
20751 .arg(&gcum)
20752 .arg(&mut u)
20753 .arg(&mut w)
20754 .arg(&hi)
20755 .arg(&ti)
20756 .arg(&ci);
20757 unsafe {
20758 b.launch(cfg)?;
20759 }
20760 }
20761 }
20762 }
20763 Ok((gcum, p, u, w))
20764 }
20765
20766 pub fn gdn_db_on() -> bool {
20770 std::env::var("MEMRA_GDN_DB").as_deref() != Ok("0")
20771 }
20772
20773 pub fn gdn_mma_enabled(&self, c: usize) -> bool {
20776 !portable_mma_gated()
20777 && c == 32
20778 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
20779 Ok("1") => true,
20780 Ok("0") => false,
20781 _ => cfg!(memra_hopper_mma),
20782 }
20783 }
20784
20785 pub fn gdn_wgmma_on(&self, c: usize) -> bool {
20788 self.gdn_mma_enabled(c)
20789 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
20790 Ok("0") => false,
20791 Ok("1") => true,
20792 _ => cfg!(memra_hopper_mma),
20793 }
20794 }
20795
20796 #[allow(clippy::too_many_arguments)]
20801 pub fn ssm_conv1d_gdn_state_pad(
20802 &self,
20803 qkv_tm: &cudarc::driver::CudaView<f32>,
20804 conv_state: &mut CudaSlice<f32>,
20805 w: &CudaSlice<f32>,
20806 q_g: &mut CudaSlice<f32>,
20807 k_g: &mut CudaSlice<f32>,
20808 v_g: &mut CudaSlice<f32>,
20809 conv_dim: usize,
20810 t: usize,
20811 d_conv: usize,
20812 d_state: usize,
20813 num_v: usize,
20814 num_k: usize,
20815 key_dim: usize,
20816 hk: usize,
20817 pad_len: Option<&CudaSlice<i32>>,
20818 ) -> Result<(), Box<dyn std::error::Error>> {
20819 assert!(
20820 t >= d_conv - 1,
20821 "fused state conv requires T >= pad (PRIME_MIN_T gates)"
20822 );
20823 {
20824 let f = self.func("ssm_conv1d_gdn_state_f32");
20825 let cfg = LaunchConfig {
20826 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
20827 block_dim: (256, 1, 1),
20828 shared_mem_bytes: 0,
20829 };
20830 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20831 let (ds, nv, nk, kd, hki) = (
20832 d_state as i32,
20833 num_v as i32,
20834 num_k as i32,
20835 key_dim as i32,
20836 hk as i32,
20837 );
20838 let __s_b = self.gpu.stream();
20839 let mut b = __s_b.launch_builder(&f);
20840 b.arg(qkv_tm)
20841 .arg(&*conv_state)
20842 .arg(w)
20843 .arg(q_g)
20844 .arg(k_g)
20845 .arg(v_g)
20846 .arg(&cd)
20847 .arg(&ti)
20848 .arg(&dc)
20849 .arg(&ds)
20850 .arg(&nv)
20851 .arg(&nk)
20852 .arg(&kd)
20853 .arg(&hki);
20854 unsafe {
20855 b.launch(cfg)?;
20856 }
20857 }
20858 match pad_len {
20859 Some(len_d) => {
20860 let f = self.func("ssm_conv_ring_update_dev_f32");
20861 let n = conv_dim * (d_conv - 1);
20862 let cfg = LaunchConfig::for_num_elems(n as u32);
20863 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20864 let __s_b = self.gpu.stream();
20865 let mut b = __s_b.launch_builder(&f);
20866 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
20867 unsafe {
20868 b.launch(cfg)?;
20869 }
20870 }
20871 None => {
20872 let f = self.func("ssm_conv_ring_update_f32");
20873 let n = conv_dim * (d_conv - 1);
20874 let cfg = LaunchConfig::for_num_elems(n as u32);
20875 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20876 let __s_b = self.gpu.stream();
20877 let mut b = __s_b.launch_builder(&f);
20878 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
20879 unsafe {
20880 b.launch(cfg)?;
20881 }
20882 }
20883 }
20884 Ok(())
20885 }
20886
20887 pub fn gdn_chunk_alloc(
20891 &self,
20892 n_head: usize,
20893 t: usize,
20894 c: usize,
20895 hk: usize,
20896 ) -> Result<GdnChunkBufs, Box<dyn std::error::Error>> {
20897 const D: usize = 128;
20898 assert!(
20899 c == 32,
20900 "gdn_chunk_alloc: varlen chain is the C==32 mma pair"
20901 );
20902 let h = n_head;
20903 let nc = (t + c - 1) / c;
20904 Ok(GdnChunkBufs {
20905 gcum: self.uninit(t * h)?,
20906 a: self.uninit(nc * h * c * c)?,
20907 p: self.uninit(nc * h * c * c)?,
20908 u: self.uninit(nc * h * c * D)?,
20909 w: self.uninit(nc * h * c * D)?,
20910 kb16: self.alloc_u8_uninit(t * hk * D * 2)?,
20911 wb16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
20912 y16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
20913 ssnap16: self.alloc_u8_uninit(nc * h * D * D * 2)?,
20914 qb16: self.alloc_u8_uninit(t * hk * D * 2)?,
20915 pb16: self.alloc_u8_uninit(nc * h * c * c * 2)?,
20916 o: self.uninit(D * h * t)?,
20917 t,
20918 nc,
20919 })
20920 }
20921
20922 pub fn f32_to_bf16_v(
20924 &self,
20925 x: &cudarc::driver::CudaView<f32>,
20926 dst: &mut CudaSlice<u8>,
20927 n: usize,
20928 ) -> Result<(), Box<dyn std::error::Error>> {
20929 let f = self.func("f32_to_bf16_bulk");
20930 let ni = n as i64;
20931 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
20932 let __s_b = self.gpu.stream();
20933 let mut b = __s_b.launch_builder(&f);
20934 b.arg(x).arg(dst).arg(&ni);
20935 unsafe {
20936 b.launch(cfg)?;
20937 }
20938 Ok(())
20939 }
20940
20941 pub fn f32_to_bf16_into(
20943 &self,
20944 x: &CudaSlice<f32>,
20945 dst: &mut CudaSlice<u8>,
20946 n: usize,
20947 ) -> Result<(), Box<dyn std::error::Error>> {
20948 let f = self.func("f32_to_bf16_bulk");
20949 let ni = n as i64;
20950 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
20951 let __s_b = self.gpu.stream();
20952 let mut b = __s_b.launch_builder(&f);
20953 b.arg(x).arg(dst).arg(&ni);
20954 unsafe {
20955 b.launch(cfg)?;
20956 }
20957 Ok(())
20958 }
20959
20960 pub fn gdn_chunk_k123_vl8(
20963 &self,
20964 seqs: &[GdnSeqVl],
20965 n_head: usize,
20966 hk: usize,
20967 wq: Option<&GdnWVl8>,
20968 ) -> Result<(), Box<dyn std::error::Error>> {
20969 let b = seqs.len();
20970 assert!(b >= 1 && b <= 8, "gdn_chunk_k123_vl8: 1..=8 sequences");
20971 let mut packed = [GdnSeqVl::default(); 8];
20972 packed[..b].copy_from_slice(seqs);
20973 let v = GdnVl8(packed);
20974 let (hi, ci) = (n_head as i32, 32i32);
20975 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
20976 {
20977 let f = self.func("gdn_chunk_cumgate_vl");
20978 let cfg = LaunchConfig {
20979 grid_dim: (max_nc, n_head as u32, b as u32),
20980 block_dim: (32, 1, 1),
20981 shared_mem_bytes: 0,
20982 };
20983 let __s_lb = self.gpu.stream();
20984 let mut lb = __s_lb.launch_builder(&f);
20985 lb.arg(&v).arg(&hi).arg(&ci);
20986 unsafe {
20987 lb.launch(cfg)?;
20988 }
20989 }
20990 let hki = hk as i32;
20991 if let Some(w) = wq {
20992 let f = self.func("gdn_k2_wgmma_vl");
20994 let cfg = LaunchConfig {
20995 grid_dim: (max_nc, n_head as u32, b as u32),
20996 block_dim: (128, 1, 1),
20997 shared_mem_bytes: 0,
20998 };
20999 let __s_lb = self.gpu.stream();
21000 let mut lb = __s_lb.launch_builder(&f);
21001 lb.arg(&v).arg(w).arg(&hi).arg(&ci).arg(&hki);
21002 unsafe {
21003 lb.launch(cfg)?;
21004 }
21005 } else {
21006 let f = self.func("gdn_chunk_attn_vl");
21007 let cfg = LaunchConfig {
21008 grid_dim: (max_nc, n_head as u32, b as u32),
21009 block_dim: (256, 1, 1),
21010 shared_mem_bytes: 0,
21011 };
21012 let __s_lb = self.gpu.stream();
21013 let mut lb = __s_lb.launch_builder(&f);
21014 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
21015 unsafe {
21016 lb.launch(cfg)?;
21017 }
21018 }
21019 {
21020 let f = self.func("gdn_chunk_solve32_vl");
21021 let cfg = LaunchConfig {
21022 grid_dim: (max_nc, n_head as u32, b as u32),
21023 block_dim: (256, 1, 1),
21024 shared_mem_bytes: 0,
21025 };
21026 let __s_lb = self.gpu.stream();
21027 let mut lb = __s_lb.launch_builder(&f);
21028 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
21029 unsafe {
21030 lb.launch(cfg)?;
21031 }
21032 }
21033 Ok(())
21034 }
21035
21036 #[allow(clippy::too_many_arguments)]
21040 pub fn gdn_prep_vl8(
21041 &self,
21042 seqs: &[GdnPrepVl],
21043 conv_w: &CudaSlice<f32>,
21044 dt_bias: &CudaSlice<f32>,
21045 a: &CudaSlice<f32>,
21046 conv_dim: usize,
21047 d_conv: usize,
21048 d_state: usize,
21049 num_v: usize,
21050 num_k: usize,
21051 key_dim: usize,
21052 hk: usize,
21053 eps: f32,
21054 ) -> Result<(), Box<dyn std::error::Error>> {
21055 let b = seqs.len();
21056 assert!(b >= 1 && b <= 8);
21057 let mut packed = [GdnPrepVl::default(); 8];
21058 packed[..b].copy_from_slice(seqs);
21059 let v = GdnPrepVl8(packed);
21060 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
21061 let (cdi, dci) = (conv_dim as i32, d_conv as i32);
21062 let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
21063 assert!(
21064 conv_fuse || hk == num_v,
21065 "de-broadcast requires the fused conv"
21066 );
21067 if conv_fuse {
21068 let f = self.func("ssm_conv1d_gdn_state_vl");
21069 let cfg = LaunchConfig {
21070 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
21071 block_dim: (256, 1, 1),
21072 shared_mem_bytes: 0,
21073 };
21074 let (dsi, nvi, nki, kdi, hki) = (
21075 d_state as i32,
21076 num_v as i32,
21077 num_k as i32,
21078 key_dim as i32,
21079 hk as i32,
21080 );
21081 let __s_lb = self.gpu.stream();
21082 let mut lb = __s_lb.launch_builder(&f);
21083 lb.arg(&v)
21084 .arg(conv_w)
21085 .arg(&cdi)
21086 .arg(&dci)
21087 .arg(&dsi)
21088 .arg(&nvi)
21089 .arg(&nki)
21090 .arg(&kdi)
21091 .arg(&hki);
21092 unsafe {
21093 lb.launch(cfg)?;
21094 }
21095 } else {
21096 let f = self.func("ssm_conv1d_tm_state_vl");
21097 let cfg = LaunchConfig {
21098 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
21099 block_dim: (256, 1, 1),
21100 shared_mem_bytes: 0,
21101 };
21102 let __s_lb = self.gpu.stream();
21103 let mut lb = __s_lb.launch_builder(&f);
21104 lb.arg(&v).arg(conv_w).arg(&cdi).arg(&dci);
21105 unsafe {
21106 lb.launch(cfg)?;
21107 }
21108 }
21109 {
21110 let f = self.func("ssm_conv_ring_update_vl");
21111 let n = (conv_dim * (d_conv - 1)) as u32;
21112 let cfg = LaunchConfig {
21113 grid_dim: (n.div_ceil(256), 1, b as u32),
21114 block_dim: (256, 1, 1),
21115 shared_mem_bytes: 0,
21116 };
21117 let __s_lb = self.gpu.stream();
21118 let mut lb = __s_lb.launch_builder(&f);
21119 lb.arg(&v).arg(&cdi).arg(&dci);
21120 unsafe {
21121 lb.launch(cfg)?;
21122 }
21123 }
21124 if !conv_fuse {
21125 let f = self.func("qkv_to_gdn_repack_vl");
21126 let n = max_t * (num_v * d_state) as u32;
21127 let cfg = LaunchConfig {
21128 grid_dim: (n.div_ceil(256), 1, b as u32),
21129 block_dim: (256, 1, 1),
21130 shared_mem_bytes: 0,
21131 };
21132 let (dsi, nvi, nki, kdi) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
21133 let __s_lb = self.gpu.stream();
21134 let mut lb = __s_lb.launch_builder(&f);
21135 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&nki).arg(&kdi);
21136 unsafe {
21137 lb.launch(cfg)?;
21138 }
21139 }
21140 if Self::l2_v2_on(d_state) {
21141 let f = self.func("gdn_l2_v2_vl");
21142 let cfg = LaunchConfig {
21143 grid_dim: ((max_t * hk as u32).div_ceil(8), 2, b as u32),
21144 block_dim: (256, 1, 1),
21145 shared_mem_bytes: 0,
21146 };
21147 let (dsi, nvi) = (d_state as i32, hk as i32);
21148 let __s_lb = self.gpu.stream();
21149 let mut lb = __s_lb.launch_builder(&f);
21150 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
21151 unsafe {
21152 lb.launch(cfg)?;
21153 }
21154 } else {
21155 let f = self.func("gdn_l2_vl");
21156 let cfg = LaunchConfig {
21157 grid_dim: (max_t * hk as u32, 2, b as u32),
21158 block_dim: (256, 1, 1),
21159 shared_mem_bytes: 0,
21160 };
21161 let (dsi, nvi) = (d_state as i32, hk as i32);
21162 let __s_lb = self.gpu.stream();
21163 let mut lb = __s_lb.launch_builder(&f);
21164 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
21165 unsafe {
21166 lb.launch(cfg)?;
21167 }
21168 }
21169 {
21170 let f = self.func("gdn_gate_prep_vl");
21171 let n = max_t * num_v as u32;
21172 let cfg = LaunchConfig {
21173 grid_dim: (n.div_ceil(256), 1, b as u32),
21174 block_dim: (256, 1, 1),
21175 shared_mem_bytes: 0,
21176 };
21177 let nvi = num_v as i32;
21178 let __s_lb = self.gpu.stream();
21179 let mut lb = __s_lb.launch_builder(&f);
21180 lb.arg(&v).arg(dt_bias).arg(a).arg(&nvi);
21181 unsafe {
21182 lb.launch(cfg)?;
21183 }
21184 }
21185 Ok(())
21186 }
21187
21188 pub fn gdn_mirror_vl8(
21190 &self,
21191 seqs: &[GdnSeqVl],
21192 n_head: usize,
21193 which: i32,
21194 hk: usize,
21195 ) -> Result<(), Box<dyn std::error::Error>> {
21196 let b = seqs.len();
21197 assert!(b >= 1 && b <= 8);
21198 let mut packed = [GdnSeqVl::default(); 8];
21199 packed[..b].copy_from_slice(seqs);
21200 let v = GdnVl8(packed);
21201 let ept = (if which == 0 { hk } else { n_head } * 128) as i32;
21202 let max_n = seqs
21203 .iter()
21204 .map(|s| {
21205 if which == 0 {
21206 s.t as i64 * ept as i64
21207 } else {
21208 s.nc as i64 * ept as i64 * 32
21209 }
21210 })
21211 .max()
21212 .unwrap();
21213 let f = self.func("gdn_mirror_vl");
21214 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
21215 let cfg = LaunchConfig {
21216 grid_dim: (blocks, 1, b as u32),
21217 block_dim: (256, 1, 1),
21218 shared_mem_bytes: 0,
21219 };
21220 let __s_lb = self.gpu.stream();
21221 let mut lb = __s_lb.launch_builder(&f);
21222 lb.arg(&v).arg(&ept).arg(&which);
21223 unsafe {
21224 lb.launch(cfg)?;
21225 }
21226 Ok(())
21227 }
21228
21229 pub fn gdn_tail_vl8(
21231 &self,
21232 seqs: &[GdnPrepVl],
21233 norm_w: &CudaSlice<f32>,
21234 d_state: usize,
21235 num_v: usize,
21236 eps: f32,
21237 ) -> Result<(), Box<dyn std::error::Error>> {
21238 let b = seqs.len();
21239 assert!(b >= 1 && b <= 8);
21240 let mut packed = [GdnPrepVl::default(); 8];
21241 packed[..b].copy_from_slice(seqs);
21242 let v = GdnPrepVl8(packed);
21243 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
21244 let f = self.func("gated_rmsnorm_f16out_vl");
21245 let cfg = LaunchConfig {
21247 grid_dim: (max_t * num_v as u32, 1, b as u32),
21248 block_dim: (128, 1, 1),
21249 shared_mem_bytes: 0,
21250 };
21251 let (dsi, nvi) = (d_state as i32, num_v as i32);
21252 let __s_lb = self.gpu.stream();
21253 let mut lb = __s_lb.launch_builder(&f);
21254 lb.arg(&v).arg(norm_w).arg(&dsi).arg(&nvi).arg(&eps);
21255 unsafe {
21256 lb.launch(cfg)?;
21257 }
21258 Ok(())
21259 }
21260
21261 pub fn addr_f32(&self, x: &CudaSlice<f32>) -> u64 {
21264 use cudarc::driver::DevicePtr;
21265 let s = self.gpu.stream();
21266 let (p, _g) = x.device_ptr(&s);
21267 p as u64
21268 }
21269 pub fn addr_f32_mut(&self, x: &mut CudaSlice<f32>) -> u64 {
21270 use cudarc::driver::DevicePtrMut;
21271 let s = self.gpu.stream();
21272 let (p, _g) = x.device_ptr_mut(&s);
21273 p as u64
21274 }
21275 pub fn addr_f32v(&self, x: &cudarc::driver::CudaView<f32>) -> u64 {
21276 use cudarc::driver::DevicePtr;
21277 let s = self.gpu.stream();
21278 let (p, _g) = x.device_ptr(&s);
21279 p as u64
21280 }
21281 pub fn addr_u8(&self, x: &CudaSlice<u8>) -> u64 {
21282 use cudarc::driver::DevicePtr;
21283 let s = self.gpu.stream();
21284 let (p, _g) = x.device_ptr(&s);
21285 p as u64
21286 }
21287
21288 pub fn gdn_chunk_vl8(
21292 &self,
21293 seqs: &[GdnSeqVl],
21294 n_head: usize,
21295 scale: f32,
21296 hk: usize,
21297 wq: Option<&GdnWVl8>,
21298 ) -> Result<(), Box<dyn std::error::Error>> {
21299 const NSPLIT: u32 = 4;
21300 let b = seqs.len();
21301 assert!(b >= 1 && b <= 8, "gdn_chunk_vl8: 1..=8 sequences");
21302 let mut packed = [GdnSeqVl::default(); 8];
21303 packed[..b].copy_from_slice(seqs);
21304 let v = GdnVl8(packed);
21305 let (hi, ci) = (n_head as i32, 32i32);
21306 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
21307 let hki = hk as i32;
21308 if let Some(w) = wq {
21309 let f = self.func("gdn_k45_wgmma_vl");
21311 let cfg = LaunchConfig {
21312 grid_dim: (n_head as u32, NSPLIT, b as u32),
21313 block_dim: (256, 1, 1),
21314 shared_mem_bytes: 0,
21315 };
21316 let __s_lb = self.gpu.stream();
21317 let mut lb = __s_lb.launch_builder(&f);
21318 lb.arg(&v).arg(w).arg(&scale).arg(&hi).arg(&ci).arg(&hki);
21319 unsafe {
21320 lb.launch(cfg)?;
21321 }
21322 let _ = max_nc;
21323 return Ok(());
21324 }
21325 {
21326 let f = self.func("gdn_chunk_state_mma_vl");
21327 let cfg = LaunchConfig {
21328 grid_dim: (n_head as u32, NSPLIT, b as u32),
21329 block_dim: (256, 1, 1),
21330 shared_mem_bytes: 0,
21331 };
21332 let __s_lb = self.gpu.stream();
21333 let mut lb = __s_lb.launch_builder(&f);
21334 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
21335 unsafe {
21336 lb.launch(cfg)?;
21337 }
21338 }
21339 {
21340 let f = self.func("gdn_chunk_output_mma_vl");
21341 let cfg = LaunchConfig {
21342 grid_dim: (max_nc, n_head as u32, b as u32),
21343 block_dim: (256, 1, 1),
21344 shared_mem_bytes: 0,
21345 };
21346 let __s_lb = self.gpu.stream();
21347 let mut lb = __s_lb.launch_builder(&f);
21348 lb.arg(&v).arg(&hi).arg(&ci).arg(&scale).arg(&hki);
21349 unsafe {
21350 lb.launch(cfg)?;
21351 }
21352 }
21353 Ok(())
21354 }
21355 pub fn gdn_scan_chunked(
21356 &self,
21357 q: &CudaSlice<f32>,
21358 k: &CudaSlice<f32>,
21359 v: &CudaSlice<f32>,
21360 g: &CudaSlice<f32>,
21361 beta: &CudaSlice<f32>,
21362 kb16_pre: Option<&CudaSlice<u8>>,
21363 qb16_pre: Option<&CudaSlice<u8>>,
21364 state_in: &CudaSlice<f32>,
21365 state_out: &mut CudaSlice<f32>,
21366 o: &mut CudaSlice<f32>,
21367 n_head: usize,
21368 t: usize,
21369 scale: f32,
21370 c: usize,
21371 hk: usize,
21372 ) -> Result<(), Box<dyn std::error::Error>> {
21373 const D: usize = 128;
21374 const NSPLIT: u32 = 4;
21375 assert!(c >= 1 && c <= 128, "gdn_scan_chunked: C must be in 1..=128");
21376 let h = n_head;
21377 let nc = (t + c - 1) / c;
21378 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
21379 let gdn_mma_pre = !portable_mma_gated()
21383 && c == 32
21384 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
21385 Ok("1") => true,
21386 Ok("0") => false,
21387 _ => cfg!(memra_hopper_mma),
21388 };
21389 let mut wb16_pre: Option<CudaSlice<u8>> = if gdn_mma_pre {
21390 Some(self.alloc_u8_uninit(nc * h * c * D * 2)?)
21391 } else {
21392 None
21393 };
21394 let gdn_wgmma_pre = gdn_mma_pre
21398 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
21399 Ok("0") => false,
21400 Ok("1") => true,
21401 _ => cfg!(memra_hopper_mma),
21402 };
21403 let nk = t * hk * D;
21404 let mut kb16_local: Option<CudaSlice<u8>> = None;
21405 if gdn_mma_pre && kb16_pre.is_none() {
21406 let mut kb = self.alloc_u8_uninit(nk * 2)?;
21407 let f = self.func("f32_to_bf16_bulk");
21408 let n2 = nk as i64;
21409 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
21410 let __s_b = self.gpu.stream();
21411 let mut b = __s_b.launch_builder(&f);
21412 b.arg(k).arg(&mut kb).arg(&n2);
21413 unsafe {
21414 b.launch(cfg2)?;
21415 }
21416 kb16_local = Some(kb);
21417 }
21418 let kb16_ref0: Option<&CudaSlice<u8>> = kb16_local.as_ref().or(kb16_pre);
21419 if let Some(kb) = kb16_pre {
21420 assert!(kb.len() >= nk * 2, "kb16_pre too small");
21421 }
21422 let mut qb16: Option<CudaSlice<u8>> = None;
21423 let mut pb16: Option<CudaSlice<u8>> = None;
21424 if gdn_wgmma_pre {
21425 if qb16_pre.is_none() {
21428 let mut qb = self.alloc_u8_uninit(nk * 2)?;
21429 let f = self.func("f32_to_bf16_bulk");
21430 let n2 = nk as i64;
21431 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
21432 let __s_b = self.gpu.stream();
21433 let mut b = __s_b.launch_builder(&f);
21434 b.arg(q).arg(&mut qb).arg(&n2);
21435 unsafe {
21436 b.launch(cfg2)?;
21437 }
21438 qb16 = Some(qb);
21439 } else if let Some(qb) = qb16_pre {
21440 assert!(qb.len() >= nk * 2, "qb16_pre too small");
21441 }
21442 pb16 = Some(self.alloc_u8_uninit(nc * h * c * c * 2)?);
21443 }
21444 let qb16_ref0: Option<&CudaSlice<u8>> = qb16.as_ref().or(qb16_pre);
21445 let k2w = if gdn_wgmma_pre {
21446 Some((
21447 *qb16_ref0.as_ref().unwrap(),
21448 *kb16_ref0.as_ref().unwrap(),
21449 pb16.as_mut().unwrap(),
21450 ))
21451 } else {
21452 None
21453 };
21454 let (gcum, p, u, w) =
21455 self.gdn_chunk_k123(q, k, v, g, beta, wb16_pre.as_mut(), n_head, t, c, hk, k2w)?;
21456 let _ = &w;
21457 let mut y = self.uninit(nc * h * c * D)?;
21458 let mut ssnap = self.uninit(nc * h * D * D)?; let gdn_mma = !portable_mma_gated()
21470 && c == 32
21471 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
21472 Ok("1") => true,
21473 Ok("0") => false,
21474 _ => cfg!(memra_hopper_mma),
21475 };
21476 if gdn_mma {
21477 let wb16 = wb16_pre
21478 .take()
21479 .expect("mma path pre-allocates wb16 (K3 store fold)");
21480 let kb16_ref: &CudaSlice<u8> = kb16_ref0.expect("mma path pre-builds kb16 above K123");
21481 if gdn_wgmma_pre {
21493 let qb16 = qb16_ref0.unwrap();
21495 let pb16 = pb16.as_ref().unwrap();
21496 {
21497 let f = self.func("gdn_k45_wgmma");
21498 let cfg = LaunchConfig {
21499 grid_dim: (h as u32, 4, 1),
21500 block_dim: (256, 1, 1),
21501 shared_mem_bytes: 0,
21502 };
21503 let hki = hk as i32;
21504 let __s_b = self.gpu.stream();
21505 let mut b = __s_b.launch_builder(&f);
21506 b.arg(kb16_ref)
21507 .arg(&gcum)
21508 .arg(beta)
21509 .arg(&u)
21510 .arg(&wb16)
21511 .arg(qb16)
21512 .arg(pb16)
21513 .arg(o)
21514 .arg(&scale)
21515 .arg(state_in)
21516 .arg(&mut *state_out)
21517 .arg(&hi)
21518 .arg(&ti)
21519 .arg(&ci)
21520 .arg(&hki);
21521 unsafe {
21522 b.launch(cfg)?;
21523 }
21524 }
21525 return Ok(());
21526 }
21527 let mut y16 = self.alloc_u8_uninit(nc * h * c * D * 2)?;
21531 let mut ssnap16 = self.alloc_u8_uninit(nc * h * D * D * 2)?;
21532 {
21533 let f = self.func("gdn_chunk_state_mma");
21534 let cfg = LaunchConfig {
21535 grid_dim: (h as u32, NSPLIT, 1),
21536 block_dim: (256, 1, 1),
21537 shared_mem_bytes: 0,
21538 };
21539 let hki = hk as i32;
21540 let __s_b = self.gpu.stream();
21541 let mut b = __s_b.launch_builder(&f);
21542 b.arg(kb16_ref)
21543 .arg(&gcum)
21544 .arg(beta)
21545 .arg(&u)
21546 .arg(&wb16)
21547 .arg(&mut y16)
21548 .arg(&mut ssnap16)
21549 .arg(state_in)
21550 .arg(&mut *state_out)
21551 .arg(&hi)
21552 .arg(&ti)
21553 .arg(&ci)
21554 .arg(&hki);
21555 unsafe {
21556 b.launch(cfg)?;
21557 }
21558 }
21559 {
21560 let f = self.func("gdn_chunk_output_mma");
21562 let jt = ((c + 31) / 32) as u32;
21563 let cfg = LaunchConfig {
21564 grid_dim: (nc as u32, h as u32, jt),
21565 block_dim: (256, 1, 1),
21566 shared_mem_bytes: 0,
21567 };
21568 let hki = hk as i32;
21569 let __s_b = self.gpu.stream();
21570 let mut b = __s_b.launch_builder(&f);
21571 b.arg(q)
21572 .arg(&gcum)
21573 .arg(&p)
21574 .arg(&y16)
21575 .arg(&ssnap16)
21576 .arg(o)
21577 .arg(&hi)
21578 .arg(&ti)
21579 .arg(&ci)
21580 .arg(&scale)
21581 .arg(&hki);
21582 unsafe {
21583 b.launch(cfg)?;
21584 }
21585 }
21586 return Ok(());
21587 }
21588 {
21589 let f = self.func("gdn_chunk_state_f32");
21591 let cfg = LaunchConfig {
21592 grid_dim: (h as u32, NSPLIT, 1),
21593 block_dim: (256, 1, 1),
21594 shared_mem_bytes: 0,
21595 };
21596 let __s_b = self.gpu.stream();
21597 let mut b = __s_b.launch_builder(&f);
21598 b.arg(k)
21599 .arg(&gcum)
21600 .arg(beta)
21601 .arg(&u)
21602 .arg(&w)
21603 .arg(&mut y)
21604 .arg(&mut ssnap)
21605 .arg(state_in)
21606 .arg(&mut *state_out)
21607 .arg(&hi)
21608 .arg(&ti)
21609 .arg(&ci);
21610 unsafe {
21611 b.launch(cfg)?;
21612 }
21613 }
21614 {
21615 let f = self.func("gdn_chunk_output_f32");
21617 let jt = ((c + 31) / 32) as u32;
21618 let cfg = LaunchConfig {
21619 grid_dim: (nc as u32, h as u32, jt),
21620 block_dim: (256, 1, 1),
21621 shared_mem_bytes: 0,
21622 };
21623 let __s_b = self.gpu.stream();
21624 let mut b = __s_b.launch_builder(&f);
21625 b.arg(q)
21626 .arg(&gcum)
21627 .arg(&p)
21628 .arg(&y)
21629 .arg(&ssnap)
21630 .arg(o)
21631 .arg(&hi)
21632 .arg(&ti)
21633 .arg(&ci)
21634 .arg(&scale);
21635 unsafe {
21636 b.launch(cfg)?;
21637 }
21638 }
21639 Ok(())
21640 }
21641
21642 #[allow(clippy::too_many_arguments)]
21651 #[allow(clippy::too_many_arguments)]
21652 pub fn gdn_scan_prefill(
21653 &self,
21654 q: &CudaSlice<f32>,
21655 k: &CudaSlice<f32>,
21656 v: &CudaSlice<f32>,
21657 g: &CudaSlice<f32>,
21658 beta: &CudaSlice<f32>,
21659 kb16_pre: Option<&CudaSlice<u8>>,
21660 qb16_pre: Option<&CudaSlice<u8>>,
21661 state_in: &CudaSlice<f32>,
21662 state_out: &mut CudaSlice<f32>,
21663 o: &mut CudaSlice<f32>,
21664 n_head: usize,
21665 t: usize,
21666 scale: f32,
21667 hk: usize,
21668 ) -> Result<(), Box<dyn std::error::Error>> {
21669 if std::env::var("MEMRA_GDN_DIFF").is_ok() && t >= 16 {
21670 assert!(hk == n_head, "GDN_DIFF oracle is broadcast-only");
21671 return self.gdn_scan_diff(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale);
21672 }
21673 if Self::gdn_chunked_enabled() && t >= 16 {
21674 self.gdn_scan_chunked(
21675 q,
21676 k,
21677 v,
21678 g,
21679 beta,
21680 kb16_pre,
21681 qb16_pre,
21682 state_in,
21683 state_out,
21684 o,
21685 n_head,
21686 t,
21687 scale,
21688 Self::gdn_chunk_size(),
21689 hk,
21690 )
21691 } else {
21692 assert!(
21693 hk == n_head,
21694 "s128 scan is broadcast-only (prep guarantees by predicate)"
21695 );
21696 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)
21697 }
21698 }
21699
21700 #[allow(clippy::too_many_arguments)]
21702 fn gdn_scan_diff(
21703 &self,
21704 q: &CudaSlice<f32>,
21705 k: &CudaSlice<f32>,
21706 v: &CudaSlice<f32>,
21707 g: &CudaSlice<f32>,
21708 beta: &CudaSlice<f32>,
21709 state_in: &CudaSlice<f32>,
21710 state_out: &mut CudaSlice<f32>,
21711 o: &mut CudaSlice<f32>,
21712 n_head: usize,
21713 t: usize,
21714 scale: f32,
21715 ) -> Result<(), Box<dyn std::error::Error>> {
21716 static CALL: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
21717 let call = CALL.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
21718 let mut o_c = self.uninit(o.len())?;
21719 let mut st_c = self.uninit(state_out.len())?;
21720 self.gdn_scan_chunked(
21721 q,
21722 k,
21723 v,
21724 g,
21725 beta,
21726 None,
21727 None,
21728 state_in,
21729 &mut st_c,
21730 &mut o_c,
21731 n_head,
21732 t,
21733 scale,
21734 Self::gdn_chunk_size(),
21735 n_head,
21736 )?;
21737 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)?;
21738 let (oh_s, oh_c) = (self.dtoh(o)?, self.dtoh(&o_c)?);
21739 let (sh_s, sh_c) = (self.dtoh(state_out)?, self.dtoh(&st_c)?);
21740 let stats = |a: &[f32], b: &[f32]| -> (f32, f32, f64) {
21741 let mut max_abs = 0f32;
21742 let mut max_rel = 0f32;
21743 let mut sum_rel = 0f64;
21744 for (x, y) in a.iter().zip(b) {
21745 let ad = (x - y).abs();
21746 let rel = ad / x.abs().max(y.abs()).max(1e-3);
21747 if ad > max_abs {
21748 max_abs = ad;
21749 }
21750 if rel > max_rel {
21751 max_rel = rel;
21752 }
21753 sum_rel += rel as f64;
21754 }
21755 (max_abs, max_rel, sum_rel / a.len() as f64)
21756 };
21757 let (o_ma, o_mr, o_mean) = stats(&oh_s, &oh_c);
21758 let (s_ma, s_mr, s_mean) = stats(&sh_s, &sh_c);
21759 println!(
21760 "[gdn-diff call {call:3} T={t} C={}] out: max_abs={o_ma:.3e} max_rel={o_mr:.3e} mean_rel={o_mean:.3e} | \
21761 state: max_abs={s_ma:.3e} max_rel={s_mr:.3e} mean_rel={s_mean:.3e}",
21762 Self::gdn_chunk_size()
21763 );
21764 Ok(())
21765 }
21766
21767 pub fn gdn_glog(
21769 &self,
21770 alpha: &CudaSlice<f32>,
21771 dt_bias: &CudaSlice<f32>,
21772 a: &CudaSlice<f32>,
21773 g_log: &mut CudaSlice<f32>,
21774 n_head: usize,
21775 t: usize,
21776 ) -> Result<(), Box<dyn std::error::Error>> {
21777 let f = self.func("gdn_glog_f32");
21778 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
21779 let (h, ti) = (n_head as i32, t as i32);
21780 let __s_b = self.gpu.stream();
21781 let mut b = __s_b.launch_builder(&f);
21782 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
21783 unsafe {
21784 b.launch(cfg)?;
21785 }
21786 Ok(())
21787 }
21788
21789 pub fn sigmoid_v(
21792 &self,
21793 x: &cudarc::driver::CudaView<f32>,
21794 y: &mut CudaSlice<f32>,
21795 n: usize,
21796 ) -> Result<(), Box<dyn std::error::Error>> {
21797 let f = self.func("sigmoid_f32");
21798 let cfg = LaunchConfig::for_num_elems(n as u32);
21799 let ni = n as i32;
21800 let __s_b = self.gpu.stream();
21801 let mut b = __s_b.launch_builder(&f);
21802 b.arg(x).arg(y).arg(&ni);
21803 unsafe {
21804 b.launch(cfg)?;
21805 }
21806 Ok(())
21807 }
21808
21809 pub fn gdn_glog_v(
21810 &self,
21811 alpha: &cudarc::driver::CudaView<f32>,
21812 dt_bias: &CudaSlice<f32>,
21813 a: &CudaSlice<f32>,
21814 g_log: &mut CudaSlice<f32>,
21815 n_head: usize,
21816 t: usize,
21817 ) -> Result<(), Box<dyn std::error::Error>> {
21818 let f = self.func("gdn_glog_f32");
21819 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
21820 let (h, ti) = (n_head as i32, t as i32);
21821 let __s_b = self.gpu.stream();
21822 let mut b = __s_b.launch_builder(&f);
21823 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
21824 unsafe {
21825 b.launch(cfg)?;
21826 }
21827 Ok(())
21828 }
21829
21830 pub fn sigmoid(
21831 &self,
21832 x: &CudaSlice<f32>,
21833 y: &mut CudaSlice<f32>,
21834 n: usize,
21835 ) -> Result<(), Box<dyn std::error::Error>> {
21836 let f = self.func("sigmoid_f32");
21837 let cfg = LaunchConfig::for_num_elems(n as u32);
21838 let ni = n as i32;
21839 let __s_b = self.gpu.stream();
21840 let mut b = __s_b.launch_builder(&f);
21841 b.arg(x).arg(y).arg(&ni);
21842 unsafe {
21843 b.launch(cfg)?;
21844 }
21845 Ok(())
21846 }
21847
21848 pub fn sig_mul_f16out(
21851 &self,
21852 a: &CudaSlice<f32>,
21853 g: &CudaSlice<f32>,
21854 dst: &mut CudaSlice<f32>,
21855 dst16: &mut CudaSlice<u8>,
21856 n: usize,
21857 ) -> Result<(), Box<dyn std::error::Error>> {
21858 let f = self.func("sig_mul_f16out_f32");
21859 let cfg = LaunchConfig::for_num_elems(n as u32);
21860 let ni = n as i32;
21861 let __s_b = self.gpu.stream();
21862 let mut b = __s_b.launch_builder(&f);
21863 b.arg(a).arg(g).arg(dst).arg(dst16).arg(&ni);
21864 unsafe {
21865 b.launch(cfg)?;
21866 }
21867 Ok(())
21868 }
21869
21870 #[allow(clippy::too_many_arguments)]
21879 pub fn attn_head_gate(
21880 &self,
21881 a: &CudaSlice<f32>,
21882 g: &CudaSlice<f32>,
21883 dst: &mut CudaSlice<f32>,
21884 dst16: Option<&mut CudaSlice<u8>>,
21885 head_dim: usize,
21886 n_head: usize,
21887 t: usize,
21888 ) -> Result<(), Box<dyn std::error::Error>> {
21889 let f = self.func("attn_head_gate_f32");
21890 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
21891 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
21892 let d16: u64 = match dst16 {
21894 Some(d) => self.addr_u8(d),
21895 None => 0,
21896 };
21897 let __s_b = self.gpu.stream();
21898 let mut b = __s_b.launch_builder(&f);
21899 b.arg(a)
21900 .arg(g)
21901 .arg(dst)
21902 .arg(&d16)
21903 .arg(&hd)
21904 .arg(&nh)
21905 .arg(&ti);
21906 unsafe {
21907 b.launch(cfg)?;
21908 }
21909 Ok(())
21910 }
21911
21912 #[allow(clippy::too_many_arguments)]
21921 pub fn swiglu_clamped_mul_scaled(
21922 &self,
21923 gate: &CudaSlice<f32>,
21924 up: &CudaSlice<f32>,
21925 gs: f32,
21926 us: f32,
21927 limit: f32,
21928 dst: &mut CudaSlice<f32>,
21929 n: usize,
21930 ) -> Result<(), Box<dyn std::error::Error>> {
21931 debug_assert!(
21932 limit > 1e-6,
21933 "swiglu_clamped needs a live limit; use silu_mul_scaled"
21934 );
21935 let f = self.func("swiglu_clamped_mul_scaled_f32");
21936 let cfg = LaunchConfig::for_num_elems(n as u32);
21937 let ni = n as i32;
21938 let __s_b = self.gpu.stream();
21939 let mut b = __s_b.launch_builder(&f);
21940 b.arg(gate)
21941 .arg(up)
21942 .arg(&gs)
21943 .arg(&us)
21944 .arg(&limit)
21945 .arg(dst)
21946 .arg(&ni);
21947 unsafe {
21948 b.launch(cfg)?;
21949 }
21950 Ok(())
21951 }
21952
21953 pub fn gated_rmsnorm(
21955 &self,
21956 o: &CudaSlice<f32>,
21957 w: &CudaSlice<f32>,
21958 z: &CudaSlice<f32>,
21959 dst: &mut CudaSlice<f32>,
21960 ncols: usize,
21961 nrows: usize,
21962 eps: f32,
21963 ) -> Result<(), Box<dyn std::error::Error>> {
21964 let f = self.func("gated_rmsnorm_f32");
21965 let cfg = LaunchConfig {
21966 grid_dim: (nrows as u32, 1, 1),
21967 block_dim: (128, 1, 1),
21968 shared_mem_bytes: 0,
21969 };
21970 let (nc, e) = (ncols as i32, eps);
21971 let __s_b = self.gpu.stream();
21972 let mut b = __s_b.launch_builder(&f);
21973 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
21974 unsafe {
21975 b.launch(cfg)?;
21976 }
21977 Ok(())
21978 }
21979
21980 pub fn gated_rmsnorm_f16out(
21983 &self,
21984 o: &CudaSlice<f32>,
21985 w: &CudaSlice<f32>,
21986 z: &CudaSlice<f32>,
21987 dst: &mut CudaSlice<f32>,
21988 dst16: &mut CudaSlice<u8>,
21989 ncols: usize,
21990 nrows: usize,
21991 eps: f32,
21992 ) -> Result<(), Box<dyn std::error::Error>> {
21993 let f = self.func("gated_rmsnorm_f16out_f32");
21994 let cfg = LaunchConfig {
21996 grid_dim: (nrows as u32, 1, 1),
21997 block_dim: (128, 1, 1),
21998 shared_mem_bytes: 0,
21999 };
22000 let (nc, e) = (ncols as i32, eps);
22001 let __s_b = self.gpu.stream();
22002 let mut b = __s_b.launch_builder(&f);
22003 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
22004 unsafe {
22005 b.launch(cfg)?;
22006 }
22007 Ok(())
22008 }
22009
22010 #[allow(clippy::too_many_arguments)]
22014 pub fn add_rms_norm_zq8(
22015 &self,
22016 a: &CudaSlice<f32>,
22017 b_in: &CudaSlice<f32>,
22018 w: &CudaSlice<f32>,
22019 res: &mut CudaSlice<f32>,
22020 z: &mut CudaSlice<f32>,
22021 ncols: usize,
22022 nrows: usize,
22023 eps: f32,
22024 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
22025 assert!(ncols % 32 == 0);
22026 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
22027 let mut d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
22028 let f = self.func("add_rms_norm_zq8");
22029 let cfg = LaunchConfig {
22030 grid_dim: (nrows as u32, 1, 1),
22031 block_dim: (1024, 1, 1),
22032 shared_mem_bytes: 0,
22033 };
22034 let (nc, ep) = (ncols as i32, eps);
22035 let __s_b = self.gpu.stream();
22036 let mut b = __s_b.launch_builder(&f);
22037 b.arg(a)
22038 .arg(b_in)
22039 .arg(w)
22040 .arg(res)
22041 .arg(z)
22042 .arg(&mut q)
22043 .arg(&mut d)
22044 .arg(&nc)
22045 .arg(&ep);
22046 unsafe {
22047 b.launch(cfg)?;
22048 }
22049 Ok((q, d))
22050 }
22051
22052 pub fn gated_rmsnorm_zv(
22057 &self,
22058 o: &CudaSlice<f32>,
22059 w: &CudaSlice<f32>,
22060 z: &cudarc::driver::CudaView<f32>,
22061 dst: &mut CudaSlice<f32>,
22062 ncols: usize,
22063 nrows: usize,
22064 eps: f32,
22065 ) -> Result<(), Box<dyn std::error::Error>> {
22066 let f = self.func("gated_rmsnorm_f32");
22067 let cfg = LaunchConfig {
22068 grid_dim: (nrows as u32, 1, 1),
22069 block_dim: (128, 1, 1),
22070 shared_mem_bytes: 0,
22071 };
22072 let (nc, e) = (ncols as i32, eps);
22073 let __s_b = self.gpu.stream();
22074 let mut b = __s_b.launch_builder(&f);
22075 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
22076 unsafe {
22077 b.launch(cfg)?;
22078 }
22079 Ok(())
22080 }
22081
22082 pub fn gated_rmsnorm_f16out_zv(
22083 &self,
22084 o: &CudaSlice<f32>,
22085 w: &CudaSlice<f32>,
22086 z: &cudarc::driver::CudaView<f32>,
22087 dst: &mut CudaSlice<f32>,
22088 dst16: &mut CudaSlice<u8>,
22089 ncols: usize,
22090 nrows: usize,
22091 eps: f32,
22092 ) -> Result<(), Box<dyn std::error::Error>> {
22093 let f = self.func("gated_rmsnorm_f16out_f32");
22094 let cfg = LaunchConfig {
22096 grid_dim: (nrows as u32, 1, 1),
22097 block_dim: (128, 1, 1),
22098 shared_mem_bytes: 0,
22099 };
22100 let (nc, e) = (ncols as i32, eps);
22101 let __s_b = self.gpu.stream();
22102 let mut b = __s_b.launch_builder(&f);
22103 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
22104 unsafe {
22105 b.launch(cfg)?;
22106 }
22107 Ok(())
22108 }
22109
22110 pub fn gated_rmsnorm_q8_1(
22111 &self,
22112 o: &CudaSlice<f32>,
22113 w: &CudaSlice<f32>,
22114 z: &CudaSlice<f32>,
22115 ncols: usize,
22116 nrows: usize,
22117 eps: f32,
22118 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
22119 assert!(ncols % 32 == 0);
22120 let f = self.func("gated_rmsnorm_q8_1");
22121 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
22122 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
22123 let cfg = LaunchConfig {
22124 grid_dim: (nrows as u32, 1, 1),
22125 block_dim: (128, 1, 1),
22126 shared_mem_bytes: 0,
22127 };
22128 let (nc, ep) = (ncols as i32, eps);
22129 let __s_b = self.gpu.stream();
22130 let mut b = __s_b.launch_builder(&f);
22131 b.arg(o)
22132 .arg(w)
22133 .arg(z)
22134 .arg(&mut out_q)
22135 .arg(&mut out_d)
22136 .arg(&nc)
22137 .arg(&ep);
22138 unsafe {
22139 b.launch(cfg)?;
22140 }
22141 Ok((out_q, out_d))
22142 }
22143
22144 pub fn transpose(
22146 &self,
22147 inp: &CudaSlice<f32>,
22148 rows: usize,
22149 cols: usize,
22150 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22151 let f = self.func("transpose_f32");
22152 let mut out = self.zeros(rows * cols)?;
22153 let cfg = LaunchConfig::for_num_elems((rows * cols) as u32);
22154 let (r, c) = (rows as i32, cols as i32);
22155 let __s_b = self.gpu.stream();
22156 let mut b = __s_b.launch_builder(&f);
22157 b.arg(inp).arg(&mut out).arg(&r).arg(&c);
22158 unsafe {
22159 b.launch(cfg)?;
22160 }
22161 Ok(out)
22162 }
22163
22164 pub fn repeat_heads(
22166 &self,
22167 inp: &CudaSlice<f32>,
22168 out: &mut CudaSlice<f32>,
22169 head_dim: usize,
22170 n_in: usize,
22171 n_out: usize,
22172 t: usize,
22173 ) -> Result<(), Box<dyn std::error::Error>> {
22174 let f = self.func("repeat_heads_f32");
22175 let cfg = LaunchConfig::for_num_elems((head_dim * n_out * t) as u32);
22176 let (hd, ni, no, ti) = (head_dim as i32, n_in as i32, n_out as i32, t as i32);
22177 let __s_b = self.gpu.stream();
22178 let mut b = __s_b.launch_builder(&f);
22179 b.arg(inp).arg(out).arg(&hd).arg(&ni).arg(&no).arg(&ti);
22180 unsafe {
22181 b.launch(cfg)?;
22182 }
22183 Ok(())
22184 }
22185
22186 pub fn q_gate_split(
22193 &self,
22194 qf: &CudaSlice<f32>,
22195 q_out: &mut CudaSlice<f32>,
22196 gate_out: &mut CudaSlice<f32>,
22197 head_dim: usize,
22198 n_head: usize,
22199 t: usize,
22200 ) -> Result<(), Box<dyn std::error::Error>> {
22201 memra_gguf::config::check_fused_q_gate_extent(qf.len(), head_dim, n_head, t)?;
22202 let out_need = head_dim * n_head * t;
22203 if q_out.len() < out_need || gate_out.len() < out_need {
22204 return Err(format!(
22205 "q_gate_split destinations too small: need {out_need} each, have q={} gate={}",
22206 q_out.len(),
22207 gate_out.len()
22208 )
22209 .into());
22210 }
22211 let f = self.func("q_gate_split_f32");
22212 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
22213 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
22214 let __s_b = self.gpu.stream();
22215 let mut b = __s_b.launch_builder(&f);
22216 b.arg(qf)
22217 .arg(q_out)
22218 .arg(gate_out)
22219 .arg(&hd)
22220 .arg(&nh)
22221 .arg(&ti);
22222 unsafe {
22223 b.launch(cfg)?;
22224 }
22225 Ok(())
22226 }
22227
22228 pub fn qkv_to_gdn_repack(
22232 &self,
22233 conv_out: &CudaSlice<f32>,
22234 q_g: &mut CudaSlice<f32>,
22235 k_g: &mut CudaSlice<f32>,
22236 v_g: &mut CudaSlice<f32>,
22237 d_state: usize,
22238 num_v: usize,
22239 num_k: usize,
22240 key_dim: usize,
22241 t: usize,
22242 ) -> Result<(), Box<dyn std::error::Error>> {
22243 let f = self.func("qkv_to_gdn_repack_f32");
22244 let cfg = LaunchConfig::for_num_elems((d_state * num_v * t) as u32);
22245 let (ds, nv, nk, kd, ti) = (
22246 d_state as i32,
22247 num_v as i32,
22248 num_k as i32,
22249 key_dim as i32,
22250 t as i32,
22251 );
22252 let __s_b = self.gpu.stream();
22253 let mut b = __s_b.launch_builder(&f);
22254 b.arg(conv_out)
22255 .arg(q_g)
22256 .arg(k_g)
22257 .arg(v_g)
22258 .arg(&ds)
22259 .arg(&nv)
22260 .arg(&nk)
22261 .arg(&kd)
22262 .arg(&ti);
22263 unsafe {
22264 b.launch(cfg)?;
22265 }
22266 Ok(())
22267 }
22268
22269 pub fn conv_left_pad(
22272 &self,
22273 src: &CudaSlice<f32>,
22274 dst: &mut CudaSlice<f32>,
22275 conv_dim: usize,
22276 t: usize,
22277 pad: usize,
22278 ) -> Result<(), Box<dyn std::error::Error>> {
22279 let f = self.func("conv_left_pad_f32");
22280 let cfg = LaunchConfig::for_num_elems((conv_dim * t) as u32);
22281 let (cd, ti, p) = (conv_dim as i32, t as i32, pad as i32);
22282 let __s_b = self.gpu.stream();
22283 let mut b = __s_b.launch_builder(&f);
22284 b.arg(src).arg(dst).arg(&cd).arg(&ti).arg(&p);
22285 unsafe {
22286 b.launch(cfg)?;
22287 }
22288 Ok(())
22289 }
22290
22291 pub fn conv_assemble_and_roll(
22295 &self,
22296 qkv_col: &CudaSlice<f32>,
22297 conv_state: &mut CudaSlice<f32>,
22298 conv_in: &mut CudaSlice<f32>,
22299 conv_dim: usize,
22300 pad: usize,
22301 ) -> Result<(), Box<dyn std::error::Error>> {
22302 let f = self.func("conv_assemble_and_roll_f32");
22303 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
22304 let (cd, p) = (conv_dim as i32, pad as i32);
22305 let __s_b = self.gpu.stream();
22306 let mut b = __s_b.launch_builder(&f);
22307 b.arg(qkv_col).arg(conv_state).arg(conv_in).arg(&cd).arg(&p);
22308 unsafe {
22309 b.launch(cfg)?;
22310 }
22311 Ok(())
22312 }
22313
22314 pub fn ssm_conv1d_fused_decode(
22320 &self,
22321 qkv_col: &CudaSlice<f32>,
22322 conv_state: &mut CudaSlice<f32>,
22323 w: &CudaSlice<f32>,
22324 conv_out: &mut CudaSlice<f32>,
22325 conv_dim: usize,
22326 d_conv: usize,
22327 ) -> Result<(), Box<dyn std::error::Error>> {
22328 let f = self.func("ssm_conv1d_fused_decode_f32");
22329 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
22330 let (cd, dc) = (conv_dim as i32, d_conv as i32);
22331 let __s_b = self.gpu.stream();
22332 let mut b = __s_b.launch_builder(&f);
22333 b.arg(qkv_col)
22334 .arg(conv_state)
22335 .arg(w)
22336 .arg(conv_out)
22337 .arg(&cd)
22338 .arg(&dc);
22339 unsafe {
22340 b.launch(cfg)?;
22341 }
22342 Ok(())
22343 }
22344
22345 pub fn slice_range(
22348 &self,
22349 src: &CudaSlice<f32>,
22350 start: usize,
22351 len: usize,
22352 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22353 let host = self.gpu.stream().clone_dtoh(src)?;
22354 self.gpu.stream().synchronize()?;
22355 Ok(self.htod(&host[start..start + len])?)
22356 }
22357}
22358
22359#[cfg(test)]
22360mod target_dispatch_tests {
22361 use super::legacy_quant_gemm_allowed;
22362
22363 #[test]
22364 fn legacy_quant_gemm_arch_policy_honors_the_escape_hatch() {
22365 assert!(legacy_quant_gemm_allowed(false, false, false));
22367 assert!(!legacy_quant_gemm_allowed(false, false, true));
22368 assert!(!legacy_quant_gemm_allowed(true, false, false));
22370 assert!(!legacy_quant_gemm_allowed(true, false, true));
22371 assert!(legacy_quant_gemm_allowed(true, true, false));
22373 assert!(!legacy_quant_gemm_allowed(true, true, true));
22374 }
22375
22376 #[cfg(all(memra_portable_cuda, not(memra_hopper_mma)))]
22377 #[test]
22378 fn portable_build_disables_legacy_quant_gemm_without_an_env_override() {
22379 assert!(!legacy_quant_gemm_allowed(
22380 cfg!(memra_portable_cuda),
22381 cfg!(memra_hopper_mma),
22382 false
22383 ));
22384 }
22385
22386 #[cfg(memra_hopper_mma)]
22387 #[test]
22388 fn hopper_mma_build_re_admits_legacy_quant_gemm() {
22389 assert!(legacy_quant_gemm_allowed(
22390 cfg!(memra_portable_cuda),
22391 cfg!(memra_hopper_mma),
22392 false
22393 ));
22394 assert!(super::portable_mma_gated() == false);
22395 }
22396}
22397
22398impl memra_kv::KvDev for Engine {
22401 fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22402 Engine::zeros(self, n)
22403 }
22404 fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22405 Engine::uninit(self, n)
22406 }
22407 fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
22408 Engine::alloc_u8(self, n)
22409 }
22410 fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
22411 Engine::htod_i32(self, v)
22412 }
22413 fn clone_dtod(
22414 &self,
22415 src: &CudaSlice<f32>,
22416 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22417 Engine::clone_dtod(self, src)
22418 }
22419 fn copy_into(
22420 &self,
22421 dst: &mut CudaSlice<f32>,
22422 off: usize,
22423 src: &CudaSlice<f32>,
22424 len: usize,
22425 ) -> Result<(), Box<dyn std::error::Error>> {
22426 Engine::copy_into(self, dst, off, src, len)
22427 }
22428 fn set_i32_one(
22429 &self,
22430 d: &mut CudaSlice<i32>,
22431 v: i32,
22432 ) -> Result<(), Box<dyn std::error::Error>> {
22433 Engine::set_i32_one(self, d, v)
22434 }
22435}
22436
22437#[cfg(test)]
22438mod fused_gate_bounds_tests {
22439 use super::*;
22440
22441 #[test]
22454 #[ignore = "requires a CUDA GPU"]
22455 fn q_gate_split_refuses_a_separate_gate_wq_instead_of_reading_past_it() {
22456 let e = Engine::new(0).unwrap();
22457 let (head_dim, n_head, t) = (8usize, 4usize, 2usize);
22458 let fused = 2 * head_dim * n_head * t;
22459 let out_n = head_dim * n_head * t;
22460
22461 let narrow = e.htod(&vec![1.0f32; out_n]).unwrap();
22463 let mut q = e.uninit(out_n).unwrap();
22464 let mut gate = e.uninit(out_n).unwrap();
22465 let err = e
22466 .q_gate_split(&narrow, &mut q, &mut gate, head_dim, n_head, t)
22467 .expect_err("half-width wq must be refused, not read past")
22468 .to_string();
22469 assert!(err.contains("NO fused gate"), "{err}");
22470 assert!(err.contains(&format!("{fused}")), "{err}");
22471
22472 let host: Vec<f32> = (0..fused).map(|i| i as f32).collect();
22475 let wide = e.htod(&host).unwrap();
22476 e.q_gate_split(&wide, &mut q, &mut gate, head_dim, n_head, t)
22477 .expect("full-width wq splits");
22478 let (qh, gh) = (e.dtoh(&q).unwrap(), e.dtoh(&gate).unwrap());
22479 for tok in 0..t {
22480 for hh in 0..n_head {
22481 for d in 0..head_dim {
22482 let base = tok * (n_head * 2 * head_dim) + hh * (2 * head_dim);
22483 let idx = tok * (n_head * head_dim) + hh * head_dim + d;
22484 assert_eq!(qh[idx], host[base + d], "q t{tok} h{hh} d{d}");
22485 assert_eq!(gh[idx], host[base + head_dim + d], "gate t{tok} h{hh} d{d}");
22486 }
22487 }
22488 }
22489
22490 let mut small = e.uninit(out_n - 1).unwrap();
22492 assert!(
22493 e.q_gate_split(&wide, &mut small, &mut gate, head_dim, n_head, t)
22494 .is_err()
22495 );
22496 }
22497}
22498
22499#[cfg(test)]
22503mod fused_rope_width_tests {
22504 use super::Engine;
22505
22506 #[test]
22509 fn full_width_is_accepted() {
22510 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope", 256, 256).is_ok());
22511 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_cat", 512, 512).is_ok());
22512 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_append", 128, 128).is_ok());
22513 }
22514
22515 #[test]
22528 fn gemma4_official_artifact_widths_pass() {
22529 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope", 512, 512).is_ok());
22530 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_append_dc", 256, 256).is_ok());
22531 }
22532
22533 #[test]
22536 fn partial_rotary_is_refused_with_the_geometry_named() {
22537 let err = Engine::full_width_rope_only("rms_norm_qkv_rope", 64, 256)
22539 .expect_err("partial rotary must refuse");
22540 let msg = err.to_string();
22541 assert!(msg.contains("PARTIAL ROTARY REFUSED"), "{msg}");
22542 assert!(msg.contains("n_rot 64"), "{msg}");
22543 assert!(msg.contains("head_dim 256"), "{msg}");
22544 assert!(
22545 msg.contains("64..256"),
22546 "names the band it would corrupt: {msg}"
22547 );
22548 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_append_dc", 64, 128).is_err());
22550 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope", 256, 128).is_err());
22552 }
22553}