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 #[allow(clippy::too_many_arguments)]
8730 pub fn rms_norm_qkv_rope_cat(
8731 &self,
8732 qkv: &CudaSlice<f32>,
8733 wq: &CudaSlice<f32>,
8734 wk: &CudaSlice<f32>,
8735 wv: &CudaSlice<f32>,
8736 q: &mut CudaSlice<f32>,
8737 k: &mut CudaSlice<f32>,
8738 v: &mut CudaSlice<f32>,
8739 head_dim: usize,
8740 rq: usize,
8741 rk: usize,
8742 pos: &CudaSlice<i32>,
8743 nh_q: usize,
8744 nh_k: usize,
8745 base: f32,
8746 freq_scale: f32,
8747 ff: Option<&CudaSlice<f32>>,
8748 eps: f32,
8749 ) -> Result<(), Box<dyn std::error::Error>> {
8750 let rows = rq + rk + rk;
8751 let theta_scale = base.powf(-2.0 / head_dim as f32);
8752 let (nc, rqi, rki, nhq, nhk) = (
8753 head_dim as i32,
8754 rq as i32,
8755 rk as i32,
8756 nh_q as i32,
8757 nh_k as i32,
8758 );
8759 if Self::pdl_on() {
8760 use cudarc::driver::{DevicePtr, DevicePtrMut};
8761 let s = &self.gpu.stream();
8762 let (pqkv, _g0) = qkv.device_ptr(s);
8763 let (pwq, _g1) = wq.device_ptr(s);
8764 let (pwk, _g2) = wk.device_ptr(s);
8765 let (pwv, _g3) = wv.device_ptr(s);
8766 let (pq, _g4) = q.device_ptr_mut(s);
8767 let (pk, _g5) = k.device_ptr_mut(s);
8768 let (pv, _g6) = v.device_ptr_mut(s);
8769 let (ppos, _g7) = pos.device_ptr(s);
8770 let (pff, _g8) = match ff {
8771 Some(t) => {
8772 let (p, g) = t.device_ptr(s);
8773 (p, Some(g))
8774 }
8775 None => (0, None),
8776 };
8777 let mut ps = [
8778 &pqkv as *const _ as *mut std::ffi::c_void,
8779 &pwq as *const _ as *mut _,
8780 &pwk as *const _ as *mut _,
8781 &pwv as *const _ as *mut _,
8782 &pq as *const _ as *mut _,
8783 &pk as *const _ as *mut _,
8784 &pv as *const _ as *mut _,
8785 &nc as *const _ as *mut _,
8786 &rqi as *const _ as *mut _,
8787 &rki as *const _ as *mut _,
8788 &ppos as *const _ as *mut _,
8789 &nhq as *const _ as *mut _,
8790 &nhk as *const _ as *mut _,
8791 &theta_scale as *const _ as *mut _,
8792 &freq_scale as *const _ as *mut _,
8793 &pff as *const _ as *mut _,
8794 &eps as *const _ as *mut _,
8795 ];
8796 unsafe {
8797 self.launch_pdl(
8798 "rms_norm_qkv_rope_cat_f32",
8799 (rows as u32, 1, 1),
8800 (rms_block(), 1, 1),
8801 &mut ps,
8802 )?;
8803 }
8804 return Ok(());
8805 }
8806 let f = self.func("rms_norm_qkv_rope_cat_f32");
8807 let cfg = LaunchConfig {
8808 grid_dim: (rows as u32, 1, 1),
8809 block_dim: (rms_block(), 1, 1),
8810 shared_mem_bytes: 0,
8811 };
8812 let __s_b = self.gpu.stream();
8813 let mut b = __s_b.launch_builder(&f);
8814 match ff {
8815 Some(t) => {
8816 b.arg(qkv)
8817 .arg(wq)
8818 .arg(wk)
8819 .arg(wv)
8820 .arg(&mut *q)
8821 .arg(&mut *k)
8822 .arg(&mut *v)
8823 .arg(&nc)
8824 .arg(&rqi)
8825 .arg(&rki)
8826 .arg(pos)
8827 .arg(&nhq)
8828 .arg(&nhk)
8829 .arg(&theta_scale)
8830 .arg(&freq_scale)
8831 .arg(t)
8832 .arg(&eps);
8833 unsafe {
8834 b.launch(cfg)?;
8835 }
8836 }
8837 None => {
8838 let null: u64 = 0;
8839 b.arg(qkv)
8840 .arg(wq)
8841 .arg(wk)
8842 .arg(wv)
8843 .arg(&mut *q)
8844 .arg(&mut *k)
8845 .arg(&mut *v)
8846 .arg(&nc)
8847 .arg(&rqi)
8848 .arg(&rki)
8849 .arg(pos)
8850 .arg(&nhq)
8851 .arg(&nhk)
8852 .arg(&theta_scale)
8853 .arg(&freq_scale)
8854 .arg(&null)
8855 .arg(&eps);
8856 unsafe {
8857 b.launch(cfg)?;
8858 }
8859 }
8860 }
8861 Ok(())
8862 }
8863
8864 #[allow(clippy::too_many_arguments)]
8866 pub fn rms_norm_qkv_rope(
8867 &self,
8868 q0: &CudaSlice<f32>,
8869 k0: &CudaSlice<f32>,
8870 v0: &CudaSlice<f32>,
8871 wq: &CudaSlice<f32>,
8872 wk: &CudaSlice<f32>,
8873 wv: &CudaSlice<f32>,
8874 q: &mut CudaSlice<f32>,
8875 k: &mut CudaSlice<f32>,
8876 v: &mut CudaSlice<f32>,
8877 head_dim: usize,
8878 rq: usize,
8879 rk: usize,
8880 pos: &CudaSlice<i32>,
8881 nh_q: usize,
8882 nh_k: usize,
8883 base: f32,
8884 freq_scale: f32,
8885 ff: Option<&CudaSlice<f32>>,
8886 eps: f32,
8887 ) -> Result<(), Box<dyn std::error::Error>> {
8888 let f = self.func("rms_norm_qkv_rope_f32");
8889 let rows = rq + rk + rk; let cfg = LaunchConfig {
8891 grid_dim: (rows as u32, 1, 1),
8892 block_dim: (rms_block(), 1, 1),
8893 shared_mem_bytes: 0,
8894 };
8895 let theta_scale = base.powf(-2.0 / head_dim as f32);
8896 let (nc, rqi, rki, nhq, nhk) = (
8897 head_dim as i32,
8898 rq as i32,
8899 rk as i32,
8900 nh_q as i32,
8901 nh_k as i32,
8902 );
8903 let __s_b = self.gpu.stream();
8904 let mut b = __s_b.launch_builder(&f);
8905 match ff {
8906 Some(t) => {
8907 b.arg(q0)
8908 .arg(k0)
8909 .arg(v0)
8910 .arg(wq)
8911 .arg(wk)
8912 .arg(wv)
8913 .arg(&mut *q)
8914 .arg(&mut *k)
8915 .arg(&mut *v)
8916 .arg(&nc)
8917 .arg(&rqi)
8918 .arg(&rki)
8919 .arg(pos)
8920 .arg(&nhq)
8921 .arg(&nhk)
8922 .arg(&theta_scale)
8923 .arg(&freq_scale)
8924 .arg(t)
8925 .arg(&eps);
8926 unsafe {
8927 b.launch(cfg)?;
8928 }
8929 }
8930 None => {
8931 let null: u64 = 0;
8932 b.arg(q0)
8933 .arg(k0)
8934 .arg(v0)
8935 .arg(wq)
8936 .arg(wk)
8937 .arg(wv)
8938 .arg(&mut *q)
8939 .arg(&mut *k)
8940 .arg(&mut *v)
8941 .arg(&nc)
8942 .arg(&rqi)
8943 .arg(&rki)
8944 .arg(pos)
8945 .arg(&nhq)
8946 .arg(&nhk)
8947 .arg(&theta_scale)
8948 .arg(&freq_scale)
8949 .arg(&null)
8950 .arg(&eps);
8951 unsafe {
8952 b.launch(cfg)?;
8953 }
8954 }
8955 }
8956 Ok(())
8957 }
8958
8959 #[allow(clippy::too_many_arguments)]
8963 pub fn rms_norm_qkv_rope_append_dc(
8964 &self,
8965 q0: &CudaSlice<f32>,
8966 k0: &CudaSlice<f32>,
8967 v0: &CudaSlice<f32>,
8968 wq: &CudaSlice<f32>,
8969 wk: &CudaSlice<f32>,
8970 wv: &CudaSlice<f32>,
8971 q: &mut CudaSlice<f32>,
8972 k: &mut CudaSlice<f32>,
8973 v: &mut CudaSlice<f32>,
8974 head_dim: usize,
8975 rq: usize,
8976 rk: usize,
8977 pos: &CudaSlice<i32>,
8978 nh_q: usize,
8979 nh_k: usize,
8980 base: f32,
8981 freq_scale: f32,
8982 ff: Option<&CudaSlice<f32>>,
8983 eps: f32,
8984 kc: &mut CudaSlice<u8>,
8985 vc: &mut CudaSlice<u8>,
8986 t_dev: &CudaSlice<i32>,
8987 k_tok_bytes: usize,
8988 v_tok_bytes: usize,
8989 g: bool,
8990 ) -> Result<(), Box<dyn std::error::Error>> {
8991 let rows = rq + rk + rk;
8992 let theta_scale = base.powf(-2.0 / head_dim as f32);
8993 let (nc, rqi, rki, nhq, nhk) = (
8994 head_dim as i32,
8995 rq as i32,
8996 rk as i32,
8997 nh_q as i32,
8998 nh_k as i32,
8999 );
9000 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
9001 if Self::pdl_on() && Self::pdl_wb_on() {
9002 use cudarc::driver::{DevicePtr, DevicePtrMut};
9003 let s = &self.gpu.stream();
9004 let (p0, _a0) = q0.device_ptr(s);
9005 let (p1, _a1) = k0.device_ptr(s);
9006 let (p2, _a2) = v0.device_ptr(s);
9007 let (pwq, _a3) = wq.device_ptr(s);
9008 let (pwk, _a4) = wk.device_ptr(s);
9009 let (pwv, _a5) = wv.device_ptr(s);
9010 let (pq, _a6) = q.device_ptr_mut(s);
9011 let (pk, _a7) = k.device_ptr_mut(s);
9012 let (pv, _a8) = v.device_ptr_mut(s);
9013 let (pp, _a9) = pos.device_ptr(s);
9014 let pff: u64 = match ff {
9015 Some(t) => {
9016 let (p, _gg) = t.device_ptr(s);
9017 p as u64
9018 }
9019 None => 0,
9020 };
9021 let (pkc, _a10) = kc.device_ptr_mut(s);
9022 let (pvc, _a11) = vc.device_ptr_mut(s);
9023 let (pt, _a12) = t_dev.device_ptr(s);
9024 let mut ps = [
9025 &p0 as *const _ as *mut std::ffi::c_void,
9026 &p1 as *const _ as *mut _,
9027 &p2 as *const _ as *mut _,
9028 &pwq as *const _ as *mut _,
9029 &pwk as *const _ as *mut _,
9030 &pwv as *const _ as *mut _,
9031 &pq as *const _ as *mut _,
9032 &pk as *const _ as *mut _,
9033 &pv as *const _ as *mut _,
9034 &nc as *const _ as *mut _,
9035 &rqi as *const _ as *mut _,
9036 &rki as *const _ as *mut _,
9037 &pp as *const _ as *mut _,
9038 &nhq as *const _ as *mut _,
9039 &nhk as *const _ as *mut _,
9040 &theta_scale as *const _ as *mut _,
9041 &freq_scale as *const _ as *mut _,
9042 &pff as *const _ as *mut _,
9043 &eps as *const _ as *mut _,
9044 &pkc as *const _ as *mut _,
9045 &pvc as *const _ as *mut _,
9046 &pt as *const _ as *mut _,
9047 &ktb as *const _ as *mut _,
9048 &vtb as *const _ as *mut _,
9049 ];
9050 unsafe {
9051 self.launch_pdl_flash(
9052 g,
9053 "rms_norm_qkv_rope_append_dc_f32",
9054 (rows as u32, 1, 1),
9055 (rms_block(), 1, 1),
9056 0,
9057 &mut ps,
9058 )?;
9059 }
9060 return Ok(());
9061 }
9062 let f = if g {
9063 self.func_g("rms_norm_qkv_rope_append_dc_f32")
9064 } else {
9065 self.func("rms_norm_qkv_rope_append_dc_f32")
9066 };
9067 let cfg = LaunchConfig {
9068 grid_dim: (rows as u32, 1, 1),
9069 block_dim: (rms_block(), 1, 1),
9070 shared_mem_bytes: 0,
9071 };
9072 let __s_b = self.gpu.stream();
9073 let mut b = __s_b.launch_builder(&f);
9074 match ff {
9075 Some(t) => {
9076 b.arg(q0)
9077 .arg(k0)
9078 .arg(v0)
9079 .arg(wq)
9080 .arg(wk)
9081 .arg(wv)
9082 .arg(&mut *q)
9083 .arg(&mut *k)
9084 .arg(&mut *v)
9085 .arg(&nc)
9086 .arg(&rqi)
9087 .arg(&rki)
9088 .arg(pos)
9089 .arg(&nhq)
9090 .arg(&nhk)
9091 .arg(&theta_scale)
9092 .arg(&freq_scale)
9093 .arg(t)
9094 .arg(&eps)
9095 .arg(&mut *kc)
9096 .arg(&mut *vc)
9097 .arg(t_dev)
9098 .arg(&ktb)
9099 .arg(&vtb);
9100 unsafe {
9101 b.launch(cfg)?;
9102 }
9103 }
9104 None => {
9105 let null: u64 = 0;
9106 b.arg(q0)
9107 .arg(k0)
9108 .arg(v0)
9109 .arg(wq)
9110 .arg(wk)
9111 .arg(wv)
9112 .arg(&mut *q)
9113 .arg(&mut *k)
9114 .arg(&mut *v)
9115 .arg(&nc)
9116 .arg(&rqi)
9117 .arg(&rki)
9118 .arg(pos)
9119 .arg(&nhq)
9120 .arg(&nhk)
9121 .arg(&theta_scale)
9122 .arg(&freq_scale)
9123 .arg(&null)
9124 .arg(&eps)
9125 .arg(&mut *kc)
9126 .arg(&mut *vc)
9127 .arg(t_dev)
9128 .arg(&ktb)
9129 .arg(&vtb);
9130 unsafe {
9131 b.launch(cfg)?;
9132 }
9133 }
9134 }
9135 Ok(())
9136 }
9137
9138 #[allow(clippy::too_many_arguments)]
9145 pub fn rms_norm_qkv_rope_append(
9146 &self,
9147 q0: &CudaSlice<f32>,
9148 k0: &CudaSlice<f32>,
9149 v0: &CudaSlice<f32>,
9150 wq: &CudaSlice<f32>,
9151 wk: &CudaSlice<f32>,
9152 wv: &CudaSlice<f32>,
9153 q: &mut CudaSlice<f32>,
9154 k: &mut CudaSlice<f32>,
9155 v: &mut CudaSlice<f32>,
9156 head_dim: usize,
9157 rq: usize,
9158 rk: usize,
9159 pos: &CudaSlice<i32>,
9160 nh_q: usize,
9161 nh_k: usize,
9162 base: f32,
9163 freq_scale: f32,
9164 ff: Option<&CudaSlice<f32>>,
9165 eps: f32,
9166 kc: &mut CudaSlice<u8>,
9167 vc: &mut CudaSlice<u8>,
9168 t: usize,
9169 k_tok_bytes: usize,
9170 v_tok_bytes: usize,
9171 g: bool,
9172 ) -> Result<(), Box<dyn std::error::Error>> {
9173 let rows = rq + rk + rk;
9174 let theta_scale = base.powf(-2.0 / head_dim as f32);
9175 let (nc, rqi, rki, nhq, nhk) = (
9176 head_dim as i32,
9177 rq as i32,
9178 rk as i32,
9179 nh_q as i32,
9180 nh_k as i32,
9181 );
9182 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
9183 let ti = t as i32;
9184 if Self::pdl_on() && Self::pdl_wb_on() {
9185 use cudarc::driver::{DevicePtr, DevicePtrMut};
9186 let s = &self.gpu.stream();
9187 let (p0, _a0) = q0.device_ptr(s);
9188 let (p1, _a1) = k0.device_ptr(s);
9189 let (p2, _a2) = v0.device_ptr(s);
9190 let (pwq, _a3) = wq.device_ptr(s);
9191 let (pwk, _a4) = wk.device_ptr(s);
9192 let (pwv, _a5) = wv.device_ptr(s);
9193 let (pq, _a6) = q.device_ptr_mut(s);
9194 let (pk, _a7) = k.device_ptr_mut(s);
9195 let (pv, _a8) = v.device_ptr_mut(s);
9196 let (pp, _a9) = pos.device_ptr(s);
9197 let pff: u64 = match ff {
9198 Some(t) => {
9199 let (p, _gg) = t.device_ptr(s);
9200 p as u64
9201 }
9202 None => 0,
9203 };
9204 let (pkc, _a10) = kc.device_ptr_mut(s);
9205 let (pvc, _a11) = vc.device_ptr_mut(s);
9206 let mut ps = [
9207 &p0 as *const _ as *mut std::ffi::c_void,
9208 &p1 as *const _ as *mut _,
9209 &p2 as *const _ as *mut _,
9210 &pwq as *const _ as *mut _,
9211 &pwk as *const _ as *mut _,
9212 &pwv as *const _ as *mut _,
9213 &pq as *const _ as *mut _,
9214 &pk as *const _ as *mut _,
9215 &pv as *const _ as *mut _,
9216 &nc as *const _ as *mut _,
9217 &rqi as *const _ as *mut _,
9218 &rki as *const _ as *mut _,
9219 &pp as *const _ as *mut _,
9220 &nhq as *const _ as *mut _,
9221 &nhk as *const _ as *mut _,
9222 &theta_scale as *const _ as *mut _,
9223 &freq_scale as *const _ as *mut _,
9224 &pff as *const _ as *mut _,
9225 &eps as *const _ as *mut _,
9226 &pkc as *const _ as *mut _,
9227 &pvc as *const _ as *mut _,
9228 &ti as *const _ as *mut _,
9229 &ktb as *const _ as *mut _,
9230 &vtb as *const _ as *mut _,
9231 ];
9232 unsafe {
9233 self.launch_pdl_flash(
9234 g,
9235 "rms_norm_qkv_rope_append_f32",
9236 (rows as u32, 1, 1),
9237 (rms_block(), 1, 1),
9238 0,
9239 &mut ps,
9240 )?;
9241 }
9242 return Ok(());
9243 }
9244 let f = if g {
9245 self.func_g("rms_norm_qkv_rope_append_f32")
9246 } else {
9247 self.func("rms_norm_qkv_rope_append_f32")
9248 };
9249 let cfg = LaunchConfig {
9250 grid_dim: (rows as u32, 1, 1),
9251 block_dim: (rms_block(), 1, 1),
9252 shared_mem_bytes: 0,
9253 };
9254 let __s_b = self.gpu.stream();
9255 let mut b = __s_b.launch_builder(&f);
9256 let null: u64 = 0;
9257 b.arg(q0)
9258 .arg(k0)
9259 .arg(v0)
9260 .arg(wq)
9261 .arg(wk)
9262 .arg(wv)
9263 .arg(&mut *q)
9264 .arg(&mut *k)
9265 .arg(&mut *v)
9266 .arg(&nc)
9267 .arg(&rqi)
9268 .arg(&rki)
9269 .arg(pos)
9270 .arg(&nhq)
9271 .arg(&nhk)
9272 .arg(&theta_scale)
9273 .arg(&freq_scale);
9274 match ff {
9275 Some(t) => {
9276 b.arg(t);
9277 }
9278 None => {
9279 b.arg(&null);
9280 }
9281 }
9282 b.arg(&eps)
9283 .arg(&mut *kc)
9284 .arg(&mut *vc)
9285 .arg(&ti)
9286 .arg(&ktb)
9287 .arg(&vtb);
9288 unsafe {
9289 b.launch(cfg)?;
9290 }
9291 Ok(())
9292 }
9293
9294 pub fn add_q8_1(
9295 &self,
9296 a: &CudaSlice<f32>,
9297 b: &CudaSlice<f32>,
9298 res: &mut CudaSlice<f32>,
9299 ncols: usize,
9300 nrows: usize,
9301 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9302 debug_assert!(ncols % 128 == 0);
9303 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
9304 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9305 let f = self.func("add_q8_1_f32");
9306 let cfg = LaunchConfig {
9307 grid_dim: (nrows as u32, 1, 1),
9308 block_dim: (rms_block(), 1, 1),
9309 shared_mem_bytes: 0,
9310 };
9311 let nc = ncols as i32;
9312 let __s_b2 = self.gpu.stream();
9313 let mut b2 = __s_b2.launch_builder(&f);
9314 b2.arg(a)
9315 .arg(b)
9316 .arg(&mut *res)
9317 .arg(&mut out_q)
9318 .arg(&mut out_d)
9319 .arg(&nc);
9320 unsafe {
9321 b2.launch(cfg)?;
9322 }
9323 Ok((out_q, out_d))
9324 }
9325
9326 pub fn rms_pre_add_q8_1(
9330 &self,
9331 a: &CudaSlice<f32>,
9332 wa: &CudaSlice<f32>,
9333 b: &CudaSlice<f32>,
9334 res: &mut CudaSlice<f32>,
9335 ncols: usize,
9336 nrows: usize,
9337 eps: f32,
9338 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9339 debug_assert!(ncols % 128 == 0);
9340 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
9341 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9342 let f = self.func("rms_pre_add_q8_1_f32");
9343 let cfg = LaunchConfig {
9344 grid_dim: (nrows as u32, 1, 1),
9345 block_dim: (rms_block(), 1, 1),
9346 shared_mem_bytes: 0,
9347 };
9348 let (nc, ep) = (ncols as i32, eps);
9349 let __s_b2 = self.gpu.stream();
9350 let mut b2 = __s_b2.launch_builder(&f);
9351 b2.arg(a)
9352 .arg(wa)
9353 .arg(b)
9354 .arg(&mut *res)
9355 .arg(&mut out_q)
9356 .arg(&mut out_d)
9357 .arg(&nc)
9358 .arg(&ep);
9359 unsafe {
9360 b2.launch(cfg)?;
9361 }
9362 Ok((out_q, out_d))
9363 }
9364
9365 pub fn l2_v2_on(ncols: usize) -> bool {
9369 ncols == 128 && std::env::var("MEMRA_L2_V2").as_deref() != Ok("0")
9370 }
9371
9372 pub fn l2_norm_pp(
9373 &self,
9374 x: &CudaSlice<f32>,
9375 dst: &mut CudaSlice<f32>,
9376 dst16: Option<&mut CudaSlice<u8>>,
9377 ncols: usize,
9378 nrows: usize,
9379 eps: f32,
9380 ) -> Result<(), Box<dyn std::error::Error>> {
9381 if Self::l2_v2_on(ncols) {
9382 let f = self.func("l2_norm_pp_v2_f32");
9383 let rows_per_block = 8u32; let cfg = LaunchConfig {
9385 grid_dim: ((nrows as u32).div_ceil(rows_per_block), 1, 1),
9386 block_dim: (256, 1, 1),
9387 shared_mem_bytes: 0,
9388 };
9389 let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
9390 let d16: u64 = match dst16 {
9392 Some(d) => self.addr_u8(d),
9393 None => 0,
9394 };
9395 let __s_b = self.gpu.stream();
9396 let mut b = __s_b.launch_builder(&f);
9397 b.arg(x).arg(dst).arg(&d16).arg(&nc).arg(&nr).arg(&e);
9398 unsafe {
9399 b.launch(cfg)?;
9400 }
9401 return Ok(());
9402 }
9403 self.l2_norm(x, dst, ncols, nrows, eps)
9404 }
9405
9406 pub fn l2_norm(
9407 &self,
9408 x: &CudaSlice<f32>,
9409 dst: &mut CudaSlice<f32>,
9410 ncols: usize,
9411 nrows: usize,
9412 eps: f32,
9413 ) -> Result<(), Box<dyn std::error::Error>> {
9414 let f = self.func("l2_norm_f32");
9415 let cfg = LaunchConfig {
9416 grid_dim: (nrows as u32, 1, 1),
9417 block_dim: (256, 1, 1),
9418 shared_mem_bytes: 0,
9419 };
9420 let (nc, e) = (ncols as i32, eps);
9421 let __s_b = self.gpu.stream();
9422 let mut b = __s_b.launch_builder(&f);
9423 b.arg(x).arg(dst).arg(&nc).arg(&e);
9424 unsafe {
9425 b.launch(cfg)?;
9426 }
9427 Ok(())
9428 }
9429
9430 pub fn l2_norm_decode(
9436 &self,
9437 x: &CudaSlice<f32>,
9438 dst: &mut CudaSlice<f32>,
9439 ncols: usize,
9440 nrows: usize,
9441 eps: f32,
9442 ) -> Result<(), Box<dyn std::error::Error>> {
9443 let f = self.func("l2_norm_f32");
9444 let cfg = LaunchConfig {
9445 grid_dim: (nrows as u32, 1, 1),
9446 block_dim: (32, 1, 1),
9447 shared_mem_bytes: 0,
9448 };
9449 let (nc, e) = (ncols as i32, eps);
9450 let __s_b = self.gpu.stream();
9451 let mut b = __s_b.launch_builder(&f);
9452 b.arg(x).arg(dst).arg(&nc).arg(&e);
9453 unsafe {
9454 b.launch(cfg)?;
9455 }
9456 Ok(())
9457 }
9458
9459 pub fn rope_neox(
9461 &self,
9462 x: &mut CudaSlice<f32>,
9463 pos: &CudaSlice<i32>,
9464 head_dim: usize,
9465 n_dims: usize,
9466 n_heads: usize,
9467 n_tokens: usize,
9468 freq_base: f32,
9469 freq_scale: f32,
9470 ) -> Result<(), Box<dyn std::error::Error>> {
9471 let f = self.func("rope_neox_f32");
9472 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9473 let grid = (n_heads * n_tokens) as u32;
9474 let cfg = LaunchConfig {
9475 grid_dim: (grid, 1, 1),
9476 block_dim: ((head_dim / 2) as u32, 1, 1),
9477 shared_mem_bytes: 0,
9478 };
9479 let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
9480 let __s_b = self.gpu.stream();
9481 let mut b = __s_b.launch_builder(&f);
9482 b.arg(x)
9483 .arg(pos)
9484 .arg(&hd)
9485 .arg(&nd)
9486 .arg(&nh)
9487 .arg(&theta_scale)
9488 .arg(&freq_scale);
9489 unsafe {
9490 b.launch(cfg)?;
9491 }
9492 Ok(())
9493 }
9494
9495 pub fn rope_neox_ff(
9497 &self,
9498 x: &mut CudaSlice<f32>,
9499 pos: &CudaSlice<i32>,
9500 head_dim: usize,
9501 n_dims: usize,
9502 n_heads: usize,
9503 n_tokens: usize,
9504 freq_base: f32,
9505 freq_scale: f32,
9506 ff: &CudaSlice<f32>,
9507 ) -> Result<(), Box<dyn std::error::Error>> {
9508 let f = self.func("rope_neox_ff_f32");
9509 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9510 let grid = (n_heads * n_tokens) as u32;
9511 let cfg = LaunchConfig {
9512 grid_dim: (grid, 1, 1),
9513 block_dim: ((head_dim / 2) as u32, 1, 1),
9514 shared_mem_bytes: 0,
9515 };
9516 let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
9517 let __s_b = self.gpu.stream();
9518 let mut b = __s_b.launch_builder(&f);
9519 b.arg(x)
9520 .arg(pos)
9521 .arg(&hd)
9522 .arg(&nd)
9523 .arg(&nh)
9524 .arg(&theta_scale)
9525 .arg(&freq_scale)
9526 .arg(ff);
9527 unsafe {
9528 b.launch(cfg)?;
9529 }
9530 Ok(())
9531 }
9532
9533 #[allow(clippy::too_many_arguments)]
9535 pub fn rope_neox2(
9536 &self,
9537 q: &mut CudaSlice<f32>,
9538 k: &mut CudaSlice<f32>,
9539 pos: &CudaSlice<i32>,
9540 head_dim: usize,
9541 n_dims: usize,
9542 nh_q: usize,
9543 nh_k: usize,
9544 n_tokens: usize,
9545 freq_base: f32,
9546 freq_scale: f32,
9547 ff: Option<&CudaSlice<f32>>,
9548 ) -> Result<(), Box<dyn std::error::Error>> {
9549 let f = self.func("rope_neox2_f32");
9550 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9551 let grid = ((nh_q + nh_k) * n_tokens) as u32;
9552 let cfg = LaunchConfig {
9553 grid_dim: (grid, 1, 1),
9554 block_dim: ((head_dim / 2) as u32, 1, 1),
9555 shared_mem_bytes: 0,
9556 };
9557 let (hd, nd, nq, nk, nt) = (
9558 head_dim as i32,
9559 n_dims as i32,
9560 nh_q as i32,
9561 nh_k as i32,
9562 n_tokens as i32,
9563 );
9564 let __s_b = self.gpu.stream();
9565 let mut b = __s_b.launch_builder(&f);
9566 b.arg(q)
9567 .arg(k)
9568 .arg(pos)
9569 .arg(&hd)
9570 .arg(&nd)
9571 .arg(&nq)
9572 .arg(&nk)
9573 .arg(&nt)
9574 .arg(&theta_scale)
9575 .arg(&freq_scale);
9576 match ff {
9577 Some(ffv) => {
9578 b.arg(ffv);
9579 unsafe {
9580 b.launch(cfg)?;
9581 }
9582 }
9583 None => {
9584 let null: u64 = 0;
9585 b.arg(&null);
9586 unsafe {
9587 b.launch(cfg)?;
9588 }
9589 }
9590 }
9591 Ok(())
9592 }
9593
9594 pub fn gelu_tanh_mul(
9596 &self,
9597 gate: &CudaSlice<f32>,
9598 up: &CudaSlice<f32>,
9599 dst: &mut CudaSlice<f32>,
9600 n: usize,
9601 ) -> Result<(), Box<dyn std::error::Error>> {
9602 let f = self.func("gelu_tanh_mul_f32");
9603 let cfg = LaunchConfig::for_num_elems(n as u32);
9604 let ni = n as i32;
9605 let __s_b = self.gpu.stream();
9606 let mut b = __s_b.launch_builder(&f);
9607 b.arg(gate).arg(up).arg(dst).arg(&ni);
9608 unsafe {
9609 b.launch(cfg)?;
9610 }
9611 Ok(())
9612 }
9613
9614 pub fn silu_mul(
9615 &self,
9616 gate: &CudaSlice<f32>,
9617 up: &CudaSlice<f32>,
9618 dst: &mut CudaSlice<f32>,
9619 n: usize,
9620 ) -> Result<(), Box<dyn std::error::Error>> {
9621 let f = self.func("silu_mul_f32");
9622 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9624 let ni = n as i32;
9625 let __s_b = self.gpu.stream();
9626 let mut b = __s_b.launch_builder(&f);
9627 b.arg(gate).arg(up).arg(dst).arg(&ni);
9628 unsafe {
9629 b.launch(cfg)?;
9630 }
9631 Ok(())
9632 }
9633
9634 pub fn silu_mul_f16out(
9637 &self,
9638 gate: &CudaSlice<f32>,
9639 up: &CudaSlice<f32>,
9640 dst: &mut CudaSlice<f32>,
9641 dst16: &mut CudaSlice<u8>,
9642 n: usize,
9643 ) -> Result<(), Box<dyn std::error::Error>> {
9644 let f = self.func("silu_mul_f16out_f32");
9645 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9646 let ni = n as i32;
9647 let __s_b = self.gpu.stream();
9648 let mut b = __s_b.launch_builder(&f);
9649 b.arg(gate).arg(up).arg(dst).arg(dst16).arg(&ni);
9650 unsafe {
9651 b.launch(cfg)?;
9652 }
9653 Ok(())
9654 }
9655
9656 pub fn silu_mul_scaled(
9663 &self,
9664 gate: &CudaSlice<f32>,
9665 up: &CudaSlice<f32>,
9666 gs: f32,
9667 us: f32,
9668 dst: &mut CudaSlice<f32>,
9669 n: usize,
9670 ) -> Result<(), Box<dyn std::error::Error>> {
9671 let f = self.func("silu_mul_scaled_f32");
9672 let cfg = LaunchConfig::for_num_elems(n as u32);
9673 let ni = n as i32;
9674 let (gsf, usf) = (gs, us);
9675 let __s_b = self.gpu.stream();
9676 let mut b = __s_b.launch_builder(&f);
9677 b.arg(gate).arg(up).arg(&gsf).arg(&usf).arg(dst).arg(&ni);
9678 unsafe {
9679 b.launch(cfg)?;
9680 }
9681 Ok(())
9682 }
9683
9684 #[allow(clippy::too_many_arguments)]
9688 pub fn swigluoai_mul_scaled(
9689 &self,
9690 gate: &CudaSlice<f32>,
9691 up: &CudaSlice<f32>,
9692 gs: f32,
9693 us: f32,
9694 alpha: f32,
9695 limit: f32,
9696 dst: &mut CudaSlice<f32>,
9697 n: usize,
9698 ) -> Result<(), Box<dyn std::error::Error>> {
9699 let f = self.func("swigluoai_mul_scaled_f32");
9700 let cfg = LaunchConfig::for_num_elems(n as u32);
9701 let ni = n as i32;
9702 let __s_b = self.gpu.stream();
9703 let mut b = __s_b.launch_builder(&f);
9704 b.arg(gate)
9705 .arg(up)
9706 .arg(&gs)
9707 .arg(&us)
9708 .arg(&alpha)
9709 .arg(&limit)
9710 .arg(dst)
9711 .arg(&ni);
9712 unsafe {
9713 b.launch(cfg)?;
9714 }
9715 Ok(())
9716 }
9717
9718 pub fn silu_mul_scaled_q8_1(
9726 &self,
9727 gate: &CudaSlice<f32>,
9728 up: &CudaSlice<f32>,
9729 gs: f32,
9730 us: f32,
9731 n: usize,
9732 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9733 let f = self.func("silu_mul_scaled_q8_1");
9734 let nblk = n / 32;
9735 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);
9739 let (gsf, usf, ni) = (gs, us, n as i32);
9740 let __s_b = self.gpu.stream();
9741 let mut b = __s_b.launch_builder(&f);
9742 b.arg(gate)
9743 .arg(up)
9744 .arg(&gsf)
9745 .arg(&usf)
9746 .arg(&mut aq)
9747 .arg(&mut ad)
9748 .arg(&ni);
9749 unsafe {
9750 b.launch(cfg)?;
9751 }
9752 Ok((aq, ad))
9753 }
9754
9755 pub fn add(
9756 &self,
9757 a: &CudaSlice<f32>,
9758 b_in: &CudaSlice<f32>,
9759 dst: &mut CudaSlice<f32>,
9760 n: usize,
9761 ) -> Result<(), Box<dyn std::error::Error>> {
9762 let f = self.func("add_f32");
9763 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9765 let ni = n as i32;
9766 let __s_bld = self.gpu.stream();
9767 let mut bld = __s_bld.launch_builder(&f);
9768 bld.arg(a).arg(b_in).arg(dst).arg(&ni);
9769 unsafe {
9770 bld.launch(cfg)?;
9771 }
9772 Ok(())
9773 }
9774
9775 pub fn mul(
9776 &self,
9777 a: &CudaSlice<f32>,
9778 b_in: &CudaSlice<f32>,
9779 dst: &mut CudaSlice<f32>,
9780 n: usize,
9781 ) -> Result<(), Box<dyn std::error::Error>> {
9782 let f = self.func("mul_f32");
9783 let cfg = LaunchConfig::for_num_elems(n as u32);
9784 let ni = n as i32;
9785 let __s_bld = self.gpu.stream();
9786 let mut bld = __s_bld.launch_builder(&f);
9787 bld.arg(a).arg(b_in).arg(dst).arg(&ni);
9788 unsafe {
9789 bld.launch(cfg)?;
9790 }
9791 Ok(())
9792 }
9793
9794 pub fn matmul(
9797 &self,
9798 w: &crate::model::GpuTensor,
9799 x: &CudaSlice<f32>,
9800 m: usize,
9801 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9802 use crate::model::GpuTensor;
9803 let in_f = w.in_features();
9804 let out_f = w.out_features();
9805 #[allow(non_snake_case)]
9813 let GEMM_M_THRESHOLD = if self.verify_exact_on() {
9816 usize::MAX
9817 } else {
9818 16usize
9819 };
9820
9821 const GEMM_MIN_OUT_F: usize = 128; if m >= GEMM_M_THRESHOLD {
9846 if let Some(y) = self.try_fp8_gemm(w, x, m)? {
9847 return Ok(y);
9848 }
9849 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
9856 return Ok(y);
9857 }
9858 if let Some(y) = self.try_f16_gemm(w, x, m)? {
9861 return Ok(y);
9862 }
9863 }
9864 if let GpuTensor::Quant { qtype, .. } = w {
9879 if *qtype == QT_F8_E4M3_BLK {
9880 if m >= GEMM_M_THRESHOLD {
9881 if let Some(y) = self.try_e4m3_blk_prefill(w, x, m)? {
9882 return Ok(y);
9883 }
9884 }
9885 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9886 if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
9887 return Ok(y);
9888 }
9889 }
9890 }
9891 if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.mmq_supports(w) {
9892 return self.qmatvec_mmq(w, x, m);
9893 }
9894 if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.gemm_supports(w) {
9895 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9896 return self.qmatvec_gemm(w, &aq, &ad, m);
9897 }
9898 if m >= GEMM_M_THRESHOLD {
9901 if let Some(y) = self.try_fp4_gemm(w, x, m, in_f, out_f)? {
9902 return Ok(y);
9903 }
9904 }
9905 let fast = std::env::var("MEMRA_FAST").as_deref() != Ok("0");
9909 if m == 1 && fast {
9914 if let GpuTensor::Quant {
9915 bytes,
9916 qtype,
9917 row_bytes,
9918 rp,
9919 rp4,
9920 scale,
9921 ..
9922 } = w
9923 {
9924 if self.mmvq_supports(*qtype) {
9925 let (bytes, rp) = match rp4 {
9929 Some(m4) => (m4, true),
9930 None => (bytes, *rp),
9931 };
9932 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9933 return self.qmatvec_mmvq(
9934 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, rp,
9935 );
9936 }
9937 }
9938 }
9939 if (2..=16).contains(&m)
9955 && fast
9956 && std::env::var("MEMRA_NO_BATCHED").is_err()
9957 && (m <= 4 || Self::b8_enabled())
9958 {
9959 let m_ok = m <= 8
9969 || matches!(w, GpuTensor::Quant { qtype, .. }
9970 if *qtype == QT_Q4_0 || *qtype == QT_Q6_K || *qtype == QT_F8_E4M3
9971 || *qtype == QT_NVFP4 || *qtype == QT_Q4_K || *qtype == QT_Q5_K || *qtype == QT_Q8_0);
9972 if m_ok {
9973 if let GpuTensor::Quant {
9974 bytes,
9975 qtype,
9976 row_bytes,
9977 rp,
9978 rp4,
9979 ..
9980 } = w
9981 {
9982 if self.batched_supports(*qtype) && self.mmvq_supports(*qtype) {
9983 let (bytes, rp) = match rp4 {
9984 Some(m4) => (m4, true),
9985 None => (bytes, *rp),
9986 };
9987 let mcols = Self::batched_mcols(m);
9988 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9989 let mut y = self.qmatvec_mmvq_batched(
9990 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, mcols, 1.0, rp,
9991 )?;
9992 if let GpuTensor::Quant { scale, .. } = w {
9993 if *scale != 1.0 {
9994 self.scale_inplace(&mut y, *scale, m * out_f)?;
9995 }
9996 }
9997 return Ok(y);
9998 }
9999 }
10000 }
10001 }
10002 if fast {
10008 if let GpuTensor::Quant {
10009 bytes,
10010 qtype,
10011 row_bytes,
10012 scale,
10013 ..
10014 } = w
10015 {
10016 if *qtype == QT_F8_E4M3 {
10017 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10018 return self.qmatvec_mmvq(
10019 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, false,
10020 );
10021 }
10022 }
10023 }
10024 let mut y = match w {
10025 GpuTensor::Quant {
10026 bytes,
10027 qtype,
10028 row_bytes,
10029 ..
10030 } if fast && *qtype == QT_Q8_0 => {
10031 self.qmatvec_q8_0_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10032 }
10033 GpuTensor::Quant {
10034 bytes,
10035 qtype,
10036 row_bytes,
10037 ..
10038 } if fast && *qtype == QT_Q4_K => {
10039 self.qmatvec_q4_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10040 }
10041 GpuTensor::Quant {
10042 bytes,
10043 qtype,
10044 row_bytes,
10045 ..
10046 } if fast && *qtype == QT_Q6_K => {
10047 self.qmatvec_q6_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10048 }
10049 GpuTensor::Quant {
10050 bytes,
10051 qtype,
10052 row_bytes,
10053 ..
10054 } if fast && *qtype == QT_Q5_K => {
10055 self.qmatvec_q5_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10056 }
10057 GpuTensor::Quant {
10058 bytes,
10059 qtype,
10060 row_bytes,
10061 ..
10062 } if fast && *qtype == QT_Q3_K => {
10063 self.qmatvec_q3_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10064 }
10065 GpuTensor::Quant {
10066 bytes,
10067 qtype,
10068 row_bytes,
10069 rp,
10070 ..
10071 } if fast && *qtype == QT_NVFP4 => self.qmatvec_dp4a_named(
10072 if *rp {
10073 "qmatvec_nvfp4_dp4a_rp"
10074 } else {
10075 "qmatvec_nvfp4_dp4a"
10076 },
10077 bytes,
10078 x,
10079 m,
10080 in_f,
10081 out_f,
10082 *row_bytes,
10083 )?,
10084 GpuTensor::Quant {
10088 bytes,
10089 qtype,
10090 row_bytes,
10091 ..
10092 } if fast && *qtype == QT_IQ4_XS && Self::iq_fast_enabled() => {
10093 self.qmatvec_iq4_XS_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10094 }
10095 GpuTensor::Quant {
10100 bytes,
10101 qtype,
10102 row_bytes,
10103 rp,
10104 ..
10105 } =>
10106 {
10109 self.qmatvec(
10110 bytes,
10111 x,
10112 m,
10113 in_f,
10114 out_f,
10115 if *rp && *qtype == QT_NVFP4 {
10116 QT_NVFP4_RP
10117 } else {
10118 *qtype
10119 },
10120 *row_bytes,
10121 )?
10122 }
10123 GpuTensor::Float { data, .. } => self.linear(x, data, m, in_f, out_f)?,
10124 GpuTensor::FloatBf16 { data, .. } => {
10127 self.linear_bf16_chunked(x, data, m, in_f, out_f, false)?
10128 }
10129 };
10130 if let GpuTensor::Quant { scale, .. } = w {
10132 if *scale != 1.0 {
10133 self.scale_inplace(&mut y, *scale, m * out_f)?;
10134 }
10135 }
10136 Ok(y)
10137 }
10138
10139 pub fn stage_a_raw_needed() -> bool {
10149 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10150 *ON.get_or_init(|| std::env::var("MEMRA_FAST").as_deref() == Ok("0"))
10151 }
10152
10153 pub fn uses_q8_1_fast(&self, w: &crate::model::GpuTensor) -> bool {
10156 use crate::model::GpuTensor;
10157 if std::env::var("MEMRA_FAST").as_deref() == Ok("0") {
10158 return false;
10159 }
10160 match w {
10161 GpuTensor::Quant { qtype, .. } => {
10168 matches!(
10169 *qtype,
10170 QT_Q8_0
10171 | QT_Q4_K
10172 | QT_Q6_K
10173 | QT_Q5_K
10174 | QT_Q3_K
10175 | QT_NVFP4
10176 | QT_F8_E4M3
10177 | QT_F8_E4M3_BLK
10178 | QT_Q4_0
10179 ) || (*qtype == QT_IQ4_XS && Self::iq_fast_enabled())
10180 }
10181 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
10182 }
10183 }
10184
10185 pub fn matmul_pre(
10190 &self,
10191 w: &crate::model::GpuTensor,
10192 aq: &CudaSlice<i8>,
10193 ad: &CudaSlice<f32>,
10194 x_fallback: &CudaSlice<f32>,
10195 m: usize,
10196 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10197 use crate::model::GpuTensor;
10198 let x_raw_ok = x_fallback.len() >= m * w.in_features();
10204 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
10207 if let Some(y) = self.try_fp8_gemm(w, x_fallback, m)? {
10208 return Ok(y);
10209 }
10210 if let Some(y) = self.try_fp8_blk_mmq(w, x_fallback, m)? {
10213 return Ok(y);
10214 }
10215 if let Some(y) = self.try_f16_gemm(w, x_fallback, m)? {
10217 return Ok(y);
10218 }
10219 }
10220 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
10226 if let Some(y) = self.try_e4m3_blk_prefill(w, x_fallback, m)? {
10227 return Ok(y);
10228 }
10229 }
10230 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
10231 return Ok(y);
10232 }
10233 if m >= 16
10238 && w.out_features() >= 128
10239 && self.mmq_supports(w)
10240 && !self.verify_exact_on()
10241 && x_raw_ok
10242 {
10243 return self.qmatvec_mmq(w, x_fallback, m);
10244 }
10245 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
10248 if let Some(y) =
10249 self.try_fp4_gemm(w, x_fallback, m, w.in_features(), w.out_features())?
10250 {
10251 return Ok(y);
10252 }
10253 }
10254 if m >= 16 && self.gemm_supports(w) && !self.verify_exact_on() {
10257 return self.qmatvec_gemm(w, aq, ad, m);
10258 }
10259 if !self.uses_q8_1_fast(w) {
10278 if !x_raw_ok {
10279 return Err(format!(
10280 "matmul_pre: q8_1-fast is off for this weight but x_fallback holds {} f32 \
10281 (need m*in_f = {}*{} = {}). This call site pre-quantized its activation and \
10282 dropped the f32, so there is nothing to fall back to — pass the real f32 \
10283 activation (see Engine::rms_norm_decode, which is bit-identical to \
10284 rms_norm_q8_1's reduction) or keep the weight on the q8_1 path.",
10285 x_fallback.len(),
10286 m,
10287 w.in_features(),
10288 m * w.in_features()
10289 )
10290 .into());
10291 }
10292 return self.matmul(w, x_fallback, m);
10293 }
10294 let in_f = w.in_features();
10295 let out_f = w.out_features();
10296 let (bytes, qtype, row_bytes, scale, rp) = match w {
10297 GpuTensor::Quant {
10298 bytes,
10299 qtype,
10300 row_bytes,
10301 scale,
10302 rp,
10303 ..
10304 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10305 _ => unreachable!("uses_q8_1_fast guaranteed Quant"),
10306 };
10307 let (mbytes, mrp) = match w {
10310 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10311 _ => (bytes, rp),
10312 };
10313 if m == 1 && self.mmvq_supports(qtype) {
10317 return self.qmatvec_mmvq(mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, mrp);
10318 }
10319 if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
10332 && std::env::var("MEMRA_NO_BATCHED").is_err()
10333 && (m <= 4 || Self::b8_enabled())
10334 && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_NVFP4
10338 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_F8_E4M3 || qtype == QT_Q8_0)
10339 {
10340 let mcols = Self::batched_mcols(m);
10341 return self.qmatvec_mmvq_batched(
10342 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, mrp,
10343 );
10344 }
10345 if qtype == QT_F8_E4M3 || qtype == QT_Q4_0 {
10351 let (b2, r2) = if qtype == QT_Q4_0 {
10352 (mbytes, mrp)
10353 } else {
10354 (bytes, rp)
10355 };
10356 return self.qmatvec_mmvq(b2, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, r2);
10357 }
10358 let name = match qtype {
10359 QT_Q8_0 => "qmatvec_q8_0_dp4a",
10360 QT_Q4_K => "qmatvec_q4_K_dp4a",
10361 QT_Q6_K => "qmatvec_q6_K_dp4a",
10362 QT_Q5_K => "qmatvec_q5_K_dp4a",
10363 QT_Q3_K => "qmatvec_q3_K_dp4a",
10364 QT_NVFP4 => {
10365 if rp {
10366 "qmatvec_nvfp4_dp4a_rp"
10367 } else {
10368 "qmatvec_nvfp4_dp4a"
10369 }
10370 }
10371 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
10372 _ => unreachable!(),
10373 };
10374 let f = self.func(name);
10375 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
10377 grid_dim: (out_f as u32, m as u32, 1),
10378 block_dim: (128, 1, 1),
10379 shared_mem_bytes: 0,
10380 };
10381 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
10382 let __s_b = self.gpu.stream();
10383 let mut b = __s_b.launch_builder(&f);
10384 b.arg(bytes)
10385 .arg(aq)
10386 .arg(ad)
10387 .arg(&mut y)
10388 .arg(&inf)
10389 .arg(&outf)
10390 .arg(&mi)
10391 .arg(&rb);
10392 unsafe {
10393 b.launch(cfg)?;
10394 }
10395 if scale != 1.0 {
10396 self.scale_inplace(&mut y, scale, m * out_f)?;
10397 }
10398 Ok(y)
10399 }
10400
10401 pub fn matmul_decode_exact(
10409 &self,
10410 w: &crate::model::GpuTensor,
10411 x: &CudaSlice<f32>,
10412 m: usize,
10413 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10414 use crate::model::GpuTensor;
10415 if let GpuTensor::Float { data, .. } = w {
10423 return self.linear_decode_exact(x, data, m, w.in_features(), w.out_features());
10424 }
10425 if let GpuTensor::FloatBf16 { data, .. } = w {
10428 let (in_f, out_f) = (w.in_features(), w.out_features());
10429 return self.linear_bf16_chunked(x, data, m, in_f, out_f, true);
10430 }
10431 if !self.uses_q8_1_fast(w) {
10432 return self.matmul(w, x, m);
10433 }
10434 let in_f = w.in_features();
10435 let out_f = w.out_features();
10436 let (bytes, qtype, row_bytes, scale, rp) = match w {
10437 GpuTensor::Quant {
10438 bytes,
10439 qtype,
10440 row_bytes,
10441 scale,
10442 rp,
10443 ..
10444 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10445 _ => return self.matmul(w, x, m),
10446 };
10447 let (bytes, rp) = match w {
10450 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10451 _ => (bytes, rp),
10452 };
10453 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10454 if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
10458 return Ok(y);
10459 }
10460 if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
10469 && std::env::var("MEMRA_NO_BATCHED").is_err()
10470 && (m <= 4 || Self::b8_enabled())
10471 && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_F8_E4M3
10474 || qtype == QT_NVFP4 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_Q8_0)
10475 {
10476 let mcols = Self::batched_mcols(m);
10477 return self.qmatvec_mmvq_batched(
10478 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
10479 );
10480 }
10481 if self.mmvq_supports(qtype) {
10482 return self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
10485 }
10486 self.matmul_pre(w, &aq, &ad, x, m)
10489 }
10490
10491 pub fn matmul_decode_exact_pre(
10501 &self,
10502 w: &crate::model::GpuTensor,
10503 aq: &CudaSlice<i8>,
10504 ad: &CudaSlice<f32>,
10505 m: usize,
10506 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10507 use crate::model::GpuTensor;
10508 debug_assert!(
10509 self.uses_q8_1_fast(w),
10510 "matmul_decode_exact_pre: caller must guarantee q8_1-fast"
10511 );
10512 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
10514 return Ok(y);
10515 }
10516 let in_f = w.in_features();
10517 let out_f = w.out_features();
10518 let (bytes, qtype, row_bytes, scale, rp) = match w {
10519 GpuTensor::Quant {
10520 bytes,
10521 qtype,
10522 row_bytes,
10523 scale,
10524 rp,
10525 ..
10526 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10527 _ => {
10528 return Err(
10529 "matmul_decode_exact_pre: Quant tensor required (q8_1-fast contract)".into(),
10530 );
10531 }
10532 };
10533 let (bytes, rp) = match w {
10535 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10536 _ => (bytes, rp),
10537 };
10538 if (2..=16).contains(&m)
10540 && self.batched_supports(qtype)
10541 && self.mmvq_supports(qtype)
10542 && std::env::var("MEMRA_NO_BATCHED").is_err()
10543 && (m <= 4 || Self::b8_enabled())
10544 && (m <= 8
10545 || qtype == QT_Q4_0
10546 || qtype == QT_Q6_K
10547 || qtype == QT_F8_E4M3
10548 || qtype == QT_NVFP4
10549 || qtype == QT_Q4_K
10550 || qtype == QT_Q5_K
10551 || qtype == QT_Q8_0)
10552 {
10553 let mcols = Self::batched_mcols(m);
10554 return self.qmatvec_mmvq_batched(
10555 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
10556 );
10557 }
10558 if self.mmvq_supports(qtype) {
10559 return self.qmatvec_mmvq(bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
10560 }
10561 let x0 = self.zeros(0)?;
10564 self.matmul_pre(w, aq, ad, &x0, m)
10565 }
10566
10567 pub fn matmul_decode_exact_dual_pre(
10576 &self,
10577 w0: &crate::model::GpuTensor,
10578 w1: &crate::model::GpuTensor,
10579 aq: &CudaSlice<i8>,
10580 ad: &CudaSlice<f32>,
10581 m: usize,
10582 ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
10583 {
10584 use crate::model::GpuTensor;
10585 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10586 let on = *ON.get_or_init(|| {
10587 std::env::var("MEMRA_SPEC_DUAL_T")
10588 .map(|v| v != "0")
10589 .unwrap_or(true)
10590 });
10591 if !on
10592 || !(2..=7).contains(&m)
10593 || std::env::var("MEMRA_NO_BATCHED").is_ok()
10594 || !self.uses_q8_1_fast(w0)
10595 || !self.uses_q8_1_fast(w1)
10596 {
10597 return Ok(None);
10598 }
10599 if !self.mmvq_supports(QT_NVFP4) {
10604 return Ok(None);
10605 }
10606 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10607 if w1.in_features() != in_f || w1.out_features() != out_f {
10608 return Ok(None);
10609 }
10610 let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
10611 (
10612 GpuTensor::Quant {
10613 bytes: b0,
10614 qtype: q0,
10615 row_bytes: rb0,
10616 scale: s0,
10617 rp: rp0,
10618 rp4: None,
10619 ..
10620 },
10621 GpuTensor::Quant {
10622 bytes: b1,
10623 qtype: q1,
10624 row_bytes: rb1,
10625 scale: s1,
10626 rp: rp1,
10627 rp4: None,
10628 ..
10629 },
10630 ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
10631 (b0, b1, *rb0, *s0, *s1, *rp0)
10632 }
10633 _ => return Ok(None),
10634 };
10635 if m > 4 && !(rp && Self::b8_enabled() && std::env::var("MEMRA_B567").as_deref() != Ok("0"))
10638 {
10639 return Ok(None);
10640 }
10641 let (y0, y1) =
10642 self.qmatvec_batched_dual_raw(b0, b1, aq, ad, m, in_f, out_f, row_bytes, rp)?;
10643 Ok(Some(((y0, s0), (y1, s1))))
10644 }
10645
10646 pub fn matmul_decode_exact_dual(
10662 &self,
10663 w0: &crate::model::GpuTensor,
10664 w1: &crate::model::GpuTensor,
10665 x: &CudaSlice<f32>,
10666 m: usize,
10667 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10668 use crate::model::GpuTensor;
10669 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10670 let on = *ON.get_or_init(|| {
10671 std::env::var("MEMRA_SPEC_DUAL_T")
10672 .map(|v| v != "0")
10673 .unwrap_or(true)
10674 });
10675 if !on
10676 || !(2..=4).contains(&m)
10677 || std::env::var("MEMRA_NO_BATCHED").is_ok()
10678 || !self.uses_q8_1_fast(w0)
10679 || !self.uses_q8_1_fast(w1)
10680 {
10681 return Ok(None);
10682 }
10683 if !self.mmvq_supports(QT_NVFP4) {
10688 return Ok(None);
10689 }
10690 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10691 if w1.in_features() != in_f || w1.out_features() != out_f {
10692 return Ok(None);
10693 }
10694 let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
10695 (
10696 GpuTensor::Quant {
10697 bytes: b0,
10698 qtype: q0,
10699 row_bytes: rb0,
10700 scale: s0,
10701 rp: rp0,
10702 rp4: None,
10703 ..
10704 },
10705 GpuTensor::Quant {
10706 bytes: b1,
10707 qtype: q1,
10708 row_bytes: rb1,
10709 scale: s1,
10710 rp: rp1,
10711 rp4: None,
10712 ..
10713 },
10714 ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
10715 (b0, b1, *rb0, *s0, *s1, *rp0)
10716 }
10717 _ => return Ok(None),
10718 };
10719 if std::env::var("MEMRA_DEBUG").is_ok() {
10722 static ONCE: std::sync::Once = std::sync::Once::new();
10723 ONCE.call_once(|| eprintln!("[memra] dual gate+up batched ENGAGED (m={m} rp={rp})"));
10724 }
10725 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10726 let (y0, y1) =
10727 self.qmatvec_batched_dual_raw(b0, b1, &aq, &ad, m, in_f, out_f, row_bytes, rp)?;
10728 let mut y0 = y0;
10729 let mut y1 = y1;
10730 if s0 != 1.0 {
10731 self.scale_inplace(&mut y0, s0, m * out_f)?;
10732 }
10733 if s1 != 1.0 {
10734 self.scale_inplace(&mut y1, s1, m * out_f)?;
10735 }
10736 Ok(Some((y0, y1)))
10737 }
10738
10739 #[allow(clippy::too_many_arguments)]
10744 pub fn qmatvec_batched_dual_raw(
10745 &self,
10746 b0: &CudaSlice<u8>,
10747 b1: &CudaSlice<u8>,
10748 aq: &CudaSlice<i8>,
10749 ad: &CudaSlice<f32>,
10750 m: usize,
10751 in_f: usize,
10752 out_f: usize,
10753 row_bytes: usize,
10754 rp: bool,
10755 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10756 const ROWS_PER_BLOCK: u32 = 4;
10757 let mcols = Self::batched_mcols(m);
10758 let tiny_rp1 = rp
10761 && mcols == 4
10762 && out_f <= 128
10763 && std::env::var("MEMRA_NVFP4_AUX_DUAL").as_deref() != Ok("0");
10764 let (name, rows_per_block) = if tiny_rp1 {
10765 ("qmatvec_nvfp4_mmvq_dual_b4_rp", ROWS_PER_BLOCK)
10766 } else {
10767 match (mcols, rp, m) {
10768 (2, false, _) => ("qmatvec_nvfp4_mmvq_dual_b2", ROWS_PER_BLOCK),
10769 (4, false, _) => ("qmatvec_nvfp4_mmvq_dual_b4_r2", ROWS_PER_BLOCK * 2),
10770 (2, true, _) => ("qmatvec_nvfp4_mmvq_dual_b2_rp", ROWS_PER_BLOCK),
10771 (4, true, _) => ("qmatvec_nvfp4_mmvq_dual_b4_rpr2", ROWS_PER_BLOCK * 2),
10772 (8, true, 5) => ("qmatvec_nvfp4_mmvq_dual_b5_rpr2", ROWS_PER_BLOCK * 2),
10773 (8, true, 6) => ("qmatvec_nvfp4_mmvq_dual_b6_rpr2", ROWS_PER_BLOCK * 2),
10774 (8, true, 7) => ("qmatvec_nvfp4_mmvq_dual_b7_rpr2", ROWS_PER_BLOCK * 2),
10775 _ => {
10776 return Err(
10777 format!("qmatvec_batched_dual_raw: no dual kernel for m {m}").into(),
10778 );
10779 }
10780 }
10781 };
10782 let f = self.func(name);
10783 let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
10784 let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
10785 let cfg = LaunchConfig {
10786 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
10787 block_dim: (32, ROWS_PER_BLOCK, 1),
10788 shared_mem_bytes: 0,
10789 };
10790 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
10791 let __s_b = self.gpu.stream();
10792 let mut b = __s_b.launch_builder(&f);
10793 b.arg(b0)
10794 .arg(b1)
10795 .arg(aq)
10796 .arg(ad)
10797 .arg(&mut y0)
10798 .arg(&mut y1)
10799 .arg(&inf)
10800 .arg(&outf)
10801 .arg(&mi)
10802 .arg(&rb);
10803 unsafe {
10804 b.launch(cfg)?;
10805 }
10806 Ok((y0, y1))
10807 }
10808
10809 pub fn matmul_pre_dual_noscale(
10821 &self,
10822 w0: &crate::model::GpuTensor,
10823 w1: &crate::model::GpuTensor,
10824 aq: &CudaSlice<i8>,
10825 ad: &CudaSlice<f32>,
10826 m: usize,
10827 ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
10828 {
10829 use crate::model::GpuTensor;
10830 if m != 1 || !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
10831 return Ok(None);
10832 }
10833 if !self.mmvq_supports(QT_NVFP4) {
10843 return Ok(None);
10844 }
10845 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10846 if w1.in_features() != in_f || w1.out_features() != out_f {
10847 return Ok(None);
10848 }
10849 let no_mirror =
10862 |w: &crate::model::GpuTensor| !matches!(w, GpuTensor::Quant { rp4: Some(_), .. });
10863 if self.q8_ffn_fuse2_on()
10864 && no_mirror(w0)
10865 && no_mirror(w1)
10866 && let Some([p0, p1]) = self.q8_fused_params(&[w0, w1])
10867 {
10868 let (y0, y1) = self.q8_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2)?;
10869 return Ok(Some(((y0, 1.0), (y1, 1.0))));
10870 }
10871 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
10881 let (y0, y1) =
10882 self.e4m3_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2, 1.0, 1.0)?;
10883 return Ok(Some(((y0, p0.3), (y1, p1.3))));
10884 }
10885 let (b0, q0, rb0, s0, rp0) = match w0 {
10886 GpuTensor::Quant {
10887 bytes,
10888 qtype,
10889 row_bytes,
10890 scale,
10891 rp,
10892 ..
10893 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10894 _ => return Ok(None),
10895 };
10896 let (b1, q1, rb1, s1, rp1) = match w1 {
10897 GpuTensor::Quant {
10898 bytes,
10899 qtype,
10900 row_bytes,
10901 scale,
10902 rp,
10903 ..
10904 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10905 _ => return Ok(None),
10906 };
10907 if q0 != QT_NVFP4 || q1 != QT_NVFP4 || rb0 != rb1 || rp0 != rp1 {
10908 return Ok(None);
10909 }
10910 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
10912 let rows_per_block = ROWS_PER_BLOCK * RPW;
10913 let f = self.func(if rp0 {
10914 "qmatvec_nvfp4_mmvq_dual_mr2_rp"
10915 } else {
10916 "qmatvec_nvfp4_mmvq_dual_mr2"
10917 });
10918 let mut y0 = self.alloc_uninit::<f32>(out_f)?;
10919 let mut y1 = self.alloc_uninit::<f32>(out_f)?;
10920 let cfg = LaunchConfig {
10921 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
10922 block_dim: (32, ROWS_PER_BLOCK, 1),
10923 shared_mem_bytes: 0,
10924 };
10925 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, rb0 as i64);
10926 let one = 1.0f32;
10929 let __s_b = self.gpu.stream();
10930 let mut b = __s_b.launch_builder(&f);
10931 b.arg(b0)
10932 .arg(b1)
10933 .arg(aq)
10934 .arg(ad)
10935 .arg(&mut y0)
10936 .arg(&mut y1)
10937 .arg(&inf)
10938 .arg(&outf)
10939 .arg(&mi)
10940 .arg(&rb)
10941 .arg(&one)
10942 .arg(&one);
10943 unsafe {
10944 b.launch(cfg)?;
10945 }
10946 Ok(Some(((y0, s0), (y1, s1))))
10947 }
10948
10949 #[allow(clippy::too_many_arguments)]
10957 pub fn matmul_nvfp4_fused3(
10958 &self,
10959 w0: &crate::model::GpuTensor,
10960 w1: &crate::model::GpuTensor,
10961 w2: &crate::model::GpuTensor,
10962 aq: &CudaSlice<i8>,
10963 ad: &CudaSlice<f32>,
10964 m: usize,
10965 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
10966 {
10967 use crate::model::GpuTensor;
10968 if m != 1
10972 || !self.mmvq_supports(QT_NVFP4)
10973 || !self.uses_q8_1_fast(w0)
10974 || !self.uses_q8_1_fast(w1)
10975 || !self.uses_q8_1_fast(w2)
10976 {
10977 return Ok(None);
10978 }
10979 let unpack = |w: &crate::model::GpuTensor| match w {
10980 GpuTensor::Quant {
10981 bytes,
10982 qtype,
10983 scale,
10984 rp,
10985 ..
10986 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
10987 _ => None,
10988 };
10989 let (Some(p0), Some(p1), Some(p2)) = (unpack(w0), unpack(w1), unpack(w2)) else {
10990 return Ok(None);
10991 };
10992 let in_f = w0.in_features();
10993 if w1.in_features() != in_f || w2.in_features() != in_f {
10994 return Ok(None);
10995 }
10996 let (o0, o1, o2) = (w0.out_features(), w1.out_features(), w2.out_features());
10997 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
10999 let rows_pb = ROWS_PER_BLOCK * RPW;
11000 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
11001 let f = self.func("qmatvec_nvfp4_mmvq_fused3_rp");
11002 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11003 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11004 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
11005 let cfg = LaunchConfig {
11006 grid_dim: (nb(o0) + nb(o1) + nb(o2), m as u32, 1),
11007 block_dim: (32, ROWS_PER_BLOCK, 1),
11008 shared_mem_bytes: 0,
11009 };
11010 let (inf, oi0, oi1, oi2, mi) = (in_f as i32, o0 as i32, o1 as i32, o2 as i32, m as i32);
11011 let (b0, b1, b2) = unsafe { (&*p0.0, &*p1.0, &*p2.0) };
11014 let __s_b = self.gpu.stream();
11015 let mut b = __s_b.launch_builder(&f);
11016 b.arg(b0)
11017 .arg(b1)
11018 .arg(b2)
11019 .arg(aq)
11020 .arg(ad)
11021 .arg(&mut y0)
11022 .arg(&mut y1)
11023 .arg(&mut y2)
11024 .arg(&inf)
11025 .arg(&oi0)
11026 .arg(&oi1)
11027 .arg(&oi2)
11028 .arg(&mi)
11029 .arg(&p0.1)
11030 .arg(&p1.1)
11031 .arg(&p2.1);
11032 unsafe {
11033 b.launch(cfg)?;
11034 }
11035 Ok(Some((y0, y1, y2)))
11036 }
11037
11038 pub fn matmul_nvfp4_fused2(
11047 &self,
11048 w0: &crate::model::GpuTensor,
11049 w1: &crate::model::GpuTensor,
11050 aq: &CudaSlice<i8>,
11051 ad: &CudaSlice<f32>,
11052 m: usize,
11053 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11054 use crate::model::GpuTensor;
11055 static FUSED2_OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11056 let off =
11057 *FUSED2_OFF.get_or_init(|| std::env::var("MEMRA_NVFP4_FUSED2").as_deref() == Ok("0"));
11058 if off
11061 || m != 1
11062 || !self.mmvq_supports(QT_NVFP4)
11063 || !self.uses_q8_1_fast(w0)
11064 || !self.uses_q8_1_fast(w1)
11065 {
11066 return Ok(None);
11067 }
11068 let unpack = |w: &crate::model::GpuTensor| match w {
11069 GpuTensor::Quant {
11070 bytes,
11071 qtype,
11072 scale,
11073 rp,
11074 ..
11075 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
11076 _ => None,
11077 };
11078 let (Some(p0), Some(p1)) = (unpack(w0), unpack(w1)) else {
11079 return Ok(None);
11080 };
11081 let in_f = w0.in_features();
11082 if w1.in_features() != in_f {
11083 return Ok(None);
11084 }
11085 let (o0, o1) = (w0.out_features(), w1.out_features());
11086 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11088 let rows_pb = ROWS_PER_BLOCK * RPW;
11089 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
11090 let f = self.func("qmatvec_nvfp4_mmvq_fused2_rp");
11091 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11092 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11093 let cfg = LaunchConfig {
11094 grid_dim: (nb(o0) + nb(o1), m as u32, 1),
11095 block_dim: (32, ROWS_PER_BLOCK, 1),
11096 shared_mem_bytes: 0,
11097 };
11098 let (inf, oi0, oi1, mi) = (in_f as i32, o0 as i32, o1 as i32, m as i32);
11099 let (b0, b1) = unsafe { (&*p0.0, &*p1.0) };
11102 if Self::pdl_on() && Self::pdl_mmvq_on() && Self::pdl_nvfp4q8_on() {
11105 {
11106 use cudarc::driver::{DevicePtr, DevicePtrMut};
11107 let s = &self.gpu.stream();
11108 let (pw0, _g0) = b0.device_ptr(s);
11109 let (pw1, _g1) = b1.device_ptr(s);
11110 let (paq, _g2) = aq.device_ptr(s);
11111 let (pad, _g3) = ad.device_ptr(s);
11112 let (py0, _g4) = y0.device_ptr_mut(s);
11113 let (py1, _g5) = y1.device_ptr_mut(s);
11114 let (s0, s1) = (p0.1, p1.1);
11115 let mut ps = [
11116 &pw0 as *const _ as *mut std::ffi::c_void,
11117 &pw1 as *const _ as *mut _,
11118 &paq as *const _ as *mut _,
11119 &pad as *const _ as *mut _,
11120 &py0 as *const _ as *mut _,
11121 &py1 as *const _ as *mut _,
11122 &inf as *const _ as *mut _,
11123 &oi0 as *const _ as *mut _,
11124 &oi1 as *const _ as *mut _,
11125 &mi as *const _ as *mut _,
11126 &s0 as *const _ as *mut _,
11127 &s1 as *const _ as *mut _,
11128 ];
11129 unsafe {
11130 self.launch_pdl(
11131 "qmatvec_nvfp4_mmvq_fused2_rp",
11132 cfg.grid_dim,
11133 cfg.block_dim,
11134 &mut ps,
11135 )?;
11136 }
11137 }
11138 return Ok(Some((y0, y1)));
11139 }
11140 let __s_b = self.gpu.stream();
11141 let mut b = __s_b.launch_builder(&f);
11142 b.arg(b0)
11143 .arg(b1)
11144 .arg(aq)
11145 .arg(ad)
11146 .arg(&mut y0)
11147 .arg(&mut y1)
11148 .arg(&inf)
11149 .arg(&oi0)
11150 .arg(&oi1)
11151 .arg(&mi)
11152 .arg(&p0.1)
11153 .arg(&p1.1);
11154 unsafe {
11155 b.launch(cfg)?;
11156 }
11157 Ok(Some((y0, y1)))
11158 }
11159
11160 pub fn matmul_nvfp4_fused2_into(
11165 &self,
11166 w0: &crate::model::GpuTensor,
11167 w1: &crate::model::GpuTensor,
11168 aq: &CudaSlice<i8>,
11169 ad: &CudaSlice<f32>,
11170 y0: &mut CudaSlice<f32>,
11171 y1: &mut CudaSlice<f32>,
11172 ) -> Result<bool, Box<dyn std::error::Error>> {
11173 use crate::model::GpuTensor;
11174 static FUSED2_OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11175 let off =
11176 *FUSED2_OFF.get_or_init(|| std::env::var("MEMRA_NVFP4_FUSED2").as_deref() == Ok("0"));
11177 if off
11178 || !self.mmvq_supports(QT_NVFP4)
11179 || !self.uses_q8_1_fast(w0)
11180 || !self.uses_q8_1_fast(w1)
11181 {
11182 return Ok(false);
11183 }
11184 let unpack = |w: &crate::model::GpuTensor| match w {
11185 GpuTensor::Quant {
11186 bytes,
11187 qtype,
11188 scale,
11189 rp,
11190 ..
11191 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
11192 _ => None,
11193 };
11194 let (Some(p0), Some(p1)) = (unpack(w0), unpack(w1)) else {
11195 return Ok(false);
11196 };
11197 let in_f = w0.in_features();
11198 if w1.in_features() != in_f {
11199 return Ok(false);
11200 }
11201 let (o0, o1) = (w0.out_features(), w1.out_features());
11202 if y0.len() < o0 || y1.len() < o1 {
11203 return Ok(false);
11204 }
11205 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11207 let rows_pb = ROWS_PER_BLOCK * RPW;
11208 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
11209 let f = self.func("qmatvec_nvfp4_mmvq_fused2_rp");
11210 let cfg = LaunchConfig {
11211 grid_dim: (nb(o0) + nb(o1), 1, 1),
11212 block_dim: (32, ROWS_PER_BLOCK, 1),
11213 shared_mem_bytes: 0,
11214 };
11215 let (inf, oi0, oi1, mi) = (in_f as i32, o0 as i32, o1 as i32, 1i32);
11216 let (b0, b1) = unsafe { (&*p0.0, &*p1.0) };
11219 let __s_b = self.gpu.stream();
11220 let mut b = __s_b.launch_builder(&f);
11221 b.arg(b0)
11222 .arg(b1)
11223 .arg(aq)
11224 .arg(ad)
11225 .arg(&mut *y0)
11226 .arg(&mut *y1)
11227 .arg(&inf)
11228 .arg(&oi0)
11229 .arg(&oi1)
11230 .arg(&mi)
11231 .arg(&p0.1)
11232 .arg(&p1.1);
11233 unsafe {
11234 b.launch(cfg)?;
11235 }
11236 Ok(true)
11237 }
11238
11239 #[allow(clippy::type_complexity)]
11244 pub fn matmul_nvfp4_fused4(
11245 &self,
11246 w0: &crate::model::GpuTensor,
11247 w1: &crate::model::GpuTensor,
11248 w2: &crate::model::GpuTensor,
11249 w3: &crate::model::GpuTensor,
11250 aq: &CudaSlice<i8>,
11251 ad: &CudaSlice<f32>,
11252 m: usize,
11253 ) -> Result<
11254 Option<(
11255 CudaSlice<f32>,
11256 CudaSlice<f32>,
11257 CudaSlice<f32>,
11258 CudaSlice<f32>,
11259 )>,
11260 Box<dyn std::error::Error>,
11261 > {
11262 use crate::model::GpuTensor;
11263 if m != 1
11265 || std::env::var("MEMRA_NVFP4_FUSED4").as_deref() == Ok("0")
11266 || !self.mmvq_supports(QT_NVFP4)
11267 || !self.uses_q8_1_fast(w0)
11268 || !self.uses_q8_1_fast(w1)
11269 || !self.uses_q8_1_fast(w2)
11270 || !self.uses_q8_1_fast(w3)
11271 {
11272 return Ok(None);
11273 }
11274 let unpack = |w: &crate::model::GpuTensor| match w {
11275 GpuTensor::Quant {
11276 bytes,
11277 qtype,
11278 scale,
11279 rp,
11280 ..
11281 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
11282 _ => None,
11283 };
11284 let (Some(p0), Some(p1), Some(p2), Some(p3)) =
11285 (unpack(w0), unpack(w1), unpack(w2), unpack(w3))
11286 else {
11287 return Ok(None);
11288 };
11289 let in_f = w0.in_features();
11290 if w1.in_features() != in_f || w2.in_features() != in_f || w3.in_features() != in_f {
11291 return Ok(None);
11292 }
11293 let (o0, o1, o2, o3) = (
11294 w0.out_features(),
11295 w1.out_features(),
11296 w2.out_features(),
11297 w3.out_features(),
11298 );
11299 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11301 let rows_pb = ROWS_PER_BLOCK * RPW;
11302 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
11303 let f = self.func("qmatvec_nvfp4_mmvq_fused4_rp");
11304 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11305 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11306 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
11307 let mut y3 = self.alloc_uninit::<f32>(m * o3)?;
11308 let cfg = LaunchConfig {
11309 grid_dim: (nb(o0) + nb(o1) + nb(o2) + nb(o3), m as u32, 1),
11310 block_dim: (32, ROWS_PER_BLOCK, 1),
11311 shared_mem_bytes: 0,
11312 };
11313 let (inf, oi0, oi1, oi2, oi3, mi) = (
11314 in_f as i32,
11315 o0 as i32,
11316 o1 as i32,
11317 o2 as i32,
11318 o3 as i32,
11319 m as i32,
11320 );
11321 let (b0, b1, b2, b3) = unsafe { (&*p0.0, &*p1.0, &*p2.0, &*p3.0) };
11324 let __s_b = self.gpu.stream();
11325 let mut b = __s_b.launch_builder(&f);
11326 b.arg(b0)
11327 .arg(b1)
11328 .arg(b2)
11329 .arg(b3)
11330 .arg(aq)
11331 .arg(ad)
11332 .arg(&mut y0)
11333 .arg(&mut y1)
11334 .arg(&mut y2)
11335 .arg(&mut y3)
11336 .arg(&inf)
11337 .arg(&oi0)
11338 .arg(&oi1)
11339 .arg(&oi2)
11340 .arg(&oi3)
11341 .arg(&mi)
11342 .arg(&p0.1)
11343 .arg(&p1.1)
11344 .arg(&p2.1)
11345 .arg(&p3.1);
11346 unsafe {
11347 b.launch(cfg)?;
11348 }
11349 Ok(Some((y0, y1, y2, y3)))
11350 }
11351
11352 pub fn matmul_q8_fused2(
11360 &self,
11361 w0: &crate::model::GpuTensor,
11362 w1: &crate::model::GpuTensor,
11363 aq: &CudaSlice<i8>,
11364 ad: &CudaSlice<f32>,
11365 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11366 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
11372 return Ok(Some(self.e4m3_fused2_core(
11373 p0.0,
11374 p1.0,
11375 aq,
11376 ad,
11377 w0.in_features(),
11378 p0.1,
11379 p1.1,
11380 p0.2,
11381 p0.3,
11382 p1.3,
11383 )?));
11384 }
11385 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
11386 return Ok(None);
11387 };
11388 Ok(Some(self.q8_fused2_core(
11389 p0.0,
11390 p1.0,
11391 aq,
11392 ad,
11393 w0.in_features(),
11394 p0.1,
11395 p1.1,
11396 p0.2,
11397 )?))
11398 }
11399
11400 #[allow(clippy::too_many_arguments)]
11401 fn q8_fused2_core(
11402 &self,
11403 b0: &CudaSlice<u8>,
11404 b1: &CudaSlice<u8>,
11405 aq: &CudaSlice<i8>,
11406 ad: &CudaSlice<f32>,
11407 in_f: usize,
11408 out0: usize,
11409 out1: usize,
11410 row_bytes: usize,
11411 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11412 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11414 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11415 let f = self.func("qmatvec_q8_0_mmvq_fused2");
11416 let mut y0 = self.alloc_uninit::<f32>(out0)?;
11417 let mut y1 = self.alloc_uninit::<f32>(out1)?;
11418 let cfg = LaunchConfig {
11419 grid_dim: (nb0 + nb1, 1, 1),
11420 block_dim: (32, ROWS_PER_BLOCK, 1),
11421 shared_mem_bytes: 0,
11422 };
11423 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
11424 let __s_b = self.gpu.stream();
11425 let mut b = __s_b.launch_builder(&f);
11426 b.arg(b0)
11427 .arg(b1)
11428 .arg(aq)
11429 .arg(ad)
11430 .arg(&mut y0)
11431 .arg(&mut y1)
11432 .arg(&inf)
11433 .arg(&o0)
11434 .arg(&o1)
11435 .arg(&rbl);
11436 unsafe {
11437 b.launch(cfg)?;
11438 }
11439 Ok((y0, y1))
11440 }
11441
11442 pub fn matmul_q8_fused2_x(
11448 &self,
11449 w0: &crate::model::GpuTensor,
11450 w1: &crate::model::GpuTensor,
11451 x: &CudaSlice<f32>,
11452 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11453 if !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
11454 return Ok(None);
11455 }
11456 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
11457 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
11458 return Ok(Some(self.e4m3_fused2_core(
11459 p0.0,
11460 p1.0,
11461 &aq,
11462 &ad,
11463 w0.in_features(),
11464 p0.1,
11465 p1.1,
11466 p0.2,
11467 p0.3,
11468 p1.3,
11469 )?));
11470 }
11471 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
11472 return Ok(None);
11473 };
11474 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
11475 Ok(Some(self.q8_fused2_core(
11476 p0.0,
11477 p1.0,
11478 &aq,
11479 &ad,
11480 w0.in_features(),
11481 p0.1,
11482 p1.1,
11483 p0.2,
11484 )?))
11485 }
11486
11487 #[allow(clippy::too_many_arguments)]
11490 pub fn qmatvec_q8_fused2_raw(
11491 &self,
11492 b0: &CudaSlice<u8>,
11493 b1: &CudaSlice<u8>,
11494 x: &CudaSlice<f32>,
11495 in_f: usize,
11496 out0: usize,
11497 out1: usize,
11498 row_bytes: usize,
11499 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11500 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
11501 self.q8_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes)
11502 }
11503
11504 pub fn matmul_q4_fused3(
11510 &self,
11511 w0: &crate::model::GpuTensor,
11512 w1: &crate::model::GpuTensor,
11513 w2: &crate::model::GpuTensor,
11514 aq: &CudaSlice<i8>,
11515 ad: &CudaSlice<f32>,
11516 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11517 {
11518 use crate::model::GpuTensor;
11519 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11520 match w {
11521 GpuTensor::Quant {
11522 qtype, row_bytes, ..
11523 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11524 _ => None,
11525 }
11526 };
11527 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
11528 return Ok(None);
11529 };
11530 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
11531 return Ok(None);
11532 }
11533 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11537 match w {
11538 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11539 Some(m) => (m, true),
11540 None => (bytes, *rp),
11541 },
11542 _ => unreachable!(),
11543 }
11544 }
11545 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
11546 if rp0 != rp1 || rp1 != rp2 {
11547 return Ok(None);
11548 }
11549 let rp = rp0;
11550 let rpb: u32 = 4;
11551 let mr1 = rp && Self::q40_mr1_on();
11555 let nb = |o: usize| {
11556 if mr1 {
11557 (o as u32).div_ceil(rpb)
11558 } else {
11559 (o as u32).div_ceil(2).div_ceil(rpb)
11560 }
11561 };
11562 let grid = nb(o0) + nb(o1) + nb(o2);
11563 let mut y0 = self.alloc_uninit::<f32>(o0)?;
11564 let mut y1 = self.alloc_uninit::<f32>(o1)?;
11565 let mut y2 = self.alloc_uninit::<f32>(o2)?;
11566 let f = self.func(if mr1 {
11567 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
11568 } else if rp {
11569 "qmatvec_q4_0_mmvq_fused3_rp"
11570 } else {
11571 "qmatvec_q4_0_mmvq_fused3"
11572 });
11573 let cfg = LaunchConfig {
11574 grid_dim: (grid, 1, 1),
11575 block_dim: (32, rpb, 1),
11576 shared_mem_bytes: 0,
11577 };
11578 let inf = w0.in_features() as i32;
11579 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
11580 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
11581 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11584 {
11585 use cudarc::driver::{DevicePtr, DevicePtrMut};
11586 let s = &self.gpu.stream();
11587 let (p0, _g0) = b0.device_ptr(s);
11588 let (p1, _g1) = b1.device_ptr(s);
11589 let (p2, _g2) = b2.device_ptr(s);
11590 let (paq, _g3) = aq.device_ptr(s);
11591 let (pad, _g4) = ad.device_ptr(s);
11592 let (py0, _g5) = y0.device_ptr_mut(s);
11593 let (py1, _g6) = y1.device_ptr_mut(s);
11594 let (py2, _g7) = y2.device_ptr_mut(s);
11595 let mut ps = [
11596 &p0 as *const _ as *mut std::ffi::c_void,
11597 &p1 as *const _ as *mut _,
11598 &p2 as *const _ as *mut _,
11599 &paq as *const _ as *mut _,
11600 &pad as *const _ as *mut _,
11601 &py0 as *const _ as *mut _,
11602 &py1 as *const _ as *mut _,
11603 &py2 as *const _ as *mut _,
11604 &inf as *const _ as *mut _,
11605 &oo0 as *const _ as *mut _,
11606 &oo1 as *const _ as *mut _,
11607 &oo2 as *const _ as *mut _,
11608 &r0 as *const _ as *mut _,
11609 &r1 as *const _ as *mut _,
11610 &r2 as *const _ as *mut _,
11611 ];
11612 unsafe {
11613 self.launch_pdl(
11614 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
11615 (grid, 1, 1),
11616 (32, rpb, 1),
11617 &mut ps,
11618 )?;
11619 }
11620 }
11621 return Ok(Some((y0, y1, y2)));
11622 }
11623 let __s_b = self.gpu.stream();
11624 let mut b = __s_b.launch_builder(&f);
11625 b.arg(b0)
11626 .arg(b1)
11627 .arg(b2)
11628 .arg(aq)
11629 .arg(ad)
11630 .arg(&mut y0)
11631 .arg(&mut y1)
11632 .arg(&mut y2)
11633 .arg(&inf)
11634 .arg(&oo0)
11635 .arg(&oo1)
11636 .arg(&oo2)
11637 .arg(&r0)
11638 .arg(&r1)
11639 .arg(&r2);
11640 unsafe {
11641 b.launch(cfg)?;
11642 }
11643 Ok(Some((y0, y1, y2)))
11644 }
11645
11646 #[allow(clippy::too_many_arguments)]
11649 pub fn matmul_q4_fused3_into(
11650 &self,
11651 w0: &crate::model::GpuTensor,
11652 w1: &crate::model::GpuTensor,
11653 w2: &crate::model::GpuTensor,
11654 aq: &CudaSlice<i8>,
11655 ad: &CudaSlice<f32>,
11656 y0: &mut CudaSlice<f32>,
11657 y1: &mut CudaSlice<f32>,
11658 y2: &mut CudaSlice<f32>,
11659 ) -> Result<bool, Box<dyn std::error::Error>> {
11660 use crate::model::GpuTensor;
11661 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11662 match w {
11663 GpuTensor::Quant {
11664 qtype, row_bytes, ..
11665 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11666 _ => None,
11667 }
11668 };
11669 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
11670 return Ok(false);
11671 };
11672 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
11673 return Ok(false);
11674 }
11675 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11676 match w {
11677 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11678 Some(m) => (m, true),
11679 None => (bytes, *rp),
11680 },
11681 _ => unreachable!(),
11682 }
11683 }
11684 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
11685 if rp0 != rp1 || rp1 != rp2 {
11686 return Ok(false);
11687 }
11688 let rp = rp0;
11689 let rpb: u32 = 4;
11690 let mr1 = rp && Self::q40_mr1_on();
11691 let nb = |o: usize| {
11692 if mr1 {
11693 (o as u32).div_ceil(rpb)
11694 } else {
11695 (o as u32).div_ceil(2).div_ceil(rpb)
11696 }
11697 };
11698 let grid = nb(o0) + nb(o1) + nb(o2);
11699 debug_assert!(y0.len() >= o0 && y1.len() >= o1 && y2.len() >= o2);
11700 let f = self.func(if mr1 {
11701 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
11702 } else if rp {
11703 "qmatvec_q4_0_mmvq_fused3_rp"
11704 } else {
11705 "qmatvec_q4_0_mmvq_fused3"
11706 });
11707 let cfg = LaunchConfig {
11708 grid_dim: (grid, 1, 1),
11709 block_dim: (32, rpb, 1),
11710 shared_mem_bytes: 0,
11711 };
11712 let inf = w0.in_features() as i32;
11713 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
11714 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
11715 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11717 use cudarc::driver::{DevicePtr, DevicePtrMut};
11718 let s = &self.gpu.stream();
11719 let (p0, _g0) = b0.device_ptr(s);
11720 let (p1, _g1) = b1.device_ptr(s);
11721 let (p2, _g2) = b2.device_ptr(s);
11722 let (paq, _g3) = aq.device_ptr(s);
11723 let (pad, _g4) = ad.device_ptr(s);
11724 let (py0, _g5) = y0.device_ptr_mut(s);
11725 let (py1, _g6) = y1.device_ptr_mut(s);
11726 let (py2, _g7) = y2.device_ptr_mut(s);
11727 let mut ps = [
11728 &p0 as *const _ as *mut std::ffi::c_void,
11729 &p1 as *const _ as *mut _,
11730 &p2 as *const _ as *mut _,
11731 &paq as *const _ as *mut _,
11732 &pad as *const _ as *mut _,
11733 &py0 as *const _ as *mut _,
11734 &py1 as *const _ as *mut _,
11735 &py2 as *const _ as *mut _,
11736 &inf as *const _ as *mut _,
11737 &oo0 as *const _ as *mut _,
11738 &oo1 as *const _ as *mut _,
11739 &oo2 as *const _ as *mut _,
11740 &r0 as *const _ as *mut _,
11741 &r1 as *const _ as *mut _,
11742 &r2 as *const _ as *mut _,
11743 ];
11744 unsafe {
11745 self.launch_pdl(
11746 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
11747 (grid, 1, 1),
11748 (32, rpb, 1),
11749 &mut ps,
11750 )?;
11751 }
11752 return Ok(true);
11753 }
11754 let __s_b = self.gpu.stream();
11755 let mut b = __s_b.launch_builder(&f);
11756 b.arg(b0)
11757 .arg(b1)
11758 .arg(b2)
11759 .arg(aq)
11760 .arg(ad)
11761 .arg(&mut *y0)
11762 .arg(&mut *y1)
11763 .arg(&mut *y2)
11764 .arg(&inf)
11765 .arg(&oo0)
11766 .arg(&oo1)
11767 .arg(&oo2)
11768 .arg(&r0)
11769 .arg(&r1)
11770 .arg(&r2);
11771 unsafe {
11772 b.launch(cfg)?;
11773 }
11774 Ok(true)
11775 }
11776
11777 pub fn matmul_q4_fused2(
11779 &self,
11780 w0: &crate::model::GpuTensor,
11781 w1: &crate::model::GpuTensor,
11782 aq: &CudaSlice<i8>,
11783 ad: &CudaSlice<f32>,
11784 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11785 use crate::model::GpuTensor;
11786 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11787 match w {
11788 GpuTensor::Quant {
11789 qtype, row_bytes, ..
11790 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11791 _ => None,
11792 }
11793 };
11794 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
11795 return Ok(None);
11796 };
11797 if w0.in_features() != w1.in_features() {
11798 return Ok(None);
11799 }
11800 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11802 match w {
11803 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11804 Some(m) => (m, true),
11805 None => (bytes, *rp),
11806 },
11807 _ => unreachable!(),
11808 }
11809 }
11810 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11811 if rp0 != rp1 {
11812 return Ok(None);
11813 }
11814 let rp = rp0;
11815 let rpb: u32 = 4;
11816 let mr1 = rp && Self::q40_mr1_on();
11818 let nb = |o: usize| {
11819 if mr1 {
11820 (o as u32).div_ceil(rpb)
11821 } else {
11822 (o as u32).div_ceil(2).div_ceil(rpb)
11823 }
11824 };
11825 let grid = nb(o0) + nb(o1);
11826 let mut y0 = self.alloc_uninit::<f32>(o0)?;
11827 let mut y1 = self.alloc_uninit::<f32>(o1)?;
11828 let f = self.func(if mr1 {
11829 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
11830 } else if rp {
11831 "qmatvec_q4_0_mmvq_fused2_rp"
11832 } else {
11833 "qmatvec_q4_0_mmvq_fused2"
11834 });
11835 let cfg = LaunchConfig {
11836 grid_dim: (grid, 1, 1),
11837 block_dim: (32, rpb, 1),
11838 shared_mem_bytes: 0,
11839 };
11840 let inf = w0.in_features() as i32;
11841 let (oo0, oo1) = (o0 as i32, o1 as i32);
11842 let (r0, r1) = (rb0 as i64, rb1 as i64);
11843 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11845 {
11846 use cudarc::driver::{DevicePtr, DevicePtrMut};
11847 let s = &self.gpu.stream();
11848 let (p0, _g0) = b0.device_ptr(s);
11849 let (p1, _g1) = b1.device_ptr(s);
11850 let (paq, _g2) = aq.device_ptr(s);
11851 let (pad, _g3) = ad.device_ptr(s);
11852 let (py0, _g4) = y0.device_ptr_mut(s);
11853 let (py1, _g5) = y1.device_ptr_mut(s);
11854 let mut ps = [
11855 &p0 as *const _ as *mut std::ffi::c_void,
11856 &p1 as *const _ as *mut _,
11857 &paq as *const _ as *mut _,
11858 &pad as *const _ as *mut _,
11859 &py0 as *const _ as *mut _,
11860 &py1 as *const _ as *mut _,
11861 &inf as *const _ as *mut _,
11862 &oo0 as *const _ as *mut _,
11863 &oo1 as *const _ as *mut _,
11864 &r0 as *const _ as *mut _,
11865 &r1 as *const _ as *mut _,
11866 ];
11867 unsafe {
11868 self.launch_pdl(
11869 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
11870 (grid, 1, 1),
11871 (32, rpb, 1),
11872 &mut ps,
11873 )?;
11874 }
11875 }
11876 return Ok(Some((y0, y1)));
11877 }
11878 let __s_b = self.gpu.stream();
11879 let mut b = __s_b.launch_builder(&f);
11880 b.arg(b0)
11881 .arg(b1)
11882 .arg(aq)
11883 .arg(ad)
11884 .arg(&mut y0)
11885 .arg(&mut y1)
11886 .arg(&inf)
11887 .arg(&oo0)
11888 .arg(&oo1)
11889 .arg(&r0)
11890 .arg(&r1);
11891 unsafe {
11892 b.launch(cfg)?;
11893 }
11894 Ok(Some((y0, y1)))
11895 }
11896
11897 pub fn matmul_q4_fused2_into(
11899 &self,
11900 w0: &crate::model::GpuTensor,
11901 w1: &crate::model::GpuTensor,
11902 aq: &CudaSlice<i8>,
11903 ad: &CudaSlice<f32>,
11904 y0: &mut CudaSlice<f32>,
11905 y1: &mut CudaSlice<f32>,
11906 ) -> Result<bool, Box<dyn std::error::Error>> {
11907 use crate::model::GpuTensor;
11908 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11909 match w {
11910 GpuTensor::Quant {
11911 qtype, row_bytes, ..
11912 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11913 _ => None,
11914 }
11915 };
11916 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
11917 return Ok(false);
11918 };
11919 if w0.in_features() != w1.in_features() {
11920 return Ok(false);
11921 }
11922 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11923 match w {
11924 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11925 Some(m) => (m, true),
11926 None => (bytes, *rp),
11927 },
11928 _ => unreachable!(),
11929 }
11930 }
11931 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11932 if rp0 != rp1 {
11933 return Ok(false);
11934 }
11935 let rp = rp0;
11936 let rpb: u32 = 4;
11937 let mr1 = rp && Self::q40_mr1_on();
11938 let nb = |o: usize| {
11939 if mr1 {
11940 (o as u32).div_ceil(rpb)
11941 } else {
11942 (o as u32).div_ceil(2).div_ceil(rpb)
11943 }
11944 };
11945 let grid = nb(o0) + nb(o1);
11946 debug_assert!(y0.len() >= o0 && y1.len() >= o1);
11947 let f = self.func(if mr1 {
11948 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
11949 } else if rp {
11950 "qmatvec_q4_0_mmvq_fused2_rp"
11951 } else {
11952 "qmatvec_q4_0_mmvq_fused2"
11953 });
11954 let cfg = LaunchConfig {
11955 grid_dim: (grid, 1, 1),
11956 block_dim: (32, rpb, 1),
11957 shared_mem_bytes: 0,
11958 };
11959 let inf = w0.in_features() as i32;
11960 let (oo0, oo1) = (o0 as i32, o1 as i32);
11961 let (r0, r1) = (rb0 as i64, rb1 as i64);
11962 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11964 use cudarc::driver::{DevicePtr, DevicePtrMut};
11965 let s = &self.gpu.stream();
11966 let (p0, _g0) = b0.device_ptr(s);
11967 let (p1, _g1) = b1.device_ptr(s);
11968 let (paq, _g2) = aq.device_ptr(s);
11969 let (pad, _g3) = ad.device_ptr(s);
11970 let (py0, _g4) = y0.device_ptr_mut(s);
11971 let (py1, _g5) = y1.device_ptr_mut(s);
11972 let mut ps = [
11973 &p0 as *const _ as *mut std::ffi::c_void,
11974 &p1 as *const _ as *mut _,
11975 &paq as *const _ as *mut _,
11976 &pad as *const _ as *mut _,
11977 &py0 as *const _ as *mut _,
11978 &py1 as *const _ as *mut _,
11979 &inf as *const _ as *mut _,
11980 &oo0 as *const _ as *mut _,
11981 &oo1 as *const _ as *mut _,
11982 &r0 as *const _ as *mut _,
11983 &r1 as *const _ as *mut _,
11984 ];
11985 unsafe {
11986 self.launch_pdl(
11987 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
11988 (grid, 1, 1),
11989 (32, rpb, 1),
11990 &mut ps,
11991 )?;
11992 }
11993 return Ok(true);
11994 }
11995 let __s_b = self.gpu.stream();
11996 let mut b = __s_b.launch_builder(&f);
11997 b.arg(b0)
11998 .arg(b1)
11999 .arg(aq)
12000 .arg(ad)
12001 .arg(&mut *y0)
12002 .arg(&mut *y1)
12003 .arg(&inf)
12004 .arg(&oo0)
12005 .arg(&oo1)
12006 .arg(&r0)
12007 .arg(&r1);
12008 unsafe {
12009 b.launch(cfg)?;
12010 }
12011 Ok(true)
12012 }
12013
12014 pub fn matmul_q4_fused2_batched(
12019 &self,
12020 w0: &crate::model::GpuTensor,
12021 w1: &crate::model::GpuTensor,
12022 aq: &CudaSlice<i8>,
12023 ad: &CudaSlice<f32>,
12024 m: usize,
12025 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
12026 use crate::model::GpuTensor;
12027 if m < 2 || m > 8 {
12028 return Ok(None);
12029 }
12030 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
12031 match w {
12032 GpuTensor::Quant {
12033 qtype, row_bytes, ..
12034 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
12035 _ => None,
12036 }
12037 };
12038 let (Some((rb0, o0)), Some((_rb1, o1))) = (q4(w0), q4(w1)) else {
12039 return Ok(None);
12040 };
12041 if w0.in_features() != w1.in_features() {
12042 return Ok(None);
12043 }
12044 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
12045 match w {
12046 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
12047 Some(mr) => (mr, true),
12048 None => (bytes, *rp),
12049 },
12050 _ => unreachable!(),
12051 }
12052 }
12053 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
12054 if !rp0 || !rp1 {
12055 return Ok(None);
12056 }
12057 let mcols = Self::batched_mcols(m);
12058 let rpb: u32 = 4;
12059 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
12060 let grid = nb(o0) + nb(o1);
12061 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
12062 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
12063 let f = self.func(match mcols {
12064 2 => "qmatvec_q4_0_mmvq_b2_f2_rp",
12065 4 => "qmatvec_q4_0_mmvq_b4_f2_rp",
12066 _ => "qmatvec_q4_0_mmvq_b8_f2_rp",
12067 });
12068 let cfg = LaunchConfig {
12069 grid_dim: (grid, 1, 1),
12070 block_dim: (32, rpb, 1),
12071 shared_mem_bytes: 0,
12072 };
12073 let inf = w0.in_features() as i32;
12074 let (oo0, oo1, mi) = (o0 as i32, o1 as i32, m as i32);
12075 let rb = rb0 as i64;
12076 let __s_b = self.gpu.stream();
12077 let mut b = __s_b.launch_builder(&f);
12078 b.arg(b0)
12079 .arg(b1)
12080 .arg(aq)
12081 .arg(ad)
12082 .arg(&mut y0)
12083 .arg(&mut y1)
12084 .arg(&inf)
12085 .arg(&oo0)
12086 .arg(&oo1)
12087 .arg(&mi)
12088 .arg(&rb);
12089 unsafe {
12090 b.launch(cfg)?;
12091 }
12092 Ok(Some((y0, y1)))
12093 }
12094
12095 #[allow(clippy::too_many_arguments)]
12098 pub fn matmul_q4_fused3_batched(
12099 &self,
12100 w0: &crate::model::GpuTensor,
12101 w1: &crate::model::GpuTensor,
12102 w2: &crate::model::GpuTensor,
12103 aq: &CudaSlice<i8>,
12104 ad: &CudaSlice<f32>,
12105 m: usize,
12106 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
12107 {
12108 use crate::model::GpuTensor;
12109 if m < 2 || m > 8 {
12110 return Ok(None);
12111 }
12112 let q4 = |w: &GpuTensor| -> Option<usize> {
12113 match w {
12114 GpuTensor::Quant { qtype, .. } if *qtype == QT_Q4_0 => Some(w.out_features()),
12115 _ => None,
12116 }
12117 };
12118 let (Some(o0), Some(o1), Some(o2)) = (q4(w0), q4(w1), q4(w2)) else {
12119 return Ok(None);
12120 };
12121 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
12122 return Ok(None);
12123 }
12124 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
12125 match w {
12126 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
12127 Some(mr) => (mr, true),
12128 None => (bytes, *rp),
12129 },
12130 _ => unreachable!(),
12131 }
12132 }
12133 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
12134 if !rp0 || !rp1 || !rp2 {
12135 return Ok(None);
12136 }
12137 let mcols = Self::batched_mcols(m);
12138 let rpb: u32 = 4;
12139 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
12140 let grid = nb(o0) + nb(o1) + nb(o2);
12141 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
12142 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
12143 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
12144 let f = self.func(match mcols {
12145 2 => "qmatvec_q4_0_mmvq_b2_f3_rp",
12146 4 => "qmatvec_q4_0_mmvq_b4_f3_rp",
12147 _ => "qmatvec_q4_0_mmvq_b8_f3_rp",
12148 });
12149 let cfg = LaunchConfig {
12150 grid_dim: (grid, 1, 1),
12151 block_dim: (32, rpb, 1),
12152 shared_mem_bytes: 0,
12153 };
12154 let inf = w0.in_features() as i32;
12155 let (oo0, oo1, oo2, mi) = (o0 as i32, o1 as i32, o2 as i32, m as i32);
12156 let rb = 0i64;
12157 let __s_b = self.gpu.stream();
12158 let mut b = __s_b.launch_builder(&f);
12159 b.arg(b0)
12160 .arg(b1)
12161 .arg(b2)
12162 .arg(aq)
12163 .arg(ad)
12164 .arg(&mut y0)
12165 .arg(&mut y1)
12166 .arg(&mut y2)
12167 .arg(&inf)
12168 .arg(&oo0)
12169 .arg(&oo1)
12170 .arg(&oo2)
12171 .arg(&mi)
12172 .arg(&rb);
12173 unsafe {
12174 b.launch(cfg)?;
12175 }
12176 Ok(Some((y0, y1, y2)))
12177 }
12178
12179 pub fn matmul_q8_fused3(
12180 &self,
12181 w0: &crate::model::GpuTensor,
12182 w1: &crate::model::GpuTensor,
12183 w2: &crate::model::GpuTensor,
12184 aq: &CudaSlice<i8>,
12185 ad: &CudaSlice<f32>,
12186 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
12187 {
12188 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
12191 return Ok(Some(self.e4m3_fused3_core(
12192 p0.0,
12193 p1.0,
12194 p2.0,
12195 aq,
12196 ad,
12197 w0.in_features(),
12198 p0.1,
12199 p1.1,
12200 p2.1,
12201 p0.2,
12202 p0.3,
12203 p1.3,
12204 p2.3,
12205 )?));
12206 }
12207 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
12208 return Ok(None);
12209 };
12210 Ok(Some(self.q8_fused3_core(
12211 p0.0,
12212 p1.0,
12213 p2.0,
12214 aq,
12215 ad,
12216 w0.in_features(),
12217 p0.1,
12218 p1.1,
12219 p2.1,
12220 p0.2,
12221 )?))
12222 }
12223
12224 #[allow(clippy::too_many_arguments)]
12225 fn q8_fused3_core(
12226 &self,
12227 b0: &CudaSlice<u8>,
12228 b1: &CudaSlice<u8>,
12229 b2: &CudaSlice<u8>,
12230 aq: &CudaSlice<i8>,
12231 ad: &CudaSlice<f32>,
12232 in_f: usize,
12233 out0: usize,
12234 out1: usize,
12235 out2: usize,
12236 row_bytes: usize,
12237 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12238 const ROWS_PER_BLOCK: u32 = 4;
12239 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12240 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12241 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12242 let f = self.func("qmatvec_q8_0_mmvq_fused3");
12243 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12244 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12245 let mut y2 = self.alloc_uninit::<f32>(out2)?;
12246 let cfg = LaunchConfig {
12247 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12248 block_dim: (32, ROWS_PER_BLOCK, 1),
12249 shared_mem_bytes: 0,
12250 };
12251 let (inf, o0, o1, o2, rbl) = (
12252 in_f as i32,
12253 out0 as i32,
12254 out1 as i32,
12255 out2 as i32,
12256 row_bytes as i64,
12257 );
12258 let __s_b = self.gpu.stream();
12259 let mut b = __s_b.launch_builder(&f);
12260 b.arg(b0)
12261 .arg(b1)
12262 .arg(b2)
12263 .arg(aq)
12264 .arg(ad)
12265 .arg(&mut y0)
12266 .arg(&mut y1)
12267 .arg(&mut y2)
12268 .arg(&inf)
12269 .arg(&o0)
12270 .arg(&o1)
12271 .arg(&o2)
12272 .arg(&rbl);
12273 unsafe {
12274 b.launch(cfg)?;
12275 }
12276 Ok((y0, y1, y2))
12277 }
12278
12279 #[allow(clippy::too_many_arguments)]
12281 pub fn qmatvec_q8_fused3_raw(
12282 &self,
12283 b0: &CudaSlice<u8>,
12284 b1: &CudaSlice<u8>,
12285 b2: &CudaSlice<u8>,
12286 x: &CudaSlice<f32>,
12287 in_f: usize,
12288 out0: usize,
12289 out1: usize,
12290 out2: usize,
12291 row_bytes: usize,
12292 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12293 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
12294 self.q8_fused3_core(b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes)
12295 }
12296
12297 pub fn matmul_q8_fused2_t(
12308 &self,
12309 w0: &crate::model::GpuTensor,
12310 w1: &crate::model::GpuTensor,
12311 aq: &CudaSlice<i8>,
12312 ad: &CudaSlice<f32>,
12313 m: usize,
12314 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
12315 if !(2..=8).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
12319 return Ok(None);
12320 }
12321 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
12324 if m > 4 && !Self::b8_enabled() {
12325 return Ok(None);
12326 }
12327 return Ok(Some(self.e4m3_fused2_t_core(
12328 p0.0,
12329 p1.0,
12330 aq,
12331 ad,
12332 m,
12333 w0.in_features(),
12334 p0.1,
12335 p1.1,
12336 p0.2,
12337 p0.3,
12338 p1.3,
12339 )?));
12340 }
12341 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
12342 return Ok(None);
12343 };
12344 Ok(Some(self.q8_fused2_t_core(
12345 p0.0,
12346 p1.0,
12347 aq,
12348 ad,
12349 m,
12350 w0.in_features(),
12351 p0.1,
12352 p1.1,
12353 p0.2,
12354 )?))
12355 }
12356
12357 #[allow(clippy::too_many_arguments)]
12358 fn q8_fused2_t_core(
12359 &self,
12360 b0: &CudaSlice<u8>,
12361 b1: &CudaSlice<u8>,
12362 aq: &CudaSlice<i8>,
12363 ad: &CudaSlice<f32>,
12364 m: usize,
12365 in_f: usize,
12366 out0: usize,
12367 out1: usize,
12368 row_bytes: usize,
12369 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12370 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12372 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12373 let f = self.func(match Self::batched_mcols(m) {
12374 2 => "qmatvec_q8_0_mmvq_fused2_b2",
12375 4 => "qmatvec_q8_0_mmvq_fused2_b4",
12376 _ => "qmatvec_q8_0_mmvq_fused2_b8",
12378 });
12379 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12380 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12381 let cfg = LaunchConfig {
12382 grid_dim: (nb0 + nb1, 1, 1),
12383 block_dim: (32, ROWS_PER_BLOCK, 1),
12384 shared_mem_bytes: 0,
12385 };
12386 let (inf, o0, o1, mi, rbl) = (
12387 in_f as i32,
12388 out0 as i32,
12389 out1 as i32,
12390 m as i32,
12391 row_bytes as i64,
12392 );
12393 let __s_b = self.gpu.stream();
12394 let mut b = __s_b.launch_builder(&f);
12395 b.arg(b0)
12396 .arg(b1)
12397 .arg(aq)
12398 .arg(ad)
12399 .arg(&mut y0)
12400 .arg(&mut y1)
12401 .arg(&inf)
12402 .arg(&o0)
12403 .arg(&o1)
12404 .arg(&mi)
12405 .arg(&rbl);
12406 unsafe {
12407 b.launch(cfg)?;
12408 }
12409 Ok((y0, y1))
12410 }
12411
12412 #[allow(clippy::too_many_arguments)]
12415 pub fn qmatvec_q8_fused2_t_raw(
12416 &self,
12417 b0: &CudaSlice<u8>,
12418 b1: &CudaSlice<u8>,
12419 x: &CudaSlice<f32>,
12420 m: usize,
12421 in_f: usize,
12422 out0: usize,
12423 out1: usize,
12424 row_bytes: usize,
12425 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12426 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12427 self.q8_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes)
12428 }
12429
12430 #[allow(clippy::too_many_arguments)]
12433 pub fn matmul_q8_fused3_t(
12434 &self,
12435 w0: &crate::model::GpuTensor,
12436 w1: &crate::model::GpuTensor,
12437 w2: &crate::model::GpuTensor,
12438 aq: &CudaSlice<i8>,
12439 ad: &CudaSlice<f32>,
12440 m: usize,
12441 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
12442 {
12443 if !(2..=4).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
12444 return Ok(None);
12445 }
12446 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
12447 return Ok(Some(self.e4m3_fused3_t_core(
12448 p0.0,
12449 p1.0,
12450 p2.0,
12451 aq,
12452 ad,
12453 m,
12454 w0.in_features(),
12455 p0.1,
12456 p1.1,
12457 p2.1,
12458 p0.2,
12459 p0.3,
12460 p1.3,
12461 p2.3,
12462 )?));
12463 }
12464 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
12465 return Ok(None);
12466 };
12467 Ok(Some(self.q8_fused3_t_core(
12468 p0.0,
12469 p1.0,
12470 p2.0,
12471 aq,
12472 ad,
12473 m,
12474 w0.in_features(),
12475 p0.1,
12476 p1.1,
12477 p2.1,
12478 p0.2,
12479 )?))
12480 }
12481
12482 #[allow(clippy::too_many_arguments)]
12483 fn q8_fused3_t_core(
12484 &self,
12485 b0: &CudaSlice<u8>,
12486 b1: &CudaSlice<u8>,
12487 b2: &CudaSlice<u8>,
12488 aq: &CudaSlice<i8>,
12489 ad: &CudaSlice<f32>,
12490 m: usize,
12491 in_f: usize,
12492 out0: usize,
12493 out1: usize,
12494 out2: usize,
12495 row_bytes: usize,
12496 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12497 const ROWS_PER_BLOCK: u32 = 4;
12498 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12499 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12500 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12501 let f = self.func(if Self::batched_mcols(m) == 2 {
12502 "qmatvec_q8_0_mmvq_fused3_b2"
12503 } else {
12504 "qmatvec_q8_0_mmvq_fused3_b4"
12505 });
12506 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12507 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12508 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
12509 let cfg = LaunchConfig {
12510 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12511 block_dim: (32, ROWS_PER_BLOCK, 1),
12512 shared_mem_bytes: 0,
12513 };
12514 let (inf, o0, o1, o2, mi, rbl) = (
12515 in_f as i32,
12516 out0 as i32,
12517 out1 as i32,
12518 out2 as i32,
12519 m as i32,
12520 row_bytes as i64,
12521 );
12522 let __s_b = self.gpu.stream();
12523 let mut b = __s_b.launch_builder(&f);
12524 b.arg(b0)
12525 .arg(b1)
12526 .arg(b2)
12527 .arg(aq)
12528 .arg(ad)
12529 .arg(&mut y0)
12530 .arg(&mut y1)
12531 .arg(&mut y2)
12532 .arg(&inf)
12533 .arg(&o0)
12534 .arg(&o1)
12535 .arg(&o2)
12536 .arg(&mi)
12537 .arg(&rbl);
12538 unsafe {
12539 b.launch(cfg)?;
12540 }
12541 Ok((y0, y1, y2))
12542 }
12543
12544 #[allow(clippy::too_many_arguments)]
12546 pub fn qmatvec_q8_fused3_t_raw(
12547 &self,
12548 b0: &CudaSlice<u8>,
12549 b1: &CudaSlice<u8>,
12550 b2: &CudaSlice<u8>,
12551 x: &CudaSlice<f32>,
12552 m: usize,
12553 in_f: usize,
12554 out0: usize,
12555 out1: usize,
12556 out2: usize,
12557 row_bytes: usize,
12558 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12559 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12560 self.q8_fused3_t_core(b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes)
12561 }
12562
12563 pub fn q8_ffn_fuse2_on(&self) -> bool {
12567 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12568 *ON.get_or_init(|| std::env::var("MEMRA_Q8_FFN_FUSE2").as_deref() != Ok("0"))
12569 }
12570
12571 #[allow(clippy::type_complexity)]
12577 fn q8_fused_params<'w, const N: usize>(
12578 &self,
12579 ws: &[&'w crate::model::GpuTensor; N],
12580 ) -> Option<[(&'w CudaSlice<u8>, usize, usize); N]> {
12581 use crate::model::GpuTensor;
12582 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
12583 return None;
12584 }
12585 if std::env::var("MEMRA_Q8_DUAL").is_ok_and(|v| v == "0") {
12586 return None;
12587 }
12588 let in_f = ws[0].in_features();
12589 let mut out: [Option<(&CudaSlice<u8>, usize, usize)>; N] = [None; N];
12590 for (i, w) in ws.iter().enumerate() {
12591 match w {
12592 GpuTensor::Quant {
12593 bytes,
12594 qtype,
12595 row_bytes,
12596 scale,
12597 ..
12598 } if *qtype == QT_Q8_0 && *scale == 1.0 && w.in_features() == in_f => {
12599 out[i] = Some((bytes, w.out_features(), *row_bytes))
12600 }
12601 _ => return None,
12602 }
12603 }
12604 Some(out.map(|o| o.unwrap()))
12605 }
12606
12607 pub fn e4m3_dual_on(&self) -> bool {
12610 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12611 *ON.get_or_init(|| std::env::var("MEMRA_E4M3_DUAL").as_deref() != Ok("0"))
12612 }
12613
12614 #[allow(clippy::type_complexity)]
12626 fn e4m3_fused_params<'w, const N: usize>(
12627 &self,
12628 ws: &[&'w crate::model::GpuTensor; N],
12629 ) -> Option<[(&'w CudaSlice<u8>, usize, usize, f32); N]> {
12630 use crate::model::GpuTensor;
12631 if !self.e4m3_dual_on() {
12632 return None;
12633 }
12634 let in_f = ws[0].in_features();
12635 let mut out: [Option<(&CudaSlice<u8>, usize, usize, f32)>; N] = [None; N];
12636 for (i, w) in ws.iter().enumerate() {
12637 match w {
12638 GpuTensor::Quant {
12639 bytes,
12640 qtype,
12641 row_bytes,
12642 scale,
12643 rp,
12644 rp4,
12645 ..
12646 } if *qtype == QT_F8_E4M3
12647 && w.in_features() == in_f
12648 && *row_bytes == in_f
12649 && !*rp
12650 && rp4.is_none() =>
12651 {
12652 out[i] = Some((bytes, w.out_features(), *row_bytes, *scale))
12653 }
12654 _ => return None,
12655 }
12656 }
12657 Some(out.map(|o| o.unwrap()))
12658 }
12659
12660 #[allow(clippy::too_many_arguments)]
12664 fn e4m3_fused2_core(
12665 &self,
12666 b0: &CudaSlice<u8>,
12667 b1: &CudaSlice<u8>,
12668 aq: &CudaSlice<i8>,
12669 ad: &CudaSlice<f32>,
12670 in_f: usize,
12671 out0: usize,
12672 out1: usize,
12673 row_bytes: usize,
12674 ws0: f32,
12675 ws1: f32,
12676 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12677 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12679 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12680 let f = self.func("qmatvec_e4m3_mmvq_fused2");
12681 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12682 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12683 let cfg = LaunchConfig {
12684 grid_dim: (nb0 + nb1, 1, 1),
12685 block_dim: (32, ROWS_PER_BLOCK, 1),
12686 shared_mem_bytes: 0,
12687 };
12688 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
12689 let __s_b = self.gpu.stream();
12690 let mut b = __s_b.launch_builder(&f);
12691 b.arg(b0)
12692 .arg(b1)
12693 .arg(aq)
12694 .arg(ad)
12695 .arg(&mut y0)
12696 .arg(&mut y1)
12697 .arg(&inf)
12698 .arg(&o0)
12699 .arg(&o1)
12700 .arg(&rbl)
12701 .arg(&ws0)
12702 .arg(&ws1);
12703 unsafe {
12704 b.launch(cfg)?;
12705 }
12706 Ok((y0, y1))
12707 }
12708
12709 #[allow(clippy::too_many_arguments)]
12711 fn e4m3_fused3_core(
12712 &self,
12713 b0: &CudaSlice<u8>,
12714 b1: &CudaSlice<u8>,
12715 b2: &CudaSlice<u8>,
12716 aq: &CudaSlice<i8>,
12717 ad: &CudaSlice<f32>,
12718 in_f: usize,
12719 out0: usize,
12720 out1: usize,
12721 out2: usize,
12722 row_bytes: usize,
12723 ws0: f32,
12724 ws1: f32,
12725 ws2: f32,
12726 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12727 const ROWS_PER_BLOCK: u32 = 4;
12728 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12729 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12730 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12731 let f = self.func("qmatvec_e4m3_mmvq_fused3");
12732 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12733 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12734 let mut y2 = self.alloc_uninit::<f32>(out2)?;
12735 let cfg = LaunchConfig {
12736 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12737 block_dim: (32, ROWS_PER_BLOCK, 1),
12738 shared_mem_bytes: 0,
12739 };
12740 let (inf, o0, o1, o2, rbl) = (
12741 in_f as i32,
12742 out0 as i32,
12743 out1 as i32,
12744 out2 as i32,
12745 row_bytes as i64,
12746 );
12747 let __s_b = self.gpu.stream();
12748 let mut b = __s_b.launch_builder(&f);
12749 b.arg(b0)
12750 .arg(b1)
12751 .arg(b2)
12752 .arg(aq)
12753 .arg(ad)
12754 .arg(&mut y0)
12755 .arg(&mut y1)
12756 .arg(&mut y2)
12757 .arg(&inf)
12758 .arg(&o0)
12759 .arg(&o1)
12760 .arg(&o2)
12761 .arg(&rbl)
12762 .arg(&ws0)
12763 .arg(&ws1)
12764 .arg(&ws2);
12765 unsafe {
12766 b.launch(cfg)?;
12767 }
12768 Ok((y0, y1, y2))
12769 }
12770
12771 #[allow(clippy::too_many_arguments)]
12775 fn e4m3_fused2_t_core(
12776 &self,
12777 b0: &CudaSlice<u8>,
12778 b1: &CudaSlice<u8>,
12779 aq: &CudaSlice<i8>,
12780 ad: &CudaSlice<f32>,
12781 m: usize,
12782 in_f: usize,
12783 out0: usize,
12784 out1: usize,
12785 row_bytes: usize,
12786 ws0: f32,
12787 ws1: f32,
12788 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12789 const ROWS_PER_BLOCK: u32 = 4;
12790 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12791 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12792 let f = self.func(match Self::batched_mcols(m) {
12793 2 => "qmatvec_e4m3_mmvq_fused2_b2",
12794 4 => "qmatvec_e4m3_mmvq_fused2_b4",
12795 _ => "qmatvec_e4m3_mmvq_fused2_b8",
12796 });
12797 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12798 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12799 let cfg = LaunchConfig {
12800 grid_dim: (nb0 + nb1, 1, 1),
12801 block_dim: (32, ROWS_PER_BLOCK, 1),
12802 shared_mem_bytes: 0,
12803 };
12804 let (inf, o0, o1, mi, rbl) = (
12805 in_f as i32,
12806 out0 as i32,
12807 out1 as i32,
12808 m as i32,
12809 row_bytes as i64,
12810 );
12811 let __s_b = self.gpu.stream();
12812 let mut b = __s_b.launch_builder(&f);
12813 b.arg(b0)
12814 .arg(b1)
12815 .arg(aq)
12816 .arg(ad)
12817 .arg(&mut y0)
12818 .arg(&mut y1)
12819 .arg(&inf)
12820 .arg(&o0)
12821 .arg(&o1)
12822 .arg(&mi)
12823 .arg(&rbl);
12824 unsafe {
12825 b.launch(cfg)?;
12826 }
12827 if ws0 != 1.0 {
12828 self.scale_inplace(&mut y0, ws0, m * out0)?;
12829 }
12830 if ws1 != 1.0 {
12831 self.scale_inplace(&mut y1, ws1, m * out1)?;
12832 }
12833 Ok((y0, y1))
12834 }
12835
12836 #[allow(clippy::too_many_arguments)]
12838 fn e4m3_fused3_t_core(
12839 &self,
12840 b0: &CudaSlice<u8>,
12841 b1: &CudaSlice<u8>,
12842 b2: &CudaSlice<u8>,
12843 aq: &CudaSlice<i8>,
12844 ad: &CudaSlice<f32>,
12845 m: usize,
12846 in_f: usize,
12847 out0: usize,
12848 out1: usize,
12849 out2: usize,
12850 row_bytes: usize,
12851 ws0: f32,
12852 ws1: f32,
12853 ws2: f32,
12854 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12855 const ROWS_PER_BLOCK: u32 = 4;
12856 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12857 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12858 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12859 let f = self.func(if Self::batched_mcols(m) == 2 {
12860 "qmatvec_e4m3_mmvq_fused3_b2"
12861 } else {
12862 "qmatvec_e4m3_mmvq_fused3_b4"
12863 });
12864 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12865 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12866 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
12867 let cfg = LaunchConfig {
12868 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12869 block_dim: (32, ROWS_PER_BLOCK, 1),
12870 shared_mem_bytes: 0,
12871 };
12872 let (inf, o0, o1, o2, mi, rbl) = (
12873 in_f as i32,
12874 out0 as i32,
12875 out1 as i32,
12876 out2 as i32,
12877 m as i32,
12878 row_bytes as i64,
12879 );
12880 let __s_b = self.gpu.stream();
12881 let mut b = __s_b.launch_builder(&f);
12882 b.arg(b0)
12883 .arg(b1)
12884 .arg(b2)
12885 .arg(aq)
12886 .arg(ad)
12887 .arg(&mut y0)
12888 .arg(&mut y1)
12889 .arg(&mut y2)
12890 .arg(&inf)
12891 .arg(&o0)
12892 .arg(&o1)
12893 .arg(&o2)
12894 .arg(&mi)
12895 .arg(&rbl);
12896 unsafe {
12897 b.launch(cfg)?;
12898 }
12899 if ws0 != 1.0 {
12900 self.scale_inplace(&mut y0, ws0, m * out0)?;
12901 }
12902 if ws1 != 1.0 {
12903 self.scale_inplace(&mut y1, ws1, m * out1)?;
12904 }
12905 if ws2 != 1.0 {
12906 self.scale_inplace(&mut y2, ws2, m * out2)?;
12907 }
12908 Ok((y0, y1, y2))
12909 }
12910
12911 pub fn qmatvec_e4m3_blk_mmvq(
12921 &self,
12922 bytes: &CudaSlice<u8>,
12923 aq: &CudaSlice<i8>,
12924 ad: &CudaSlice<f32>,
12925 scales: &CudaSlice<f32>,
12926 m: usize,
12927 in_f: usize,
12928 out_f: usize,
12929 row_bytes: usize,
12930 scale_cols: usize,
12931 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12932 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_e4m3_blk_mmvq_into(
12934 bytes, aq, ad, scales, m, in_f, out_f, row_bytes, scale_cols, &mut y,
12935 )?;
12936 Ok(y)
12937 }
12938
12939 #[allow(clippy::too_many_arguments)]
12941 pub fn qmatvec_e4m3_blk_mmvq_into(
12942 &self,
12943 bytes: &CudaSlice<u8>,
12944 aq: &CudaSlice<i8>,
12945 ad: &CudaSlice<f32>,
12946 scales: &CudaSlice<f32>,
12947 m: usize,
12948 in_f: usize,
12949 out_f: usize,
12950 row_bytes: usize,
12951 scale_cols: usize,
12952 y: &mut CudaSlice<f32>,
12953 ) -> Result<(), Box<dyn std::error::Error>> {
12954 const ROWS_PER_BLOCK: u32 = 4; let f = self.func("qmatvec_e4m3_blk_mmvq");
12956 let cfg = LaunchConfig {
12957 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), m as u32, 1),
12958 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
12961 let (inf, outf, mi, rb, sc) = (
12962 in_f as i32,
12963 out_f as i32,
12964 m as i32,
12965 row_bytes as i64,
12966 scale_cols as i32,
12967 );
12968 let __s_b = self.gpu.stream();
12969 let mut b = __s_b.launch_builder(&f);
12970 b.arg(bytes)
12971 .arg(aq)
12972 .arg(ad)
12973 .arg(scales)
12974 .arg(&mut *y)
12975 .arg(&inf)
12976 .arg(&outf)
12977 .arg(&mi)
12978 .arg(&rb)
12979 .arg(&sc);
12980 unsafe {
12981 b.launch(cfg)?;
12982 }
12983 Ok(())
12984 }
12985
12986 #[allow(clippy::too_many_arguments)]
12992 pub fn qmatvec_e4m3_blk_mmvq_batched(
12993 &self,
12994 bytes: &CudaSlice<u8>,
12995 aq: &CudaSlice<i8>,
12996 ad: &CudaSlice<f32>,
12997 scales: &CudaSlice<f32>,
12998 m: usize,
12999 in_f: usize,
13000 out_f: usize,
13001 row_bytes: usize,
13002 scale_cols: usize,
13003 mcols: usize,
13004 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13005 const ROWS_PER_BLOCK: u32 = 4; debug_assert!(mcols >= m, "blk batched: mcols {mcols} < m {m}");
13007 let name = match mcols {
13008 2 => "qmatvec_e4m3_blk_mmvq_b2",
13009 4 => "qmatvec_e4m3_blk_mmvq_b4",
13010 8 => "qmatvec_e4m3_blk_mmvq_b8",
13011 16 => "qmatvec_e4m3_blk_mmvq_b16",
13012 _ => {
13013 return Err(
13014 format!("qmatvec_e4m3_blk_mmvq_batched: no kernel for mcols {mcols}").into(),
13015 );
13016 }
13017 };
13018 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13019 let f = self.func(name);
13020 let cfg = LaunchConfig {
13021 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), 1, 1),
13022 block_dim: (32, ROWS_PER_BLOCK, 1),
13023 shared_mem_bytes: 0,
13024 };
13025 let (inf, outf, mi, rb, sc) = (
13026 in_f as i32,
13027 out_f as i32,
13028 m as i32,
13029 row_bytes as i64,
13030 scale_cols as i32,
13031 );
13032 let __s_b = self.gpu.stream();
13033 let mut b = __s_b.launch_builder(&f);
13034 b.arg(bytes)
13035 .arg(aq)
13036 .arg(ad)
13037 .arg(scales)
13038 .arg(&mut y)
13039 .arg(&inf)
13040 .arg(&outf)
13041 .arg(&mi)
13042 .arg(&rb)
13043 .arg(&sc);
13044 unsafe {
13045 b.launch(cfg)?;
13046 }
13047 Ok(y)
13048 }
13049
13050 #[allow(clippy::too_many_arguments)]
13053 pub fn qmatvec_e4m3_blk_batched_raw(
13054 &self,
13055 bytes: &CudaSlice<u8>,
13056 x: &CudaSlice<f32>,
13057 scales: &CudaSlice<f32>,
13058 m: usize,
13059 in_f: usize,
13060 out_f: usize,
13061 row_bytes: usize,
13062 scale_cols: usize,
13063 mcols: usize,
13064 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13065 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13066 self.qmatvec_e4m3_blk_mmvq_batched(
13067 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols, mcols,
13068 )
13069 }
13070
13071 #[allow(clippy::too_many_arguments)]
13074 pub fn qmatvec_e4m3_blk_mmvq_raw(
13075 &self,
13076 bytes: &CudaSlice<u8>,
13077 x: &CudaSlice<f32>,
13078 scales: &CudaSlice<f32>,
13079 m: usize,
13080 in_f: usize,
13081 out_f: usize,
13082 row_bytes: usize,
13083 scale_cols: usize,
13084 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13085 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13086 self.qmatvec_e4m3_blk_mmvq(
13087 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols,
13088 )
13089 }
13090
13091 #[allow(clippy::too_many_arguments)]
13094 pub fn qmatvec_e4m3_fused2_raw(
13095 &self,
13096 b0: &CudaSlice<u8>,
13097 b1: &CudaSlice<u8>,
13098 x: &CudaSlice<f32>,
13099 in_f: usize,
13100 out0: usize,
13101 out1: usize,
13102 row_bytes: usize,
13103 ws0: f32,
13104 ws1: f32,
13105 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13106 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
13107 self.e4m3_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes, ws0, ws1)
13108 }
13109
13110 #[allow(clippy::too_many_arguments)]
13111 pub fn qmatvec_e4m3_fused3_raw(
13112 &self,
13113 b0: &CudaSlice<u8>,
13114 b1: &CudaSlice<u8>,
13115 b2: &CudaSlice<u8>,
13116 x: &CudaSlice<f32>,
13117 in_f: usize,
13118 out0: usize,
13119 out1: usize,
13120 out2: usize,
13121 row_bytes: usize,
13122 ws0: f32,
13123 ws1: f32,
13124 ws2: f32,
13125 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13126 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
13127 self.e4m3_fused3_core(
13128 b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
13129 )
13130 }
13131
13132 #[allow(clippy::too_many_arguments)]
13133 pub fn qmatvec_e4m3_fused2_t_raw(
13134 &self,
13135 b0: &CudaSlice<u8>,
13136 b1: &CudaSlice<u8>,
13137 x: &CudaSlice<f32>,
13138 m: usize,
13139 in_f: usize,
13140 out0: usize,
13141 out1: usize,
13142 row_bytes: usize,
13143 ws0: f32,
13144 ws1: f32,
13145 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13146 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13147 self.e4m3_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes, ws0, ws1)
13148 }
13149
13150 #[allow(clippy::too_many_arguments)]
13151 pub fn qmatvec_e4m3_fused3_t_raw(
13152 &self,
13153 b0: &CudaSlice<u8>,
13154 b1: &CudaSlice<u8>,
13155 b2: &CudaSlice<u8>,
13156 x: &CudaSlice<f32>,
13157 m: usize,
13158 in_f: usize,
13159 out0: usize,
13160 out1: usize,
13161 out2: usize,
13162 row_bytes: usize,
13163 ws0: f32,
13164 ws1: f32,
13165 ws2: f32,
13166 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13167 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13168 self.e4m3_fused3_t_core(
13169 b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
13170 )
13171 }
13172
13173 fn try_e4m3_blk_pre(
13184 &self,
13185 w: &crate::model::GpuTensor,
13186 aq: &CudaSlice<i8>,
13187 ad: &CudaSlice<f32>,
13188 m: usize,
13189 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13190 use crate::model::GpuTensor;
13191 if let GpuTensor::Quant {
13192 bytes,
13193 qtype,
13194 row_bytes,
13195 blk: Some(g),
13196 ..
13197 } = w
13198 {
13199 if *qtype == QT_F8_E4M3_BLK {
13200 if (2..=16).contains(&m)
13206 && std::env::var("MEMRA_NO_BATCHED").is_err()
13207 && (m <= 4 || Self::b8_enabled())
13208 {
13209 let mcols = Self::batched_mcols(m);
13210 return Ok(Some(self.qmatvec_e4m3_blk_mmvq_batched(
13211 bytes,
13212 aq,
13213 ad,
13214 &g.scales,
13215 m,
13216 w.in_features(),
13217 w.out_features(),
13218 *row_bytes,
13219 g.cols,
13220 mcols,
13221 )?));
13222 }
13223 return Ok(Some(self.qmatvec_e4m3_blk_mmvq(
13224 bytes,
13225 aq,
13226 ad,
13227 &g.scales,
13228 m,
13229 w.in_features(),
13230 w.out_features(),
13231 *row_bytes,
13232 g.cols,
13233 )?));
13234 }
13235 }
13236 Ok(None)
13237 }
13238
13239 fn try_e4m3_blk_prefill(
13286 &self,
13287 w: &crate::model::GpuTensor,
13288 x: &CudaSlice<f32>,
13289 m: usize,
13290 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13291 use crate::model::GpuTensor;
13292 let GpuTensor::Quant {
13293 bytes,
13294 qtype,
13295 blk: Some(g),
13296 ..
13297 } = w
13298 else {
13299 return Ok(None);
13300 };
13301 if *qtype != QT_F8_E4M3_BLK {
13302 return Ok(None);
13303 }
13304 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
13309 return Ok(Some(y));
13310 }
13311 let (in_f, out_f) = (w.in_features(), w.out_features());
13312 let slab = self.fp8_blk_dequant_q8_0_dev(bytes, &g.scales, out_f, in_f)?;
13313 let tmp = GpuTensor::Quant {
13314 bytes: slab,
13315 qtype: QT_Q8_0,
13316 row_bytes: in_f / 32 * 34,
13317 ne: vec![in_f as u64, out_f as u64],
13318 scale: 1.0,
13319 rp: false,
13320 #[cfg(memra_cutlass)]
13321 cutlass: None,
13322 fp8: None,
13323 blk: None,
13324 f16: None,
13325 rp4: None,
13326 };
13327 Ok(Some(self.matmul(&tmp, x, m)?))
13329 }
13330
13331 pub fn matmul_pre_noscale(
13332 &self,
13333 w: &crate::model::GpuTensor,
13334 aq: &CudaSlice<i8>,
13335 ad: &CudaSlice<f32>,
13336 m: usize,
13337 ) -> Result<Option<(CudaSlice<f32>, f32)>, Box<dyn std::error::Error>> {
13338 use crate::model::GpuTensor;
13339 if m == 1 {
13343 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
13344 return Ok(Some((y, 1.0)));
13345 }
13346 }
13347 if m != 1 || !self.uses_q8_1_fast(w) {
13349 return Ok(None);
13350 }
13351 let in_f = w.in_features();
13352 let out_f = w.out_features();
13353 let (bytes, qtype, row_bytes, scale, rp) = match w {
13354 GpuTensor::Quant {
13355 bytes,
13356 qtype,
13357 row_bytes,
13358 scale,
13359 rp,
13360 ..
13361 } => (bytes, *qtype, *row_bytes, *scale, *rp),
13362 _ => return Ok(None),
13363 };
13364 if self.mmvq_supports(qtype) {
13366 let (mbytes, mrp) = match w {
13368 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
13369 _ => (bytes, rp),
13370 };
13371 let y = self.qmatvec_mmvq(
13372 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, 1.0, mrp,
13373 )?;
13374 return Ok(Some((y, scale)));
13375 }
13376 let name = match qtype {
13378 QT_Q8_0 => "qmatvec_q8_0_dp4a",
13379 QT_Q4_K => "qmatvec_q4_K_dp4a",
13380 QT_Q6_K => "qmatvec_q6_K_dp4a",
13381 QT_Q5_K => "qmatvec_q5_K_dp4a",
13382 QT_Q3_K => "qmatvec_q3_K_dp4a",
13383 QT_NVFP4 => {
13384 if rp {
13385 "qmatvec_nvfp4_dp4a_rp"
13386 } else {
13387 "qmatvec_nvfp4_dp4a"
13388 }
13389 }
13390 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
13391 _ => return Ok(None),
13392 };
13393 let f = self.func(name);
13394 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13395 let cfg = LaunchConfig {
13396 grid_dim: (out_f as u32, m as u32, 1),
13397 block_dim: (128, 1, 1),
13398 shared_mem_bytes: 0,
13399 };
13400 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13401 let __s_b = self.gpu.stream();
13402 let mut b = __s_b.launch_builder(&f);
13403 b.arg(bytes)
13404 .arg(aq)
13405 .arg(ad)
13406 .arg(&mut y)
13407 .arg(&inf)
13408 .arg(&outf)
13409 .arg(&mi)
13410 .arg(&rb);
13411 unsafe {
13412 b.launch(cfg)?;
13413 }
13414 Ok(Some((y, scale)))
13415 }
13416
13417 pub fn mmvq_supports(&self, qtype: i32) -> bool {
13420 if qtype == QT_F8_E4M3 {
13425 return true;
13426 }
13427 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
13428 return false;
13429 }
13430 matches!(
13431 qtype,
13432 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_Q4_0
13433 )
13434 }
13435
13436 pub fn qmatvec_mmvq(
13441 &self,
13442 bytes: &CudaSlice<u8>,
13443 aq: &CudaSlice<i8>,
13444 ad: &CudaSlice<f32>,
13445 m: usize,
13446 in_f: usize,
13447 out_f: usize,
13448 qtype: i32,
13449 row_bytes: usize,
13450 scale: f32,
13451 rp: bool,
13452 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13453 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_mmvq_into(
13455 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp, &mut y,
13456 )?;
13457 Ok(y)
13458 }
13459
13460 #[allow(clippy::too_many_arguments)]
13462 pub fn qmatvec_mmvq_into(
13463 &self,
13464 bytes: &CudaSlice<u8>,
13465 aq: &CudaSlice<i8>,
13466 ad: &CudaSlice<f32>,
13467 m: usize,
13468 in_f: usize,
13469 out_f: usize,
13470 qtype: i32,
13471 row_bytes: usize,
13472 scale: f32,
13473 rp: bool,
13474 y: &mut CudaSlice<f32>,
13475 ) -> Result<(), Box<dyn std::error::Error>> {
13476 debug_assert!(y.len() >= m * out_f);
13477 const ROWS_PER_BLOCK: u32 = 4; if qtype == QT_Q8_0
13483 && rp
13484 && m == 1
13485 && out_f >= 64
13486 && (out_f as u32).div_ceil(ROWS_PER_BLOCK) < 4 * self.sm_count() as u32
13487 && {
13488 static G2: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13489 *G2.get_or_init(|| std::env::var("MEMRA_Q80_G2").as_deref() != Ok("0"))
13490 }
13491 {
13492 let f = self.func("qmatvec_q8_0_mmvq_rp_g2");
13493 let cfg = LaunchConfig {
13494 grid_dim: ((out_f as u32).div_ceil(2), 1, 1),
13495 block_dim: (32, 2, 1),
13496 shared_mem_bytes: 0,
13497 };
13498 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, row_bytes as i64);
13499 let __s_b = self.gpu.stream();
13500 let mut b = __s_b.launch_builder(&f);
13501 b.arg(bytes)
13502 .arg(aq)
13503 .arg(ad)
13504 .arg(&mut *y)
13505 .arg(&inf)
13506 .arg(&outf)
13507 .arg(&mi)
13508 .arg(&rb);
13509 unsafe {
13510 b.launch(cfg)?;
13511 }
13512 if scale != 1.0 {
13513 self.scale_inplace(y, scale, out_f)?;
13514 }
13515 return Ok(());
13516 }
13517 let mut mr: u32 = if m == 1 && (qtype == QT_NVFP4 || qtype == QT_Q5_K) {
13526 2
13527 } else {
13528 1
13529 };
13530 if m == 1 && qtype == QT_Q4_0 {
13535 static Q40MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
13536 mr = *Q40MR.get_or_init(|| {
13539 std::env::var("MEMRA_Q40_MR")
13540 .ok()
13541 .and_then(|v| v.parse().ok())
13542 .unwrap_or(1)
13543 });
13544 }
13545 let q5_mode = std::env::var("MEMRA_Q5K_ISSUE").ok();
13556 let q5_force = q5_mode.as_deref() == Some("2");
13557 let q5_il = qtype == QT_Q5_K
13560 && m == 1
13561 && (q5_force || q5_mode.as_deref().map(|v| v != "0").unwrap_or(true));
13562 if q5_il && !q5_force && out_f > 65536 {
13563 mr = 1;
13564 }
13565 if qtype == QT_Q4_0 && rp && mr != 1 {
13568 mr = 2;
13569 }
13570 if qtype == QT_Q8_0 && rp {
13574 static Q80MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
13575 mr = *Q80MR.get_or_init(|| {
13576 std::env::var("MEMRA_Q80_MR")
13577 .ok()
13578 .and_then(|v| v.parse().ok())
13579 .unwrap_or(1)
13580 });
13581 }
13582 let name = match (qtype, mr, rp) {
13583 (QT_NVFP4, 2, false) => "qmatvec_nvfp4_mmvq_mr2",
13584 (QT_NVFP4, 2, true) => "qmatvec_nvfp4_mmvq_mr2_rp",
13585 (QT_NVFP4, _, true) => "qmatvec_nvfp4_mmvq_rp",
13586 (QT_Q4_0, 1, true) => "qmatvec_q4_0_mmvq_rp",
13587 (QT_Q4_0, _, true) => "qmatvec_q4_0_mmvq_mr2_rp",
13588 (QT_Q5_K, 2, _) => {
13589 if q5_il {
13590 "qmatvec_q5_K_mmvq_mr2_il"
13591 } else {
13592 "qmatvec_q5_K_mmvq_mr2"
13593 }
13594 }
13595 (QT_Q8_0, 2, true) => "qmatvec_q8_0_mmvq_mr2_rp",
13596 (QT_Q8_0, _, true)
13601 if in_f % 1024 == 0 && {
13602 static CA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13603 *CA.get_or_init(|| std::env::var("MEMRA_Q80_CA").as_deref() == Ok("1"))
13604 } =>
13605 {
13606 "qmatvec_q8_0_mmvq_rpca"
13607 }
13608 (QT_Q8_0, _, true) => "qmatvec_q8_0_mmvq_rp",
13609 (QT_Q8_0, _, _) => "qmatvec_q8_0_mmvq",
13610 (QT_Q4_K, _, true) => "qmatvec_q4_K_mmvq_rp",
13614 (QT_Q6_K, _, true) => "qmatvec_q6_K_mmvq_rp",
13615 (QT_Q4_K, _, _) => "qmatvec_q4_K_mmvq",
13616 (QT_Q4_0, 2, false) => "qmatvec_q4_0_mmvq_mr2",
13617 (QT_Q4_0, _, false) => "qmatvec_q4_0_mmvq",
13618 (QT_Q5_K, _, _) => {
13619 if q5_il {
13620 "qmatvec_q5_K_mmvq_il"
13621 } else {
13622 "qmatvec_q5_K_mmvq"
13623 }
13624 }
13625 (QT_Q6_K, _, _) => "qmatvec_q6_K_mmvq",
13626 (QT_NVFP4, _, false) => "qmatvec_nvfp4_mmvq",
13627 (QT_F8_E4M3, _, _) => "qmatvec_e4m3_mmvq",
13628 _ => panic!("qmatvec_mmvq: qtype {qtype} has no MMVQ kernel"),
13629 };
13630 let f = self.func(name);
13631 let rows_per_block = ROWS_PER_BLOCK * mr;
13633 let cfg = LaunchConfig {
13634 grid_dim: (
13635 (out_f as u32 + rows_per_block - 1) / rows_per_block,
13636 m as u32,
13637 1,
13638 ),
13639 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
13642 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13643 let __s_b = self.gpu.stream();
13644 let mut b = __s_b.launch_builder(&f);
13645 if qtype == QT_NVFP4 || qtype == QT_F8_E4M3 {
13650 if Self::pdl_on()
13653 && Self::pdl_mmvq_on()
13654 && Self::pdl_nvfp4q8_on()
13655 && name == "qmatvec_nvfp4_mmvq_mr2_rp"
13656 {
13657 use cudarc::driver::{DevicePtr, DevicePtrMut};
13658 let s = &self.gpu.stream();
13659 let (pw, _g0) = bytes.device_ptr(s);
13660 let (paq, _g1) = aq.device_ptr(s);
13661 let (pad, _g2) = ad.device_ptr(s);
13662 let (py, _g3) = y.device_ptr_mut(s);
13663 let mut ps = [
13664 &pw as *const _ as *mut std::ffi::c_void,
13665 &paq as *const _ as *mut _,
13666 &pad as *const _ as *mut _,
13667 &py as *const _ as *mut _,
13668 &inf as *const _ as *mut _,
13669 &outf as *const _ as *mut _,
13670 &mi as *const _ as *mut _,
13671 &rb as *const _ as *mut _,
13672 &scale as *const _ as *mut _,
13673 ];
13674 unsafe {
13675 self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
13676 }
13677 return Ok(());
13678 }
13679 b.arg(bytes)
13680 .arg(aq)
13681 .arg(ad)
13682 .arg(&mut *y)
13683 .arg(&inf)
13684 .arg(&outf)
13685 .arg(&mi)
13686 .arg(&rb)
13687 .arg(&scale);
13688 unsafe {
13689 b.launch(cfg)?;
13690 }
13691 } else if Self::pdl_on()
13692 && Self::pdl_mmvq_on()
13693 && (matches!(
13694 name,
13695 "qmatvec_q4_0_mmvq_rp" | "qmatvec_q6_K_mmvq" | "qmatvec_q6_K_mmvq_rp"
13696 ) || (Self::pdl_nvfp4q8_on()
13697 && matches!(name, "qmatvec_q8_0_mmvq_rp" | "qmatvec_q8_0_mmvq_mr2_rp")))
13698 {
13699 {
13703 use cudarc::driver::{DevicePtr, DevicePtrMut};
13704 let s = &self.gpu.stream();
13705 let (pw, _g0) = bytes.device_ptr(s);
13706 let (paq, _g1) = aq.device_ptr(s);
13707 let (pad, _g2) = ad.device_ptr(s);
13708 let (py, _g3) = y.device_ptr_mut(s);
13709 let mut ps = [
13710 &pw as *const _ as *mut std::ffi::c_void,
13711 &paq as *const _ as *mut _,
13712 &pad as *const _ as *mut _,
13713 &py as *const _ as *mut _,
13714 &inf as *const _ as *mut _,
13715 &outf as *const _ as *mut _,
13716 &mi as *const _ as *mut _,
13717 &rb as *const _ as *mut _,
13718 ];
13719 unsafe {
13720 self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
13721 }
13722 }
13723 if scale != 1.0 {
13724 self.scale_inplace(y, scale, m * out_f)?;
13725 }
13726 } else {
13727 b.arg(bytes)
13728 .arg(aq)
13729 .arg(ad)
13730 .arg(&mut *y)
13731 .arg(&inf)
13732 .arg(&outf)
13733 .arg(&mi)
13734 .arg(&rb);
13735 unsafe {
13736 b.launch(cfg)?;
13737 }
13738 if scale != 1.0 {
13739 self.scale_inplace(y, scale, m * out_f)?;
13740 }
13741 }
13742 Ok(())
13743 }
13744
13745 pub fn qmatvec_mmvq_raw(
13749 &self,
13750 bytes: &CudaSlice<u8>,
13751 x: &CudaSlice<f32>,
13752 m: usize,
13753 in_f: usize,
13754 out_f: usize,
13755 qtype: i32,
13756 row_bytes: usize,
13757 rp: bool,
13758 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13759 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13760 self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, 1.0, rp)
13761 }
13762
13763 pub fn batched_supports(&self, qtype: i32) -> bool {
13767 matches!(
13768 qtype,
13769 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_F8_E4M3 | QT_Q4_0
13770 )
13771 }
13772
13773 pub fn iq_fast_enabled() -> bool {
13781 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13782 *ON.get_or_init(|| {
13783 std::env::var("MEMRA_IQ_FAST")
13784 .map(|v| v != "0")
13785 .unwrap_or(true)
13786 })
13787 }
13788
13789 pub fn b8_enabled() -> bool {
13792 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13793 *ON.get_or_init(|| std::env::var("MEMRA_B8").map(|v| v != "0").unwrap_or(true))
13794 }
13795
13796 pub fn batched_mcols(m: usize) -> usize {
13798 if m == 2 {
13799 2
13800 } else if m <= 4 {
13801 4
13802 } else if m <= 8 {
13803 8
13804 } else {
13805 16
13806 }
13807 }
13808
13809 fn batched_kernel_name(qtype: i32, mcols: usize) -> Option<&'static str> {
13814 Some(match (qtype, mcols) {
13815 (QT_Q8_0, 2) => "qmatvec_q8_0_mmvq_b2",
13816 (QT_Q8_0, 4) => "qmatvec_q8_0_mmvq_b4",
13817 (QT_Q8_0, 8) => "qmatvec_q8_0_mmvq_b8",
13818 (QT_Q8_0, 16) => "qmatvec_q8_0_mmvq_b16",
13824 (QT_Q4_K, 2) => "qmatvec_q4_K_mmvq_b2",
13825 (QT_Q4_K, 4) => "qmatvec_q4_K_mmvq_b4",
13826 (QT_Q4_K, 8) => "qmatvec_q4_K_mmvq_b8",
13827 (QT_Q4_K, 16) => "qmatvec_q4_K_mmvq_b16",
13830 (QT_Q5_K, 2) => "qmatvec_q5_K_mmvq_b2",
13831 (QT_Q5_K, 4) => "qmatvec_q5_K_mmvq_b4",
13832 (QT_Q5_K, 8) => "qmatvec_q5_K_mmvq_b8",
13833 (QT_Q5_K, 16) => "qmatvec_q5_K_mmvq_b16",
13836 (QT_Q6_K, 2) => "qmatvec_q6_K_mmvq_b2",
13837 (QT_Q6_K, 4) => "qmatvec_q6_K_mmvq_b4",
13838 (QT_Q6_K, 8) => "qmatvec_q6_K_mmvq_b8",
13839 (QT_Q6_K, 16) => "qmatvec_q6_K_mmvq_b16",
13840 (QT_NVFP4, 2) => "qmatvec_nvfp4_mmvq_b2",
13841 (QT_NVFP4, 4) => "qmatvec_nvfp4_mmvq_b4",
13842 (QT_NVFP4, 8) => "qmatvec_nvfp4_mmvq_b8",
13843 (QT_NVFP4, 16) => "qmatvec_nvfp4_mmvq_b16",
13847 (QT_F8_E4M3, 2) => "qmatvec_e4m3_mmvq_b2",
13848 (QT_F8_E4M3, 4) => "qmatvec_e4m3_mmvq_b4",
13849 (QT_F8_E4M3, 8) => "qmatvec_e4m3_mmvq_b8",
13850 (QT_F8_E4M3, 16) => "qmatvec_e4m3_mmvq_b16",
13854 (QT_Q4_0, 2) => "qmatvec_q4_0_mmvq_b2",
13855 (QT_Q4_0, 4) => "qmatvec_q4_0_mmvq_b4",
13856 (QT_Q4_0, 8) => "qmatvec_q4_0_mmvq_b8",
13857 (QT_Q4_0, 16) => "qmatvec_q4_0_mmvq_b16",
13858 _ => return None,
13859 })
13860 }
13861
13862 pub fn sm_count(&self) -> i32 {
13897 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
13898 *SMS.get_or_init(|| {
13899 use cudarc::driver::sys::CUdevice_attribute_enum as A;
13900 self.gpu
13901 .ctx
13902 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
13903 .unwrap_or(82)
13904 })
13905 }
13906
13907 pub fn batched_variant(
13908 &self,
13909 _m: usize,
13910 in_f: usize,
13911 out_f: usize,
13912 qtype: i32,
13913 row_bytes: usize,
13914 mcols: usize,
13915 rp: bool,
13916 ) -> &'static str {
13917 if qtype == QT_Q8_0 {
13922 return if rp { "rp" } else { "base" };
13923 }
13924 static BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13925 let bv = *BV.get_or_init(|| match std::env::var("MEMRA_MMVQ_BV").as_deref() {
13926 Ok("base") => "base",
13927 Ok("pf") => "pf",
13928 Ok("r2") => "r2",
13929 Ok("r2w8") => "r2w8",
13930 Ok("pfr2") => "pfr2",
13931 Ok("ca") => "ca",
13932 Ok("car2") => "car2",
13933 Ok("rp") => "rp",
13936 Ok("rpr2") => "rpr2",
13937 Ok("rpr2w8") => "rpr2w8",
13938 Ok("rpca") => "rpca",
13941 Ok("rpcar2") => "rpcar2",
13942 Ok("rpsc") => "rpsc",
13949 Ok("rpms") => "rpms",
13950 Ok("rpmsc") => "rpmsc",
13951 Ok("rpks") => "rpks",
13952 Ok("rpksc") => "rpksc",
13953 _ => "auto",
13954 });
13955 let ca_ok = qtype == QT_NVFP4 && (row_bytes % 16 == 0) && (in_f % 1024 == 0);
13959 static KS_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13964 let ks_on = *KS_ON.get_or_init(|| std::env::var("MEMRA_KS").as_deref() != Ok("0"));
13965 let sc_ok = ks_on && qtype == QT_NVFP4 && (in_f % 256 == 0) && (in_f / 64 <= 272);
13966 let ks_ok = ks_on && qtype == QT_NVFP4 && (in_f % 512 == 0) && (in_f / 64 <= 272);
13967 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
13968 let sms = *SMS.get_or_init(|| {
13969 use cudarc::driver::sys::CUdevice_attribute_enum as A;
13970 self.gpu
13971 .ctx
13972 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
13973 .unwrap_or(82)
13974 });
13975 let kq_r2 = matches!(qtype, QT_Q4_K | QT_Q5_K | QT_Q6_K);
13995 static KQBV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13998 let kq_bv = *KQBV.get_or_init(|| match std::env::var("MEMRA_KQ_BV").as_deref() {
13999 Ok("base") => "base",
14000 Ok("r2") => "r2",
14001 Ok("r2w8") => "r2w8",
14002 _ => "auto",
14003 });
14004 let variant: &'static str = if qtype == QT_Q4_0 {
14005 static Q40BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
14009 let q40 = *Q40BV.get_or_init(|| match std::env::var("MEMRA_Q40_BV").as_deref() {
14010 Ok("base") => "base",
14016 Ok("r2") => "r2",
14017 Ok("ms") => "ms",
14018 Ok("sm") => "sm",
14019 Ok("la") => "la",
14020 _ => "auto",
14021 });
14022 let v = if q40 != "auto" {
14023 q40
14024 } else if (out_f as u32).div_ceil(8) >= 4 * sms as u32 {
14025 "r2"
14026 } else {
14027 "base"
14028 };
14029 if rp {
14034 match v {
14035 "ms" => "r2ms_rp",
14036 "sm" => "r2sm_rp",
14037 "la" => "r2la_rp",
14038 "r2" => "r2_rp",
14039 _ => "rp",
14040 }
14041 } else if matches!(v, "ms" | "sm" | "la") {
14042 "r2"
14043 } else {
14044 v
14045 }
14046 } else if qtype != QT_NVFP4 && !kq_r2 {
14047 "base"
14048 } else if kq_r2 && rp {
14049 "rp"
14053 } else if kq_r2 {
14054 if kq_bv != "auto" {
14057 if kq_bv == "r2w8" && mcols != 4 {
14058 "r2"
14059 } else {
14060 kq_bv
14061 }
14062 } else if bv != "auto" {
14063 match bv {
14064 "r2" | "pfr2" | "rpr2" | "car2" => "r2",
14065 "r2w8" | "rpr2w8" => {
14066 if mcols != 4 {
14067 "r2"
14068 } else {
14069 "r2w8"
14070 }
14071 }
14072 _ => "base", }
14074 } else {
14075 let blocks = (out_f + 7) / 8;
14076 let waves = blocks as f64 / (7 * sms as usize) as f64;
14077 let filled = blocks >= 4 * sms as usize;
14078 let use_r2 = if qtype == QT_Q4_K {
14079 filled
14080 } else {
14081 waves >= 2.0
14082 };
14083 if use_r2 { "r2" } else { "base" }
14084 }
14085 } else if bv != "auto" {
14086 let v = if bv == "r2w8" && mcols == 2 {
14091 "r2"
14092 } else if bv == "ca" && (!ca_ok || mcols == 8) {
14093 "pf"
14094 } else if bv == "car2" && (!ca_ok || mcols == 8) {
14095 "r2"
14096 } else if bv == "pfr2" && mcols == 8 {
14097 "r2"
14098 } else if (bv == "rpr2w8" || bv == "rpr2") && mcols == 2 {
14099 "rpr2"
14100 }
14101 else if (bv == "rpca" || bv == "rpcar2") && (!ca_ok || mcols == 8) {
14103 if mcols == 8 { "rpr2w8" } else { "rpr2" }
14104 } else if bv == "rpcar2" && mcols == 2 {
14105 "rpca"
14106 }
14107 else if (bv == "rpsc" || bv == "rpmsc") && !sc_ok {
14110 "rpr2"
14111 } else if (bv == "rpks" || bv == "rpksc") && !ks_ok {
14112 "rpr2"
14113 } else {
14114 bv
14115 };
14116 if rp {
14117 match v {
14118 "base" | "pf" | "ca" | "rp" => "rp",
14119 "r2" | "pfr2" | "car2" | "rpr2" => "rpr2",
14120 "r2w8" | "rpr2w8" => {
14121 if mcols == 2 {
14122 "rpr2"
14123 } else {
14124 "rpr2w8"
14125 }
14126 }
14127 other => other, }
14129 } else {
14130 v
14131 }
14132 } else if mcols == 8 {
14133 if rp {
14144 if sc_ok { "rpsc" } else { "rpr2w8" }
14145 } else {
14146 "r2w8"
14147 }
14148 } else if mcols >= 4 {
14149 let blocks = (out_f + 7) / 8;
14153 let r7 = 7 * sms as usize;
14154 let r8 = 8 * sms as usize;
14155 let waves = blocks as f64 / r7 as f64;
14156 let filled = blocks >= 4 * sms as usize;
14157 if filled && blocks.div_ceil(r8) < blocks.div_ceil(r7) {
14161 if rp { "rpr2w8" } else { "r2w8" }
14165 } else if waves >= 2.0 || (waves <= 1.0 && filled) {
14166 if rp { "rpr2" } else { "r2" }
14169 } else {
14170 if rp { "rp" } else { "pf" }
14174 }
14175 } else if in_f >= 6144 {
14176 if rp { "rpr2" } else { "r2" }
14180 } else if rp {
14181 let waves = ((out_f + 7) / 8) as f64 / (7 * sms as usize) as f64;
14186 if sc_ok && waves >= 0.9 && waves <= 1.1 {
14187 "rpsc"
14188 } else {
14189 "rp"
14190 }
14191 } else {
14192 "base"
14193 };
14194 variant
14195 }
14196
14197 pub fn qmatvec_mmvq_batched(
14198 &self,
14199 bytes: &CudaSlice<u8>,
14200 aq: &CudaSlice<i8>,
14201 ad: &CudaSlice<f32>,
14202 m: usize,
14203 in_f: usize,
14204 out_f: usize,
14205 qtype: i32,
14206 row_bytes: usize,
14207 mcols: usize,
14208 scale: f32,
14209 rp: bool,
14210 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14211 const ROWS_PER_BLOCK: u32 = 4;
14212 let forced: Option<&'static str> = {
14217 static V: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
14218 V.get_or_init(|| std::env::var("MEMRA_BVAR").ok())
14219 .as_deref()
14220 .map(|s| Box::leak(s.to_string().into_boxed_str()) as &'static str)
14221 };
14222 let variant = match forced {
14223 Some(v) if !rp || v.contains("rp") => v,
14224 _ => self.batched_variant(m, in_f, out_f, qtype, row_bytes, mcols, rp),
14225 };
14226 let base_name = Self::batched_kernel_name(qtype, mcols).ok_or_else(|| {
14227 format!("qmatvec_mmvq_batched: no kernel for qtype {qtype} mcols {mcols}")
14228 })?;
14229 let variant = if mcols == 16 {
14233 if rp { "rp" } else { "base" }
14234 } else {
14235 variant
14236 };
14237 static B567: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14244 let b567 = *B567.get_or_init(|| std::env::var("MEMRA_B567").as_deref() != Ok("0"));
14245 if b567
14246 && qtype == QT_NVFP4
14247 && rp
14248 && mcols == 8
14249 && (5..=7).contains(&m)
14250 && matches!(variant, "rpsc" | "rpr2w8")
14251 {
14252 let f = self.func(&format!("qmatvec_nvfp4_mmvq_b{m}_{variant}"));
14253 let rows_per_block = ROWS_PER_BLOCK * 2; let mut y = self.alloc_uninit::<f32>(m * out_f)?;
14255 let cfg = LaunchConfig {
14256 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
14257 block_dim: (32, ROWS_PER_BLOCK, 1),
14258 shared_mem_bytes: 0,
14259 };
14260 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14261 let __s_b = self.gpu.stream();
14262 let mut b = __s_b.launch_builder(&f);
14263 b.arg(bytes)
14264 .arg(aq)
14265 .arg(ad)
14266 .arg(&mut y)
14267 .arg(&inf)
14268 .arg(&outf)
14269 .arg(&mi)
14270 .arg(&rb);
14271 unsafe {
14272 b.launch(cfg)?;
14273 }
14274 if scale != 1.0 {
14275 self.scale_inplace(&mut y, scale, m * out_f)?;
14276 }
14277 return Ok(y);
14278 }
14279 let (name, rows_per_block): (std::borrow::Cow<'static, str>, u32) = match variant {
14280 "base" => (base_name.into(), ROWS_PER_BLOCK),
14281 "pf" => (format!("{base_name}_pf").into(), ROWS_PER_BLOCK),
14282 "ca" => (format!("{base_name}_ca").into(), ROWS_PER_BLOCK),
14283 "rp" => (format!("{base_name}_rp").into(), ROWS_PER_BLOCK),
14284 "rpca" => (format!("{base_name}_rpca").into(), ROWS_PER_BLOCK), "rpks" => (format!("{base_name}_rpks").into(), ROWS_PER_BLOCK),
14288 "rpksc" => (format!("{base_name}_rpksc").into(), ROWS_PER_BLOCK),
14289 "rpms" => (format!("{base_name}_rpms").into(), ROWS_PER_BLOCK),
14290 "rpmsc" => (format!("{base_name}_rpmsc").into(), ROWS_PER_BLOCK),
14291 "r2ms_rp" => (format!("{base_name}_r2ms_rp").into(), ROWS_PER_BLOCK),
14292 "r2sm_rp" => (format!("{base_name}_r2sm_rp").into(), ROWS_PER_BLOCK * 2),
14293 "r2la_rp" => (format!("{base_name}_r2la_rp").into(), ROWS_PER_BLOCK * 2),
14294 v => (format!("{base_name}_{v}").into(), ROWS_PER_BLOCK * 2), };
14296 debug_assert!(
14297 !rp || name.contains("_rp"),
14298 "rp weight dispatched to a GGUF-layout kernel"
14299 );
14300 let f = self.func(&name);
14301 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
14302 let smem = if name.contains("_r2sm_rp") {
14304 (mcols * 32 * 9 * 4 + mcols * 32 * 4) as u32
14305 } else {
14306 0
14307 };
14308 let cfg = LaunchConfig {
14309 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
14310 block_dim: (32, ROWS_PER_BLOCK, 1),
14311 shared_mem_bytes: smem,
14312 };
14313 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14314 let __s_b = self.gpu.stream();
14315 let mut b = __s_b.launch_builder(&f);
14316 b.arg(bytes)
14317 .arg(aq)
14318 .arg(ad)
14319 .arg(&mut y)
14320 .arg(&inf)
14321 .arg(&outf)
14322 .arg(&mi)
14323 .arg(&rb);
14324 unsafe {
14325 b.launch(cfg)?;
14326 }
14327 if scale != 1.0 {
14328 self.scale_inplace(&mut y, scale, m * out_f)?;
14329 }
14330 Ok(y)
14331 }
14332
14333 pub fn qmatvec_batched_raw(
14337 &self,
14338 bytes: &CudaSlice<u8>,
14339 x: &CudaSlice<f32>,
14340 m: usize,
14341 in_f: usize,
14342 out_f: usize,
14343 qtype: i32,
14344 row_bytes: usize,
14345 mcols: usize,
14346 rp: bool,
14347 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14348 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
14349 self.qmatvec_mmvq_batched(
14350 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, 1.0, rp,
14351 )
14352 }
14353
14354 pub fn qmatvec_nvfp4_batched_raw(
14356 &self,
14357 bytes: &CudaSlice<u8>,
14358 x: &CudaSlice<f32>,
14359 m: usize,
14360 in_f: usize,
14361 out_f: usize,
14362 row_bytes: usize,
14363 mcols: usize,
14364 rp: bool,
14365 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14366 self.qmatvec_batched_raw(bytes, x, m, in_f, out_f, QT_NVFP4, row_bytes, mcols, rp)
14367 }
14368
14369 fn try_fp4_gemm(
14373 &self,
14374 w: &crate::model::GpuTensor,
14375 x: &CudaSlice<f32>,
14376 m: usize,
14377 in_f: usize,
14378 out_f: usize,
14379 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
14380 use crate::model::GpuTensor;
14381 if cfg!(memra_portable_cuda) {
14382 return Ok(None);
14383 }
14384 if std::env::var("MEMRA_FP4").is_err() {
14385 return Ok(None);
14386 }
14387 #[cfg(memra_cutlass)]
14396 if m >= 128 && std::env::var("MEMRA_FP4_CUTLASS").is_ok() {
14397 if let GpuTensor::Quant {
14398 bytes,
14399 qtype,
14400 scale,
14401 row_bytes,
14402 cutlass,
14403 ..
14404 } = w
14405 {
14406 if *qtype == QT_NVFP4 && in_f % 64 == 0 {
14407 if let Some(cw) = cutlass {
14408 let y = self.cutlass_fp4_gemm(
14410 &cw.b_packed,
14411 &cw.sfb_swizzled,
14412 x,
14413 *scale,
14414 m,
14415 out_f,
14416 in_f,
14417 )?;
14418 return Ok(Some(y));
14419 } else if std::env::var("MEMRA_FP4_CUTLASS_OTF").is_ok() {
14420 let (b_packed, sfb_sw) =
14425 self.build_cutlass_weight(bytes, out_f, in_f, *row_bytes)?;
14426 let y =
14427 self.cutlass_fp4_gemm(&b_packed, &sfb_sw, x, *scale, m, out_f, in_f)?;
14428 return Ok(Some(y));
14429 }
14430 }
14431 }
14432 }
14433 if let GpuTensor::Quant {
14434 bytes,
14435 qtype,
14436 row_bytes,
14437 scale,
14438 rp,
14439 ..
14440 } = w
14441 {
14442 if *qtype == QT_NVFP4 && in_f % 64 == 0 && !*rp {
14445 let y =
14446 self.qmatvec_gemm_nvfp4_fp4(bytes, x, m, in_f, out_f, *row_bytes, *scale)?;
14447 return Ok(Some(y));
14448 }
14449 }
14450 Ok(None)
14451 }
14452
14453 pub fn rms_norm_f16out(
14457 &self,
14458 x: &CudaSlice<f32>,
14459 w: &CudaSlice<f32>,
14460 dst: &mut CudaSlice<f32>,
14461 dst16: &mut CudaSlice<u8>,
14462 ncols: usize,
14463 nrows: usize,
14464 eps: f32,
14465 ) -> Result<(), Box<dyn std::error::Error>> {
14466 let f = self.func("rms_norm_f16out_f32");
14467 let cfg = LaunchConfig {
14468 grid_dim: (nrows as u32, 1, 1),
14469 block_dim: (rms_block(), 1, 1),
14470 shared_mem_bytes: 0,
14471 };
14472 let (nc, e) = (ncols as i32, eps);
14473 let __s_b = self.gpu.stream();
14474 let mut b = __s_b.launch_builder(&f);
14475 b.arg(x).arg(w).arg(dst).arg(dst16).arg(&nc).arg(&e);
14476 unsafe {
14477 b.launch(cfg)?;
14478 }
14479 Ok(())
14480 }
14481
14482 #[allow(clippy::too_many_arguments)]
14485 pub fn add_rms_norm_f16out(
14486 &self,
14487 a: &CudaSlice<f32>,
14488 b: &CudaSlice<f32>,
14489 w: &CudaSlice<f32>,
14490 res: &mut CudaSlice<f32>,
14491 dst: &mut CudaSlice<f32>,
14492 dst16: &mut CudaSlice<u8>,
14493 ncols: usize,
14494 nrows: usize,
14495 eps: f32,
14496 ) -> Result<(), Box<dyn std::error::Error>> {
14497 let f = self.func("add_rms_norm_f16out_f32");
14498 let cfg = LaunchConfig {
14499 grid_dim: (nrows as u32, 1, 1),
14500 block_dim: (rms_block(), 1, 1),
14501 shared_mem_bytes: 0,
14502 };
14503 let (nc, e) = (ncols as i32, eps);
14504 let __s_lb = self.gpu.stream();
14505 let mut lb = __s_lb.launch_builder(&f);
14506 lb.arg(a)
14507 .arg(b)
14508 .arg(w)
14509 .arg(res)
14510 .arg(dst)
14511 .arg(dst16)
14512 .arg(&nc)
14513 .arg(&e);
14514 unsafe {
14515 lb.launch(cfg)?;
14516 }
14517 Ok(())
14518 }
14519
14520 pub fn matmul_group_xh(
14523 &self,
14524 ws: &[&crate::model::GpuTensor],
14525 x: &CudaSlice<f32>,
14526 xh: &CudaSlice<u8>,
14527 m: usize,
14528 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
14529 let mut out = Vec::with_capacity(ws.len());
14530 let in_f = ws[0].in_features();
14531 for w in ws {
14532 if w.in_features() == in_f && m >= 16 && !self.verify_exact_on() {
14533 if let Some(y) = self.try_f16_gemm_pre(w, xh, m)? {
14534 out.push(y);
14535 continue;
14536 }
14537 }
14538 out.push(self.matmul(w, x, m)?);
14539 }
14540 Ok(out)
14541 }
14542
14543 pub fn gdn_pad_mask(
14546 &self,
14547 beta: &mut CudaSlice<f32>,
14548 g_log: &mut CudaSlice<f32>,
14549 len_d: &CudaSlice<i32>,
14550 h: usize,
14551 t: usize,
14552 ) -> Result<(), Box<dyn std::error::Error>> {
14553 let f = self.func("gdn_pad_mask_f32");
14554 let cfg = LaunchConfig::for_num_elems((t * h) as u32);
14555 let (hi, ti) = (h as i32, t as i32);
14556 let __s_b = self.gpu.stream();
14557 let mut b = __s_b.launch_builder(&f);
14558 b.arg(beta).arg(g_log).arg(len_d).arg(&hi).arg(&ti);
14559 unsafe {
14560 b.launch(cfg)?;
14561 }
14562 Ok(())
14563 }
14564
14565 pub fn row_gather_dev(
14568 &self,
14569 src: &CudaSlice<f32>,
14570 dst: &mut CudaSlice<f32>,
14571 len_d: &CudaSlice<i32>,
14572 ncols: usize,
14573 ) -> Result<(), Box<dyn std::error::Error>> {
14574 let f = self.func("row_gather_dev_f32");
14575 let cfg = LaunchConfig::for_num_elems(ncols as u32);
14576 let nc = ncols as i32;
14577 let __s_b = self.gpu.stream();
14578 let mut b = __s_b.launch_builder(&f);
14579 b.arg(src).arg(dst).arg(len_d).arg(&nc);
14580 unsafe {
14581 b.launch(cfg)?;
14582 }
14583 Ok(())
14584 }
14585
14586 pub fn matmul_group(
14593 &self,
14594 ws: &[&crate::model::GpuTensor],
14595 x: &CudaSlice<f32>,
14596 m: usize,
14597 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
14598 use crate::model::GpuTensor;
14599 let mut out = Vec::with_capacity(ws.len());
14600 let any_mirror = ws
14601 .iter()
14602 .any(|w| matches!(w, GpuTensor::Quant { f16: Some(_), .. }));
14603 if m >= 16 && any_mirror && !self.verify_exact_on() {
14604 let in_f = ws[0].in_features();
14605 let xh = self.f16_act(x, m * in_f, in_f)?;
14606 for w in ws {
14607 if w.in_features() == in_f {
14608 if let Some(y) = self.try_f16_gemm_pre(w, &xh, m)? {
14609 out.push(y);
14610 continue;
14611 }
14612 }
14613 out.push(self.matmul(w, x, m)?);
14614 }
14615 return Ok(out);
14616 }
14617 for w in ws {
14618 out.push(self.matmul(w, x, m)?);
14619 }
14620 Ok(out)
14621 }
14622
14623 pub fn matmul_group_multi(
14630 &self,
14631 ws: &[&crate::model::GpuTensor],
14632 xs: &[&CudaSlice<f32>],
14633 ms: &[usize],
14634 ) -> Result<Vec<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
14635 assert_eq!(xs.len(), ms.len());
14636 let in_f = ws[0].in_features();
14637 let total: usize = ms.iter().sum();
14638 let mut xcat = self.uninit(total * in_f)?;
14639 let mut off = 0usize;
14640 for (x, &m) in xs.iter().zip(ms) {
14641 self.copy_into(&mut xcat, off * in_f, x, m * in_f)?;
14642 off += m;
14643 }
14644 let ys = self.matmul_group(ws, &xcat, total)?;
14645 let mut out: Vec<Vec<CudaSlice<f32>>> = (0..xs.len()).map(|_| Vec::new()).collect();
14646 for (w, y) in ws.iter().zip(ys) {
14647 let out_f = w.out_features();
14648 let mut off = 0usize;
14649 for (s, &m) in ms.iter().enumerate() {
14650 let mut ys_s = self.uninit(m * out_f)?;
14651 let src = y.slice(off * out_f..(off + m) * out_f);
14652 self.gpu.stream().memcpy_dtod(&src, &mut ys_s)?;
14653 out[s].push(ys_s);
14654 off += m;
14655 }
14656 }
14657 Ok(out)
14658 }
14659
14660 pub fn gemm_supports(&self, w: &crate::model::GpuTensor) -> bool {
14670 use crate::model::GpuTensor;
14671 if !legacy_quant_gemm_allowed(
14672 cfg!(memra_portable_cuda),
14673 cfg!(memra_hopper_mma),
14674 std::env::var_os("MEMRA_NO_GEMM").is_some(),
14675 ) {
14676 return false;
14677 }
14678 match w {
14679 GpuTensor::Quant { qtype, .. } => {
14680 matches!(*qtype, QT_Q8_0 | QT_Q4_K | QT_Q6_K | QT_Q5_K | QT_Q4_0)
14681 || (*qtype == QT_NVFP4 && w.in_features() % 64 == 0)
14682 }
14683 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
14684 }
14685 }
14686
14687 pub fn qmatvec_gemm(
14694 &self,
14695 w: &crate::model::GpuTensor,
14696 aq: &CudaSlice<i8>,
14697 ad: &CudaSlice<f32>,
14698 m: usize,
14699 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14700 use crate::model::GpuTensor;
14701 let in_f = w.in_features();
14702 let out_f = w.out_features();
14703 let (bytes, qtype, row_bytes, scale, rp) = match w {
14704 GpuTensor::Quant {
14705 bytes,
14706 qtype,
14707 row_bytes,
14708 scale,
14709 rp,
14710 ..
14711 } => (bytes, *qtype, *row_bytes, *scale, *rp),
14712 _ => unreachable!("gemm_supports guaranteed Quant"),
14713 };
14714 if cfg!(memra_hopper_mma) && qtype == QT_Q8_0 && out_f % 64 == 0 && wgmma_gemm_enabled() {
14720 if let GpuTensor::Quant { rp4: Some(m4), .. } = w {
14721 let mut y = self.qmatvec_gemm_q8_0_wgmma_raw(m4, aq, ad, m, in_f, out_f)?;
14722 if scale != 1.0 {
14723 self.scale_inplace(&mut y, scale, m * out_f)?;
14724 }
14725 return Ok(y);
14726 }
14727 }
14728 let name = match qtype {
14729 QT_Q8_0 => "qmatvec_gemm_q8_0",
14730 QT_Q4_K => "qmatvec_gemm_q4_K",
14731 QT_Q4_0 => {
14732 if rp {
14733 "qmatvec_gemm_q4_0_rp"
14734 } else {
14735 "qmatvec_gemm_q4_0"
14736 }
14737 }
14738 QT_Q5_K => "qmatvec_gemm_q5_K",
14739 QT_Q6_K => "qmatvec_gemm_q6_K",
14740 QT_NVFP4 => {
14741 if rp {
14742 "qmatvec_gemm_nvfp4_rp"
14743 } else {
14744 "qmatvec_gemm_nvfp4"
14745 }
14746 }
14747 _ => unreachable!(),
14748 };
14749 let f = self.func(name);
14750 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);
14755 let k1_tile = if is_k1 {
14757 k1_launch_override().unwrap_or((128, 128, 8))
14758 } else {
14759 (128, 128, 8)
14760 };
14761 let (bm, bn): (u32, u32) = if is_k1 {
14762 (k1_tile.0, k1_tile.1)
14763 } else {
14764 (64, 256)
14765 };
14766 let warps: u32 = if is_k1 {
14767 k1_tile.2
14768 } else {
14769 match qtype {
14770 QT_NVFP4 => 8,
14771 _ => 4,
14772 }
14773 };
14774 let cfg = LaunchConfig {
14775 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
14776 block_dim: (32, warps, 1),
14777 shared_mem_bytes: 0,
14778 };
14779 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14780 let __s_b = self.gpu.stream();
14781 let mut b = __s_b.launch_builder(&f);
14782 b.arg(bytes)
14783 .arg(aq)
14784 .arg(ad)
14785 .arg(&mut y)
14786 .arg(&inf)
14787 .arg(&outf)
14788 .arg(&mi)
14789 .arg(&rb);
14790 unsafe {
14791 b.launch(cfg)?;
14792 }
14793 if scale != 1.0 {
14794 self.scale_inplace(&mut y, scale, m * out_f)?;
14795 }
14796 Ok(y)
14797 }
14798
14799 pub fn qmatvec_gemm_raw(
14804 &self,
14805 bytes: &CudaSlice<u8>,
14806 x: &CudaSlice<f32>,
14807 m: usize,
14808 in_f: usize,
14809 out_f: usize,
14810 qtype: i32,
14811 row_bytes: usize,
14812 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14813 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
14814 let name = match qtype {
14815 QT_Q8_0 => "qmatvec_gemm_q8_0",
14816 QT_Q4_K => "qmatvec_gemm_q4_K",
14817 QT_Q4_0 => "qmatvec_gemm_q4_0",
14818 QT_Q5_K => "qmatvec_gemm_q5_K",
14819 QT_Q6_K => "qmatvec_gemm_q6_K",
14820 QT_NVFP4 => "qmatvec_gemm_nvfp4",
14821 QT_NVFP4_RP => "qmatvec_gemm_nvfp4_rp",
14822 _ => panic!("qmatvec_gemm_raw: qtype {qtype} has no GEMM kernel"),
14823 };
14824 let f = self.func(name);
14825 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);
14829 let k1_tile = if is_k1 {
14831 k1_launch_override().unwrap_or((128, 128, 8))
14832 } else {
14833 (128, 128, 8)
14834 };
14835 let (bm, bn): (u32, u32) = if is_k1 {
14836 (k1_tile.0, k1_tile.1)
14837 } else {
14838 (64, 256)
14839 };
14840 let warps: u32 = if is_k1 {
14841 k1_tile.2
14842 } else {
14843 match qtype {
14844 QT_NVFP4 | QT_NVFP4_RP => 8,
14845 _ => 4,
14846 }
14847 };
14848 let cfg = LaunchConfig {
14849 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
14850 block_dim: (32, warps, 1),
14851 shared_mem_bytes: 0,
14852 };
14853 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14854 let __s_b = self.gpu.stream();
14855 let mut b = __s_b.launch_builder(&f);
14856 b.arg(bytes)
14857 .arg(&aq)
14858 .arg(&ad)
14859 .arg(&mut y)
14860 .arg(&inf)
14861 .arg(&outf)
14862 .arg(&mi)
14863 .arg(&rb);
14864 unsafe {
14865 b.launch(cfg)?;
14866 }
14867 Ok(y)
14868 }
14869
14870 pub fn qmatvec_gemm_q8_0_wgmma_raw(
14877 &self,
14878 rp4: &CudaSlice<u8>,
14879 aq: &CudaSlice<i8>,
14880 ad: &CudaSlice<f32>,
14881 m: usize,
14882 in_f: usize,
14883 out_f: usize,
14884 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14885 assert!(
14886 out_f % 64 == 0 && in_f % 32 == 0,
14887 "wgmma GEMM needs out_f%64==0, in_f%32==0"
14888 );
14889 let f = self.func("qmatvec_gemm_q8_0_wgmma");
14890 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
14892 grid_dim: ((out_f / 64) as u32, (m as u32).div_ceil(64), 1),
14893 block_dim: (128, 1, 1),
14894 shared_mem_bytes: 0,
14895 };
14896 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
14897 let __s_b = self.gpu.stream();
14898 let mut b = __s_b.launch_builder(&f);
14899 b.arg(rp4)
14900 .arg(aq)
14901 .arg(ad)
14902 .arg(&mut y)
14903 .arg(&inf)
14904 .arg(&outf)
14905 .arg(&mi);
14906 unsafe {
14907 b.launch(cfg)?;
14908 }
14909 Ok(y)
14910 }
14911
14912 pub fn scale_inplace(
14914 &self,
14915 y: &mut CudaSlice<f32>,
14916 s: f32,
14917 n: usize,
14918 ) -> Result<(), Box<dyn std::error::Error>> {
14919 let f = self.func("scale_f32");
14920 let cfg = LaunchConfig::for_num_elems(n as u32);
14921 let (sf, ni) = (s, n as i32);
14922 let __s_b = self.gpu.stream();
14923 let mut b = __s_b.launch_builder(&f);
14924 b.arg(y).arg(&sf).arg(&ni);
14925 unsafe {
14926 b.launch(cfg)?;
14927 }
14928 Ok(())
14929 }
14930
14931 pub fn bf16_to_f32(
14936 &self,
14937 data: &cudarc::driver::CudaView<'_, u8>,
14938 n: usize,
14939 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14940 let mut out = self.alloc_uninit::<f32>(n)?;
14941 let f = self.func("bf16_to_f32");
14942 let cfg = LaunchConfig::for_num_elems(n as u32);
14943 let ni = n as i32;
14944 let __s_b = self.gpu.stream();
14945 let mut b = __s_b.launch_builder(&f);
14946 b.arg(data).arg(&mut out).arg(&ni);
14947 unsafe {
14948 b.launch(cfg)?;
14949 }
14950 Ok(out)
14951 }
14952
14953 fn linear_bf16_chunked(
14960 &self,
14961 x: &CudaSlice<f32>,
14962 data: &CudaSlice<u8>,
14963 m: usize,
14964 in_f: usize,
14965 out_f: usize,
14966 exact: bool,
14967 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14968 const CHUNK_BYTES: usize = 256 << 20;
14969 let chunk_rows = (CHUNK_BYTES / (in_f * 4)).max(1).min(out_f);
14970 if chunk_rows >= out_f {
14971 let wf32 = self.bf16_to_f32(&data.slice(0..in_f * out_f * 2), in_f * out_f)?;
14972 return if exact {
14973 self.linear_decode_exact(x, &wf32, m, in_f, out_f)
14974 } else {
14975 self.linear(x, &wf32, m, in_f, out_f)
14976 };
14977 }
14978 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
14979 let mut r0 = 0usize;
14980 while r0 < out_f {
14981 let rows = chunk_rows.min(out_f - r0);
14982 let wslice = data.slice(r0 * in_f * 2..(r0 + rows) * in_f * 2);
14983 let wf32 = self.bf16_to_f32(&wslice, in_f * rows)?;
14984 let yc = if exact {
14985 self.linear_decode_exact(x, &wf32, m, in_f, rows)?
14986 } else {
14987 self.linear(x, &wf32, m, in_f, rows)?
14988 };
14989 for mi in 0..m {
14991 let src = yc.slice(mi * rows..(mi + 1) * rows);
14992 let mut dst = y.slice_mut(mi * out_f + r0..mi * out_f + r0 + rows);
14993 self.gpu.stream().memcpy_dtod(&src, &mut dst)?;
14994 }
14995 r0 += rows;
14996 }
14997 Ok(y)
14998 }
14999
15000 pub fn linear_decode_exact(
15007 &self,
15008 x: &CudaSlice<f32>,
15009 w: &CudaSlice<f32>,
15010 m_tokens: usize,
15011 in_f: usize,
15012 out_f: usize,
15013 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15014 if m_tokens == 1 {
15015 return self.linear(x, w, 1, in_f, out_f);
15016 }
15017 let xv = self.view(x, m_tokens * in_f);
15018 let mut y = self.alloc_uninit::<f32>(m_tokens * out_f)?;
15019 for t in 0..m_tokens {
15020 let row = xv.slice(t * in_f..(t + 1) * in_f);
15021 let mut xr = self.alloc_uninit::<f32>(in_f)?;
15022 self.copy_view_into(&mut xr, 0, &row, in_f)?;
15023 let yr = self.linear(&xr, w, 1, in_f, out_f)?;
15024 self.copy_into(&mut y, t * out_f, &yr, out_f)?;
15025 }
15026 Ok(y)
15027 }
15028
15029 pub fn linear(
15030 &self,
15031 x: &CudaSlice<f32>,
15032 w: &CudaSlice<f32>,
15033 m_tokens: usize,
15034 in_f: usize,
15035 out_f: usize,
15036 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15037 use cudarc::cublaslt::{Matmul, MatmulConfig};
15038 let mut c = self.alloc_uninit::<f32>(m_tokens * out_f)?; let cfg = MatmulConfig {
15040 transa: true,
15041 transb: false,
15042 transc: false,
15043 m: out_f as u64,
15044 n: m_tokens as u64,
15045 k: in_f as u64,
15046 alpha: 1.0,
15047 lda: in_f as i64,
15048 ldb: in_f as i64,
15049 beta: 0.0,
15050 ldc: out_f as i64,
15051 stride_a: None,
15052 stride_b: None,
15053 stride_c: None,
15054 stride_bias: None,
15055 batch_size: None,
15056 };
15057 unsafe {
15058 self.gpu.blas.matmul(cfg, w, x, &mut c, None, None)?;
15059 }
15060 Ok(c)
15061 }
15062
15063 pub fn sdpa_naive(
15065 &self,
15066 q: &CudaSlice<f32>,
15067 k: &CudaSlice<f32>,
15068 v: &CudaSlice<f32>,
15069 o: &mut CudaSlice<f32>,
15070 head_dim: usize,
15071 n_head: usize,
15072 n_head_kv: usize,
15073 t: usize,
15074 t_kv: usize,
15075 scale: f32,
15076 causal: bool,
15077 ) -> Result<(), Box<dyn std::error::Error>> {
15078 let f = self.func("sdpa_naive_f32");
15079 let cfg = LaunchConfig {
15080 grid_dim: (n_head as u32, t as u32, 1),
15081 block_dim: (128, 1, 1),
15082 shared_mem_bytes: (t_kv * 4) as u32,
15083 };
15084 let (hd, nh, nhkv, ti, tkvi, cz) = (
15085 head_dim as i32,
15086 n_head as i32,
15087 n_head_kv as i32,
15088 t as i32,
15089 t_kv as i32,
15090 causal as i32,
15091 );
15092 let __s_b = self.gpu.stream();
15093 let mut b = __s_b.launch_builder(&f);
15094 b.arg(q)
15095 .arg(k)
15096 .arg(v)
15097 .arg(o)
15098 .arg(&hd)
15099 .arg(&nh)
15100 .arg(&nhkv)
15101 .arg(&ti)
15102 .arg(&tkvi)
15103 .arg(&scale)
15104 .arg(&cz);
15105 unsafe {
15106 b.launch(cfg)?;
15107 }
15108 Ok(())
15109 }
15110
15111 #[allow(clippy::too_many_arguments)]
15116 pub fn sdpa_naive_island(
15117 &self,
15118 q: &CudaSlice<f32>,
15119 k: &CudaSlice<f32>,
15120 v: &CudaSlice<f32>,
15121 o: &mut CudaSlice<f32>,
15122 span_id: &CudaSlice<i32>,
15123 head_dim: usize,
15124 n_head: usize,
15125 n_head_kv: usize,
15126 t: usize,
15127 t_kv: usize,
15128 scale: f32,
15129 window: usize,
15130 ) -> Result<(), Box<dyn std::error::Error>> {
15131 let f = self.func("sdpa_naive_island_f32");
15132 let cfg = LaunchConfig {
15133 grid_dim: (n_head as u32, t as u32, 1),
15134 block_dim: (128, 1, 1),
15135 shared_mem_bytes: (t_kv * 4) as u32,
15136 };
15137 let (hd, nh, nhkv, ti, tkvi, wi) = (
15138 head_dim as i32,
15139 n_head as i32,
15140 n_head_kv as i32,
15141 t as i32,
15142 t_kv as i32,
15143 window as i32,
15144 );
15145 let __s_b = self.gpu.stream();
15146 let mut b = __s_b.launch_builder(&f);
15147 b.arg(q)
15148 .arg(k)
15149 .arg(v)
15150 .arg(o)
15151 .arg(span_id)
15152 .arg(&hd)
15153 .arg(&nh)
15154 .arg(&nhkv)
15155 .arg(&ti)
15156 .arg(&tkvi)
15157 .arg(&scale)
15158 .arg(&wi);
15159 unsafe {
15160 b.launch(cfg)?;
15161 }
15162 Ok(())
15163 }
15164
15165 #[allow(clippy::too_many_arguments)]
15167 pub fn sdpa_naive_w(
15168 &self,
15169 q: &CudaSlice<f32>,
15170 k: &CudaSlice<f32>,
15171 v: &CudaSlice<f32>,
15172 o: &mut CudaSlice<f32>,
15173 head_dim: usize,
15174 n_head: usize,
15175 n_head_kv: usize,
15176 t: usize,
15177 t_kv: usize,
15178 scale: f32,
15179 causal: bool,
15180 window: usize,
15181 ) -> Result<(), Box<dyn std::error::Error>> {
15182 let f = self.func("sdpa_naive_w_f32");
15183 let cfg = LaunchConfig {
15184 grid_dim: (n_head as u32, t as u32, 1),
15185 block_dim: (128, 1, 1),
15186 shared_mem_bytes: (t_kv * 4) as u32,
15187 };
15188 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15189 head_dim as i32,
15190 n_head as i32,
15191 n_head_kv as i32,
15192 t as i32,
15193 t_kv as i32,
15194 causal as i32,
15195 window as i32,
15196 );
15197 let __s_b = self.gpu.stream();
15198 let mut b = __s_b.launch_builder(&f);
15199 b.arg(q)
15200 .arg(k)
15201 .arg(v)
15202 .arg(o)
15203 .arg(&hd)
15204 .arg(&nh)
15205 .arg(&nhkv)
15206 .arg(&ti)
15207 .arg(&tkvi)
15208 .arg(&scale)
15209 .arg(&cz)
15210 .arg(&wi);
15211 unsafe {
15212 b.launch(cfg)?;
15213 }
15214 Ok(())
15215 }
15216
15217 pub fn sdpa_naive_view(
15219 &self,
15220 q: &CudaSlice<f32>,
15221 k: &cudarc::driver::CudaView<f32>,
15222 v: &cudarc::driver::CudaView<f32>,
15223 o: &mut CudaSlice<f32>,
15224 head_dim: usize,
15225 n_head: usize,
15226 n_head_kv: usize,
15227 t: usize,
15228 t_kv: usize,
15229 scale: f32,
15230 causal: bool,
15231 ) -> Result<(), Box<dyn std::error::Error>> {
15232 let f = self.func("sdpa_naive_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) = (
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 );
15246 let __s_b = self.gpu.stream();
15247 let mut b = __s_b.launch_builder(&f);
15248 b.arg(q)
15249 .arg(k)
15250 .arg(v)
15251 .arg(o)
15252 .arg(&hd)
15253 .arg(&nh)
15254 .arg(&nhkv)
15255 .arg(&ti)
15256 .arg(&tkvi)
15257 .arg(&scale)
15258 .arg(&cz);
15259 unsafe {
15260 b.launch(cfg)?;
15261 }
15262 Ok(())
15263 }
15264
15265 #[allow(clippy::too_many_arguments)]
15273 pub fn fa_dequant_kv_view_f32(
15274 &self,
15275 k: &cudarc::driver::CudaView<u8>,
15276 v: &cudarc::driver::CudaView<u8>,
15277 kf: &mut CudaSlice<f32>,
15278 vf: &mut CudaSlice<f32>,
15279 kv_dim_k: usize,
15280 kv_dim_v: usize,
15281 t_kv: usize,
15282 k_tok_bytes: usize,
15283 v_tok_bytes: usize,
15284 g: bool,
15285 ) -> Result<(), Box<dyn std::error::Error>> {
15286 let f = if g {
15287 self.func_g("fa_dequant_kv_ws_f32")
15288 } else {
15289 self.func("fa_dequant_kv_ws_f32")
15290 };
15291 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
15292 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
15293 let cfg = LaunchConfig {
15294 grid_dim: (nblk.max(1), 1, 1),
15295 block_dim: (256, 1, 1),
15296 shared_mem_bytes: 0,
15297 };
15298 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
15299 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
15300 let __s_b = self.gpu.stream();
15301 let mut b = __s_b.launch_builder(&f);
15302 b.arg(k)
15303 .arg(v)
15304 .arg(&mut *kf)
15305 .arg(&mut *vf)
15306 .arg(&kdk)
15307 .arg(&kdv)
15308 .arg(&tkvi)
15309 .arg(&ktb)
15310 .arg(&vtb);
15311 unsafe {
15312 b.launch(cfg)?;
15313 }
15314 Ok(())
15315 }
15316
15317 #[allow(clippy::too_many_arguments)]
15318 pub fn sdpa_naive_quantized_view(
15319 &self,
15320 q: &CudaSlice<f32>,
15321 k: &cudarc::driver::CudaView<u8>,
15322 v: &cudarc::driver::CudaView<u8>,
15323 o: &mut CudaSlice<f32>,
15324 head_dim: usize,
15325 n_head: usize,
15326 n_head_kv: usize,
15327 t: usize,
15328 t_kv: usize,
15329 scale: f32,
15330 causal: bool,
15331 k_tok_bytes: usize,
15332 v_tok_bytes: usize,
15333 ) -> Result<(), Box<dyn std::error::Error>> {
15334 let kv_dim = n_head_kv * head_dim;
15335 let mut kf = self.uninit(t_kv * kv_dim)?;
15336 let mut vf = self.uninit(t_kv * kv_dim)?;
15337 let f = self.func("fa_dequant_kv_ws_f32");
15338 let total = (2 * t_kv * kv_dim) as u64;
15339 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
15340 let cfg = LaunchConfig {
15341 grid_dim: (nblk.max(1), 1, 1),
15342 block_dim: (256, 1, 1),
15343 shared_mem_bytes: 0,
15344 };
15345 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
15346 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
15347 let __s_b = self.gpu.stream();
15348 let mut b = __s_b.launch_builder(&f);
15349 b.arg(k)
15350 .arg(v)
15351 .arg(&mut kf)
15352 .arg(&mut vf)
15353 .arg(&kv_dim_i)
15354 .arg(&kv_dim_i)
15355 .arg(&t_kv_i)
15356 .arg(&k_tok_bytes_i)
15357 .arg(&v_tok_bytes_i);
15358 unsafe { b.launch(cfg)? };
15359 self.sdpa_naive(
15360 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
15361 )
15362 }
15363
15364 #[allow(clippy::too_many_arguments)]
15376 pub fn sdpa_naive_w_quantized_view(
15377 &self,
15378 q: &CudaSlice<f32>,
15379 k: &cudarc::driver::CudaView<u8>,
15380 v: &cudarc::driver::CudaView<u8>,
15381 o: &mut CudaSlice<f32>,
15382 head_dim: usize,
15383 n_head: usize,
15384 n_head_kv: usize,
15385 t: usize,
15386 t_kv: usize,
15387 scale: f32,
15388 causal: bool,
15389 window: usize,
15390 k_tok_bytes: usize,
15391 v_tok_bytes: usize,
15392 ) -> Result<(), Box<dyn std::error::Error>> {
15393 let kv_dim = n_head_kv * head_dim;
15394 let mut kf = self.uninit(t_kv * kv_dim)?;
15395 let mut vf = self.uninit(t_kv * kv_dim)?;
15396 let f = self.func("fa_dequant_kv_ws_f32");
15397 let total = (2 * t_kv * kv_dim) as u64;
15398 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
15399 let cfg = LaunchConfig {
15400 grid_dim: (nblk.max(1), 1, 1),
15401 block_dim: (256, 1, 1),
15402 shared_mem_bytes: 0,
15403 };
15404 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
15405 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
15406 let __s_b = self.gpu.stream();
15407 let mut b = __s_b.launch_builder(&f);
15408 b.arg(k)
15409 .arg(v)
15410 .arg(&mut kf)
15411 .arg(&mut vf)
15412 .arg(&kv_dim_i)
15413 .arg(&kv_dim_i)
15414 .arg(&t_kv_i)
15415 .arg(&k_tok_bytes_i)
15416 .arg(&v_tok_bytes_i);
15417 unsafe { b.launch(cfg)? };
15418 self.sdpa_naive_w(
15419 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
15420 )
15421 }
15422
15423 pub fn fa_prefill(
15427 &self,
15428 q: &CudaSlice<f32>,
15429 k: &CudaSlice<f32>,
15430 v: &CudaSlice<f32>,
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 ) -> Result<(), Box<dyn std::error::Error>> {
15440 if portable_mma_gated() {
15441 return self.sdpa_naive(
15442 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
15443 );
15444 }
15445 let fa3_on = head_dim == 256
15453 && causal
15454 && t == t_kv
15455 && match std::env::var("MEMRA_FA3").as_deref() {
15456 Ok("0") => false,
15457 Ok("1") => true,
15458 _ => cfg!(memra_hopper_mma),
15459 };
15460 if fa3_on {
15461 let n = t * n_head * head_dim;
15462 let nkv = t * n_head_kv * head_dim;
15463 let mut q16 = self.alloc_u8_uninit(n * 2)?;
15464 let mut k16 = self.alloc_u8_uninit(nkv * 2)?;
15465 let mut v16 = self.alloc_u8_uninit(nkv * 2)?;
15466 self.f32_to_bf16_into(q, &mut q16, n)?;
15467 self.f32_to_bf16_into(k, &mut k16, nkv)?;
15468 self.f32_to_bf16_into(v, &mut v16, nkv)?;
15469 let rc = {
15470 use cudarc::driver::{DevicePtr, DevicePtrMut};
15471 let stream = self.gpu.stream();
15472 let (qp, _g1) = q16.device_ptr(&stream);
15473 let (kp, _g2) = k16.device_ptr(&stream);
15474 let (vp, _g3) = v16.device_ptr(&stream);
15475 let (op, _g4) = o.device_ptr_mut(&stream);
15476 unsafe {
15477 memra_fa3_prefill(
15478 qp as *const core::ffi::c_void,
15479 kp as *const core::ffi::c_void,
15480 vp as *const core::ffi::c_void,
15481 op as *mut f32,
15482 t as i32,
15483 n_head as i32,
15484 n_head_kv as i32,
15485 head_dim as i32,
15486 scale,
15487 stream.cu_stream() as *mut core::ffi::c_void,
15488 )
15489 }
15490 };
15491 if rc != 0 {
15492 return Err(format!("memra_fa3_prefill rc={rc}").into());
15493 }
15494 return Ok(());
15495 }
15496 static FA_P1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15501 let fa_p1 = *FA_P1.get_or_init(|| std::env::var("MEMRA_FA_P1").as_deref() == Ok("1"));
15502 if fa_p1 && head_dim == 256 && !std::env::var("MEMRA_FA_FLOOR").is_ok() {
15503 const BLOCK_Q: usize = 64;
15504 const BKX: usize = 32;
15505 let f = self.func("fa_prefill_bf16_p1");
15506 let shmem = (2 * (2 * BKX * head_dim + BLOCK_Q * BKX)
15507 + 4 * (BLOCK_Q * BKX + 2 * BLOCK_Q)) as u32;
15508 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15509 f.set_attribute(
15510 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15511 shmem as i32,
15512 )?;
15513 let cfg = LaunchConfig {
15514 grid_dim: (
15515 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15516 n_head as u32,
15517 1,
15518 ),
15519 block_dim: (32, 4, 1),
15520 shared_mem_bytes: shmem,
15521 };
15522 let (hd, nh, nhkv, ti, tkvi, cz) = (
15523 head_dim as i32,
15524 n_head as i32,
15525 n_head_kv as i32,
15526 t as i32,
15527 t_kv as i32,
15528 causal as i32,
15529 );
15530 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15531 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15532 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15533 let __s_b = self.gpu.stream();
15534 let mut b = __s_b.launch_builder(&f);
15535 b.arg(&qb)
15536 .arg(&kb)
15537 .arg(&vb)
15538 .arg(o)
15539 .arg(&hd)
15540 .arg(&nh)
15541 .arg(&nhkv)
15542 .arg(&ti)
15543 .arg(&tkvi)
15544 .arg(&scale)
15545 .arg(&cz);
15546 unsafe {
15547 b.launch(cfg)?;
15548 }
15549 return Ok(());
15550 }
15551 const BK: usize = 32;
15557 let w2 = std::env::var("MEMRA_FA_PP_W2").as_deref() == Ok("1");
15560 let (block_q, warps, w2_sfx): (usize, u32, &str) =
15561 if w2 { (32, 2, "_w2") } else { (64, 4, "") };
15562 let hd_sfx = fa_hd_suffix(head_dim)?;
15566 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
15567 let bf16kv = !floor && !w2 && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0");
15572 let (kb16, vb16) = if bf16kv {
15573 let n = t_kv * n_head_kv * head_dim;
15574 let mut kb = self.alloc_u8_uninit(n * 2)?;
15575 let mut vb = self.alloc_u8_uninit(n * 2)?;
15576 let fcv = self.func("f32_to_bf16_bulk");
15577 let ni = n as i64;
15578 let cfgc = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
15579 let __s_b = self.gpu.stream();
15580 let mut b = __s_b.launch_builder(&fcv);
15581 b.arg(k).arg(&mut kb).arg(&ni);
15582 unsafe {
15583 b.launch(cfgc)?;
15584 }
15585 let __s_b = self.gpu.stream();
15586 let mut b = __s_b.launch_builder(&fcv);
15587 b.arg(v).arg(&mut vb).arg(&ni);
15588 unsafe {
15589 b.launch(cfgc)?;
15590 }
15591 (Some(kb), Some(vb))
15592 } else {
15593 (None, None)
15594 };
15595 let f = self.func(&if bf16kv {
15596 format!("fa_prefill_bf16kv_pp{hd_sfx}")
15597 } else {
15598 format!(
15599 "fa_prefill_f32{}{}{hd_sfx}",
15600 if floor { "" } else { "_pp" },
15601 if floor { "" } else { w2_sfx }
15602 )
15603 });
15604 let kv_stages = if bf16kv { 2 } else { 1 };
15607 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
15608 + 4 * (block_q * BK + 2 * block_q)) as u32;
15609 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15610 f.set_attribute(
15611 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15612 shmem as i32,
15613 )?;
15614 let cfg = LaunchConfig {
15615 grid_dim: (
15616 (t as u32 + block_q as u32 - 1) / block_q as u32,
15617 n_head as u32,
15618 1,
15619 ),
15620 block_dim: (32, warps, 1),
15621 shared_mem_bytes: shmem,
15622 };
15623 let (hd, nh, nhkv, ti, tkvi, cz) = (
15624 head_dim as i32,
15625 n_head as i32,
15626 n_head_kv as i32,
15627 t as i32,
15628 t_kv as i32,
15629 causal as i32,
15630 );
15631 let __s_b = self.gpu.stream();
15632 let mut b = __s_b.launch_builder(&f);
15633 b.arg(q);
15634 match (&kb16, &vb16) {
15635 (Some(kb), Some(vb)) => {
15636 b.arg(kb).arg(vb);
15637 }
15638 _ => {
15639 b.arg(k).arg(v);
15640 }
15641 }
15642 b.arg(o)
15643 .arg(&hd)
15644 .arg(&nh)
15645 .arg(&nhkv)
15646 .arg(&ti)
15647 .arg(&tkvi)
15648 .arg(&scale)
15649 .arg(&cz);
15650 unsafe {
15651 b.launch(cfg)?;
15652 }
15653 Ok(())
15654 }
15655
15656 #[allow(clippy::too_many_arguments)]
15660 pub fn fa_prefill_w(
15661 &self,
15662 q: &CudaSlice<f32>,
15663 k: &CudaSlice<f32>,
15664 v: &CudaSlice<f32>,
15665 o: &mut CudaSlice<f32>,
15666 head_dim: usize,
15667 n_head: usize,
15668 n_head_kv: usize,
15669 t: usize,
15670 t_kv: usize,
15671 scale: f32,
15672 causal: bool,
15673 window: usize,
15674 ) -> Result<(), Box<dyn std::error::Error>> {
15675 if portable_mma_gated() {
15678 return self.sdpa_naive_w(
15679 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
15680 );
15681 }
15682 static FAW_F32: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15686 let faw_f32 =
15687 *FAW_F32.get_or_init(|| std::env::var("MEMRA_FAW_STAGE").as_deref() == Ok("f32"));
15688 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
15689 self.fa_prefill_w_arm(
15690 q,
15691 k,
15692 v,
15693 o,
15694 head_dim,
15695 n_head,
15696 n_head_kv,
15697 t,
15698 t_kv,
15699 scale,
15700 causal,
15701 window,
15702 floor || faw_f32,
15703 floor,
15704 )
15705 }
15706
15707 #[allow(clippy::too_many_arguments)]
15710 pub fn fa_prefill_w_pre(
15711 &self,
15712 qb: &CudaSlice<u8>,
15713 kb: &CudaSlice<u8>,
15714 vb: &CudaSlice<u8>,
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 v_f16: bool,
15725 ) -> Result<(), Box<dyn std::error::Error>> {
15726 const BLOCK_Q: usize = 64;
15727 const BK: usize = 32;
15728 debug_assert_eq!(head_dim, 256);
15729 let hp = fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15730 debug_assert!(!v_f16 || hp, "f16 V emitted but the SWA hp arm is off");
15731 if hp {
15732 const BLOCK_QH: usize = 32;
15733 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
15736 let vh: &CudaSlice<u8> = if v_f16 {
15737 vb
15738 } else {
15739 let n = t_kv * n_head_kv * head_dim;
15740 if vguard.as_ref().map(|b| b.len() < n * 2).unwrap_or(true) {
15741 *vguard = Some(self.alloc_uninit::<u8>(n * 2)?);
15742 }
15743 self.bf16_to_f16_into(vb, n, vguard.as_mut().unwrap())?;
15744 vguard.as_ref().unwrap()
15745 };
15746 let f = self.func("fa_prefill_w_bf16_p1h2");
15747 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
15748 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15749 f.set_attribute(
15750 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15751 shmem as i32,
15752 )?;
15753 let cfg = LaunchConfig {
15754 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
15755 block_dim: (32, 4, 1),
15756 shared_mem_bytes: shmem,
15757 };
15758 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15759 head_dim as i32,
15760 n_head as i32,
15761 n_head_kv as i32,
15762 t as i32,
15763 t_kv as i32,
15764 causal as i32,
15765 window as i32,
15766 );
15767 let __s_b = self.gpu.stream();
15768 let mut b = __s_b.launch_builder(&f);
15769 b.arg(qb)
15770 .arg(kb)
15771 .arg(vh)
15772 .arg(o)
15773 .arg(&hd)
15774 .arg(&nh)
15775 .arg(&nhkv)
15776 .arg(&ti)
15777 .arg(&tkvi)
15778 .arg(&scale)
15779 .arg(&cz)
15780 .arg(&wi);
15781 unsafe {
15782 b.launch(cfg)?;
15783 }
15784 return Ok(());
15785 }
15786 let f = self.func("fa_prefill_w_bf16_p1");
15787 let shmem =
15788 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15789 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15790 f.set_attribute(
15791 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15792 shmem as i32,
15793 )?;
15794 let cfg = LaunchConfig {
15795 grid_dim: (
15796 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15797 n_head as u32,
15798 1,
15799 ),
15800 block_dim: (32, 4, 1),
15801 shared_mem_bytes: shmem,
15802 };
15803 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15804 head_dim as i32,
15805 n_head as i32,
15806 n_head_kv as i32,
15807 t as i32,
15808 t_kv as i32,
15809 causal as i32,
15810 window as i32,
15811 );
15812 let __s_b = self.gpu.stream();
15813 let mut b = __s_b.launch_builder(&f);
15814 b.arg(qb)
15815 .arg(kb)
15816 .arg(vb)
15817 .arg(o)
15818 .arg(&hd)
15819 .arg(&nh)
15820 .arg(&nhkv)
15821 .arg(&ti)
15822 .arg(&tkvi)
15823 .arg(&scale)
15824 .arg(&cz)
15825 .arg(&wi);
15826 unsafe {
15827 b.launch(cfg)?;
15828 }
15829 Ok(())
15830 }
15831
15832 #[allow(clippy::too_many_arguments)]
15834 pub fn fa_prefill_w_arm(
15835 &self,
15836 q: &CudaSlice<f32>,
15837 k: &CudaSlice<f32>,
15838 v: &CudaSlice<f32>,
15839 o: &mut CudaSlice<f32>,
15840 head_dim: usize,
15841 n_head: usize,
15842 n_head_kv: usize,
15843 t: usize,
15844 t_kv: usize,
15845 scale: f32,
15846 causal: bool,
15847 window: usize,
15848 f32_stage: bool,
15849 floor: bool,
15850 ) -> Result<(), Box<dyn std::error::Error>> {
15851 const BLOCK_Q: usize = 64;
15852 const BK: usize = 32;
15853 debug_assert_eq!(head_dim, 256, "fa_prefill_w is stamped hd256 only");
15854 static P1_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15858 let p1 = !floor
15859 && !f32_stage
15860 && *P1_ON.get_or_init(|| {
15861 std::env::var("MEMRA_FAW_P1")
15862 .map(|v| v != "0")
15863 .unwrap_or(true)
15864 });
15865 let hp =
15866 p1 && fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15867 if hp {
15868 const BLOCK_QH: usize = 32;
15869 let f = self.func("fa_prefill_w_bf16_p1h2");
15870 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
15871 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15872 f.set_attribute(
15873 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15874 shmem as i32,
15875 )?;
15876 let cfg = LaunchConfig {
15877 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
15878 block_dim: (32, 4, 1),
15879 shared_mem_bytes: shmem,
15880 };
15881 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15882 head_dim as i32,
15883 n_head as i32,
15884 n_head_kv as i32,
15885 t as i32,
15886 t_kv as i32,
15887 causal as i32,
15888 window as i32,
15889 );
15890 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15891 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15892 let vh = self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?;
15893 let __s_b = self.gpu.stream();
15894 let mut b = __s_b.launch_builder(&f);
15895 b.arg(&qb)
15896 .arg(&kb)
15897 .arg(&vh)
15898 .arg(o)
15899 .arg(&hd)
15900 .arg(&nh)
15901 .arg(&nhkv)
15902 .arg(&ti)
15903 .arg(&tkvi)
15904 .arg(&scale)
15905 .arg(&cz)
15906 .arg(&wi);
15907 unsafe {
15908 b.launch(cfg)?;
15909 }
15910 return Ok(());
15911 }
15912 if p1 {
15913 let f = self.func("fa_prefill_w_bf16_p1");
15914 let shmem =
15915 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15916 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15917 f.set_attribute(
15918 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15919 shmem as i32,
15920 )?;
15921 let cfg = LaunchConfig {
15922 grid_dim: (
15923 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15924 n_head as u32,
15925 1,
15926 ),
15927 block_dim: (32, 4, 1),
15928 shared_mem_bytes: shmem,
15929 };
15930 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15931 head_dim as i32,
15932 n_head as i32,
15933 n_head_kv as i32,
15934 t as i32,
15935 t_kv as i32,
15936 causal as i32,
15937 window as i32,
15938 );
15939 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15940 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15941 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15942 let __s_b = self.gpu.stream();
15943 let mut b = __s_b.launch_builder(&f);
15944 b.arg(&qb)
15945 .arg(&kb)
15946 .arg(&vb)
15947 .arg(o)
15948 .arg(&hd)
15949 .arg(&nh)
15950 .arg(&nhkv)
15951 .arg(&ti)
15952 .arg(&tkvi)
15953 .arg(&scale)
15954 .arg(&cz)
15955 .arg(&wi);
15956 unsafe {
15957 b.launch(cfg)?;
15958 }
15959 return Ok(());
15960 }
15961 static G4_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15964 let g4 = !floor
15965 && !f32_stage
15966 && n_head_kv == 1
15967 && n_head % 4 == 0
15968 && *G4_ON.get_or_init(|| {
15969 std::env::var("MEMRA_FAW_G4")
15970 .map(|v| v != "0")
15971 .unwrap_or(true)
15972 });
15973 if g4 {
15974 const SP_M: usize = 16;
15975 static O2_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15978 let o2 = *O2_ON.get_or_init(|| {
15979 std::env::var("MEMRA_FAW_O2")
15980 .map(|v| v != "0")
15981 .unwrap_or(true)
15982 });
15983 let f = self.func(if o2 {
15984 "fa_prefill_w_bf16_g4o2"
15985 } else {
15986 "fa_prefill_w_bf16_g4"
15987 });
15988 let shmem = if o2 {
15989 (2 * (4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M)) as u32
15990 } else {
15991 (2 * (2 * BK * head_dim + 4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M))
15992 as u32
15993 };
15994 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15995 f.set_attribute(
15996 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15997 shmem as i32,
15998 )?;
15999 let cfg = LaunchConfig {
16000 grid_dim: ((t as u32).div_ceil(SP_M as u32), (n_head / 4) as u32, 1),
16001 block_dim: (32, 4, 1),
16002 shared_mem_bytes: shmem,
16003 };
16004 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
16005 head_dim as i32,
16006 n_head as i32,
16007 n_head_kv as i32,
16008 t as i32,
16009 t_kv as i32,
16010 causal as i32,
16011 window as i32,
16012 );
16013 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16014 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16015 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
16016 let __s_b = self.gpu.stream();
16017 let mut b = __s_b.launch_builder(&f);
16018 b.arg(&qb)
16019 .arg(&kb)
16020 .arg(&vb)
16021 .arg(o)
16022 .arg(&hd)
16023 .arg(&nh)
16024 .arg(&nhkv)
16025 .arg(&ti)
16026 .arg(&tkvi)
16027 .arg(&scale)
16028 .arg(&cz)
16029 .arg(&wi);
16030 unsafe {
16031 b.launch(cfg)?;
16032 }
16033 return Ok(());
16034 }
16035 let f = self.func(if floor {
16036 "fa_prefill_w_f32"
16037 } else if f32_stage {
16038 "fa_prefill_w_f32_pp"
16039 } else {
16040 "fa_prefill_w_bf16_pp"
16041 });
16042 let shmem =
16043 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
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: (
16051 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16052 n_head as u32,
16053 1,
16054 ),
16055 block_dim: (32, 4, 1),
16056 shared_mem_bytes: shmem,
16057 };
16058 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
16059 head_dim as i32,
16060 n_head as i32,
16061 n_head_kv as i32,
16062 t as i32,
16063 t_kv as i32,
16064 causal as i32,
16065 window as i32,
16066 );
16067 if f32_stage {
16068 let __s_b = self.gpu.stream();
16069 let mut b = __s_b.launch_builder(&f);
16070 b.arg(q)
16071 .arg(k)
16072 .arg(v)
16073 .arg(o)
16074 .arg(&hd)
16075 .arg(&nh)
16076 .arg(&nhkv)
16077 .arg(&ti)
16078 .arg(&tkvi)
16079 .arg(&scale)
16080 .arg(&cz)
16081 .arg(&wi);
16082 unsafe {
16083 b.launch(cfg)?;
16084 }
16085 } else {
16086 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16087 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16088 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
16089 let __s_b = self.gpu.stream();
16090 let mut b = __s_b.launch_builder(&f);
16091 b.arg(&qb)
16092 .arg(&kb)
16093 .arg(&vb)
16094 .arg(o)
16095 .arg(&hd)
16096 .arg(&nh)
16097 .arg(&nhkv)
16098 .arg(&ti)
16099 .arg(&tkvi)
16100 .arg(&scale)
16101 .arg(&cz)
16102 .arg(&wi);
16103 unsafe {
16104 b.launch(cfg)?;
16105 }
16106 }
16107 Ok(())
16108 }
16109
16110 #[allow(clippy::too_many_arguments)]
16114 pub fn fa_prefill_hd512(
16115 &self,
16116 q: &CudaSlice<f32>,
16117 k: &CudaSlice<f32>,
16118 v: &CudaSlice<f32>,
16119 o: &mut CudaSlice<f32>,
16120 head_dim: usize,
16121 n_head: usize,
16122 n_head_kv: usize,
16123 t: usize,
16124 t_kv: usize,
16125 scale: f32,
16126 causal: bool,
16127 ) -> Result<(), Box<dyn std::error::Error>> {
16128 if portable_mma_gated() {
16130 return self.sdpa_naive(
16131 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
16132 );
16133 }
16134 static F32_STAGE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16140 let f32_stage =
16141 *F32_STAGE.get_or_init(|| std::env::var("MEMRA_FA512_STAGE").as_deref() == Ok("f32"));
16142 static SP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16146 let sp = !f32_stage
16147 && *SP_ON.get_or_init(|| {
16148 std::env::var("MEMRA_FA512_SP")
16149 .map(|v| v != "0")
16150 .unwrap_or(true)
16151 });
16152 self.fa_prefill_hd512_arm(
16153 q,
16154 k,
16155 v,
16156 o,
16157 head_dim,
16158 n_head,
16159 n_head_kv,
16160 t,
16161 t_kv,
16162 scale,
16163 causal,
16164 f32_stage,
16165 sp,
16166 sp && fa_f16pv_on(),
16167 )
16168 }
16169
16170 #[allow(clippy::too_many_arguments)]
16172 pub fn fa_prefill_hd512_pre(
16173 &self,
16174 qb: &CudaSlice<u8>,
16175 kb: &CudaSlice<u8>,
16176 vb: &CudaSlice<u8>,
16177 o: &mut CudaSlice<f32>,
16178 head_dim: usize,
16179 n_head: usize,
16180 n_head_kv: usize,
16181 t: usize,
16182 t_kv: usize,
16183 scale: f32,
16184 causal: bool,
16185 v_f16: bool,
16186 ) -> Result<(), Box<dyn std::error::Error>> {
16187 debug_assert_eq!(head_dim, 512);
16188 const SP_M: usize = 16;
16189 const BKS: usize = 32;
16190 let f16pv = fa_f16pv_on();
16194 let nw = if f16pv { fa512_wide_warps() } else { 2 };
16195 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
16196 debug_assert!(!v_f16 || f16pv, "f16 V emitted without the door on");
16197 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
16198 let vref: &CudaSlice<u8> = if f16pv && !v_f16 {
16199 let n = t_kv * n_head_kv * head_dim;
16201 let need = n * 2;
16202 if vguard.as_ref().map(|b| b.len() < need).unwrap_or(true) {
16203 *vguard = Some(self.alloc_uninit::<u8>(need)?);
16204 }
16205 let dst = vguard.as_mut().unwrap();
16206 self.bf16_to_f16_into(vb, n, dst)?;
16207 vguard.as_ref().unwrap()
16208 } else {
16209 vb
16210 };
16211 let f = self.func(if hp {
16212 "fa_prefill_bf16_hd512_sp16h2"
16213 } else {
16214 match (f16pv, nw) {
16215 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
16216 (true, _) => "fa_prefill_bf16_hd512_sp16",
16217 _ => "fa_prefill_bf16_hd512_sp",
16218 }
16219 });
16220 let (nwarp, npart) = if hp {
16221 (4usize, 4usize)
16222 } else if nw > 2 {
16223 (nw, nw)
16224 } else {
16225 (2, 1)
16226 };
16227 let shmem = if hp {
16229 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS) + 4 * (2 * npart * SP_M * BKS + 2 * SP_M))
16230 as u32
16231 } else {
16232 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
16233 + 4 * (npart * SP_M * BKS + SP_M)) as u32
16234 };
16235 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16236 f.set_attribute(
16237 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16238 shmem as i32,
16239 )?;
16240 let grid_y = if hp {
16241 (n_head / 2) as u32
16242 } else {
16243 n_head as u32
16244 };
16245 let cfg = LaunchConfig {
16246 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
16247 block_dim: (32, nwarp as u32, 1),
16248 shared_mem_bytes: shmem,
16249 };
16250 let (hd, nh, nhkv, ti, tkvi, cz) = (
16251 head_dim as i32,
16252 n_head as i32,
16253 n_head_kv as i32,
16254 t as i32,
16255 t_kv as i32,
16256 causal as i32,
16257 );
16258 let __s_b = self.gpu.stream();
16259 let mut b = __s_b.launch_builder(&f);
16260 b.arg(qb)
16261 .arg(kb)
16262 .arg(vref)
16263 .arg(o)
16264 .arg(&hd)
16265 .arg(&nh)
16266 .arg(&nhkv)
16267 .arg(&ti)
16268 .arg(&tkvi)
16269 .arg(&scale)
16270 .arg(&cz);
16271 unsafe {
16272 b.launch(cfg)?;
16273 }
16274 Ok(())
16275 }
16276
16277 #[allow(clippy::too_many_arguments)]
16280 pub fn fa_prefill_hd512_arm(
16281 &self,
16282 q: &CudaSlice<f32>,
16283 k: &CudaSlice<f32>,
16284 v: &CudaSlice<f32>,
16285 o: &mut CudaSlice<f32>,
16286 head_dim: usize,
16287 n_head: usize,
16288 n_head_kv: usize,
16289 t: usize,
16290 t_kv: usize,
16291 scale: f32,
16292 causal: bool,
16293 f32_stage: bool,
16294 sp: bool,
16295 f16pv: bool,
16296 ) -> Result<(), Box<dyn std::error::Error>> {
16297 debug_assert_eq!(head_dim, 512, "fa_prefill_hd512 is hd512 only");
16298 if sp && !f32_stage {
16299 const SP_M: usize = 16;
16303 const BKS: usize = 32;
16304 let nw = if f16pv { fa512_wide_warps() } else { 2 };
16305 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
16306 let f = self.func(if hp {
16307 "fa_prefill_bf16_hd512_sp16h2"
16308 } else {
16309 match (f16pv, nw) {
16310 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
16311 (true, _) => "fa_prefill_bf16_hd512_sp16",
16312 _ => "fa_prefill_bf16_hd512_sp",
16313 }
16314 });
16315 let (nwarp, npart) = if hp {
16316 (4usize, 4usize)
16317 } else if nw > 2 {
16318 (nw, nw)
16319 } else {
16320 (2, 1)
16321 };
16322 let shmem = if hp {
16323 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS)
16324 + 4 * (2 * npart * SP_M * BKS + 2 * SP_M)) as u32
16325 } else {
16326 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
16327 + 4 * (npart * SP_M * BKS + SP_M)) as u32
16328 };
16329 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16330 f.set_attribute(
16331 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16332 shmem as i32,
16333 )?;
16334 let grid_y = if hp {
16335 (n_head / 2) as u32
16336 } else {
16337 n_head as u32
16338 };
16339 let cfg = LaunchConfig {
16340 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
16341 block_dim: (32, nwarp as u32, 1),
16342 shared_mem_bytes: shmem,
16343 };
16344 let (hd, nh, nhkv, ti, tkvi, cz) = (
16345 head_dim as i32,
16346 n_head as i32,
16347 n_head_kv as i32,
16348 t as i32,
16349 t_kv as i32,
16350 causal as i32,
16351 );
16352 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16353 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16354 let vb = if f16pv {
16355 self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?
16356 } else {
16357 self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?
16358 };
16359 let __s_b = self.gpu.stream();
16360 let mut b = __s_b.launch_builder(&f);
16361 b.arg(&qb)
16362 .arg(&kb)
16363 .arg(&vb)
16364 .arg(o)
16365 .arg(&hd)
16366 .arg(&nh)
16367 .arg(&nhkv)
16368 .arg(&ti)
16369 .arg(&tkvi)
16370 .arg(&scale)
16371 .arg(&cz);
16372 unsafe {
16373 b.launch(cfg)?;
16374 }
16375 return Ok(());
16376 }
16377 const BLOCK_Q: usize = 32;
16378 const BK: usize = 32;
16379 const HALF: usize = 256;
16380 let f = self.func(if f32_stage {
16381 "fa_prefill_f32_hd512"
16382 } else {
16383 "fa_prefill_bf16_hd512"
16384 });
16385 let shmem = (2 * (BLOCK_Q * head_dim + BK * head_dim + BK * HALF + BLOCK_Q * BK)
16387 + 4 * BLOCK_Q) as u32;
16388 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16389 f.set_attribute(
16390 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16391 shmem as i32,
16392 )?;
16393 let cfg = LaunchConfig {
16394 grid_dim: (
16395 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16396 n_head as u32,
16397 2,
16398 ),
16399 block_dim: (32, 2, 1),
16400 shared_mem_bytes: shmem,
16401 };
16402 let (hd, nh, nhkv, ti, tkvi, cz) = (
16403 head_dim as i32,
16404 n_head as i32,
16405 n_head_kv as i32,
16406 t as i32,
16407 t_kv as i32,
16408 causal as i32,
16409 );
16410 if f32_stage {
16411 let __s_b = self.gpu.stream();
16412 let mut b = __s_b.launch_builder(&f);
16413 b.arg(q)
16414 .arg(k)
16415 .arg(v)
16416 .arg(o)
16417 .arg(&hd)
16418 .arg(&nh)
16419 .arg(&nhkv)
16420 .arg(&ti)
16421 .arg(&tkvi)
16422 .arg(&scale)
16423 .arg(&cz);
16424 unsafe {
16425 b.launch(cfg)?;
16426 }
16427 } else {
16428 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16429 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16430 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
16431 let __s_b = self.gpu.stream();
16432 let mut b = __s_b.launch_builder(&f);
16433 b.arg(&qb)
16434 .arg(&kb)
16435 .arg(&vb)
16436 .arg(o)
16437 .arg(&hd)
16438 .arg(&nh)
16439 .arg(&nhkv)
16440 .arg(&ti)
16441 .arg(&tkvi)
16442 .arg(&scale)
16443 .arg(&cz);
16444 unsafe {
16445 b.launch(cfg)?;
16446 }
16447 }
16448 Ok(())
16449 }
16450
16451 #[allow(clippy::too_many_arguments)]
16455 pub fn rope_neox2_bf16e(
16456 &self,
16457 q: &mut CudaSlice<f32>,
16458 k: &mut CudaSlice<f32>,
16459 qb: &mut CudaSlice<u8>,
16460 kb: &mut CudaSlice<u8>,
16461 pos: &CudaSlice<i32>,
16462 head_dim: usize,
16463 n_dims: usize,
16464 nh_q: usize,
16465 nh_k: usize,
16466 n_tokens: usize,
16467 base: f32,
16468 freq_scale: f32,
16469 ff: Option<&CudaSlice<f32>>,
16470 ) -> Result<(), Box<dyn std::error::Error>> {
16471 let f = self.func("rope_neox2_bf16e_f32");
16472 let rows = ((nh_q + nh_k) * n_tokens) as u32;
16473 let cfg = LaunchConfig {
16474 grid_dim: (rows, 1, 1),
16475 block_dim: ((head_dim / 2) as u32, 1, 1),
16476 shared_mem_bytes: 0,
16477 };
16478 let theta_scale = base.powf(-2.0 / n_dims as f32);
16479 let (hd, nd, nhq, nhk, nt) = (
16480 head_dim as i32,
16481 n_dims as i32,
16482 nh_q as i32,
16483 nh_k as i32,
16484 n_tokens as i32,
16485 );
16486 let __s_b = self.gpu.stream();
16487 let mut b = __s_b.launch_builder(&f);
16488 match ff {
16489 Some(t) => {
16490 b.arg(&mut *q)
16491 .arg(&mut *k)
16492 .arg(&mut *qb)
16493 .arg(&mut *kb)
16494 .arg(pos)
16495 .arg(&hd)
16496 .arg(&nd)
16497 .arg(&nhq)
16498 .arg(&nhk)
16499 .arg(&nt)
16500 .arg(&theta_scale)
16501 .arg(&freq_scale)
16502 .arg(t);
16503 unsafe {
16504 b.launch(cfg)?;
16505 }
16506 }
16507 None => {
16508 let null: u64 = 0;
16509 b.arg(&mut *q)
16510 .arg(&mut *k)
16511 .arg(&mut *qb)
16512 .arg(&mut *kb)
16513 .arg(pos)
16514 .arg(&hd)
16515 .arg(&nd)
16516 .arg(&nhq)
16517 .arg(&nhk)
16518 .arg(&nt)
16519 .arg(&theta_scale)
16520 .arg(&freq_scale)
16521 .arg(&null);
16522 unsafe {
16523 b.launch(cfg)?;
16524 }
16525 }
16526 }
16527 Ok(())
16528 }
16529
16530 pub fn f32_to_bf16(
16533 &self,
16534 x: &CudaSlice<f32>,
16535 n: usize,
16536 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
16537 assert!(n % 4 == 0, "f32_to_bf16 requires n % 4 == 0, got {n}");
16538 let mut y = self.alloc_uninit::<u8>(n * 2)?;
16539 let f = self.func("f32_to_bf16_flat");
16540 let n_i = n as i64;
16541 let cfg = LaunchConfig {
16542 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
16543 block_dim: (256, 1, 1),
16544 shared_mem_bytes: 0,
16545 };
16546 let __s_b = self.gpu.stream();
16547 let mut b = __s_b.launch_builder(&f);
16548 b.arg(x).arg(&mut y).arg(&n_i);
16549 unsafe {
16550 b.launch(cfg)?;
16551 }
16552 Ok(y)
16553 }
16554
16555 pub fn f32_to_f16(
16556 &self,
16557 x: &CudaSlice<f32>,
16558 n: usize,
16559 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
16560 assert!(n % 4 == 0, "f32_to_f16 requires n % 4 == 0, got {n}");
16561 let mut y = self.alloc_uninit::<u8>(n * 2)?;
16562 let f = self.func("f32_to_f16_flat");
16563 let n_i = n as i64;
16564 let cfg = LaunchConfig {
16565 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
16566 block_dim: (256, 1, 1),
16567 shared_mem_bytes: 0,
16568 };
16569 let __s_b = self.gpu.stream();
16570 let mut b = __s_b.launch_builder(&f);
16571 b.arg(x).arg(&mut y).arg(&n_i);
16572 unsafe {
16573 b.launch(cfg)?;
16574 }
16575 Ok(y)
16576 }
16577
16578 pub fn bf16_to_f16(
16580 &self,
16581 xb: &CudaSlice<u8>,
16582 n: usize,
16583 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
16584 let mut y = self.alloc_uninit::<u8>(n * 2)?;
16585 self.bf16_to_f16_into(xb, n, &mut y)?;
16586 Ok(y)
16587 }
16588
16589 pub fn bf16_to_f16_into(
16591 &self,
16592 xb: &CudaSlice<u8>,
16593 n: usize,
16594 y: &mut CudaSlice<u8>,
16595 ) -> Result<(), Box<dyn std::error::Error>> {
16596 assert!(n % 2 == 0, "bf16_to_f16 requires n % 2 == 0, got {n}");
16597 assert!(y.len() >= n * 2);
16598 let f = self.func("bf16_to_f16_flat");
16599 let n2 = (n / 2) as i64;
16600 let cfg = LaunchConfig {
16601 grid_dim: (((n / 2) as u32).div_ceil(256), 1, 1),
16602 block_dim: (256, 1, 1),
16603 shared_mem_bytes: 0,
16604 };
16605 let __s_b = self.gpu.stream();
16606 let mut b = __s_b.launch_builder(&f);
16607 b.arg(xb).arg(y).arg(&n2);
16608 unsafe {
16609 b.launch(cfg)?;
16610 }
16611 Ok(())
16612 }
16613
16614 #[allow(clippy::too_many_arguments)]
16619 pub fn fa_prefill_vl8(
16620 &self,
16621 seqs: &[FaSeqVl],
16622 head_dim: usize,
16623 n_head: usize,
16624 n_head_kv: usize,
16625 scale: f32,
16626 ) -> Result<(), Box<dyn std::error::Error>> {
16627 const BK: usize = 32;
16628 let b = seqs.len();
16629 assert!(b >= 1 && b <= 8);
16630 let mut packed = [FaSeqVl::default(); 8];
16631 packed[..b].copy_from_slice(seqs);
16632 let v = FaVl8(packed);
16633 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
16634 let ept = (n_head_kv * head_dim) as i32;
16635 {
16636 let f = self.func("fa_mirror_vl");
16637 let max_n = (max_t as i64) * ept as i64;
16638 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
16639 for which in 0..2i32 {
16640 let cfg = LaunchConfig {
16641 grid_dim: (blocks, 1, b as u32),
16642 block_dim: (256, 1, 1),
16643 shared_mem_bytes: 0,
16644 };
16645 let __s_lb = self.gpu.stream();
16646 let mut lb = __s_lb.launch_builder(&f);
16647 lb.arg(&v).arg(&ept).arg(&which);
16648 unsafe {
16649 lb.launch(cfg)?;
16650 }
16651 }
16652 }
16653 let hd_sfx = fa_hd_suffix(head_dim)?;
16654 let f = self.func(&format!("fa_prefill_bf16kv_vl{hd_sfx}"));
16655 let block_q = 64usize;
16656 let kv_stages = 2usize;
16657 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
16658 + 4 * (block_q * BK + 2 * block_q)) as u32;
16659 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16660 f.set_attribute(
16661 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16662 shmem as i32,
16663 )?;
16664 let cfg = LaunchConfig {
16665 grid_dim: (max_t.div_ceil(block_q as u32), n_head as u32, b as u32),
16666 block_dim: (32, 4, 1),
16667 shared_mem_bytes: shmem,
16668 };
16669 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
16670 let __s_lb = self.gpu.stream();
16671 let mut lb = __s_lb.launch_builder(&f);
16672 lb.arg(&v).arg(&hd).arg(&nh).arg(&nhkv).arg(&scale);
16673 unsafe {
16674 lb.launch(cfg)?;
16675 }
16676 Ok(())
16677 }
16678
16679 #[allow(clippy::too_many_arguments)]
16683 pub fn attn_pre_vl8(
16684 &self,
16685 seqs: &[AttnPreVl],
16686 wq: &CudaSlice<f32>,
16687 wk: &CudaSlice<f32>,
16688 head_dim: usize,
16689 rope_dims: usize,
16690 n_head: usize,
16691 n_head_kv: usize,
16692 eps: f32,
16693 freq_base: f32,
16694 freq_scale: f32,
16695 kv_dim_k: usize,
16696 kv_dim_v: usize,
16697 k_tok_bytes: usize,
16698 v_tok_bytes: usize,
16699 ) -> Result<(), Box<dyn std::error::Error>> {
16700 let b = seqs.len();
16701 assert!(b >= 1 && b <= 8);
16702 let mut packed = [AttnPreVl::default(); 8];
16703 packed[..b].copy_from_slice(seqs);
16704 let v = AttnPreVl8(packed);
16705 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
16706 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
16707 {
16708 let f = self.func("q_gate_split_vl");
16709 let n = max_t * (n_head * head_dim) as u32;
16710 let cfg = LaunchConfig {
16711 grid_dim: (n.div_ceil(256), 1, b as u32),
16712 block_dim: (256, 1, 1),
16713 shared_mem_bytes: 0,
16714 };
16715 let __s_lb = self.gpu.stream();
16716 let mut lb = __s_lb.launch_builder(&f);
16717 lb.arg(&v).arg(&hd).arg(&nh);
16718 unsafe {
16719 lb.launch(cfg)?;
16720 }
16721 }
16722 {
16723 let f = self.func("attn_rms_vl");
16724 let cfg = LaunchConfig {
16725 grid_dim: (max_t * n_head as u32, 2, b as u32),
16726 block_dim: (rms_block(), 1, 1),
16727 shared_mem_bytes: 0,
16728 };
16729 let __s_lb = self.gpu.stream();
16730 let mut lb = __s_lb.launch_builder(&f);
16731 lb.arg(&v)
16732 .arg(wq)
16733 .arg(wk)
16734 .arg(&hd)
16735 .arg(&nh)
16736 .arg(&nhkv)
16737 .arg(&eps);
16738 unsafe {
16739 lb.launch(cfg)?;
16740 }
16741 }
16742 {
16743 let f = self.func("attn_rope_vl");
16744 let theta_scale = freq_base.powf(-2.0 / rope_dims as f32);
16745 let nd = rope_dims as i32;
16746 let cfg = LaunchConfig {
16747 grid_dim: (max_t * n_head as u32, 2, b as u32),
16748 block_dim: ((head_dim / 2) as u32, 1, 1),
16749 shared_mem_bytes: 0,
16750 };
16751 let __s_lb = self.gpu.stream();
16752 let mut lb = __s_lb.launch_builder(&f);
16753 lb.arg(&v)
16754 .arg(&hd)
16755 .arg(&nd)
16756 .arg(&nh)
16757 .arg(&nhkv)
16758 .arg(&theta_scale)
16759 .arg(&freq_scale);
16760 unsafe {
16761 lb.launch(cfg)?;
16762 }
16763 }
16764 {
16765 let f = self.func("append_kv_vl");
16766 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
16767 let cfg = LaunchConfig {
16768 grid_dim: (nblk, max_t, b as u32),
16769 block_dim: (32, 1, 1),
16770 shared_mem_bytes: 0,
16771 };
16772 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
16773 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16774 let __s_lb = self.gpu.stream();
16775 let mut lb = __s_lb.launch_builder(&f);
16776 lb.arg(&v).arg(&kdk).arg(&kdv).arg(&ktb).arg(&vtb);
16777 unsafe {
16778 lb.launch(cfg)?;
16779 }
16780 }
16781 Ok(())
16782 }
16783
16784 pub fn fa_prefill_view(
16789 &self,
16790 q: &CudaSlice<f32>,
16791 k: &cudarc::driver::CudaView<u8>,
16792 v: &cudarc::driver::CudaView<u8>,
16793 o: &mut CudaSlice<f32>,
16794 head_dim: usize,
16795 n_head: usize,
16796 n_head_kv: usize,
16797 t: usize,
16798 t_kv: usize,
16799 scale: f32,
16800 causal: bool,
16801 k_tok_bytes: usize,
16802 v_tok_bytes: usize,
16803 g: bool,
16804 ) -> Result<(), Box<dyn std::error::Error>> {
16805 if portable_mma_gated() {
16806 return self.sdpa_naive_quantized_view(
16807 q,
16808 k,
16809 v,
16810 o,
16811 head_dim,
16812 n_head,
16813 n_head_kv,
16814 t,
16815 t_kv,
16816 scale,
16817 causal,
16818 k_tok_bytes,
16819 v_tok_bytes,
16820 );
16821 }
16822 const BLOCK_Q: usize = 64;
16823 const BK: usize = 32;
16824 let name = format!("fa_prefill_q{}", fa_hd_suffix(head_dim)?);
16827 let f = if g {
16828 self.func_g(&name)
16829 } else {
16830 self.func(&name)
16831 };
16832 let shmem =
16833 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
16834 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16835 f.set_attribute(
16836 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16837 shmem as i32,
16838 )?;
16839 let cfg = LaunchConfig {
16840 grid_dim: (
16841 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16842 n_head as u32,
16843 1,
16844 ),
16845 block_dim: (32, 4, 1),
16846 shared_mem_bytes: shmem,
16847 };
16848 let (hd, nh, nhkv, ti, tkvi, cz) = (
16849 head_dim as i32,
16850 n_head as i32,
16851 n_head_kv as i32,
16852 t as i32,
16853 t_kv as i32,
16854 causal as i32,
16855 );
16856 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16857 let __s_b = self.gpu.stream();
16858 let mut b = __s_b.launch_builder(&f);
16859 b.arg(q)
16860 .arg(k)
16861 .arg(v)
16862 .arg(o)
16863 .arg(&hd)
16864 .arg(&nh)
16865 .arg(&nhkv)
16866 .arg(&ti)
16867 .arg(&tkvi)
16868 .arg(&scale)
16869 .arg(&cz)
16870 .arg(&ktb)
16871 .arg(&vtb);
16872 unsafe {
16873 b.launch(cfg)?;
16874 }
16875 Ok(())
16876 }
16877
16878 #[allow(clippy::too_many_arguments)]
16888 pub fn fa_prefill_view_ws(
16889 &self,
16890 q: &CudaSlice<f32>,
16891 k: &cudarc::driver::CudaView<u8>,
16892 v: &cudarc::driver::CudaView<u8>,
16893 o: &mut CudaSlice<f32>,
16894 head_dim: usize,
16895 n_head: usize,
16896 n_head_kv: usize,
16897 t: usize,
16898 t_kv: usize,
16899 scale: f32,
16900 causal: bool,
16901 k_tok_bytes: usize,
16902 v_tok_bytes: usize,
16903 g: bool,
16904 ) -> Result<(), Box<dyn std::error::Error>> {
16905 if portable_mma_gated() {
16906 return self.sdpa_naive_quantized_view(
16907 q,
16908 k,
16909 v,
16910 o,
16911 head_dim,
16912 n_head,
16913 n_head_kv,
16914 t,
16915 t_kv,
16916 scale,
16917 causal,
16918 k_tok_bytes,
16919 v_tok_bytes,
16920 );
16921 }
16922 const BLOCK_Q: usize = 64;
16923 const BK: usize = 32;
16924 let kv_dim_k = n_head_kv * head_dim;
16925 let kv_dim_v = n_head_kv * head_dim;
16926 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
16928 let mut guard = self.prime_deqw_ws.lock().unwrap();
16930 let need_grow = match guard.as_ref() {
16931 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
16932 None => true,
16933 };
16934 if need_grow {
16935 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
16936 let (ck, cv) = guard
16937 .as_ref()
16938 .map(|(a, b)| (a.len(), b.len()))
16939 .unwrap_or((0, 0));
16940 *guard = Some((
16941 self.alloc_u8(grow(ck, k_ws_bytes))?,
16942 self.alloc_u8(grow(cv, v_ws_bytes))?,
16943 ));
16944 }
16945 let (kw, vw) = guard.as_mut().unwrap();
16946 {
16948 let f = if g {
16950 self.func_g("fa_dequant_kv_ws_bf16")
16951 } else {
16952 self.func("fa_dequant_kv_ws_bf16")
16953 };
16954 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
16955 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
16956 let cfg = LaunchConfig {
16957 grid_dim: (nblk.max(1), 1, 1),
16958 block_dim: (256, 1, 1),
16959 shared_mem_bytes: 0,
16960 };
16961 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
16962 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16963 let __s_b = self.gpu.stream();
16964 let mut b = __s_b.launch_builder(&f);
16965 b.arg(k)
16966 .arg(v)
16967 .arg(&mut *kw)
16968 .arg(&mut *vw)
16969 .arg(&kdk)
16970 .arg(&kdv)
16971 .arg(&tkvi)
16972 .arg(&ktb)
16973 .arg(&vtb);
16974 unsafe {
16975 b.launch(cfg)?;
16976 }
16977 }
16978 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
16986 .map(|v| v != "0")
16987 .unwrap_or(true);
16988 {
16989 let hd_sfx = fa_hd_suffix(head_dim)?;
16990 let f = self.func(&format!(
16991 "fa_prefill_qw{}{hd_sfx}",
16992 if db { "_db" } else { "" }
16993 ));
16994 let shmem = if db {
16995 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
16997 } else {
16998 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
16999 };
17000 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17001 f.set_attribute(
17002 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17003 shmem as i32,
17004 )?;
17005 let cfg = LaunchConfig {
17006 grid_dim: (
17007 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
17008 n_head as u32,
17009 1,
17010 ),
17011 block_dim: (32, 4, 1),
17012 shared_mem_bytes: shmem,
17013 };
17014 let (hd, nh, nhkv, ti, tkvi, cz) = (
17015 head_dim as i32,
17016 n_head as i32,
17017 n_head_kv as i32,
17018 t as i32,
17019 t_kv as i32,
17020 causal as i32,
17021 );
17022 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
17023 let __s_b = self.gpu.stream();
17024 let mut b = __s_b.launch_builder(&f);
17025 b.arg(q)
17026 .arg(&*kw)
17027 .arg(&*vw)
17028 .arg(o)
17029 .arg(&hd)
17030 .arg(&nh)
17031 .arg(&nhkv)
17032 .arg(&ti)
17033 .arg(&tkvi)
17034 .arg(&scale)
17035 .arg(&cz)
17036 .arg(&kdk)
17037 .arg(&kdv);
17038 unsafe {
17039 b.launch(cfg)?;
17040 }
17041 }
17042 Ok(())
17043 }
17044
17045 #[allow(clippy::too_many_arguments)]
17061 pub fn fa_prefill_view_ws_w_hd128(
17062 &self,
17063 q: &CudaSlice<f32>,
17064 k: &cudarc::driver::CudaView<u8>,
17065 v: &cudarc::driver::CudaView<u8>,
17066 o: &mut CudaSlice<f32>,
17067 head_dim: usize,
17068 n_head: usize,
17069 n_head_kv: usize,
17070 t: usize,
17071 t_kv: usize,
17072 scale: f32,
17073 causal: bool,
17074 window: usize,
17075 k_tok_bytes: usize,
17076 v_tok_bytes: usize,
17077 ) -> Result<(), Box<dyn std::error::Error>> {
17078 assert_eq!(
17079 head_dim, 128,
17080 "fa_prefill_view_ws_w_hd128: only the hd128 twin is stamped"
17081 );
17082 if portable_mma_gated() {
17083 return self.sdpa_naive_w_quantized_view(
17084 q,
17085 k,
17086 v,
17087 o,
17088 head_dim,
17089 n_head,
17090 n_head_kv,
17091 t,
17092 t_kv,
17093 scale,
17094 causal,
17095 window,
17096 k_tok_bytes,
17097 v_tok_bytes,
17098 );
17099 }
17100 const BLOCK_Q: usize = 64;
17101 const BK: usize = 32;
17102 let kv_dim_k = n_head_kv * head_dim;
17103 let kv_dim_v = n_head_kv * head_dim;
17104 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
17106 let mut guard = self.prime_deqw_ws.lock().unwrap();
17107 let need_grow = match guard.as_ref() {
17108 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
17109 None => true,
17110 };
17111 if need_grow {
17112 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
17113 let (ck, cv) = guard
17114 .as_ref()
17115 .map(|(a, b)| (a.len(), b.len()))
17116 .unwrap_or((0, 0));
17117 *guard = Some((
17118 self.alloc_u8(grow(ck, k_ws_bytes))?,
17119 self.alloc_u8(grow(cv, v_ws_bytes))?,
17120 ));
17121 }
17122 let (kw, vw) = guard.as_mut().unwrap();
17123 {
17126 let f = self.func("fa_dequant_kv_ws_bf16");
17127 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
17128 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
17129 let cfg = LaunchConfig {
17130 grid_dim: (nblk.max(1), 1, 1),
17131 block_dim: (256, 1, 1),
17132 shared_mem_bytes: 0,
17133 };
17134 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
17135 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17136 let __s_b = self.gpu.stream();
17137 let mut b = __s_b.launch_builder(&f);
17138 b.arg(k)
17139 .arg(v)
17140 .arg(&mut *kw)
17141 .arg(&mut *vw)
17142 .arg(&kdk)
17143 .arg(&kdv)
17144 .arg(&tkvi)
17145 .arg(&ktb)
17146 .arg(&vtb);
17147 unsafe {
17148 b.launch(cfg)?;
17149 }
17150 }
17151 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
17153 .map(|v| v != "0")
17154 .unwrap_or(true);
17155 {
17156 let f = self.func(if db {
17157 "fa_prefill_qw_db_w_hd128"
17158 } else {
17159 "fa_prefill_qw_w_hd128"
17160 });
17161 let shmem = if db {
17162 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
17163 } else {
17164 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
17165 };
17166 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17167 f.set_attribute(
17168 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17169 shmem as i32,
17170 )?;
17171 let cfg = LaunchConfig {
17172 grid_dim: (
17173 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
17174 n_head as u32,
17175 1,
17176 ),
17177 block_dim: (32, 4, 1),
17178 shared_mem_bytes: shmem,
17179 };
17180 let (hd, nh, nhkv, ti, tkvi, cz) = (
17181 head_dim as i32,
17182 n_head as i32,
17183 n_head_kv as i32,
17184 t as i32,
17185 t_kv as i32,
17186 causal as i32,
17187 );
17188 let (kdk, kdv, wnd) = (kv_dim_k as i32, kv_dim_v as i32, window as i32);
17189 let __s_b = self.gpu.stream();
17190 let mut b = __s_b.launch_builder(&f);
17191 b.arg(q)
17192 .arg(&*kw)
17193 .arg(&*vw)
17194 .arg(o)
17195 .arg(&hd)
17196 .arg(&nh)
17197 .arg(&nhkv)
17198 .arg(&ti)
17199 .arg(&tkvi)
17200 .arg(&scale)
17201 .arg(&cz)
17202 .arg(&kdk)
17203 .arg(&kdv)
17204 .arg(&wnd);
17205 unsafe {
17206 b.launch(cfg)?;
17207 }
17208 }
17209 Ok(())
17210 }
17211
17212 pub fn fa_decode(
17216 &self,
17217 q: &CudaSlice<f32>,
17218 k: &cudarc::driver::CudaView<u8>,
17219 v: &cudarc::driver::CudaView<u8>,
17220 o: &mut CudaSlice<f32>,
17221 head_dim: usize,
17222 n_head: usize,
17223 n_head_kv: usize,
17224 t_kv: usize,
17225 scale: f32,
17226 k_tok_bytes: usize,
17227 v_tok_bytes: usize,
17228 ) -> Result<(), Box<dyn std::error::Error>> {
17229 self.fa_decode_kvmod(
17230 q,
17231 k,
17232 v,
17233 o,
17234 head_dim,
17235 n_head,
17236 n_head_kv,
17237 t_kv,
17238 scale,
17239 k_tok_bytes,
17240 v_tok_bytes,
17241 false,
17242 )
17243 }
17244
17245 #[allow(clippy::too_many_arguments)]
17249 #[allow(clippy::too_many_arguments)]
17253 #[allow(clippy::too_many_arguments)]
17254 fn fa_decode_scalar_unified(
17255 &self,
17256 q: &cudarc::driver::CudaView<f32>,
17257 k: &cudarc::driver::CudaView<u8>,
17258 v: &cudarc::driver::CudaView<u8>,
17259 o: &mut cudarc::driver::CudaViewMut<f32>,
17260 head_dim: usize,
17261 n_head: usize,
17262 n_head_kv: usize,
17263 t_kv_host: usize,
17264 t_kv_dev: Option<&CudaSlice<i32>>,
17265 scale: f32,
17266 n_splits: usize,
17267 split_keys: usize,
17268 k_tok_bytes: usize,
17269 v_tok_bytes: usize,
17270 g: bool,
17271 part_o: &mut CudaSlice<f32>,
17272 part_m: &mut CudaSlice<f32>,
17273 part_l: &mut CudaSlice<f32>,
17274 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
17275 ) -> Result<(), Box<dyn std::error::Error>> {
17276 let f = if g {
17277 self.func_g("fa_decode_f32")
17278 } else {
17279 self.fa_func("fa_decode_f32", head_dim)
17280 };
17281 let cfg = LaunchConfig {
17282 grid_dim: (n_head as u32, n_splits as u32, 1),
17283 block_dim: (head_dim as u32, 1, 1),
17284 shared_mem_bytes: (4 * (head_dim + 32)) as u32,
17285 };
17286 let (hd, nh, nhkv, nsp) = (
17287 head_dim as i32,
17288 n_head as i32,
17289 n_head_kv as i32,
17290 n_splits as i32,
17291 );
17292 let (ktb, vtb, tkvi, ski) = (
17293 k_tok_bytes as i64,
17294 v_tok_bytes as i64,
17295 t_kv_host as i32,
17296 split_keys as i32,
17297 );
17298 let __s_b = self.gpu.stream();
17299 let mut b = __s_b.launch_builder(&f);
17300 match t_kv_dev {
17301 Some(d) => {
17302 b.arg(q)
17303 .arg(k)
17304 .arg(v)
17305 .arg(&mut *part_o)
17306 .arg(&mut *part_m)
17307 .arg(&mut *part_l)
17308 .arg(&hd)
17309 .arg(&nh)
17310 .arg(&nhkv)
17311 .arg(&tkvi)
17312 .arg(d)
17313 .arg(&scale)
17314 .arg(&nsp)
17315 .arg(&ski)
17316 .arg(&ktb)
17317 .arg(&vtb);
17318 unsafe {
17319 b.launch(cfg)?;
17320 }
17321 }
17322 None => {
17323 let null: u64 = 0;
17324 b.arg(q)
17325 .arg(k)
17326 .arg(v)
17327 .arg(&mut *part_o)
17328 .arg(&mut *part_m)
17329 .arg(&mut *part_l)
17330 .arg(&hd)
17331 .arg(&nh)
17332 .arg(&nhkv)
17333 .arg(&tkvi)
17334 .arg(&null)
17335 .arg(&scale)
17336 .arg(&nsp)
17337 .arg(&ski)
17338 .arg(&ktb)
17339 .arg(&vtb);
17340 unsafe {
17341 b.launch(cfg)?;
17342 }
17343 }
17344 }
17345 let cfg2 = LaunchConfig {
17346 grid_dim: (n_head as u32, 1, 1),
17347 block_dim: (head_dim as u32, 1, 1),
17348 shared_mem_bytes: 0,
17349 };
17350 if let Some((oq, od)) = q8_out {
17351 let fc = if g {
17353 self.func_g("fa_decode_combine_q8_1")
17354 } else {
17355 self.fa_func("fa_decode_combine_q8_1", head_dim)
17356 };
17357 let __s_b2 = self.gpu.stream();
17358 let mut b2 = __s_b2.launch_builder(&fc);
17359 b2.arg(&*part_o)
17360 .arg(&*part_m)
17361 .arg(&*part_l)
17362 .arg(oq)
17363 .arg(od)
17364 .arg(&hd)
17365 .arg(&nh)
17366 .arg(&nsp);
17367 unsafe {
17368 b2.launch(cfg2)?;
17369 }
17370 return Ok(());
17371 }
17372 let fc = if g {
17373 self.func_g("fa_decode_combine_f32")
17374 } else {
17375 self.fa_func("fa_decode_combine_f32", head_dim)
17376 };
17377 let __s_b2 = self.gpu.stream();
17378 let mut b2 = __s_b2.launch_builder(&fc);
17379 b2.arg(&*part_o)
17380 .arg(&*part_m)
17381 .arg(&*part_l)
17382 .arg(o)
17383 .arg(&hd)
17384 .arg(&nh)
17385 .arg(&nsp);
17386 unsafe {
17387 b2.launch(cfg2)?;
17388 }
17389 Ok(())
17390 }
17391
17392 pub fn fa_decode_kvmod(
17393 &self,
17394 q: &CudaSlice<f32>,
17395 k: &cudarc::driver::CudaView<u8>,
17396 v: &cudarc::driver::CudaView<u8>,
17397 o: &mut CudaSlice<f32>,
17398 head_dim: usize,
17399 n_head: usize,
17400 n_head_kv: usize,
17401 t_kv: usize,
17402 scale: f32,
17403 k_tok_bytes: usize,
17404 v_tok_bytes: usize,
17405 g: bool,
17406 ) -> Result<(), Box<dyn std::error::Error>> {
17407 let q_view = q.as_view();
17408 let mut o_view = o.as_view_mut();
17409 self.fa_decode_kvmod_view(
17410 &q_view,
17411 k,
17412 v,
17413 &mut o_view,
17414 head_dim,
17415 n_head,
17416 n_head_kv,
17417 t_kv,
17418 scale,
17419 k_tok_bytes,
17420 v_tok_bytes,
17421 g,
17422 )
17423 }
17424
17425 #[allow(clippy::too_many_arguments)]
17430 pub fn fa_decode_kvmod_view(
17431 &self,
17432 q: &cudarc::driver::CudaView<f32>,
17433 k: &cudarc::driver::CudaView<u8>,
17434 v: &cudarc::driver::CudaView<u8>,
17435 o: &mut cudarc::driver::CudaViewMut<f32>,
17436 head_dim: usize,
17437 n_head: usize,
17438 n_head_kv: usize,
17439 t_kv: usize,
17440 scale: f32,
17441 k_tok_bytes: usize,
17442 v_tok_bytes: usize,
17443 g: bool,
17444 ) -> Result<(), Box<dyn std::error::Error>> {
17445 let mut fa_vec = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
17466 if g && head_dim == 256 && !fa_v4_at(t_kv) {
17470 fa_vec = false;
17471 }
17472 let sp = fa_split_keys(t_kv, n_head_kv);
17473 let n_splits = if fa_vec {
17474 ((t_kv + sp - 1) / sp).max(1)
17475 } else {
17476 ((t_kv + 255) / 256).max(1)
17477 };
17478 let o_len = n_head * n_splits * head_dim;
17479 let ml_len = n_head * n_splits;
17480 let mut part_guard = self.fa_part_pool.lock().unwrap();
17481 if part_guard
17482 .as_ref()
17483 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17484 .unwrap_or(true)
17485 {
17486 let old = part_guard.take();
17497 let (co, cm) = old
17498 .as_ref()
17499 .map(|pp| (pp.0.len(), pp.1.len()))
17500 .unwrap_or((0, 0));
17501 if let Some(old) = old {
17502 self.fa_part_retired.lock().unwrap().push(old);
17503 }
17504 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17505 eprintln!(
17506 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17507 co, o_len, cm, ml_len
17508 );
17509 }
17510 *part_guard = Some((
17511 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17512 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17513 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17514 ));
17515 }
17516 let pg = part_guard.as_mut().unwrap();
17517 self.gpu
17518 .stream()
17519 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17520 self.gpu
17521 .stream()
17522 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17523 self.gpu
17524 .stream()
17525 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17526 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17527 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
17528 let (hd, nh, nhkv, tkvi, nsp) = (
17529 head_dim as i32,
17530 n_head as i32,
17531 n_head_kv as i32,
17532 t_kv as i32,
17533 n_splits as i32,
17534 );
17535 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17536 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
17540 let fa512_min = fa512_min_tkv();
17545 let deep = fa_vec
17548 && head_dim == 256
17549 && fa_v4_at(t_kv)
17550 && !g
17551 && fa_deep_at(t_kv)
17552 && !matches!(fa_v4_mode(), "noB3" | "stage");
17553 let (f, cfg) = if fa_vec && head_dim == 512 && t_kv >= fa512_min {
17554 let gqa = (n_head / n_head_kv).max(1) as u32;
17557 let fv = self.fa_func("fa_decode_vec_q_dpl16", head_dim);
17558 (
17559 fv,
17560 LaunchConfig {
17561 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17562 block_dim: (32, gqa, 1),
17563 shared_mem_bytes: 0,
17564 },
17565 )
17566 } else if fa_vec && head_dim <= 256 {
17567 let gqa = (n_head / n_head_kv).max(1) as u32;
17568 static SMEM_TKV: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17579 let smem_tkv = *SMEM_TKV.get_or_init(|| {
17580 std::env::var("MEMRA_FA_SMEM_TKV")
17581 .ok()
17582 .and_then(|v| v.parse().ok())
17583 .unwrap_or_else(|| {
17584 FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
17585 })
17586 });
17587 if fa_v4_at(t_kv) && head_dim == 256 {
17588 let v4name = match fa_v4_mode() {
17592 "noB3" => "fa_decode_vec_q_v4_noB3", "stage" => "fa_decode_vec_q_v4_stage", _ if deep => "fa_decode_vec_q_v4_deep",
17595 _ => "fa_decode_vec_q_v4",
17596 };
17597 let fv = if g {
17598 self.func_g(v4name)
17599 } else {
17600 self.func(v4name)
17601 };
17602 let shmem = (if deep { 12160 } else { 11520 }
17605 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
17606 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17607 fv.set_attribute(
17608 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17609 shmem as i32,
17610 )?;
17611 (
17612 fv,
17613 LaunchConfig {
17614 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17615 block_dim: (32, gqa, 1),
17616 shared_mem_bytes: shmem,
17617 },
17618 )
17619 } else if fa_v3_active(head_dim) {
17620 let fv = if g {
17623 self.func_g("fa_decode_vec_q_v3")
17624 } else {
17625 self.func("fa_decode_vec_q_v3")
17626 };
17627 let shmem = (32 * head_dim * 2) as u32; (
17629 fv,
17630 LaunchConfig {
17631 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17632 block_dim: (32, gqa, 1),
17633 shared_mem_bytes: shmem,
17634 },
17635 )
17636 } else if fa_v2_on() {
17637 let fv = if g {
17641 self.func_g("fa_decode_vec_q_v2")
17642 } else {
17643 self.func("fa_decode_vec_q_v2")
17644 };
17645 let shmem = (2 * 32 * head_dim * 2) as u32; (
17647 fv,
17648 LaunchConfig {
17649 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17650 block_dim: (32, gqa, 1),
17651 shared_mem_bytes: shmem,
17652 },
17653 )
17654 } else if smem_tkv > 0 && t_kv >= smem_tkv && !g && !(head_dim == 512 && Self::gkv_on())
17655 {
17656 let fv = if g {
17660 self.func_g("fa_decode_vec_q_smem")
17661 } else {
17662 self.func("fa_decode_vec_q_smem")
17663 };
17664 let shmem = (2 * 32 * head_dim * 2) as u32; use cudarc::driver::sys::CUfunction_attribute_enum as A;
17666 fv.set_attribute(
17667 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17668 shmem as i32,
17669 )?;
17670 (
17671 fv,
17672 LaunchConfig {
17673 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17674 block_dim: (32, gqa, 1),
17675 shared_mem_bytes: shmem,
17676 },
17677 )
17678 } else {
17679 let fv = if g {
17682 self.func_g("fa_decode_vec_q")
17683 } else {
17684 self.func("fa_decode_vec_q")
17685 };
17686 (
17687 fv,
17688 LaunchConfig {
17689 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17690 block_dim: (32, gqa, 1),
17691 shared_mem_bytes: 0,
17692 },
17693 )
17694 }
17695 } else {
17696 return self.fa_decode_scalar_unified(
17699 q,
17700 k,
17701 v,
17702 o,
17703 head_dim,
17704 n_head,
17705 n_head_kv,
17706 t_kv,
17707 None,
17708 scale,
17709 n_splits,
17710 if fa_vec { sp } else { 256 },
17711 k_tok_bytes,
17712 v_tok_bytes,
17713 g,
17714 part_o,
17715 part_m,
17716 part_l,
17717 None,
17718 );
17719 };
17720 let __s_b = self.gpu.stream();
17721 let mut b = __s_b.launch_builder(&f);
17722 b.arg(q)
17723 .arg(k)
17724 .arg(v)
17725 .arg(&mut *part_o)
17726 .arg(&mut *part_m)
17727 .arg(&mut *part_l)
17728 .arg(&hd)
17729 .arg(&nh)
17730 .arg(&nhkv)
17731 .arg(&tkvi)
17732 .arg(&scale)
17733 .arg(&nsp)
17734 .arg(&ktb)
17735 .arg(&vtb);
17736 unsafe {
17737 b.launch(cfg)?;
17738 }
17739 let (fc, cfg2) = (
17742 if g {
17743 self.func_g("fa_decode_combine_f32")
17744 } else {
17745 self.fa_func("fa_decode_combine_f32", head_dim)
17746 },
17747 LaunchConfig {
17748 grid_dim: (n_head as u32, 1, 1),
17749 block_dim: (head_dim as u32, 1, 1),
17750 shared_mem_bytes: 0,
17751 },
17752 );
17753 let __s_b2 = self.gpu.stream();
17754 let mut b2 = __s_b2.launch_builder(&fc);
17755 b2.arg(&*part_o)
17756 .arg(&*part_m)
17757 .arg(&*part_l)
17758 .arg(o)
17759 .arg(&hd)
17760 .arg(&nh)
17761 .arg(&nsp);
17762 unsafe {
17763 b2.launch(cfg2)?;
17764 }
17765 Ok(())
17766 }
17767
17768 #[allow(clippy::too_many_arguments)]
17779 pub fn fa_decode_batch_seqs_v4(
17780 &self,
17781 q: &CudaSlice<f32>,
17782 kv_ptrs: &cudarc::driver::CudaView<u64>,
17783 pos_seq: &CudaSlice<i32>,
17784 o: &mut CudaSlice<f32>,
17785 head_dim: usize,
17786 n_head: usize,
17787 n_head_kv: usize,
17788 b_n: usize,
17789 t_kv_max: usize,
17790 scale: f32,
17791 split_keys: usize,
17792 k_tok_bytes: usize,
17793 v_tok_bytes: usize,
17794 ) -> Result<(), Box<dyn std::error::Error>> {
17795 debug_assert!(head_dim == 256, "seqs twin is v4-stamped (hd256 only)");
17796 let n_splits_max = (t_kv_max + split_keys - 1) / split_keys;
17797 let o_len = b_n * n_head * n_splits_max * head_dim;
17798 let ml_len = b_n * n_head * n_splits_max;
17799 let mut part_guard = self.fa_part_pool.lock().unwrap();
17800 if part_guard
17801 .as_ref()
17802 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17803 .unwrap_or(true)
17804 {
17805 let old = part_guard.take();
17816 let (co, cm) = old
17817 .as_ref()
17818 .map(|pp| (pp.0.len(), pp.1.len()))
17819 .unwrap_or((0, 0));
17820 if let Some(old) = old {
17821 self.fa_part_retired.lock().unwrap().push(old);
17822 }
17823 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17824 eprintln!(
17825 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17826 co, o_len, cm, ml_len
17827 );
17828 }
17829 *part_guard = Some((
17830 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17831 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17832 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17833 ));
17834 }
17835 let pg = part_guard.as_mut().unwrap();
17836 self.gpu
17837 .stream()
17838 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17839 self.gpu
17840 .stream()
17841 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17842 self.gpu
17843 .stream()
17844 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17845 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17846 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17847 let (nspm, spk) = (n_splits_max as i32, split_keys as i32);
17848 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17849 let gqa = (n_head / n_head_kv).max(1) as u32;
17850 let f = self.func("fa_decode_vec_q_seqs_v4");
17851 let shmem = (11520 + 32 * head_dim * 2) as u32;
17853 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17854 f.set_attribute(
17855 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17856 shmem as i32,
17857 )?;
17858 let cfg = LaunchConfig {
17859 grid_dim: (n_head_kv as u32, n_splits_max as u32, b_n as u32),
17860 block_dim: (32, gqa, 1),
17861 shared_mem_bytes: shmem,
17862 };
17863 {
17864 let __s_b = self.gpu.stream();
17865 let mut b = __s_b.launch_builder(&f);
17866 b.arg(q)
17867 .arg(kv_ptrs)
17868 .arg(pos_seq)
17869 .arg(&mut *part_o)
17870 .arg(&mut *part_m)
17871 .arg(&mut *part_l)
17872 .arg(&hd)
17873 .arg(&nh)
17874 .arg(&nhkv)
17875 .arg(&scale)
17876 .arg(&nspm)
17877 .arg(&spk)
17878 .arg(&ktb)
17879 .arg(&vtb);
17880 unsafe {
17881 b.launch(cfg)?;
17882 }
17883 }
17884 let fc = self.func("fa_decode_combine_seqs");
17885 let cfg2 = LaunchConfig {
17886 grid_dim: (n_head as u32, b_n as u32, 1),
17887 block_dim: (head_dim as u32, 1, 1),
17888 shared_mem_bytes: 0,
17889 };
17890 let __s_b2 = self.gpu.stream();
17891 let mut b2 = __s_b2.launch_builder(&fc);
17892 b2.arg(&*part_o)
17893 .arg(&*part_m)
17894 .arg(&*part_l)
17895 .arg(o)
17896 .arg(&hd)
17897 .arg(&nh)
17898 .arg(pos_seq)
17899 .arg(&nspm)
17900 .arg(&spk);
17901 unsafe {
17902 b2.launch(cfg2)?;
17903 }
17904 Ok(())
17905 }
17906
17907 #[allow(clippy::too_many_arguments)]
17914 pub fn append_kv_quantized_seqs(
17915 &self,
17916 k_rows: &CudaSlice<f32>,
17917 v_rows: &CudaSlice<f32>,
17918 kv_ptrs: &cudarc::driver::CudaView<u64>,
17919 pos_seq: &CudaSlice<i32>,
17920 b_n: usize,
17921 kv_dim_k: usize,
17922 kv_dim_v: usize,
17923 k_tok_bytes: usize,
17924 v_tok_bytes: usize,
17925 ) -> Result<(), Box<dyn std::error::Error>> {
17926 let f = self.func("append_quantize_kv_q8_0_q5_1_seqs");
17927 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
17928 let cfg = LaunchConfig {
17929 grid_dim: (nblk, b_n as u32, 1),
17930 block_dim: (32, 1, 1),
17931 shared_mem_bytes: 0,
17932 };
17933 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
17934 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17935 let __s_b = self.gpu.stream();
17936 let mut b = __s_b.launch_builder(&f);
17937 b.arg(k_rows)
17938 .arg(v_rows)
17939 .arg(kv_ptrs)
17940 .arg(pos_seq)
17941 .arg(&kdk)
17942 .arg(&kdv)
17943 .arg(&ktb)
17944 .arg(&vtb);
17945 unsafe {
17946 b.launch(cfg)?;
17947 }
17948 Ok(())
17949 }
17950
17951 pub fn fa_rows_eligible(&self, base_len: usize, head_dim: usize) -> bool {
17957 std::env::var("MEMRA_NO_FA_VEC").is_err()
17958 && std::env::var("MEMRA_FA_ROWS_OFF").is_err()
17959 && base_len + 1 >= fa_vec_min_tkv()
17960 && head_dim <= 256
17961 && head_dim % 32 == 0
17962 }
17963
17964 #[allow(clippy::too_many_arguments)]
17973 pub fn fa_decode_rows(
17974 &self,
17975 q: &CudaSlice<f32>,
17976 k: &cudarc::driver::CudaView<u8>,
17977 v: &cudarc::driver::CudaView<u8>,
17978 o: &mut CudaSlice<f32>,
17979 head_dim: usize,
17980 n_head: usize,
17981 n_head_kv: usize,
17982 base_len: usize,
17983 t: usize,
17984 scale: f32,
17985 k_tok_bytes: usize,
17986 v_tok_bytes: usize,
17987 base_dev: Option<(&CudaSlice<i32>, i32)>,
17991 kv_shared: bool,
17994 g: bool,
17998 mut q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
18001 ) -> Result<(), Box<dyn std::error::Error>> {
18002 debug_assert!(base_len + 1 >= fa_vec_min_tkv() && head_dim <= 512 && head_dim % 32 == 0);
18003 let t_kv_max = base_len + t; let mut sp = fa_split_keys(t_kv_max, n_head_kv); if head_dim == 512 {
18010 static SP512: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18011 let v = *SP512.get_or_init(|| {
18014 std::env::var("MEMRA_FA_SP512")
18015 .ok()
18016 .and_then(|x| x.parse().ok())
18017 .unwrap_or(0)
18018 });
18019 sp = if v >= 8 {
18020 v
18021 } else {
18022 FA_SP512_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
18023 };
18024 }
18025 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18026 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18027 let gqa = (n_head / n_head_kv).max(1) as u32;
18028 let mut groups: Vec<(usize, usize, usize)> = Vec::new(); if head_dim == 512 || fa_split_keys(base_len + 1, n_head_kv) == sp {
18039 groups.push((0, t, sp));
18040 } else {
18041 let mut r0 = 0usize;
18042 while r0 < t {
18043 let sp_g = fa_split_keys(base_len + r0 + 1, n_head_kv);
18044 let mut r1 = r0 + 1;
18045 while r1 < t && fa_split_keys(base_len + r1 + 1, n_head_kv) == sp_g {
18046 r1 += 1;
18047 }
18048 groups.push((r0, r1 - r0, sp_g));
18049 r0 = r1;
18050 }
18051 }
18052 static SMEM_TKV_R: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18056 let smem_tkv = *SMEM_TKV_R.get_or_init(|| {
18057 std::env::var("MEMRA_FA_SMEM_TKV")
18058 .ok()
18059 .and_then(|v| v.parse().ok())
18060 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
18061 });
18062 let v4 = fa_v4_at(base_len + t) && head_dim == 256;
18063 let v3 = fa_v3_active(head_dim);
18064 let smem_rows =
18065 head_dim <= 256 && !v3 && !fa_v2_on() && smem_tkv > 0 && t_kv_max >= smem_tkv;
18066 let _ = kv_shared;
18071 let i2 = head_dim == 512 && std::env::var("MEMRA_FA_I2").as_deref() != Ok("0");
18074 static TB512: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
18088 let tb512 = head_dim == 512
18090 && sp <= 32
18091 && n_head / n_head_kv.max(1) <= 16
18092 && *TB512.get_or_init(|| std::env::var("MEMRA_FA_TB512").as_deref() != Ok("0"));
18093 let fname = if tb512 {
18094 "fa_decode_vec_q_rows_v4_512_tb"
18095 } else if i2 {
18096 "fa_decode_vec_q_rows_dpl16_i2"
18097 } else if head_dim == 512 {
18098 "fa_decode_vec_q_rows_dpl16"
18099 }
18100 else if v4 {
18102 "fa_decode_vec_q_rows_v4"
18103 } else if v3 {
18104 "fa_decode_vec_q_rows_v3"
18105 } else if fa_v2_on() {
18106 "fa_decode_vec_q_rows_v2"
18107 } else if smem_rows {
18108 "fa_decode_vec_q_rows_smem"
18109 } else {
18110 "fa_decode_vec_q_rows"
18111 };
18112 let f = if head_dim == 512 {
18113 self.fa_func(fname, head_dim)
18114 } else if g {
18115 self.func_g(if smem_rows {
18123 "fa_decode_vec_q_rows"
18124 } else {
18125 fname
18126 })
18127 } else {
18128 self.func(fname)
18129 };
18130 let shmem = if tb512 {
18131 let gk = Self::gkv_on();
18133 let sh =
18134 (8192 + 1024 + 32 * 512 + 32 * 64 + 32 * head_dim * if gk { 1 } else { 2 }) as u32;
18135 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18136 f.set_attribute(
18137 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18138 sh as i32,
18139 )?;
18140 sh
18141 } else if v4 || v3 || smem_rows || fa_v2_on() {
18142 let sh = (if v4 {
18144 11520 + 32 * head_dim * if g { 1 } else { 2 }
18145 } else if v3 {
18146 32 * head_dim * 2
18147 } else {
18148 2 * 32 * head_dim * 2
18149 }) as u32;
18150 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18151 f.set_attribute(
18152 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18153 sh as i32,
18154 )?;
18155 sh
18156 } else {
18157 0
18158 };
18159 for &(r0, t_g, sp_g) in &groups {
18163 let n_splits_g = (base_len + r0 + t_g).div_ceil(sp_g);
18164 let (nspm, spk) = (n_splits_g as i32, sp_g as i32);
18165 let base_i = (base_len + r0) as i32;
18166 let o_len = t_g * n_head * n_splits_g * head_dim;
18167 let ml_len = t_g * n_head * n_splits_g;
18168 let mut part_guard = self.fa_part_pool.lock().unwrap();
18169 if part_guard
18170 .as_ref()
18171 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18172 .unwrap_or(true)
18173 {
18174 let old = part_guard.take();
18185 let (co, cm) = old
18186 .as_ref()
18187 .map(|pp| (pp.0.len(), pp.1.len()))
18188 .unwrap_or((0, 0));
18189 if let Some(old) = old {
18190 self.fa_part_retired.lock().unwrap().push(old);
18191 }
18192 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18193 eprintln!(
18194 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18195 co, o_len, cm, ml_len
18196 );
18197 }
18198 *part_guard = Some((
18199 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18200 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18201 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18202 ));
18203 }
18204 let pg = part_guard.as_mut().unwrap();
18205 self.gpu
18206 .stream()
18207 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18208 self.gpu
18209 .stream()
18210 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18211 self.gpu
18212 .stream()
18213 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18214 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18215 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
18216 let qv = self.view(q, t * n_head * head_dim);
18217 let q_g = qv.slice(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
18218 let cfg = LaunchConfig {
18219 grid_dim: (n_head_kv as u32, n_splits_g as u32, t_g as u32),
18220 block_dim: (32, gqa, 1),
18221 shared_mem_bytes: shmem,
18222 };
18223 {
18224 let __s_b = self.gpu.stream();
18225 let mut b = __s_b.launch_builder(&f);
18226 if tb512 {
18227 let (bd, plus) =
18229 base_dev.expect("hd512 rows twin requires a device base counter");
18230 let plus_g = plus + r0 as i32;
18231 let nr = t_g as i32;
18232 if Self::pdl_on() && Self::pdl_wb_on() {
18233 use cudarc::driver::{DevicePtr, DevicePtrMut};
18235 let s = &self.gpu.stream();
18236 let (pq, _b0) = q_g.device_ptr(s);
18237 let (pk, _b1) = k.device_ptr(s);
18238 let (pv, _b2) = v.device_ptr(s);
18239 let (po, _b3) = part_o.device_ptr_mut(s);
18240 let (pm, _b4) = part_m.device_ptr_mut(s);
18241 let (pl, _b5) = part_l.device_ptr_mut(s);
18242 let (pb, _b6) = bd.device_ptr(s);
18243 let mut ps = [
18244 &pq as *const _ as *mut std::ffi::c_void,
18245 &pk as *const _ as *mut _,
18246 &pv as *const _ as *mut _,
18247 &po as *const _ as *mut _,
18248 &pm as *const _ as *mut _,
18249 &pl as *const _ as *mut _,
18250 &hd as *const _ as *mut _,
18251 &nh as *const _ as *mut _,
18252 &nhkv as *const _ as *mut _,
18253 &pb as *const _ as *mut _,
18254 &plus_g as *const _ as *mut _,
18255 &scale as *const _ as *mut _,
18256 &nspm as *const _ as *mut _,
18257 &spk as *const _ as *mut _,
18258 &ktb as *const _ as *mut _,
18259 &vtb as *const _ as *mut _,
18260 &nr as *const _ as *mut _,
18261 ];
18262 unsafe {
18263 self.launch_pdl_flash(
18264 Self::gkv_on(),
18265 "fa_decode_vec_q_rows_v4_512_tb",
18266 (n_head_kv as u32, n_splits_g as u32, 1),
18267 (32, gqa, 1),
18268 shmem,
18269 &mut ps,
18270 )?;
18271 }
18272 } else {
18273 let cfg_tb = LaunchConfig {
18274 grid_dim: (n_head_kv as u32, n_splits_g as u32, 1),
18275 block_dim: (32, gqa, 1),
18276 shared_mem_bytes: shmem,
18277 };
18278 b.arg(&q_g)
18279 .arg(k)
18280 .arg(v)
18281 .arg(&mut *part_o)
18282 .arg(&mut *part_m)
18283 .arg(&mut *part_l)
18284 .arg(&hd)
18285 .arg(&nh)
18286 .arg(&nhkv)
18287 .arg(bd)
18288 .arg(&plus_g)
18289 .arg(&scale)
18290 .arg(&nspm)
18291 .arg(&spk)
18292 .arg(&ktb)
18293 .arg(&vtb)
18294 .arg(&nr);
18295 unsafe {
18296 b.launch(cfg_tb)?;
18297 }
18298 }
18299 } else if head_dim == 512 {
18300 let (bd, plus) =
18301 base_dev.expect("hd512 rows twin requires a device base counter");
18302 let plus_g = plus + r0 as i32;
18303 b.arg(&q_g)
18304 .arg(k)
18305 .arg(v)
18306 .arg(&mut *part_o)
18307 .arg(&mut *part_m)
18308 .arg(&mut *part_l)
18309 .arg(&hd)
18310 .arg(&nh)
18311 .arg(&nhkv)
18312 .arg(bd)
18313 .arg(&plus_g)
18314 .arg(&scale)
18315 .arg(&nspm)
18316 .arg(&spk)
18317 .arg(&ktb)
18318 .arg(&vtb);
18319 unsafe {
18320 b.launch(cfg)?;
18321 }
18322 } else {
18323 b.arg(&q_g)
18324 .arg(k)
18325 .arg(v)
18326 .arg(&mut *part_o)
18327 .arg(&mut *part_m)
18328 .arg(&mut *part_l)
18329 .arg(&hd)
18330 .arg(&nh)
18331 .arg(&nhkv)
18332 .arg(&base_i)
18333 .arg(&scale)
18334 .arg(&nspm)
18335 .arg(&spk)
18336 .arg(&ktb)
18337 .arg(&vtb);
18338 unsafe {
18339 b.launch(cfg)?;
18340 }
18341 }
18342 }
18343 let cfg2 = LaunchConfig {
18344 grid_dim: (n_head as u32, t_g as u32, 1),
18345 block_dim: (head_dim as u32, 1, 1),
18346 shared_mem_bytes: 0,
18347 };
18348 let mut o_g = o.slice_mut(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
18349 if head_dim == 512 {
18350 let (bd, plus) = base_dev.unwrap();
18353 let plus_g = plus + r0 as i32;
18354 if let Some((oq, od)) = q8_out.as_mut() {
18355 debug_assert!(t == 1, "rows q8 emit is a t=1 decode arm");
18357 if Self::pdl_on() && Self::pdl_wb_on() {
18358 use cudarc::driver::{DevicePtr, DevicePtrMut};
18360 let s = &self.gpu.stream();
18361 let (po, _g0) = part_o.device_ptr(s);
18362 let (pm, _g1) = part_m.device_ptr(s);
18363 let (pl, _g2) = part_l.device_ptr(s);
18364 let (pq, _g3) = oq.device_ptr_mut(s);
18365 let (pd, _g4) = od.device_ptr_mut(s);
18366 let (pb, _g5) = bd.device_ptr(s);
18367 let mut ps = [
18368 &po as *const _ as *mut std::ffi::c_void,
18369 &pm as *const _ as *mut _,
18370 &pl as *const _ as *mut _,
18371 &pq as *const _ as *mut _,
18372 &pd as *const _ as *mut _,
18373 &hd as *const _ as *mut _,
18374 &nh as *const _ as *mut _,
18375 &pb as *const _ as *mut _,
18376 &plus_g as *const _ as *mut _,
18377 &nspm as *const _ as *mut _,
18378 &spk as *const _ as *mut _,
18379 ];
18380 unsafe {
18381 self.launch_pdl_flash(
18382 Self::gkv_on(),
18383 "fa_decode_combine_rows_dc_q8_1",
18384 cfg2.grid_dim,
18385 cfg2.block_dim,
18386 0,
18387 &mut ps,
18388 )?;
18389 }
18390 continue;
18391 }
18392 let fc = self.fa_func("fa_decode_combine_rows_dc_q8_1", head_dim);
18393 let __s_b2 = self.gpu.stream();
18394 let mut b2 = __s_b2.launch_builder(&fc);
18395 b2.arg(&*part_o)
18396 .arg(&*part_m)
18397 .arg(&*part_l)
18398 .arg(&mut **oq)
18399 .arg(&mut **od)
18400 .arg(&hd)
18401 .arg(&nh)
18402 .arg(bd)
18403 .arg(&plus_g)
18404 .arg(&nspm)
18405 .arg(&spk);
18406 unsafe {
18407 b2.launch(cfg2)?;
18408 }
18409 continue;
18410 }
18411 let fc = self.fa_func("fa_decode_combine_rows_dc", head_dim);
18412 let __s_b2 = self.gpu.stream();
18413 let mut b2 = __s_b2.launch_builder(&fc);
18414 b2.arg(&*part_o)
18415 .arg(&*part_m)
18416 .arg(&*part_l)
18417 .arg(&mut o_g)
18418 .arg(&hd)
18419 .arg(&nh)
18420 .arg(bd)
18421 .arg(&plus_g)
18422 .arg(&nspm)
18423 .arg(&spk);
18424 unsafe {
18425 b2.launch(cfg2)?;
18426 }
18427 } else {
18428 assert!(
18431 q8_out.is_none(),
18432 "rows q8 emit requires the hd512 dc combine"
18433 );
18434 let fc = self.func("fa_decode_combine_rows");
18435 let __s_b2 = self.gpu.stream();
18436 let mut b2 = __s_b2.launch_builder(&fc);
18437 b2.arg(&*part_o)
18438 .arg(&*part_m)
18439 .arg(&*part_l)
18440 .arg(&mut o_g)
18441 .arg(&hd)
18442 .arg(&nh)
18443 .arg(&base_i)
18444 .arg(&nspm)
18445 .arg(&spk);
18446 unsafe {
18447 b2.launch(cfg2)?;
18448 }
18449 }
18450 }
18451 Ok(())
18452 }
18453
18454 #[allow(clippy::too_many_arguments)]
18458 pub fn fa_decode_rows_w(
18459 &self,
18460 q: &CudaSlice<f32>,
18461 k: &cudarc::driver::CudaView<u8>,
18462 v: &cudarc::driver::CudaView<u8>,
18463 o: &mut CudaSlice<f32>,
18464 head_dim: usize,
18465 n_head: usize,
18466 n_head_kv: usize,
18467 base_dev: &CudaSlice<i32>,
18468 base_plus: i32,
18469 t: usize,
18470 scale: f32,
18471 window: usize,
18472 k_tok_bytes: usize,
18473 v_tok_bytes: usize,
18474 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
18475 ) -> Result<(), Box<dyn std::error::Error>> {
18476 debug_assert!(head_dim == 256);
18481 let sp = {
18489 static SPW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18490 let v = *SPW.get_or_init(|| {
18491 std::env::var("MEMRA_FA_SPW")
18492 .ok()
18493 .and_then(|x| x.parse().ok())
18494 .unwrap_or(0)
18495 });
18496 if v >= 8 {
18497 v
18498 } else {
18499 FA_SPW_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
18500 }
18501 };
18502 let n_splits_max = (window + sp - 1) / sp;
18503 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18504 let (nspm, spk, wini) = (n_splits_max as i32, sp as i32, window as i32);
18505 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18506 let gqa = (n_head / n_head_kv).max(1) as u32;
18507 let o_len = t * n_head * n_splits_max * head_dim;
18508 let ml_len = t * n_head * n_splits_max;
18509 let mut part_guard = self.fa_part_pool.lock().unwrap();
18510 if part_guard
18511 .as_ref()
18512 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18513 .unwrap_or(true)
18514 {
18515 let old = part_guard.take();
18526 let (co, cm) = old
18527 .as_ref()
18528 .map(|pp| (pp.0.len(), pp.1.len()))
18529 .unwrap_or((0, 0));
18530 if let Some(old) = old {
18531 self.fa_part_retired.lock().unwrap().push(old);
18532 }
18533 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18534 eprintln!(
18535 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18536 co, o_len, cm, ml_len
18537 );
18538 }
18539 *part_guard = Some((
18540 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18541 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18542 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18543 ));
18544 }
18545 let pg = part_guard.as_mut().unwrap();
18546 self.gpu
18547 .stream()
18548 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18549 self.gpu
18550 .stream()
18551 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18552 self.gpu
18553 .stream()
18554 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18555 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18556 static SMEM_TKV_W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18562 let smem_tkv = *SMEM_TKV_W.get_or_init(|| {
18563 std::env::var("MEMRA_FA_SMEM_TKV")
18564 .ok()
18565 .and_then(|v| v.parse().ok())
18566 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
18567 });
18568 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18574 let wg = Self::wkv_on();
18579 let sp2 =
18582 gqa <= 4 && fa_v4_at(window) && std::env::var("MEMRA_FA_SPW2").as_deref() != Ok("0");
18583 if sp2 {
18584 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
18585 if Self::pdl_on() && Self::pdl_wb_on() {
18586 use cudarc::driver::{DevicePtr, DevicePtrMut};
18588 let s = &self.gpu.stream();
18589 let (pq, _b0) = q.device_ptr(s);
18590 let (pk, _b1) = k.device_ptr(s);
18591 let (pv, _b2) = v.device_ptr(s);
18592 let (po, _b3) = part_o.device_ptr_mut(s);
18593 let (pm, _b4) = part_m.device_ptr_mut(s);
18594 let (pl, _b5) = part_l.device_ptr_mut(s);
18595 let (pb, _b6) = base_dev.device_ptr(s);
18596 let mut ps = [
18597 &pq as *const _ as *mut std::ffi::c_void,
18598 &pk as *const _ as *mut _,
18599 &pv as *const _ as *mut _,
18600 &po as *const _ as *mut _,
18601 &pm as *const _ as *mut _,
18602 &pl as *const _ as *mut _,
18603 &hd as *const _ as *mut _,
18604 &nh as *const _ as *mut _,
18605 &nhkv as *const _ as *mut _,
18606 &pb as *const _ as *mut _,
18607 &base_plus as *const _ as *mut _,
18608 &scale as *const _ as *mut _,
18609 &nspm as *const _ as *mut _,
18610 &spk as *const _ as *mut _,
18611 &ktb as *const _ as *mut _,
18612 &vtb as *const _ as *mut _,
18613 &wini as *const _ as *mut _,
18614 ];
18615 unsafe {
18616 self.launch_pdl_flash(
18617 wg,
18618 "fa_decode_vec_q_rows_v4_w_sp",
18619 (n_head_kv as u32, n_splits_max as u32, t as u32),
18620 (32, gqa + 1, 1),
18621 sh,
18622 &mut ps,
18623 )?;
18624 }
18625 } else {
18626 let f = if wg {
18627 self.func_g("fa_decode_vec_q_rows_v4_w_sp")
18628 } else {
18629 self.func("fa_decode_vec_q_rows_v4_w_sp")
18630 };
18631 f.set_attribute(
18632 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18633 sh as i32,
18634 )?;
18635 let cfg = LaunchConfig {
18636 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18637 block_dim: (32, gqa + 1, 1),
18638 shared_mem_bytes: sh,
18639 };
18640 let __s_b = self.gpu.stream();
18641 let mut b = __s_b.launch_builder(&f);
18642 b.arg(q)
18643 .arg(k)
18644 .arg(v)
18645 .arg(&mut *part_o)
18646 .arg(&mut *part_m)
18647 .arg(&mut *part_l)
18648 .arg(&hd)
18649 .arg(&nh)
18650 .arg(&nhkv)
18651 .arg(base_dev)
18652 .arg(&base_plus)
18653 .arg(&scale)
18654 .arg(&nspm)
18655 .arg(&spk)
18656 .arg(&ktb)
18657 .arg(&vtb)
18658 .arg(&wini);
18659 unsafe {
18660 b.launch(cfg)?;
18661 }
18662 }
18663 } else {
18664 if fa_v4_at(window) && Self::pdl_on() && Self::pdl_wb_on() {
18665 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
18667 use cudarc::driver::{DevicePtr, DevicePtrMut};
18668 let s = &self.gpu.stream();
18669 let (pq, _b0) = q.device_ptr(s);
18670 let (pk, _b1) = k.device_ptr(s);
18671 let (pv, _b2) = v.device_ptr(s);
18672 let (po, _b3) = part_o.device_ptr_mut(s);
18673 let (pm, _b4) = part_m.device_ptr_mut(s);
18674 let (pl, _b5) = part_l.device_ptr_mut(s);
18675 let (pb, _b6) = base_dev.device_ptr(s);
18676 let mut ps = [
18677 &pq as *const _ as *mut std::ffi::c_void,
18678 &pk as *const _ as *mut _,
18679 &pv as *const _ as *mut _,
18680 &po as *const _ as *mut _,
18681 &pm as *const _ as *mut _,
18682 &pl as *const _ as *mut _,
18683 &hd as *const _ as *mut _,
18684 &nh as *const _ as *mut _,
18685 &nhkv as *const _ as *mut _,
18686 &pb as *const _ as *mut _,
18687 &base_plus as *const _ as *mut _,
18688 &scale as *const _ as *mut _,
18689 &nspm as *const _ as *mut _,
18690 &spk as *const _ as *mut _,
18691 &ktb as *const _ as *mut _,
18692 &vtb as *const _ as *mut _,
18693 &wini as *const _ as *mut _,
18694 ];
18695 unsafe {
18696 self.launch_pdl_flash(
18697 wg,
18698 "fa_decode_vec_q_rows_v4_w",
18699 (n_head_kv as u32, n_splits_max as u32, t as u32),
18700 (32, gqa, 1),
18701 sh,
18702 &mut ps,
18703 )?;
18704 }
18705 } else {
18706 let pick = |name: &str| {
18707 if wg {
18708 self.func_g(name)
18709 } else {
18710 self.func(name)
18711 }
18712 };
18713 let (f, sh) = if fa_v4_at(window) {
18714 let f = pick("fa_decode_vec_q_rows_v4_w");
18715 (f, (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32)
18716 } else if smem_tkv > 0 && window >= smem_tkv {
18717 (
18720 pick("fa_decode_vec_q_rows_smem_w"),
18721 (2 * 32 * head_dim * 2) as u32,
18722 )
18723 } else {
18724 (pick("fa_decode_vec_q_rows_reg_w"), 0u32)
18725 };
18726 f.set_attribute(
18727 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18728 sh as i32,
18729 )?;
18730 let cfg = LaunchConfig {
18731 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18732 block_dim: (32, gqa, 1),
18733 shared_mem_bytes: sh,
18734 };
18735 let __s_b = self.gpu.stream();
18736 let mut b = __s_b.launch_builder(&f);
18737 b.arg(q)
18738 .arg(k)
18739 .arg(v)
18740 .arg(&mut *part_o)
18741 .arg(&mut *part_m)
18742 .arg(&mut *part_l)
18743 .arg(&hd)
18744 .arg(&nh)
18745 .arg(&nhkv)
18746 .arg(base_dev)
18747 .arg(&base_plus)
18748 .arg(&scale)
18749 .arg(&nspm)
18750 .arg(&spk)
18751 .arg(&ktb)
18752 .arg(&vtb)
18753 .arg(&wini);
18754 unsafe {
18755 b.launch(cfg)?;
18756 }
18757 }
18758 }
18759 let cfg2 = LaunchConfig {
18760 grid_dim: (n_head as u32, t as u32, 1),
18761 block_dim: (head_dim as u32, 1, 1),
18762 shared_mem_bytes: 0,
18763 };
18764 if let Some((oq, od)) = q8_out {
18765 if Self::pdl_on() && Self::pdl_wb_on() {
18768 use cudarc::driver::{DevicePtr, DevicePtrMut};
18770 let s = &self.gpu.stream();
18771 let (po, _g0) = part_o.device_ptr(s);
18772 let (pm, _g1) = part_m.device_ptr(s);
18773 let (pl, _g2) = part_l.device_ptr(s);
18774 let (pq, _g3) = oq.device_ptr_mut(s);
18775 let (pd, _g4) = od.device_ptr_mut(s);
18776 let mut ps = [
18777 &po as *const _ as *mut std::ffi::c_void,
18778 &pm as *const _ as *mut _,
18779 &pl as *const _ as *mut _,
18780 &pq as *const _ as *mut _,
18781 &pd as *const _ as *mut _,
18782 &hd as *const _ as *mut _,
18783 &nh as *const _ as *mut _,
18784 &nspm as *const _ as *mut _,
18785 &spk as *const _ as *mut _,
18786 &wini as *const _ as *mut _,
18787 ];
18788 unsafe {
18789 self.launch_pdl_flash(
18790 wg,
18791 "fa_decode_combine_rows_w_q8_1",
18792 cfg2.grid_dim,
18793 cfg2.block_dim,
18794 0,
18795 &mut ps,
18796 )?;
18797 }
18798 return Ok(());
18799 }
18800 let fc = if wg {
18801 self.func_g("fa_decode_combine_rows_w_q8_1")
18802 } else {
18803 self.func("fa_decode_combine_rows_w_q8_1")
18804 };
18805 let __s_b2 = self.gpu.stream();
18806 let mut b2 = __s_b2.launch_builder(&fc);
18807 b2.arg(&*part_o)
18808 .arg(&*part_m)
18809 .arg(&*part_l)
18810 .arg(oq)
18811 .arg(od)
18812 .arg(&hd)
18813 .arg(&nh)
18814 .arg(&nspm)
18815 .arg(&spk)
18816 .arg(&wini);
18817 unsafe {
18818 b2.launch(cfg2)?;
18819 }
18820 return Ok(());
18821 }
18822 let fc = if wg {
18823 self.func_g("fa_decode_combine_rows_w")
18824 } else {
18825 self.func("fa_decode_combine_rows_w")
18826 };
18827 let __s_b2 = self.gpu.stream();
18828 let mut b2 = __s_b2.launch_builder(&fc);
18829 b2.arg(&*part_o)
18830 .arg(&*part_m)
18831 .arg(&*part_l)
18832 .arg(o)
18833 .arg(&hd)
18834 .arg(&nh)
18835 .arg(&nspm)
18836 .arg(&spk)
18837 .arg(&wini);
18838 unsafe {
18839 b2.launch(cfg2)?;
18840 }
18841 Ok(())
18842 }
18843
18844 #[allow(clippy::too_many_arguments)]
18850 pub fn fa_decode_rows_dc(
18851 &self,
18852 q: &CudaSlice<f32>,
18853 k: &cudarc::driver::CudaView<u8>,
18854 v: &cudarc::driver::CudaView<u8>,
18855 o: &mut CudaSlice<f32>,
18856 head_dim: usize,
18857 n_head: usize,
18858 n_head_kv: usize,
18859 base_dev: &CudaSlice<i32>,
18860 t_kv_upper: usize,
18861 t: usize,
18862 scale: f32,
18863 k_tok_bytes: usize,
18864 v_tok_bytes: usize,
18865 base_plus: i32,
18866 g: bool,
18867 ) -> Result<(), Box<dyn std::error::Error>> {
18868 let v4 = head_dim == 256 && fa_v4_at(t_kv_upper);
18869 assert!(
18870 v4 || fa_v3_active(head_dim),
18871 "stream fa rows requires the v3 or v4 lane"
18872 );
18873 assert!(v4 || base_plus == 0, "v3_dc kernel takes no plus arg");
18874 if v4 {
18875 let sp = fa_split_keys(t_kv_upper, n_head_kv);
18876 let n_splits_max = (t_kv_upper + sp - 1) / sp;
18877 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18878 let (nspm, spk) = (n_splits_max as i32, sp as i32);
18879 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18880 let gqa = (n_head / n_head_kv).max(1) as u32;
18881 let o_len = t * n_head * n_splits_max * head_dim;
18882 let ml_len = t * n_head * n_splits_max;
18883 let mut part_guard = self.fa_part_pool.lock().unwrap();
18884 if part_guard
18885 .as_ref()
18886 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18887 .unwrap_or(true)
18888 {
18889 let old = part_guard.take();
18900 let (co, cm) = old
18901 .as_ref()
18902 .map(|pp| (pp.0.len(), pp.1.len()))
18903 .unwrap_or((0, 0));
18904 if let Some(old) = old {
18905 self.fa_part_retired.lock().unwrap().push(old);
18906 }
18907 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18908 eprintln!(
18909 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18910 co, o_len, cm, ml_len
18911 );
18912 }
18913 *part_guard = Some((
18914 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18915 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18916 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18917 ));
18918 }
18919 let pg = part_guard.as_mut().unwrap();
18920 self.gpu
18921 .stream()
18922 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18923 self.gpu
18924 .stream()
18925 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18926 self.gpu
18927 .stream()
18928 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18929 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18930 let f = if g {
18931 self.func_g("fa_decode_vec_q_rows_v4_dc")
18932 } else {
18933 self.func("fa_decode_vec_q_rows_v4_dc")
18934 };
18935 let sh = (11520 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
18936 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18937 f.set_attribute(
18938 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18939 sh as i32,
18940 )?;
18941 let cfg = LaunchConfig {
18942 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18943 block_dim: (32, gqa, 1),
18944 shared_mem_bytes: sh,
18945 };
18946 let __s_b = self.gpu.stream();
18947 let mut b = __s_b.launch_builder(&f);
18948 b.arg(q)
18949 .arg(k)
18950 .arg(v)
18951 .arg(&mut *part_o)
18952 .arg(&mut *part_m)
18953 .arg(&mut *part_l)
18954 .arg(&hd)
18955 .arg(&nh)
18956 .arg(&nhkv)
18957 .arg(base_dev)
18958 .arg(&base_plus)
18959 .arg(&scale)
18960 .arg(&nspm)
18961 .arg(&spk)
18962 .arg(&ktb)
18963 .arg(&vtb);
18964 unsafe {
18965 b.launch(cfg)?;
18966 }
18967 let fc = self.func("fa_decode_combine_rows_dc");
18968 let cfg2 = LaunchConfig {
18969 grid_dim: (n_head as u32, t as u32, 1),
18970 block_dim: (head_dim as u32, 1, 1),
18971 shared_mem_bytes: 0,
18972 };
18973 let __s_b2 = self.gpu.stream();
18974 let mut b2 = __s_b2.launch_builder(&fc);
18975 b2.arg(&*part_o)
18976 .arg(&*part_m)
18977 .arg(&*part_l)
18978 .arg(o)
18979 .arg(&hd)
18980 .arg(&nh)
18981 .arg(base_dev)
18982 .arg(&base_plus)
18983 .arg(&nspm)
18984 .arg(&spk);
18985 unsafe {
18986 b2.launch(cfg2)?;
18987 }
18988 return Ok(());
18989 }
18990 let sp = fa_split_keys(t_kv_upper, n_head_kv);
18991 let n_splits_max = (t_kv_upper + sp - 1) / sp;
18992 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18993 let (nspm, spk) = (n_splits_max as i32, sp as i32);
18994 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18995 let gqa = (n_head / n_head_kv).max(1) as u32;
18996 let o_len = t * n_head * n_splits_max * head_dim;
18997 let ml_len = t * n_head * n_splits_max;
18998 let mut part_guard = self.fa_part_pool.lock().unwrap();
18999 if part_guard
19000 .as_ref()
19001 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
19002 .unwrap_or(true)
19003 {
19004 let old = part_guard.take();
19015 let (co, cm) = old
19016 .as_ref()
19017 .map(|pp| (pp.0.len(), pp.1.len()))
19018 .unwrap_or((0, 0));
19019 if let Some(old) = old {
19020 self.fa_part_retired.lock().unwrap().push(old);
19021 }
19022 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
19023 eprintln!(
19024 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
19025 co, o_len, cm, ml_len
19026 );
19027 }
19028 *part_guard = Some((
19029 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
19030 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19031 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19032 ));
19033 }
19034 let pg = part_guard.as_mut().unwrap();
19035 self.gpu
19036 .stream()
19037 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
19038 self.gpu
19039 .stream()
19040 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
19041 self.gpu
19042 .stream()
19043 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
19044 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
19045 let f = self.func("fa_decode_vec_q_rows_v3_dc");
19046 let sh = (32 * head_dim * 2) as u32;
19047 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19048 f.set_attribute(
19049 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19050 sh as i32,
19051 )?;
19052 let cfg = LaunchConfig {
19053 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
19054 block_dim: (32, gqa, 1),
19055 shared_mem_bytes: sh,
19056 };
19057 let __s_b = self.gpu.stream();
19058 let mut b = __s_b.launch_builder(&f);
19059 b.arg(q)
19060 .arg(k)
19061 .arg(v)
19062 .arg(&mut *part_o)
19063 .arg(&mut *part_m)
19064 .arg(&mut *part_l)
19065 .arg(&hd)
19066 .arg(&nh)
19067 .arg(&nhkv)
19068 .arg(base_dev)
19069 .arg(&scale)
19070 .arg(&nspm)
19071 .arg(&spk)
19072 .arg(&ktb)
19073 .arg(&vtb);
19074 unsafe {
19075 b.launch(cfg)?;
19076 }
19077 let fc = self.func("fa_decode_combine_rows_dc");
19078 let cfg2 = LaunchConfig {
19079 grid_dim: (n_head as u32, t as u32, 1),
19080 block_dim: (head_dim as u32, 1, 1),
19081 shared_mem_bytes: 0,
19082 };
19083 let plus0 = 0i32;
19084 let __s_b2 = self.gpu.stream();
19085 let mut b2 = __s_b2.launch_builder(&fc);
19086 b2.arg(&*part_o)
19087 .arg(&*part_m)
19088 .arg(&*part_l)
19089 .arg(o)
19090 .arg(&hd)
19091 .arg(&nh)
19092 .arg(base_dev)
19093 .arg(&plus0)
19094 .arg(&nspm)
19095 .arg(&spk);
19096 unsafe {
19097 b2.launch(cfg2)?;
19098 }
19099 Ok(())
19100 }
19101
19102 pub fn fa_decode_dc(
19113 &self,
19114 q: &CudaSlice<f32>,
19115 k: &cudarc::driver::CudaView<u8>,
19116 v: &cudarc::driver::CudaView<u8>,
19117 o: &mut CudaSlice<f32>,
19118 head_dim: usize,
19119 n_head: usize,
19120 n_head_kv: usize,
19121 t_kv_dev: &CudaSlice<i32>,
19122 bucket_max: usize,
19123 scale: f32,
19124 k_tok_bytes: usize,
19125 v_tok_bytes: usize,
19126 g: bool,
19127 ) -> Result<(), Box<dyn std::error::Error>> {
19128 self.fa_decode_dc_q8(
19129 q,
19130 k,
19131 v,
19132 o,
19133 head_dim,
19134 n_head,
19135 n_head_kv,
19136 t_kv_dev,
19137 bucket_max,
19138 scale,
19139 k_tok_bytes,
19140 v_tok_bytes,
19141 g,
19142 None,
19143 )
19144 }
19145
19146 #[allow(clippy::too_many_arguments)]
19149 pub fn fa_decode_dc_q8(
19150 &self,
19151 q: &CudaSlice<f32>,
19152 k: &cudarc::driver::CudaView<u8>,
19153 v: &cudarc::driver::CudaView<u8>,
19154 o: &mut CudaSlice<f32>,
19155 head_dim: usize,
19156 n_head: usize,
19157 n_head_kv: usize,
19158 t_kv_dev: &CudaSlice<i32>,
19159 bucket_max: usize,
19160 scale: f32,
19161 k_tok_bytes: usize,
19162 v_tok_bytes: usize,
19163 g: bool,
19164 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
19165 ) -> Result<(), Box<dyn std::error::Error>> {
19166 let mut fa_vec =
19174 std::env::var("MEMRA_NO_FA_VEC").is_err() && bucket_max >= fa_vec_min_tkv();
19175 if g && head_dim == 256 && !fa_v4_at(bucket_max) {
19176 fa_vec = false;
19177 } let sp = fa_split_keys(bucket_max, n_head_kv);
19179 let n_splits = if fa_vec {
19180 ((bucket_max + sp - 1) / sp).max(1)
19181 } else {
19182 ((bucket_max + 255) / 256).max(1)
19183 };
19184 let o_len = n_head * n_splits * head_dim;
19185 let ml_len = n_head * n_splits;
19186 let mut part_guard = self.fa_part_pool.lock().unwrap();
19187 if part_guard
19188 .as_ref()
19189 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
19190 .unwrap_or(true)
19191 {
19192 let old = part_guard.take();
19203 let (co, cm) = old
19204 .as_ref()
19205 .map(|pp| (pp.0.len(), pp.1.len()))
19206 .unwrap_or((0, 0));
19207 if let Some(old) = old {
19208 self.fa_part_retired.lock().unwrap().push(old);
19209 }
19210 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
19211 eprintln!(
19212 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
19213 co, o_len, cm, ml_len
19214 );
19215 }
19216 *part_guard = Some((
19217 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
19218 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19219 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19220 ));
19221 }
19222 let pg = part_guard.as_mut().unwrap();
19223 self.gpu
19224 .stream()
19225 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
19226 self.gpu
19227 .stream()
19228 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
19229 self.gpu
19230 .stream()
19231 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
19232 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
19233 let (hd, nh, nhkv, nsp) = (
19234 head_dim as i32,
19235 n_head as i32,
19236 n_head_kv as i32,
19237 n_splits as i32,
19238 );
19239 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
19240 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
19241 let deep = fa_vec
19244 && head_dim == 256
19245 && fa_v4_at(bucket_max)
19246 && !g
19247 && fa_deep_at(bucket_max)
19248 && !matches!(fa_v4_mode(), "noB3" | "stage");
19249 let (f, cfg) = if fa_vec
19250 && head_dim == 512
19251 && bucket_max >= {
19252 static FA512_MIN_DC: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
19253 *FA512_MIN_DC.get_or_init(|| {
19254 std::env::var("MEMRA_FA512_MIN")
19255 .ok()
19256 .and_then(|v| v.parse().ok())
19257 .unwrap_or(512)
19258 })
19259 } {
19260 let gqa = (n_head / n_head_kv).max(1) as u32;
19262 (
19263 self.fa_func("fa_decode_vec_q_dpl16_dc", head_dim),
19264 LaunchConfig {
19265 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19266 block_dim: (32, gqa, 1),
19267 shared_mem_bytes: 0,
19268 },
19269 )
19270 } else if fa_vec && head_dim == 512 {
19271 let q_view = q.as_view();
19274 let mut o_view = o.as_view_mut();
19275 return self.fa_decode_scalar_unified(
19276 &q_view,
19277 k,
19278 v,
19279 &mut o_view,
19280 head_dim,
19281 n_head,
19282 n_head_kv,
19283 0,
19284 Some(t_kv_dev),
19285 scale,
19286 n_splits,
19287 sp,
19288 k_tok_bytes,
19289 v_tok_bytes,
19290 g,
19291 &mut *part_o,
19292 &mut *part_m,
19293 &mut *part_l,
19294 q8_out,
19295 );
19296 } else if fa_vec && head_dim == 256 && fa_v4_at(bucket_max) {
19297 let gqa = (n_head / n_head_kv).max(1) as u32;
19300 let fv = if g {
19301 self.func_g("fa_decode_vec_q_v4_dc")
19302 } else if deep {
19303 self.func("fa_decode_vec_q_v4_deep_dc")
19304 } else {
19305 self.func("fa_decode_vec_q_v4_dc")
19306 };
19307 let shmem =
19308 (if deep { 12160 } else { 11520 } + 32 * head_dim * if g { 1 } else { 2 }) as u32;
19309 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19310 fv.set_attribute(
19311 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19312 shmem as i32,
19313 )?;
19314 (
19315 fv,
19316 LaunchConfig {
19317 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19318 block_dim: (32, gqa, 1),
19319 shared_mem_bytes: shmem,
19320 },
19321 )
19322 } else if fa_vec && fa_v3_active(head_dim) {
19323 let gqa = (n_head / n_head_kv).max(1) as u32;
19326 let fv = if g {
19327 self.func_g("fa_decode_vec_q_v3_dc")
19328 } else {
19329 self.func("fa_decode_vec_q_v3_dc")
19330 };
19331 let shmem = (32 * head_dim * 2) as u32; (
19333 fv,
19334 LaunchConfig {
19335 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19336 block_dim: (32, gqa, 1),
19337 shared_mem_bytes: shmem,
19338 },
19339 )
19340 } else if fa_vec && fa_v2_on() {
19341 let gqa = (n_head / n_head_kv).max(1) as u32;
19345 let fv = if g {
19346 self.func_g("fa_decode_vec_q_v2_dc")
19347 } else {
19348 self.func("fa_decode_vec_q_v2_dc")
19349 };
19350 let shmem = (2 * 32 * head_dim * 2) as u32; (
19352 fv,
19353 LaunchConfig {
19354 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19355 block_dim: (32, gqa, 1),
19356 shared_mem_bytes: shmem,
19357 },
19358 )
19359 } else if fa_vec {
19360 let gqa = (n_head / n_head_kv).max(1) as u32;
19361 let fv = if g {
19363 self.func_g("fa_decode_vec_q_dc")
19364 } else {
19365 self.func("fa_decode_vec_q_dc")
19366 };
19367 (
19368 fv,
19369 LaunchConfig {
19370 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19371 block_dim: (32, gqa, 1),
19372 shared_mem_bytes: 0,
19373 },
19374 )
19375 } else {
19376 let q_view = q.as_view();
19377 let mut o_view = o.as_view_mut();
19378 return self.fa_decode_scalar_unified(
19379 &q_view,
19380 k,
19381 v,
19382 &mut o_view,
19383 head_dim,
19384 n_head,
19385 n_head_kv,
19386 0,
19387 Some(t_kv_dev),
19388 scale,
19389 n_splits,
19390 if fa_vec { sp } else { 256 },
19391 k_tok_bytes,
19392 v_tok_bytes,
19393 g,
19394 &mut *part_o,
19395 &mut *part_m,
19396 &mut *part_l,
19397 q8_out,
19398 );
19399 };
19400 let ski = sp as i32; let __s_b = self.gpu.stream();
19402 let mut b = __s_b.launch_builder(&f);
19403 b.arg(q)
19404 .arg(k)
19405 .arg(v)
19406 .arg(&mut *part_o)
19407 .arg(&mut *part_m)
19408 .arg(&mut *part_l)
19409 .arg(&hd)
19410 .arg(&nh)
19411 .arg(&nhkv)
19412 .arg(t_kv_dev)
19413 .arg(&scale)
19414 .arg(&nsp)
19415 .arg(&ski)
19416 .arg(&ktb)
19417 .arg(&vtb);
19418 unsafe {
19419 b.launch(cfg)?;
19420 }
19421 let cfg2 = LaunchConfig {
19422 grid_dim: (n_head as u32, 1, 1),
19423 block_dim: (head_dim as u32, 1, 1),
19424 shared_mem_bytes: 0,
19425 };
19426 if let Some((oq, od)) = q8_out {
19427 let fc = if g {
19428 self.func_g("fa_decode_combine_q8_1")
19429 } else {
19430 self.fa_func("fa_decode_combine_q8_1", head_dim)
19431 };
19432 let __s_b2 = self.gpu.stream();
19433 let mut b2 = __s_b2.launch_builder(&fc);
19434 b2.arg(&*part_o)
19435 .arg(&*part_m)
19436 .arg(&*part_l)
19437 .arg(oq)
19438 .arg(od)
19439 .arg(&hd)
19440 .arg(&nh)
19441 .arg(&nsp);
19442 unsafe {
19443 b2.launch(cfg2)?;
19444 }
19445 return Ok(());
19446 }
19447 let fc = if g {
19448 self.func_g("fa_decode_combine_f32")
19449 } else {
19450 self.fa_func("fa_decode_combine_f32", head_dim)
19451 };
19452 let __s_b2 = self.gpu.stream();
19453 let mut b2 = __s_b2.launch_builder(&fc);
19454 b2.arg(&*part_o)
19455 .arg(&*part_m)
19456 .arg(&*part_l)
19457 .arg(o)
19458 .arg(&hd)
19459 .arg(&nh)
19460 .arg(&nsp);
19461 unsafe {
19462 b2.launch(cfg2)?;
19463 }
19464 Ok(())
19465 }
19466
19467 pub fn fa_geom_eager(
19473 &self,
19474 t_kv: usize,
19475 head_dim: usize,
19476 n_head_kv: usize,
19477 g: bool,
19478 ) -> (bool, usize) {
19479 let fa_ok = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
19483 let vec512 = fa_ok && head_dim == 512 && t_kv >= fa512_min_tkv();
19489 let mut fa_vec = vec512 || (fa_ok && head_dim <= 256 && head_dim % 32 == 0);
19490 if g && head_dim == 256 && !fa_v4_at(t_kv) {
19496 fa_vec = false;
19497 }
19498 let sp = fa_split_keys(t_kv, n_head_kv);
19499 let n_splits = if fa_vec {
19500 ((t_kv + sp - 1) / sp).max(1)
19501 } else {
19502 ((t_kv + 255) / 256).max(1)
19503 };
19504 (fa_vec, n_splits)
19505 }
19506
19507 pub fn fa_bucket_key(
19513 &self,
19514 t_kv: usize,
19515 head_dim: usize,
19516 n_head_kv: usize,
19517 g: bool,
19518 ) -> (bool, usize) {
19519 self.fa_geom_eager(t_kv, head_dim, n_head_kv, g)
19520 }
19521
19522 pub fn capture_graph_retained<F>(
19534 &self,
19535 step: F,
19536 ) -> Result<
19537 (
19538 cudarc::driver::CudaGraph,
19539 Vec<Box<dyn std::any::Any + Send>>,
19540 ),
19541 Box<dyn std::error::Error>,
19542 >
19543 where
19544 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
19545 {
19546 use cudarc::driver::sys::CUgraphInstantiate_flags;
19547 self.capture_graph_retained_flags(
19548 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
19549 step,
19550 )
19551 }
19552
19553 pub fn capture_graph_retained_flags<F>(
19558 &self,
19559 flags: cudarc::driver::sys::CUgraphInstantiate_flags,
19560 mut step: F,
19561 ) -> Result<
19562 (
19563 cudarc::driver::CudaGraph,
19564 Vec<Box<dyn std::any::Any + Send>>,
19565 ),
19566 Box<dyn std::error::Error>,
19567 >
19568 where
19569 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
19570 {
19571 use cudarc::driver::sys::CUstreamCaptureMode;
19572 self.capture_keep.lock().unwrap().clear();
19580 let was_tracking = self.gpu.ctx.is_event_tracking();
19581 if was_tracking {
19582 unsafe {
19583 self.gpu.ctx.disable_event_tracking();
19584 }
19585 }
19586 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
19587 self.capture_keep_on
19588 .store(true, std::sync::atomic::Ordering::Relaxed);
19589 let w = (|| {
19590 step(self)?;
19591 step(self)
19592 })();
19593 self.capture_keep_on
19594 .store(false, std::sync::atomic::Ordering::Relaxed);
19595 w?;
19596 self.gpu.stream().synchronize()?;
19597 self.gpu
19598 .stream()
19599 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
19600 let r = step(self);
19601 let g = self.gpu.stream().end_capture(flags);
19602 r?;
19603 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
19604 graph.upload()?;
19605 Ok(graph)
19606 };
19607 let result = run();
19608 self.capture_keep_on
19609 .store(false, std::sync::atomic::Ordering::Relaxed);
19610 if was_tracking {
19611 unsafe {
19612 self.gpu.ctx.enable_event_tracking();
19613 }
19614 }
19615 let keeper = std::mem::take(&mut *self.capture_keep.lock().unwrap());
19616 Ok((result?, keeper))
19617 }
19618
19619 pub fn capture_graph<F>(
19620 &self,
19621 mut step: F,
19622 ) -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>>
19623 where
19624 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
19625 {
19626 use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
19627 let was_tracking = self.gpu.ctx.is_event_tracking();
19635 if was_tracking {
19636 unsafe {
19637 self.gpu.ctx.disable_event_tracking();
19638 }
19639 }
19640 let iflag = {
19647 static F: std::sync::OnceLock<CUgraphInstantiate_flags> = std::sync::OnceLock::new();
19648 *F.get_or_init(|| match std::env::var("MEMRA_GRAPH_IFLAG").as_deref() {
19649 Ok("upload") => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_UPLOAD,
19652 Ok("priority") => {
19653 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY
19654 }
19655 _ => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
19656 })
19657 };
19658 let ct = {
19665 static T: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19666 *T.get_or_init(|| std::env::var("MEMRA_GRAPH_CAPTIME").as_deref() == Ok("1"))
19667 };
19668 let warmups = {
19691 static W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
19692 *W.get_or_init(|| {
19693 std::env::var("MEMRA_GRAPH_WARMUPS")
19694 .ok()
19695 .and_then(|v| v.parse().ok())
19696 .filter(|n| *n >= 1)
19697 .unwrap_or(1)
19698 })
19699 };
19700 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
19701 let t_w = std::time::Instant::now();
19702 for _ in 0..warmups {
19704 step(self)?;
19705 }
19706 self.gpu.stream().synchronize()?;
19707 let ms_warm = t_w.elapsed().as_secs_f64() * 1e3;
19708 let t_c = std::time::Instant::now();
19710 self.gpu
19711 .stream()
19712 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
19713 let r = step(self);
19716 let ms_body = t_c.elapsed().as_secs_f64() * 1e3;
19717 let t_i = std::time::Instant::now();
19718 let g = self.gpu.stream().end_capture(iflag);
19719 let ms_inst = t_i.elapsed().as_secs_f64() * 1e3;
19720 r?;
19721 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
19722 let t_u = std::time::Instant::now();
19723 graph.upload()?;
19724 if ct {
19725 println!(
19726 "[graph-captime] warmup2x {ms_warm:.2} ms capture-body {ms_body:.2} ms \
19727 instantiate {ms_inst:.2} ms upload {:.2} ms",
19728 t_u.elapsed().as_secs_f64() * 1e3
19729 );
19730 }
19731 Ok(graph)
19732 };
19733 let result = run();
19734 if was_tracking {
19735 unsafe {
19736 self.gpu.ctx.enable_event_tracking();
19737 }
19738 }
19739 result
19740 }
19741
19742 pub fn gdn_scan_s128_view(
19744 &self,
19745 q: &CudaSlice<f32>,
19746 k: &CudaSlice<f32>,
19747 v: &CudaSlice<f32>,
19748 g: &CudaSlice<f32>,
19749 beta: &CudaSlice<f32>,
19750 state_in: &cudarc::driver::CudaView<f32>,
19751 state_out: &mut cudarc::driver::CudaViewMut<f32>,
19752 o: &mut CudaSlice<f32>,
19753 n_head: usize,
19754 t: usize,
19755 scale: f32,
19756 ) -> Result<(), Box<dyn std::error::Error>> {
19757 let f = self.func("gdn_scan_s128");
19758 const S_V: u32 = 128;
19759 const WARP: u32 = 32;
19760 const COLS: u32 = 4;
19761 let cfg = LaunchConfig {
19762 grid_dim: (n_head as u32, 1, S_V / COLS),
19763 block_dim: (WARP, COLS, 1),
19764 shared_mem_bytes: 0,
19765 };
19766 let (h, ti) = (n_head as i32, t as i32);
19767 let __s_b = self.gpu.stream();
19768 let mut b = __s_b.launch_builder(&f);
19769 b.arg(q)
19770 .arg(k)
19771 .arg(v)
19772 .arg(g)
19773 .arg(beta)
19774 .arg(state_in)
19775 .arg(state_out)
19776 .arg(o)
19777 .arg(&h)
19778 .arg(&ti)
19779 .arg(&scale);
19780 unsafe {
19781 b.launch(cfg)?;
19782 }
19783 Ok(())
19784 }
19785
19786 pub fn ssm_conv1d_view(
19788 &self,
19789 x: &cudarc::driver::CudaView<f32>,
19790 w: &CudaSlice<f32>,
19791 y: &mut CudaSlice<f32>,
19792 conv_dim: usize,
19793 t: usize,
19794 d_conv: usize,
19795 silu: bool,
19796 ) -> Result<(), Box<dyn std::error::Error>> {
19797 let f = self.func("ssm_conv1d_silu_f32");
19798 let cfg = LaunchConfig {
19800 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
19801 block_dim: (256, 1, 1),
19802 shared_mem_bytes: 0,
19803 };
19804 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
19805 let __s_b = self.gpu.stream();
19806 let mut b = __s_b.launch_builder(&f);
19807 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
19808 unsafe {
19809 b.launch(cfg)?;
19810 }
19811 Ok(())
19812 }
19813
19814 pub fn ssm_conv1d_tm(
19821 &self,
19822 qkv_tm: &CudaSlice<f32>,
19823 w: &CudaSlice<f32>,
19824 y: &mut CudaSlice<f32>,
19825 conv_dim: usize,
19826 t: usize,
19827 d_conv: usize,
19828 ) -> Result<(), Box<dyn std::error::Error>> {
19829 let f = self.func("ssm_conv1d_tm_f32");
19830 let cfg = LaunchConfig {
19831 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19832 block_dim: (256, 1, 1),
19833 shared_mem_bytes: 0,
19834 };
19835 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19836 let __s_b = self.gpu.stream();
19837 let mut b = __s_b.launch_builder(&f);
19838 b.arg(qkv_tm).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc);
19839 unsafe {
19840 b.launch(cfg)?;
19841 }
19842 Ok(())
19843 }
19844
19845 pub fn ssm_conv1d_tm_state(
19853 &self,
19854 qkv_tm: &CudaSlice<f32>,
19855 conv_state: &mut CudaSlice<f32>,
19856 w: &CudaSlice<f32>,
19857 y: &mut CudaSlice<f32>,
19858 conv_dim: usize,
19859 t: usize,
19860 d_conv: usize,
19861 ) -> Result<(), Box<dyn std::error::Error>> {
19862 self.ssm_conv1d_tm_state_pad(qkv_tm, conv_state, w, y, conv_dim, t, d_conv, None)
19863 }
19864
19865 #[allow(clippy::too_many_arguments)]
19868 pub fn ssm_conv1d_tm_state_pad(
19869 &self,
19870 qkv_tm: &CudaSlice<f32>,
19871 conv_state: &mut CudaSlice<f32>,
19872 w: &CudaSlice<f32>,
19873 y: &mut CudaSlice<f32>,
19874 conv_dim: usize,
19875 t: usize,
19876 d_conv: usize,
19877 pad_len: Option<&CudaSlice<i32>>,
19878 ) -> Result<(), Box<dyn std::error::Error>> {
19879 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
19880 let ring_old = if t < d_conv - 1 {
19884 Some(self.clone_dtod(conv_state)?)
19885 } else {
19886 None
19887 };
19888 {
19889 let f = self.func("ssm_conv1d_tm_state_f32");
19890 let cfg = LaunchConfig {
19891 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19892 block_dim: (256, 1, 1),
19893 shared_mem_bytes: 0,
19894 };
19895 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19896 let __s_b = self.gpu.stream();
19897 let mut b = __s_b.launch_builder(&f);
19898 b.arg(qkv_tm)
19899 .arg(&*conv_state)
19900 .arg(w)
19901 .arg(y)
19902 .arg(&cd)
19903 .arg(&ti)
19904 .arg(&dc);
19905 unsafe {
19906 b.launch(cfg)?;
19907 }
19908 }
19909 match (ring_old, pad_len) {
19910 (None, Some(len_d)) => {
19911 let f = self.func("ssm_conv_ring_update_dev_f32");
19912 let n = conv_dim * (d_conv - 1);
19913 let cfg = LaunchConfig::for_num_elems(n as u32);
19914 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19915 let __s_b = self.gpu.stream();
19916 let mut b = __s_b.launch_builder(&f);
19917 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
19918 unsafe {
19919 b.launch(cfg)?;
19920 }
19921 }
19922 (None, None) => {
19923 let f = self.func("ssm_conv_ring_update_f32");
19924 let n = conv_dim * (d_conv - 1);
19925 let cfg = LaunchConfig::for_num_elems(n as u32);
19926 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19927 let __s_b = self.gpu.stream();
19928 let mut b = __s_b.launch_builder(&f);
19929 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
19930 unsafe {
19931 b.launch(cfg)?;
19932 }
19933 }
19934 (Some(old), _) => {
19935 self.ssm_conv_ring_rebuild(qkv_tm, &old, conv_state, conv_dim, t, d_conv)?
19936 }
19937 }
19938 Ok(())
19939 }
19940
19941 pub fn ssm_conv1d_tm_state_pad_v(
19943 &self,
19944 qkv_tm: &cudarc::driver::CudaView<f32>,
19945 conv_state: &mut CudaSlice<f32>,
19946 w: &CudaSlice<f32>,
19947 y: &mut CudaSlice<f32>,
19948 conv_dim: usize,
19949 t: usize,
19950 d_conv: usize,
19951 pad_len: Option<&CudaSlice<i32>>,
19952 ) -> Result<(), Box<dyn std::error::Error>> {
19953 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
19954 let ring_old = if t < d_conv - 1 {
19958 Some(self.clone_dtod(conv_state)?)
19959 } else {
19960 None
19961 };
19962 {
19963 let f = self.func("ssm_conv1d_tm_state_f32");
19964 let cfg = LaunchConfig {
19965 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19966 block_dim: (256, 1, 1),
19967 shared_mem_bytes: 0,
19968 };
19969 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19970 let __s_b = self.gpu.stream();
19971 let mut b = __s_b.launch_builder(&f);
19972 b.arg(qkv_tm)
19973 .arg(&*conv_state)
19974 .arg(w)
19975 .arg(y)
19976 .arg(&cd)
19977 .arg(&ti)
19978 .arg(&dc);
19979 unsafe {
19980 b.launch(cfg)?;
19981 }
19982 }
19983 match (ring_old, pad_len) {
19984 (None, Some(len_d)) => {
19985 let f = self.func("ssm_conv_ring_update_dev_f32");
19986 let n = conv_dim * (d_conv - 1);
19987 let cfg = LaunchConfig::for_num_elems(n as u32);
19988 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19989 let __s_b = self.gpu.stream();
19990 let mut b = __s_b.launch_builder(&f);
19991 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
19992 unsafe {
19993 b.launch(cfg)?;
19994 }
19995 }
19996 (None, None) => {
19997 let f = self.func("ssm_conv_ring_update_f32");
19998 let n = conv_dim * (d_conv - 1);
19999 let cfg = LaunchConfig::for_num_elems(n as u32);
20000 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20001 let __s_b = self.gpu.stream();
20002 let mut b = __s_b.launch_builder(&f);
20003 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
20004 unsafe {
20005 b.launch(cfg)?;
20006 }
20007 }
20008 (Some(_), _) => unreachable!(
20009 "ssm_conv1d_tm_state_pad_v: T < d_conv-1 has no view path (PRIME_MIN_T gates it)"
20010 ),
20011 }
20012 Ok(())
20013 }
20014
20015 pub fn ssm_conv_ring_rebuild(
20020 &self,
20021 qkv_tm: &CudaSlice<f32>,
20022 ring_old: &CudaSlice<f32>,
20023 conv_state: &mut CudaSlice<f32>,
20024 conv_dim: usize,
20025 tc: usize,
20026 d_conv: usize,
20027 ) -> Result<(), Box<dyn std::error::Error>> {
20028 let f = self.func("ssm_conv_ring_rebuild_f32");
20029 let n = conv_dim * (d_conv - 1);
20030 let cfg = LaunchConfig::for_num_elems(n as u32);
20031 let (cd, ti, dc) = (conv_dim as i32, tc as i32, d_conv as i32);
20032 let __s_b = self.gpu.stream();
20033 let mut b = __s_b.launch_builder(&f);
20034 b.arg(qkv_tm)
20035 .arg(ring_old)
20036 .arg(conv_state)
20037 .arg(&cd)
20038 .arg(&ti)
20039 .arg(&dc);
20040 unsafe {
20041 b.launch(cfg)?;
20042 }
20043 Ok(())
20044 }
20045
20046 #[allow(clippy::too_many_arguments)]
20051 pub fn gdn_prep_decode(
20052 &self,
20053 conv_out: &CudaSlice<f32>,
20054 beta_raw: &CudaSlice<f32>,
20055 alpha: &CudaSlice<f32>,
20056 dt_bias: &CudaSlice<f32>,
20057 a: &CudaSlice<f32>,
20058 q_l2: &mut CudaSlice<f32>,
20059 k_l2: &mut CudaSlice<f32>,
20060 v_g: &mut CudaSlice<f32>,
20061 beta: &mut CudaSlice<f32>,
20062 g_log: &mut CudaSlice<f32>,
20063 d_state: usize,
20064 num_v: usize,
20065 num_k: usize,
20066 key_dim: usize,
20067 eps: f32,
20068 ) -> Result<(), Box<dyn std::error::Error>> {
20069 let f = self.func("gdn_prep_decode_f32");
20070 let cfg = LaunchConfig {
20071 grid_dim: (num_v as u32, 1, 1),
20072 block_dim: (32, 4, 1),
20073 shared_mem_bytes: 0,
20074 };
20075 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
20076 let __s_b = self.gpu.stream();
20077 let mut b = __s_b.launch_builder(&f);
20078 b.arg(conv_out)
20079 .arg(beta_raw)
20080 .arg(alpha)
20081 .arg(dt_bias)
20082 .arg(a)
20083 .arg(q_l2)
20084 .arg(k_l2)
20085 .arg(v_g)
20086 .arg(beta)
20087 .arg(g_log)
20088 .arg(&ds)
20089 .arg(&nv)
20090 .arg(&nk)
20091 .arg(&kd)
20092 .arg(&eps);
20093 unsafe {
20094 b.launch(cfg)?;
20095 }
20096 Ok(())
20097 }
20098
20099 #[allow(clippy::too_many_arguments)]
20103 pub fn ssm_conv1d_gdn(
20104 &self,
20105 qkv_tm: &CudaSlice<f32>,
20106 w: &CudaSlice<f32>,
20107 q_g: &mut CudaSlice<f32>,
20108 k_g: &mut CudaSlice<f32>,
20109 v_g: &mut CudaSlice<f32>,
20110 conv_dim: usize,
20111 t: usize,
20112 d_conv: usize,
20113 d_state: usize,
20114 num_v: usize,
20115 num_k: usize,
20116 key_dim: usize,
20117 ) -> Result<(), Box<dyn std::error::Error>> {
20118 let f = self.func("ssm_conv1d_gdn_f32");
20119 let cfg = LaunchConfig {
20120 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
20121 block_dim: (256, 1, 1),
20122 shared_mem_bytes: 0,
20123 };
20124 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
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(qkv_tm)
20129 .arg(w)
20130 .arg(q_g)
20131 .arg(k_g)
20132 .arg(v_g)
20133 .arg(&cd)
20134 .arg(&ti)
20135 .arg(&dc)
20136 .arg(&ds)
20137 .arg(&nv)
20138 .arg(&nk)
20139 .arg(&kd);
20140 unsafe {
20141 b.launch(cfg)?;
20142 }
20143 Ok(())
20144 }
20145
20146 pub fn ssm_conv1d(
20147 &self,
20148 x: &CudaSlice<f32>,
20149 w: &CudaSlice<f32>,
20150 y: &mut CudaSlice<f32>,
20151 conv_dim: usize,
20152 t: usize,
20153 d_conv: usize,
20154 silu: bool,
20155 ) -> Result<(), Box<dyn std::error::Error>> {
20156 let f = self.func("ssm_conv1d_silu_f32");
20157 let cfg = LaunchConfig {
20158 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
20159 block_dim: (256, 1, 1),
20160 shared_mem_bytes: 0,
20161 };
20162 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
20163 let __s_b = self.gpu.stream();
20164 let mut b = __s_b.launch_builder(&f);
20165 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
20166 unsafe {
20167 b.launch(cfg)?;
20168 }
20169 Ok(())
20170 }
20171
20172 pub fn gdn_scan_s128(
20175 &self,
20176 q: &CudaSlice<f32>,
20177 k: &CudaSlice<f32>,
20178 v: &CudaSlice<f32>,
20179 g: &CudaSlice<f32>,
20180 beta: &CudaSlice<f32>,
20181 state_in: &CudaSlice<f32>,
20182 state_out: &mut CudaSlice<f32>,
20183 o: &mut CudaSlice<f32>,
20184 n_head: usize,
20185 t: usize,
20186 scale: f32,
20187 ) -> Result<(), Box<dyn std::error::Error>> {
20188 let f = self.func("gdn_scan_s128");
20189 const S_V: u32 = 128;
20190 const WARP: u32 = 32;
20191 const COLS_PER_BLOCK: u32 = 4;
20192 let cfg = LaunchConfig {
20193 grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
20194 block_dim: (WARP, COLS_PER_BLOCK, 1),
20195 shared_mem_bytes: 0,
20196 };
20197 let (h, ti) = (n_head as i32, t as i32);
20198 let __s_b = self.gpu.stream();
20199 let mut b = __s_b.launch_builder(&f);
20200 b.arg(q)
20201 .arg(k)
20202 .arg(v)
20203 .arg(g)
20204 .arg(beta)
20205 .arg(state_in)
20206 .arg(state_out)
20207 .arg(o)
20208 .arg(&h)
20209 .arg(&ti)
20210 .arg(&scale);
20211 unsafe {
20212 b.launch(cfg)?;
20213 }
20214 Ok(())
20215 }
20216
20217 #[allow(clippy::too_many_arguments)]
20222 pub fn ssm_conv1d_fused_decode_b(
20223 &self,
20224 qkv_cols: &CudaSlice<f32>,
20225 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
20226 w: &CudaSlice<f32>,
20227 conv_outs: &mut CudaSlice<f32>,
20228 conv_dim: usize,
20229 d_conv: usize,
20230 b_n: usize,
20231 ) -> Result<(), Box<dyn std::error::Error>> {
20232 let f = self.func("ssm_conv1d_fused_decode_b_f32");
20233 let cfg = LaunchConfig {
20234 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
20235 block_dim: (256, 1, 1),
20236 shared_mem_bytes: 0,
20237 };
20238 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20239 let __s_b = self.gpu.stream();
20240 let mut b = __s_b.launch_builder(&f);
20241 b.arg(qkv_cols)
20242 .arg(conv_state_ptrs)
20243 .arg(w)
20244 .arg(conv_outs)
20245 .arg(&cd)
20246 .arg(&dc);
20247 unsafe {
20248 b.launch(cfg)?;
20249 }
20250 Ok(())
20251 }
20252
20253 #[allow(clippy::too_many_arguments)]
20254 pub fn gdn_prep_decode_b(
20255 &self,
20256 conv_outs: &CudaSlice<f32>,
20257 beta_raws: &CudaSlice<f32>,
20258 alphas: &CudaSlice<f32>,
20259 dt_bias: &CudaSlice<f32>,
20260 a: &CudaSlice<f32>,
20261 q_l2: &mut CudaSlice<f32>,
20262 k_l2: &mut CudaSlice<f32>,
20263 v_g: &mut CudaSlice<f32>,
20264 beta: &mut CudaSlice<f32>,
20265 g_log: &mut CudaSlice<f32>,
20266 d_state: usize,
20267 num_v: usize,
20268 num_k: usize,
20269 key_dim: usize,
20270 eps: f32,
20271 conv_dim: usize,
20272 b_n: usize,
20273 ) -> Result<(), Box<dyn std::error::Error>> {
20274 let f = self.func("gdn_prep_decode_b_f32");
20275 let cfg = LaunchConfig {
20276 grid_dim: (num_v as u32, 1, b_n as u32),
20277 block_dim: (32, 4, 1),
20278 shared_mem_bytes: 0,
20279 };
20280 let (ds, nv, nk, kd, cd) = (
20281 d_state as i32,
20282 num_v as i32,
20283 num_k as i32,
20284 key_dim as i32,
20285 conv_dim as i32,
20286 );
20287 let __s_b = self.gpu.stream();
20288 let mut b = __s_b.launch_builder(&f);
20289 b.arg(conv_outs)
20290 .arg(beta_raws)
20291 .arg(alphas)
20292 .arg(dt_bias)
20293 .arg(a)
20294 .arg(q_l2)
20295 .arg(k_l2)
20296 .arg(v_g)
20297 .arg(beta)
20298 .arg(g_log)
20299 .arg(&ds)
20300 .arg(&nv)
20301 .arg(&nk)
20302 .arg(&kd)
20303 .arg(&eps)
20304 .arg(&cd);
20305 unsafe {
20306 b.launch(cfg)?;
20307 }
20308 Ok(())
20309 }
20310
20311 #[allow(clippy::too_many_arguments)]
20312 pub fn gdn_scan_s128_batched(
20313 &self,
20314 q: &CudaSlice<f32>,
20315 k: &CudaSlice<f32>,
20316 v: &CudaSlice<f32>,
20317 g: &CudaSlice<f32>,
20318 beta: &CudaSlice<f32>,
20319 state_in_ptrs: &cudarc::driver::CudaView<u64>,
20320 state_out_ptrs: &cudarc::driver::CudaView<u64>,
20321 o: &mut CudaSlice<f32>,
20322 n_head: usize,
20323 b_n: usize,
20324 scale: f32,
20325 ) -> Result<(), Box<dyn std::error::Error>> {
20326 let f = self.func("gdn_scan_s128_b");
20327 const S_V: u32 = 128;
20328 const WARP: u32 = 32;
20329 const COLS_PER_BLOCK: u32 = 4;
20330 let cfg = LaunchConfig {
20331 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
20332 block_dim: (WARP, COLS_PER_BLOCK, 1),
20333 shared_mem_bytes: 0,
20334 };
20335 let h = n_head as i32;
20336 let __s_b = self.gpu.stream();
20337 let mut b = __s_b.launch_builder(&f);
20338 b.arg(q)
20339 .arg(k)
20340 .arg(v)
20341 .arg(g)
20342 .arg(beta)
20343 .arg(state_in_ptrs)
20344 .arg(state_out_ptrs)
20345 .arg(o)
20346 .arg(&h)
20347 .arg(&scale);
20348 unsafe {
20349 b.launch(cfg)?;
20350 }
20351 Ok(())
20352 }
20353
20354 #[allow(clippy::too_many_arguments)]
20360 pub fn ssm_conv1d_fused_decode_b_view(
20361 &self,
20362 qkv_cols: &cudarc::driver::CudaView<f32>,
20363 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
20364 w: &CudaSlice<f32>,
20365 conv_outs: &mut CudaSlice<f32>,
20366 conv_dim: usize,
20367 d_conv: usize,
20368 b_n: usize,
20369 ) -> Result<(), Box<dyn std::error::Error>> {
20370 let f = self.func("ssm_conv1d_fused_decode_b_f32");
20371 let cfg = LaunchConfig {
20372 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
20373 block_dim: (256, 1, 1),
20374 shared_mem_bytes: 0,
20375 };
20376 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20377 let __s_b = self.gpu.stream();
20378 let mut b = __s_b.launch_builder(&f);
20379 b.arg(qkv_cols)
20380 .arg(conv_state_ptrs)
20381 .arg(w)
20382 .arg(conv_outs)
20383 .arg(&cd)
20384 .arg(&dc);
20385 unsafe {
20386 b.launch(cfg)?;
20387 }
20388 Ok(())
20389 }
20390
20391 #[allow(clippy::too_many_arguments)]
20392 pub fn gdn_prep_decode_b_view(
20393 &self,
20394 conv_outs: &CudaSlice<f32>,
20395 beta_raws: &cudarc::driver::CudaView<f32>,
20396 alphas: &cudarc::driver::CudaView<f32>,
20397 dt_bias: &CudaSlice<f32>,
20398 a: &CudaSlice<f32>,
20399 q_l2: &mut CudaSlice<f32>,
20400 k_l2: &mut CudaSlice<f32>,
20401 v_g: &mut CudaSlice<f32>,
20402 beta: &mut CudaSlice<f32>,
20403 g_log: &mut CudaSlice<f32>,
20404 d_state: usize,
20405 num_v: usize,
20406 num_k: usize,
20407 key_dim: usize,
20408 eps: f32,
20409 conv_dim: usize,
20410 b_n: usize,
20411 ) -> Result<(), Box<dyn std::error::Error>> {
20412 let f = self.func("gdn_prep_decode_b_f32");
20413 let cfg = LaunchConfig {
20414 grid_dim: (num_v as u32, 1, b_n as u32),
20415 block_dim: (32, 4, 1),
20416 shared_mem_bytes: 0,
20417 };
20418 let (ds, nv, nk, kd, cd) = (
20419 d_state as i32,
20420 num_v as i32,
20421 num_k as i32,
20422 key_dim as i32,
20423 conv_dim as i32,
20424 );
20425 let __s_b = self.gpu.stream();
20426 let mut b = __s_b.launch_builder(&f);
20427 b.arg(conv_outs)
20428 .arg(beta_raws)
20429 .arg(alphas)
20430 .arg(dt_bias)
20431 .arg(a)
20432 .arg(q_l2)
20433 .arg(k_l2)
20434 .arg(v_g)
20435 .arg(beta)
20436 .arg(g_log)
20437 .arg(&ds)
20438 .arg(&nv)
20439 .arg(&nk)
20440 .arg(&kd)
20441 .arg(&eps)
20442 .arg(&cd);
20443 unsafe {
20444 b.launch(cfg)?;
20445 }
20446 Ok(())
20447 }
20448
20449 #[allow(clippy::too_many_arguments)]
20450 pub fn gdn_scan_s128_batched_view(
20451 &self,
20452 q: &CudaSlice<f32>,
20453 k: &CudaSlice<f32>,
20454 v: &CudaSlice<f32>,
20455 g: &CudaSlice<f32>,
20456 beta: &CudaSlice<f32>,
20457 state_in_ptrs: &cudarc::driver::CudaView<u64>,
20458 state_out_ptrs: &cudarc::driver::CudaView<u64>,
20459 o: &mut cudarc::driver::CudaViewMut<f32>,
20460 n_head: usize,
20461 b_n: usize,
20462 scale: f32,
20463 ) -> Result<(), Box<dyn std::error::Error>> {
20464 let f = self.func("gdn_scan_s128_b");
20465 const S_V: u32 = 128;
20466 const WARP: u32 = 32;
20467 const COLS_PER_BLOCK: u32 = 4;
20468 let cfg = LaunchConfig {
20469 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
20470 block_dim: (WARP, COLS_PER_BLOCK, 1),
20471 shared_mem_bytes: 0,
20472 };
20473 let h = n_head as i32;
20474 let __s_b = self.gpu.stream();
20475 let mut b = __s_b.launch_builder(&f);
20476 b.arg(q)
20477 .arg(k)
20478 .arg(v)
20479 .arg(g)
20480 .arg(beta)
20481 .arg(state_in_ptrs)
20482 .arg(state_out_ptrs)
20483 .arg(o)
20484 .arg(&h)
20485 .arg(&scale);
20486 unsafe {
20487 b.launch(cfg)?;
20488 }
20489 Ok(())
20490 }
20491
20492 pub fn gdn_chunked_enabled() -> bool {
20501 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
20502 *E.get_or_init(|| {
20503 std::env::var("MEMRA_GDN_CHUNKED")
20504 .map(|v| v != "0")
20505 .unwrap_or(true)
20506 })
20507 }
20508
20509 pub fn gdn_chunk_size() -> usize {
20514 static C: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
20515 *C.get_or_init(|| {
20516 let c: usize = std::env::var("MEMRA_GDN_CHUNK")
20517 .ok()
20518 .and_then(|v| v.parse().ok())
20519 .unwrap_or(32);
20520 c.clamp(32, 128) / 32 * 32
20521 })
20522 }
20523
20524 #[allow(clippy::too_many_arguments)]
20529 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
20532 #[allow(clippy::too_many_arguments)]
20533 pub fn gdn_chunk_k123(
20534 &self,
20535 q: &CudaSlice<f32>,
20536 k: &CudaSlice<f32>,
20537 v: &CudaSlice<f32>,
20538 g: &CudaSlice<f32>,
20539 beta: &CudaSlice<f32>,
20540 wb16: Option<&mut CudaSlice<u8>>,
20541 n_head: usize,
20542 t: usize,
20543 c: usize,
20544 hk: usize,
20545 k2w: Option<(&CudaSlice<u8>, &CudaSlice<u8>, &mut CudaSlice<u8>)>,
20546 ) -> Result<
20547 (
20548 CudaSlice<f32>,
20549 CudaSlice<f32>,
20550 CudaSlice<f32>,
20551 CudaSlice<f32>,
20552 ),
20553 Box<dyn std::error::Error>,
20554 > {
20555 const D: usize = 128;
20556 let h = n_head;
20557 let nc = (t + c - 1) / c;
20558 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
20559 let mut gcum = self.uninit(t * h)?;
20560 let mut a = self.uninit(nc * h * c * c)?;
20561 let mut p = self.uninit(nc * h * c * c)?;
20562 let mut u = self.uninit(nc * h * c * D)?;
20563 let mut w = self.uninit(nc * h * c * D)?;
20564 {
20565 let f = self.func("gdn_chunk_cumgate_f32");
20567 let cfg = LaunchConfig {
20568 grid_dim: (nc as u32, h as u32, 1),
20569 block_dim: (32, 1, 1),
20570 shared_mem_bytes: 0,
20571 };
20572 let __s_b = self.gpu.stream();
20573 let mut b = __s_b.launch_builder(&f);
20574 b.arg(g).arg(&mut gcum).arg(&hi).arg(&ti).arg(&ci);
20575 unsafe {
20576 b.launch(cfg)?;
20577 }
20578 }
20579 if let Some((qb, kb, pb)) = k2w {
20580 assert!(c == 32, "gdn_k2_wgmma is a C==32 tile");
20583 let f = self.func("gdn_k2_wgmma");
20584 let cfg = LaunchConfig {
20585 grid_dim: (nc as u32, h as u32, 1),
20586 block_dim: (128, 1, 1),
20587 shared_mem_bytes: 0,
20588 };
20589 let hki = hk as i32;
20590 let __s_b = self.gpu.stream();
20591 let mut b = __s_b.launch_builder(&f);
20592 b.arg(qb)
20593 .arg(kb)
20594 .arg(&gcum)
20595 .arg(beta)
20596 .arg(&mut a)
20597 .arg(&mut *pb)
20598 .arg(&hi)
20599 .arg(&ti)
20600 .arg(&ci)
20601 .arg(&hki);
20602 unsafe {
20603 b.launch(cfg)?;
20604 }
20605 } else if c <= 64 && !portable_mma_gated() {
20606 let f = self.func("gdn_chunk_attn_f32");
20608 let jt = ((c + 31) / 32) as u32;
20609 let cfg = LaunchConfig {
20610 grid_dim: (nc as u32, h as u32, jt),
20611 block_dim: (256, 1, 1),
20612 shared_mem_bytes: 0,
20613 };
20614 let hki = hk as i32;
20615 let __s_b = self.gpu.stream();
20616 let mut b = __s_b.launch_builder(&f);
20617 b.arg(q)
20618 .arg(k)
20619 .arg(&gcum)
20620 .arg(beta)
20621 .arg(&mut a)
20622 .arg(&mut p)
20623 .arg(&hi)
20624 .arg(&ti)
20625 .arg(&ci)
20626 .arg(&hki);
20627 unsafe {
20628 b.launch(cfg)?;
20629 }
20630 } else {
20631 assert!(
20633 hk == h,
20634 "generic K2 is broadcast-only (de-broadcast rides C==32)"
20635 );
20636 let f = self.func("gdn_chunk_attn_g_f32");
20637 let cfg = LaunchConfig {
20638 grid_dim: (nc as u32, h as u32, 1),
20639 block_dim: (32, 8, 1),
20640 shared_mem_bytes: 0,
20641 };
20642 let __s_b = self.gpu.stream();
20643 let mut b = __s_b.launch_builder(&f);
20644 b.arg(q)
20645 .arg(k)
20646 .arg(&gcum)
20647 .arg(beta)
20648 .arg(&mut a)
20649 .arg(&mut p)
20650 .arg(&hi)
20651 .arg(&ti)
20652 .arg(&ci);
20653 unsafe {
20654 b.launch(cfg)?;
20655 }
20656 }
20657 {
20658 let cfg = LaunchConfig {
20660 grid_dim: (nc as u32, h as u32, 1),
20661 block_dim: (256, 1, 1),
20662 shared_mem_bytes: 0,
20663 };
20664 match c {
20665 32 | 64 => {
20666 let f = self.func(if c == 32 {
20667 "gdn_chunk_solve32_f32"
20668 } else {
20669 "gdn_chunk_solve64_f32"
20670 });
20671 let wb: u64 = match wb16 {
20673 Some(d) => self.addr_u8(d),
20674 None => 0,
20675 };
20676 let hki = hk as i32;
20677 let __s_b = self.gpu.stream();
20678 let mut b = __s_b.launch_builder(&f);
20679 b.arg(v)
20680 .arg(k)
20681 .arg(&a)
20682 .arg(&gcum)
20683 .arg(&mut u)
20684 .arg(&mut w)
20685 .arg(&wb)
20686 .arg(&hi)
20687 .arg(&ti)
20688 .arg(&hki);
20689 unsafe {
20690 b.launch(cfg)?;
20691 }
20692 }
20693 _ => {
20694 assert!(hk == h, "generic K3 is broadcast-only");
20695 let f = self.func("gdn_chunk_solve_f32");
20696 let __s_b = self.gpu.stream();
20697 let mut b = __s_b.launch_builder(&f);
20698 b.arg(v)
20699 .arg(k)
20700 .arg(&a)
20701 .arg(&gcum)
20702 .arg(&mut u)
20703 .arg(&mut w)
20704 .arg(&hi)
20705 .arg(&ti)
20706 .arg(&ci);
20707 unsafe {
20708 b.launch(cfg)?;
20709 }
20710 }
20711 }
20712 }
20713 Ok((gcum, p, u, w))
20714 }
20715
20716 pub fn gdn_db_on() -> bool {
20720 std::env::var("MEMRA_GDN_DB").as_deref() != Ok("0")
20721 }
20722
20723 pub fn gdn_mma_enabled(&self, c: usize) -> bool {
20726 !portable_mma_gated()
20727 && c == 32
20728 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
20729 Ok("1") => true,
20730 Ok("0") => false,
20731 _ => cfg!(memra_hopper_mma),
20732 }
20733 }
20734
20735 pub fn gdn_wgmma_on(&self, c: usize) -> bool {
20738 self.gdn_mma_enabled(c)
20739 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
20740 Ok("0") => false,
20741 Ok("1") => true,
20742 _ => cfg!(memra_hopper_mma),
20743 }
20744 }
20745
20746 #[allow(clippy::too_many_arguments)]
20751 pub fn ssm_conv1d_gdn_state_pad(
20752 &self,
20753 qkv_tm: &cudarc::driver::CudaView<f32>,
20754 conv_state: &mut CudaSlice<f32>,
20755 w: &CudaSlice<f32>,
20756 q_g: &mut CudaSlice<f32>,
20757 k_g: &mut CudaSlice<f32>,
20758 v_g: &mut CudaSlice<f32>,
20759 conv_dim: usize,
20760 t: usize,
20761 d_conv: usize,
20762 d_state: usize,
20763 num_v: usize,
20764 num_k: usize,
20765 key_dim: usize,
20766 hk: usize,
20767 pad_len: Option<&CudaSlice<i32>>,
20768 ) -> Result<(), Box<dyn std::error::Error>> {
20769 assert!(
20770 t >= d_conv - 1,
20771 "fused state conv requires T >= pad (PRIME_MIN_T gates)"
20772 );
20773 {
20774 let f = self.func("ssm_conv1d_gdn_state_f32");
20775 let cfg = LaunchConfig {
20776 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
20777 block_dim: (256, 1, 1),
20778 shared_mem_bytes: 0,
20779 };
20780 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20781 let (ds, nv, nk, kd, hki) = (
20782 d_state as i32,
20783 num_v as i32,
20784 num_k as i32,
20785 key_dim as i32,
20786 hk as i32,
20787 );
20788 let __s_b = self.gpu.stream();
20789 let mut b = __s_b.launch_builder(&f);
20790 b.arg(qkv_tm)
20791 .arg(&*conv_state)
20792 .arg(w)
20793 .arg(q_g)
20794 .arg(k_g)
20795 .arg(v_g)
20796 .arg(&cd)
20797 .arg(&ti)
20798 .arg(&dc)
20799 .arg(&ds)
20800 .arg(&nv)
20801 .arg(&nk)
20802 .arg(&kd)
20803 .arg(&hki);
20804 unsafe {
20805 b.launch(cfg)?;
20806 }
20807 }
20808 match pad_len {
20809 Some(len_d) => {
20810 let f = self.func("ssm_conv_ring_update_dev_f32");
20811 let n = conv_dim * (d_conv - 1);
20812 let cfg = LaunchConfig::for_num_elems(n as u32);
20813 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20814 let __s_b = self.gpu.stream();
20815 let mut b = __s_b.launch_builder(&f);
20816 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
20817 unsafe {
20818 b.launch(cfg)?;
20819 }
20820 }
20821 None => {
20822 let f = self.func("ssm_conv_ring_update_f32");
20823 let n = conv_dim * (d_conv - 1);
20824 let cfg = LaunchConfig::for_num_elems(n as u32);
20825 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20826 let __s_b = self.gpu.stream();
20827 let mut b = __s_b.launch_builder(&f);
20828 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
20829 unsafe {
20830 b.launch(cfg)?;
20831 }
20832 }
20833 }
20834 Ok(())
20835 }
20836
20837 pub fn gdn_chunk_alloc(
20841 &self,
20842 n_head: usize,
20843 t: usize,
20844 c: usize,
20845 hk: usize,
20846 ) -> Result<GdnChunkBufs, Box<dyn std::error::Error>> {
20847 const D: usize = 128;
20848 assert!(
20849 c == 32,
20850 "gdn_chunk_alloc: varlen chain is the C==32 mma pair"
20851 );
20852 let h = n_head;
20853 let nc = (t + c - 1) / c;
20854 Ok(GdnChunkBufs {
20855 gcum: self.uninit(t * h)?,
20856 a: self.uninit(nc * h * c * c)?,
20857 p: self.uninit(nc * h * c * c)?,
20858 u: self.uninit(nc * h * c * D)?,
20859 w: self.uninit(nc * h * c * D)?,
20860 kb16: self.alloc_u8_uninit(t * hk * D * 2)?,
20861 wb16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
20862 y16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
20863 ssnap16: self.alloc_u8_uninit(nc * h * D * D * 2)?,
20864 qb16: self.alloc_u8_uninit(t * hk * D * 2)?,
20865 pb16: self.alloc_u8_uninit(nc * h * c * c * 2)?,
20866 o: self.uninit(D * h * t)?,
20867 t,
20868 nc,
20869 })
20870 }
20871
20872 pub fn f32_to_bf16_v(
20874 &self,
20875 x: &cudarc::driver::CudaView<f32>,
20876 dst: &mut CudaSlice<u8>,
20877 n: usize,
20878 ) -> Result<(), Box<dyn std::error::Error>> {
20879 let f = self.func("f32_to_bf16_bulk");
20880 let ni = n as i64;
20881 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
20882 let __s_b = self.gpu.stream();
20883 let mut b = __s_b.launch_builder(&f);
20884 b.arg(x).arg(dst).arg(&ni);
20885 unsafe {
20886 b.launch(cfg)?;
20887 }
20888 Ok(())
20889 }
20890
20891 pub fn f32_to_bf16_into(
20893 &self,
20894 x: &CudaSlice<f32>,
20895 dst: &mut CudaSlice<u8>,
20896 n: usize,
20897 ) -> Result<(), Box<dyn std::error::Error>> {
20898 let f = self.func("f32_to_bf16_bulk");
20899 let ni = n as i64;
20900 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
20901 let __s_b = self.gpu.stream();
20902 let mut b = __s_b.launch_builder(&f);
20903 b.arg(x).arg(dst).arg(&ni);
20904 unsafe {
20905 b.launch(cfg)?;
20906 }
20907 Ok(())
20908 }
20909
20910 pub fn gdn_chunk_k123_vl8(
20913 &self,
20914 seqs: &[GdnSeqVl],
20915 n_head: usize,
20916 hk: usize,
20917 wq: Option<&GdnWVl8>,
20918 ) -> Result<(), Box<dyn std::error::Error>> {
20919 let b = seqs.len();
20920 assert!(b >= 1 && b <= 8, "gdn_chunk_k123_vl8: 1..=8 sequences");
20921 let mut packed = [GdnSeqVl::default(); 8];
20922 packed[..b].copy_from_slice(seqs);
20923 let v = GdnVl8(packed);
20924 let (hi, ci) = (n_head as i32, 32i32);
20925 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
20926 {
20927 let f = self.func("gdn_chunk_cumgate_vl");
20928 let cfg = LaunchConfig {
20929 grid_dim: (max_nc, n_head as u32, b as u32),
20930 block_dim: (32, 1, 1),
20931 shared_mem_bytes: 0,
20932 };
20933 let __s_lb = self.gpu.stream();
20934 let mut lb = __s_lb.launch_builder(&f);
20935 lb.arg(&v).arg(&hi).arg(&ci);
20936 unsafe {
20937 lb.launch(cfg)?;
20938 }
20939 }
20940 let hki = hk as i32;
20941 if let Some(w) = wq {
20942 let f = self.func("gdn_k2_wgmma_vl");
20944 let cfg = LaunchConfig {
20945 grid_dim: (max_nc, n_head as u32, b as u32),
20946 block_dim: (128, 1, 1),
20947 shared_mem_bytes: 0,
20948 };
20949 let __s_lb = self.gpu.stream();
20950 let mut lb = __s_lb.launch_builder(&f);
20951 lb.arg(&v).arg(w).arg(&hi).arg(&ci).arg(&hki);
20952 unsafe {
20953 lb.launch(cfg)?;
20954 }
20955 } else {
20956 let f = self.func("gdn_chunk_attn_vl");
20957 let cfg = LaunchConfig {
20958 grid_dim: (max_nc, n_head as u32, b as u32),
20959 block_dim: (256, 1, 1),
20960 shared_mem_bytes: 0,
20961 };
20962 let __s_lb = self.gpu.stream();
20963 let mut lb = __s_lb.launch_builder(&f);
20964 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
20965 unsafe {
20966 lb.launch(cfg)?;
20967 }
20968 }
20969 {
20970 let f = self.func("gdn_chunk_solve32_vl");
20971 let cfg = LaunchConfig {
20972 grid_dim: (max_nc, n_head as u32, b as u32),
20973 block_dim: (256, 1, 1),
20974 shared_mem_bytes: 0,
20975 };
20976 let __s_lb = self.gpu.stream();
20977 let mut lb = __s_lb.launch_builder(&f);
20978 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
20979 unsafe {
20980 lb.launch(cfg)?;
20981 }
20982 }
20983 Ok(())
20984 }
20985
20986 #[allow(clippy::too_many_arguments)]
20990 pub fn gdn_prep_vl8(
20991 &self,
20992 seqs: &[GdnPrepVl],
20993 conv_w: &CudaSlice<f32>,
20994 dt_bias: &CudaSlice<f32>,
20995 a: &CudaSlice<f32>,
20996 conv_dim: usize,
20997 d_conv: usize,
20998 d_state: usize,
20999 num_v: usize,
21000 num_k: usize,
21001 key_dim: usize,
21002 hk: usize,
21003 eps: f32,
21004 ) -> Result<(), Box<dyn std::error::Error>> {
21005 let b = seqs.len();
21006 assert!(b >= 1 && b <= 8);
21007 let mut packed = [GdnPrepVl::default(); 8];
21008 packed[..b].copy_from_slice(seqs);
21009 let v = GdnPrepVl8(packed);
21010 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
21011 let (cdi, dci) = (conv_dim as i32, d_conv as i32);
21012 let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
21013 assert!(
21014 conv_fuse || hk == num_v,
21015 "de-broadcast requires the fused conv"
21016 );
21017 if conv_fuse {
21018 let f = self.func("ssm_conv1d_gdn_state_vl");
21019 let cfg = LaunchConfig {
21020 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
21021 block_dim: (256, 1, 1),
21022 shared_mem_bytes: 0,
21023 };
21024 let (dsi, nvi, nki, kdi, hki) = (
21025 d_state as i32,
21026 num_v as i32,
21027 num_k as i32,
21028 key_dim as i32,
21029 hk as i32,
21030 );
21031 let __s_lb = self.gpu.stream();
21032 let mut lb = __s_lb.launch_builder(&f);
21033 lb.arg(&v)
21034 .arg(conv_w)
21035 .arg(&cdi)
21036 .arg(&dci)
21037 .arg(&dsi)
21038 .arg(&nvi)
21039 .arg(&nki)
21040 .arg(&kdi)
21041 .arg(&hki);
21042 unsafe {
21043 lb.launch(cfg)?;
21044 }
21045 } else {
21046 let f = self.func("ssm_conv1d_tm_state_vl");
21047 let cfg = LaunchConfig {
21048 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
21049 block_dim: (256, 1, 1),
21050 shared_mem_bytes: 0,
21051 };
21052 let __s_lb = self.gpu.stream();
21053 let mut lb = __s_lb.launch_builder(&f);
21054 lb.arg(&v).arg(conv_w).arg(&cdi).arg(&dci);
21055 unsafe {
21056 lb.launch(cfg)?;
21057 }
21058 }
21059 {
21060 let f = self.func("ssm_conv_ring_update_vl");
21061 let n = (conv_dim * (d_conv - 1)) as u32;
21062 let cfg = LaunchConfig {
21063 grid_dim: (n.div_ceil(256), 1, b as u32),
21064 block_dim: (256, 1, 1),
21065 shared_mem_bytes: 0,
21066 };
21067 let __s_lb = self.gpu.stream();
21068 let mut lb = __s_lb.launch_builder(&f);
21069 lb.arg(&v).arg(&cdi).arg(&dci);
21070 unsafe {
21071 lb.launch(cfg)?;
21072 }
21073 }
21074 if !conv_fuse {
21075 let f = self.func("qkv_to_gdn_repack_vl");
21076 let n = max_t * (num_v * d_state) as u32;
21077 let cfg = LaunchConfig {
21078 grid_dim: (n.div_ceil(256), 1, b as u32),
21079 block_dim: (256, 1, 1),
21080 shared_mem_bytes: 0,
21081 };
21082 let (dsi, nvi, nki, kdi) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
21083 let __s_lb = self.gpu.stream();
21084 let mut lb = __s_lb.launch_builder(&f);
21085 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&nki).arg(&kdi);
21086 unsafe {
21087 lb.launch(cfg)?;
21088 }
21089 }
21090 if Self::l2_v2_on(d_state) {
21091 let f = self.func("gdn_l2_v2_vl");
21092 let cfg = LaunchConfig {
21093 grid_dim: ((max_t * hk as u32).div_ceil(8), 2, b as u32),
21094 block_dim: (256, 1, 1),
21095 shared_mem_bytes: 0,
21096 };
21097 let (dsi, nvi) = (d_state as i32, hk as i32);
21098 let __s_lb = self.gpu.stream();
21099 let mut lb = __s_lb.launch_builder(&f);
21100 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
21101 unsafe {
21102 lb.launch(cfg)?;
21103 }
21104 } else {
21105 let f = self.func("gdn_l2_vl");
21106 let cfg = LaunchConfig {
21107 grid_dim: (max_t * hk as u32, 2, b as u32),
21108 block_dim: (256, 1, 1),
21109 shared_mem_bytes: 0,
21110 };
21111 let (dsi, nvi) = (d_state as i32, hk as i32);
21112 let __s_lb = self.gpu.stream();
21113 let mut lb = __s_lb.launch_builder(&f);
21114 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
21115 unsafe {
21116 lb.launch(cfg)?;
21117 }
21118 }
21119 {
21120 let f = self.func("gdn_gate_prep_vl");
21121 let n = max_t * num_v as u32;
21122 let cfg = LaunchConfig {
21123 grid_dim: (n.div_ceil(256), 1, b as u32),
21124 block_dim: (256, 1, 1),
21125 shared_mem_bytes: 0,
21126 };
21127 let nvi = num_v as i32;
21128 let __s_lb = self.gpu.stream();
21129 let mut lb = __s_lb.launch_builder(&f);
21130 lb.arg(&v).arg(dt_bias).arg(a).arg(&nvi);
21131 unsafe {
21132 lb.launch(cfg)?;
21133 }
21134 }
21135 Ok(())
21136 }
21137
21138 pub fn gdn_mirror_vl8(
21140 &self,
21141 seqs: &[GdnSeqVl],
21142 n_head: usize,
21143 which: i32,
21144 hk: usize,
21145 ) -> Result<(), Box<dyn std::error::Error>> {
21146 let b = seqs.len();
21147 assert!(b >= 1 && b <= 8);
21148 let mut packed = [GdnSeqVl::default(); 8];
21149 packed[..b].copy_from_slice(seqs);
21150 let v = GdnVl8(packed);
21151 let ept = (if which == 0 { hk } else { n_head } * 128) as i32;
21152 let max_n = seqs
21153 .iter()
21154 .map(|s| {
21155 if which == 0 {
21156 s.t as i64 * ept as i64
21157 } else {
21158 s.nc as i64 * ept as i64 * 32
21159 }
21160 })
21161 .max()
21162 .unwrap();
21163 let f = self.func("gdn_mirror_vl");
21164 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
21165 let cfg = LaunchConfig {
21166 grid_dim: (blocks, 1, b as u32),
21167 block_dim: (256, 1, 1),
21168 shared_mem_bytes: 0,
21169 };
21170 let __s_lb = self.gpu.stream();
21171 let mut lb = __s_lb.launch_builder(&f);
21172 lb.arg(&v).arg(&ept).arg(&which);
21173 unsafe {
21174 lb.launch(cfg)?;
21175 }
21176 Ok(())
21177 }
21178
21179 pub fn gdn_tail_vl8(
21181 &self,
21182 seqs: &[GdnPrepVl],
21183 norm_w: &CudaSlice<f32>,
21184 d_state: usize,
21185 num_v: usize,
21186 eps: f32,
21187 ) -> Result<(), Box<dyn std::error::Error>> {
21188 let b = seqs.len();
21189 assert!(b >= 1 && b <= 8);
21190 let mut packed = [GdnPrepVl::default(); 8];
21191 packed[..b].copy_from_slice(seqs);
21192 let v = GdnPrepVl8(packed);
21193 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
21194 let f = self.func("gated_rmsnorm_f16out_vl");
21195 let cfg = LaunchConfig {
21197 grid_dim: (max_t * num_v as u32, 1, b as u32),
21198 block_dim: (128, 1, 1),
21199 shared_mem_bytes: 0,
21200 };
21201 let (dsi, nvi) = (d_state as i32, num_v as i32);
21202 let __s_lb = self.gpu.stream();
21203 let mut lb = __s_lb.launch_builder(&f);
21204 lb.arg(&v).arg(norm_w).arg(&dsi).arg(&nvi).arg(&eps);
21205 unsafe {
21206 lb.launch(cfg)?;
21207 }
21208 Ok(())
21209 }
21210
21211 pub fn addr_f32(&self, x: &CudaSlice<f32>) -> u64 {
21214 use cudarc::driver::DevicePtr;
21215 let s = self.gpu.stream();
21216 let (p, _g) = x.device_ptr(&s);
21217 p as u64
21218 }
21219 pub fn addr_f32_mut(&self, x: &mut CudaSlice<f32>) -> u64 {
21220 use cudarc::driver::DevicePtrMut;
21221 let s = self.gpu.stream();
21222 let (p, _g) = x.device_ptr_mut(&s);
21223 p as u64
21224 }
21225 pub fn addr_f32v(&self, x: &cudarc::driver::CudaView<f32>) -> u64 {
21226 use cudarc::driver::DevicePtr;
21227 let s = self.gpu.stream();
21228 let (p, _g) = x.device_ptr(&s);
21229 p as u64
21230 }
21231 pub fn addr_u8(&self, x: &CudaSlice<u8>) -> u64 {
21232 use cudarc::driver::DevicePtr;
21233 let s = self.gpu.stream();
21234 let (p, _g) = x.device_ptr(&s);
21235 p as u64
21236 }
21237
21238 pub fn gdn_chunk_vl8(
21242 &self,
21243 seqs: &[GdnSeqVl],
21244 n_head: usize,
21245 scale: f32,
21246 hk: usize,
21247 wq: Option<&GdnWVl8>,
21248 ) -> Result<(), Box<dyn std::error::Error>> {
21249 const NSPLIT: u32 = 4;
21250 let b = seqs.len();
21251 assert!(b >= 1 && b <= 8, "gdn_chunk_vl8: 1..=8 sequences");
21252 let mut packed = [GdnSeqVl::default(); 8];
21253 packed[..b].copy_from_slice(seqs);
21254 let v = GdnVl8(packed);
21255 let (hi, ci) = (n_head as i32, 32i32);
21256 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
21257 let hki = hk as i32;
21258 if let Some(w) = wq {
21259 let f = self.func("gdn_k45_wgmma_vl");
21261 let cfg = LaunchConfig {
21262 grid_dim: (n_head as u32, NSPLIT, b as u32),
21263 block_dim: (256, 1, 1),
21264 shared_mem_bytes: 0,
21265 };
21266 let __s_lb = self.gpu.stream();
21267 let mut lb = __s_lb.launch_builder(&f);
21268 lb.arg(&v).arg(w).arg(&scale).arg(&hi).arg(&ci).arg(&hki);
21269 unsafe {
21270 lb.launch(cfg)?;
21271 }
21272 let _ = max_nc;
21273 return Ok(());
21274 }
21275 {
21276 let f = self.func("gdn_chunk_state_mma_vl");
21277 let cfg = LaunchConfig {
21278 grid_dim: (n_head as u32, NSPLIT, b as u32),
21279 block_dim: (256, 1, 1),
21280 shared_mem_bytes: 0,
21281 };
21282 let __s_lb = self.gpu.stream();
21283 let mut lb = __s_lb.launch_builder(&f);
21284 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
21285 unsafe {
21286 lb.launch(cfg)?;
21287 }
21288 }
21289 {
21290 let f = self.func("gdn_chunk_output_mma_vl");
21291 let cfg = LaunchConfig {
21292 grid_dim: (max_nc, n_head as u32, b as u32),
21293 block_dim: (256, 1, 1),
21294 shared_mem_bytes: 0,
21295 };
21296 let __s_lb = self.gpu.stream();
21297 let mut lb = __s_lb.launch_builder(&f);
21298 lb.arg(&v).arg(&hi).arg(&ci).arg(&scale).arg(&hki);
21299 unsafe {
21300 lb.launch(cfg)?;
21301 }
21302 }
21303 Ok(())
21304 }
21305 pub fn gdn_scan_chunked(
21306 &self,
21307 q: &CudaSlice<f32>,
21308 k: &CudaSlice<f32>,
21309 v: &CudaSlice<f32>,
21310 g: &CudaSlice<f32>,
21311 beta: &CudaSlice<f32>,
21312 kb16_pre: Option<&CudaSlice<u8>>,
21313 qb16_pre: Option<&CudaSlice<u8>>,
21314 state_in: &CudaSlice<f32>,
21315 state_out: &mut CudaSlice<f32>,
21316 o: &mut CudaSlice<f32>,
21317 n_head: usize,
21318 t: usize,
21319 scale: f32,
21320 c: usize,
21321 hk: usize,
21322 ) -> Result<(), Box<dyn std::error::Error>> {
21323 const D: usize = 128;
21324 const NSPLIT: u32 = 4;
21325 assert!(c >= 1 && c <= 128, "gdn_scan_chunked: C must be in 1..=128");
21326 let h = n_head;
21327 let nc = (t + c - 1) / c;
21328 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
21329 let gdn_mma_pre = !portable_mma_gated()
21333 && c == 32
21334 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
21335 Ok("1") => true,
21336 Ok("0") => false,
21337 _ => cfg!(memra_hopper_mma),
21338 };
21339 let mut wb16_pre: Option<CudaSlice<u8>> = if gdn_mma_pre {
21340 Some(self.alloc_u8_uninit(nc * h * c * D * 2)?)
21341 } else {
21342 None
21343 };
21344 let gdn_wgmma_pre = gdn_mma_pre
21348 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
21349 Ok("0") => false,
21350 Ok("1") => true,
21351 _ => cfg!(memra_hopper_mma),
21352 };
21353 let nk = t * hk * D;
21354 let mut kb16_local: Option<CudaSlice<u8>> = None;
21355 if gdn_mma_pre && kb16_pre.is_none() {
21356 let mut kb = self.alloc_u8_uninit(nk * 2)?;
21357 let f = self.func("f32_to_bf16_bulk");
21358 let n2 = nk as i64;
21359 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
21360 let __s_b = self.gpu.stream();
21361 let mut b = __s_b.launch_builder(&f);
21362 b.arg(k).arg(&mut kb).arg(&n2);
21363 unsafe {
21364 b.launch(cfg2)?;
21365 }
21366 kb16_local = Some(kb);
21367 }
21368 let kb16_ref0: Option<&CudaSlice<u8>> = kb16_local.as_ref().or(kb16_pre);
21369 if let Some(kb) = kb16_pre {
21370 assert!(kb.len() >= nk * 2, "kb16_pre too small");
21371 }
21372 let mut qb16: Option<CudaSlice<u8>> = None;
21373 let mut pb16: Option<CudaSlice<u8>> = None;
21374 if gdn_wgmma_pre {
21375 if qb16_pre.is_none() {
21378 let mut qb = self.alloc_u8_uninit(nk * 2)?;
21379 let f = self.func("f32_to_bf16_bulk");
21380 let n2 = nk as i64;
21381 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
21382 let __s_b = self.gpu.stream();
21383 let mut b = __s_b.launch_builder(&f);
21384 b.arg(q).arg(&mut qb).arg(&n2);
21385 unsafe {
21386 b.launch(cfg2)?;
21387 }
21388 qb16 = Some(qb);
21389 } else if let Some(qb) = qb16_pre {
21390 assert!(qb.len() >= nk * 2, "qb16_pre too small");
21391 }
21392 pb16 = Some(self.alloc_u8_uninit(nc * h * c * c * 2)?);
21393 }
21394 let qb16_ref0: Option<&CudaSlice<u8>> = qb16.as_ref().or(qb16_pre);
21395 let k2w = if gdn_wgmma_pre {
21396 Some((
21397 *qb16_ref0.as_ref().unwrap(),
21398 *kb16_ref0.as_ref().unwrap(),
21399 pb16.as_mut().unwrap(),
21400 ))
21401 } else {
21402 None
21403 };
21404 let (gcum, p, u, w) =
21405 self.gdn_chunk_k123(q, k, v, g, beta, wb16_pre.as_mut(), n_head, t, c, hk, k2w)?;
21406 let _ = &w;
21407 let mut y = self.uninit(nc * h * c * D)?;
21408 let mut ssnap = self.uninit(nc * h * D * D)?; let gdn_mma = !portable_mma_gated()
21420 && c == 32
21421 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
21422 Ok("1") => true,
21423 Ok("0") => false,
21424 _ => cfg!(memra_hopper_mma),
21425 };
21426 if gdn_mma {
21427 let wb16 = wb16_pre
21428 .take()
21429 .expect("mma path pre-allocates wb16 (K3 store fold)");
21430 let kb16_ref: &CudaSlice<u8> = kb16_ref0.expect("mma path pre-builds kb16 above K123");
21431 if gdn_wgmma_pre {
21443 let qb16 = qb16_ref0.unwrap();
21445 let pb16 = pb16.as_ref().unwrap();
21446 {
21447 let f = self.func("gdn_k45_wgmma");
21448 let cfg = LaunchConfig {
21449 grid_dim: (h as u32, 4, 1),
21450 block_dim: (256, 1, 1),
21451 shared_mem_bytes: 0,
21452 };
21453 let hki = hk as i32;
21454 let __s_b = self.gpu.stream();
21455 let mut b = __s_b.launch_builder(&f);
21456 b.arg(kb16_ref)
21457 .arg(&gcum)
21458 .arg(beta)
21459 .arg(&u)
21460 .arg(&wb16)
21461 .arg(qb16)
21462 .arg(pb16)
21463 .arg(o)
21464 .arg(&scale)
21465 .arg(state_in)
21466 .arg(&mut *state_out)
21467 .arg(&hi)
21468 .arg(&ti)
21469 .arg(&ci)
21470 .arg(&hki);
21471 unsafe {
21472 b.launch(cfg)?;
21473 }
21474 }
21475 return Ok(());
21476 }
21477 let mut y16 = self.alloc_u8_uninit(nc * h * c * D * 2)?;
21481 let mut ssnap16 = self.alloc_u8_uninit(nc * h * D * D * 2)?;
21482 {
21483 let f = self.func("gdn_chunk_state_mma");
21484 let cfg = LaunchConfig {
21485 grid_dim: (h as u32, NSPLIT, 1),
21486 block_dim: (256, 1, 1),
21487 shared_mem_bytes: 0,
21488 };
21489 let hki = hk as i32;
21490 let __s_b = self.gpu.stream();
21491 let mut b = __s_b.launch_builder(&f);
21492 b.arg(kb16_ref)
21493 .arg(&gcum)
21494 .arg(beta)
21495 .arg(&u)
21496 .arg(&wb16)
21497 .arg(&mut y16)
21498 .arg(&mut ssnap16)
21499 .arg(state_in)
21500 .arg(&mut *state_out)
21501 .arg(&hi)
21502 .arg(&ti)
21503 .arg(&ci)
21504 .arg(&hki);
21505 unsafe {
21506 b.launch(cfg)?;
21507 }
21508 }
21509 {
21510 let f = self.func("gdn_chunk_output_mma");
21512 let jt = ((c + 31) / 32) as u32;
21513 let cfg = LaunchConfig {
21514 grid_dim: (nc as u32, h as u32, jt),
21515 block_dim: (256, 1, 1),
21516 shared_mem_bytes: 0,
21517 };
21518 let hki = hk as i32;
21519 let __s_b = self.gpu.stream();
21520 let mut b = __s_b.launch_builder(&f);
21521 b.arg(q)
21522 .arg(&gcum)
21523 .arg(&p)
21524 .arg(&y16)
21525 .arg(&ssnap16)
21526 .arg(o)
21527 .arg(&hi)
21528 .arg(&ti)
21529 .arg(&ci)
21530 .arg(&scale)
21531 .arg(&hki);
21532 unsafe {
21533 b.launch(cfg)?;
21534 }
21535 }
21536 return Ok(());
21537 }
21538 {
21539 let f = self.func("gdn_chunk_state_f32");
21541 let cfg = LaunchConfig {
21542 grid_dim: (h as u32, NSPLIT, 1),
21543 block_dim: (256, 1, 1),
21544 shared_mem_bytes: 0,
21545 };
21546 let __s_b = self.gpu.stream();
21547 let mut b = __s_b.launch_builder(&f);
21548 b.arg(k)
21549 .arg(&gcum)
21550 .arg(beta)
21551 .arg(&u)
21552 .arg(&w)
21553 .arg(&mut y)
21554 .arg(&mut ssnap)
21555 .arg(state_in)
21556 .arg(&mut *state_out)
21557 .arg(&hi)
21558 .arg(&ti)
21559 .arg(&ci);
21560 unsafe {
21561 b.launch(cfg)?;
21562 }
21563 }
21564 {
21565 let f = self.func("gdn_chunk_output_f32");
21567 let jt = ((c + 31) / 32) as u32;
21568 let cfg = LaunchConfig {
21569 grid_dim: (nc as u32, h as u32, jt),
21570 block_dim: (256, 1, 1),
21571 shared_mem_bytes: 0,
21572 };
21573 let __s_b = self.gpu.stream();
21574 let mut b = __s_b.launch_builder(&f);
21575 b.arg(q)
21576 .arg(&gcum)
21577 .arg(&p)
21578 .arg(&y)
21579 .arg(&ssnap)
21580 .arg(o)
21581 .arg(&hi)
21582 .arg(&ti)
21583 .arg(&ci)
21584 .arg(&scale);
21585 unsafe {
21586 b.launch(cfg)?;
21587 }
21588 }
21589 Ok(())
21590 }
21591
21592 #[allow(clippy::too_many_arguments)]
21601 #[allow(clippy::too_many_arguments)]
21602 pub fn gdn_scan_prefill(
21603 &self,
21604 q: &CudaSlice<f32>,
21605 k: &CudaSlice<f32>,
21606 v: &CudaSlice<f32>,
21607 g: &CudaSlice<f32>,
21608 beta: &CudaSlice<f32>,
21609 kb16_pre: Option<&CudaSlice<u8>>,
21610 qb16_pre: Option<&CudaSlice<u8>>,
21611 state_in: &CudaSlice<f32>,
21612 state_out: &mut CudaSlice<f32>,
21613 o: &mut CudaSlice<f32>,
21614 n_head: usize,
21615 t: usize,
21616 scale: f32,
21617 hk: usize,
21618 ) -> Result<(), Box<dyn std::error::Error>> {
21619 if std::env::var("MEMRA_GDN_DIFF").is_ok() && t >= 16 {
21620 assert!(hk == n_head, "GDN_DIFF oracle is broadcast-only");
21621 return self.gdn_scan_diff(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale);
21622 }
21623 if Self::gdn_chunked_enabled() && t >= 16 {
21624 self.gdn_scan_chunked(
21625 q,
21626 k,
21627 v,
21628 g,
21629 beta,
21630 kb16_pre,
21631 qb16_pre,
21632 state_in,
21633 state_out,
21634 o,
21635 n_head,
21636 t,
21637 scale,
21638 Self::gdn_chunk_size(),
21639 hk,
21640 )
21641 } else {
21642 assert!(
21643 hk == n_head,
21644 "s128 scan is broadcast-only (prep guarantees by predicate)"
21645 );
21646 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)
21647 }
21648 }
21649
21650 #[allow(clippy::too_many_arguments)]
21652 fn gdn_scan_diff(
21653 &self,
21654 q: &CudaSlice<f32>,
21655 k: &CudaSlice<f32>,
21656 v: &CudaSlice<f32>,
21657 g: &CudaSlice<f32>,
21658 beta: &CudaSlice<f32>,
21659 state_in: &CudaSlice<f32>,
21660 state_out: &mut CudaSlice<f32>,
21661 o: &mut CudaSlice<f32>,
21662 n_head: usize,
21663 t: usize,
21664 scale: f32,
21665 ) -> Result<(), Box<dyn std::error::Error>> {
21666 static CALL: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
21667 let call = CALL.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
21668 let mut o_c = self.uninit(o.len())?;
21669 let mut st_c = self.uninit(state_out.len())?;
21670 self.gdn_scan_chunked(
21671 q,
21672 k,
21673 v,
21674 g,
21675 beta,
21676 None,
21677 None,
21678 state_in,
21679 &mut st_c,
21680 &mut o_c,
21681 n_head,
21682 t,
21683 scale,
21684 Self::gdn_chunk_size(),
21685 n_head,
21686 )?;
21687 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)?;
21688 let (oh_s, oh_c) = (self.dtoh(o)?, self.dtoh(&o_c)?);
21689 let (sh_s, sh_c) = (self.dtoh(state_out)?, self.dtoh(&st_c)?);
21690 let stats = |a: &[f32], b: &[f32]| -> (f32, f32, f64) {
21691 let mut max_abs = 0f32;
21692 let mut max_rel = 0f32;
21693 let mut sum_rel = 0f64;
21694 for (x, y) in a.iter().zip(b) {
21695 let ad = (x - y).abs();
21696 let rel = ad / x.abs().max(y.abs()).max(1e-3);
21697 if ad > max_abs {
21698 max_abs = ad;
21699 }
21700 if rel > max_rel {
21701 max_rel = rel;
21702 }
21703 sum_rel += rel as f64;
21704 }
21705 (max_abs, max_rel, sum_rel / a.len() as f64)
21706 };
21707 let (o_ma, o_mr, o_mean) = stats(&oh_s, &oh_c);
21708 let (s_ma, s_mr, s_mean) = stats(&sh_s, &sh_c);
21709 println!(
21710 "[gdn-diff call {call:3} T={t} C={}] out: max_abs={o_ma:.3e} max_rel={o_mr:.3e} mean_rel={o_mean:.3e} | \
21711 state: max_abs={s_ma:.3e} max_rel={s_mr:.3e} mean_rel={s_mean:.3e}",
21712 Self::gdn_chunk_size()
21713 );
21714 Ok(())
21715 }
21716
21717 pub fn gdn_glog(
21719 &self,
21720 alpha: &CudaSlice<f32>,
21721 dt_bias: &CudaSlice<f32>,
21722 a: &CudaSlice<f32>,
21723 g_log: &mut CudaSlice<f32>,
21724 n_head: usize,
21725 t: usize,
21726 ) -> Result<(), Box<dyn std::error::Error>> {
21727 let f = self.func("gdn_glog_f32");
21728 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
21729 let (h, ti) = (n_head as i32, t as i32);
21730 let __s_b = self.gpu.stream();
21731 let mut b = __s_b.launch_builder(&f);
21732 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
21733 unsafe {
21734 b.launch(cfg)?;
21735 }
21736 Ok(())
21737 }
21738
21739 pub fn sigmoid_v(
21742 &self,
21743 x: &cudarc::driver::CudaView<f32>,
21744 y: &mut CudaSlice<f32>,
21745 n: usize,
21746 ) -> Result<(), Box<dyn std::error::Error>> {
21747 let f = self.func("sigmoid_f32");
21748 let cfg = LaunchConfig::for_num_elems(n as u32);
21749 let ni = n as i32;
21750 let __s_b = self.gpu.stream();
21751 let mut b = __s_b.launch_builder(&f);
21752 b.arg(x).arg(y).arg(&ni);
21753 unsafe {
21754 b.launch(cfg)?;
21755 }
21756 Ok(())
21757 }
21758
21759 pub fn gdn_glog_v(
21760 &self,
21761 alpha: &cudarc::driver::CudaView<f32>,
21762 dt_bias: &CudaSlice<f32>,
21763 a: &CudaSlice<f32>,
21764 g_log: &mut CudaSlice<f32>,
21765 n_head: usize,
21766 t: usize,
21767 ) -> Result<(), Box<dyn std::error::Error>> {
21768 let f = self.func("gdn_glog_f32");
21769 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
21770 let (h, ti) = (n_head as i32, t as i32);
21771 let __s_b = self.gpu.stream();
21772 let mut b = __s_b.launch_builder(&f);
21773 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
21774 unsafe {
21775 b.launch(cfg)?;
21776 }
21777 Ok(())
21778 }
21779
21780 pub fn sigmoid(
21781 &self,
21782 x: &CudaSlice<f32>,
21783 y: &mut CudaSlice<f32>,
21784 n: usize,
21785 ) -> Result<(), Box<dyn std::error::Error>> {
21786 let f = self.func("sigmoid_f32");
21787 let cfg = LaunchConfig::for_num_elems(n as u32);
21788 let ni = n as i32;
21789 let __s_b = self.gpu.stream();
21790 let mut b = __s_b.launch_builder(&f);
21791 b.arg(x).arg(y).arg(&ni);
21792 unsafe {
21793 b.launch(cfg)?;
21794 }
21795 Ok(())
21796 }
21797
21798 pub fn sig_mul_f16out(
21801 &self,
21802 a: &CudaSlice<f32>,
21803 g: &CudaSlice<f32>,
21804 dst: &mut CudaSlice<f32>,
21805 dst16: &mut CudaSlice<u8>,
21806 n: usize,
21807 ) -> Result<(), Box<dyn std::error::Error>> {
21808 let f = self.func("sig_mul_f16out_f32");
21809 let cfg = LaunchConfig::for_num_elems(n as u32);
21810 let ni = n as i32;
21811 let __s_b = self.gpu.stream();
21812 let mut b = __s_b.launch_builder(&f);
21813 b.arg(a).arg(g).arg(dst).arg(dst16).arg(&ni);
21814 unsafe {
21815 b.launch(cfg)?;
21816 }
21817 Ok(())
21818 }
21819
21820 #[allow(clippy::too_many_arguments)]
21829 pub fn attn_head_gate(
21830 &self,
21831 a: &CudaSlice<f32>,
21832 g: &CudaSlice<f32>,
21833 dst: &mut CudaSlice<f32>,
21834 dst16: Option<&mut CudaSlice<u8>>,
21835 head_dim: usize,
21836 n_head: usize,
21837 t: usize,
21838 ) -> Result<(), Box<dyn std::error::Error>> {
21839 let f = self.func("attn_head_gate_f32");
21840 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
21841 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
21842 let d16: u64 = match dst16 {
21844 Some(d) => self.addr_u8(d),
21845 None => 0,
21846 };
21847 let __s_b = self.gpu.stream();
21848 let mut b = __s_b.launch_builder(&f);
21849 b.arg(a)
21850 .arg(g)
21851 .arg(dst)
21852 .arg(&d16)
21853 .arg(&hd)
21854 .arg(&nh)
21855 .arg(&ti);
21856 unsafe {
21857 b.launch(cfg)?;
21858 }
21859 Ok(())
21860 }
21861
21862 #[allow(clippy::too_many_arguments)]
21871 pub fn swiglu_clamped_mul_scaled(
21872 &self,
21873 gate: &CudaSlice<f32>,
21874 up: &CudaSlice<f32>,
21875 gs: f32,
21876 us: f32,
21877 limit: f32,
21878 dst: &mut CudaSlice<f32>,
21879 n: usize,
21880 ) -> Result<(), Box<dyn std::error::Error>> {
21881 debug_assert!(
21882 limit > 1e-6,
21883 "swiglu_clamped needs a live limit; use silu_mul_scaled"
21884 );
21885 let f = self.func("swiglu_clamped_mul_scaled_f32");
21886 let cfg = LaunchConfig::for_num_elems(n as u32);
21887 let ni = n as i32;
21888 let __s_b = self.gpu.stream();
21889 let mut b = __s_b.launch_builder(&f);
21890 b.arg(gate)
21891 .arg(up)
21892 .arg(&gs)
21893 .arg(&us)
21894 .arg(&limit)
21895 .arg(dst)
21896 .arg(&ni);
21897 unsafe {
21898 b.launch(cfg)?;
21899 }
21900 Ok(())
21901 }
21902
21903 pub fn gated_rmsnorm(
21905 &self,
21906 o: &CudaSlice<f32>,
21907 w: &CudaSlice<f32>,
21908 z: &CudaSlice<f32>,
21909 dst: &mut CudaSlice<f32>,
21910 ncols: usize,
21911 nrows: usize,
21912 eps: f32,
21913 ) -> Result<(), Box<dyn std::error::Error>> {
21914 let f = self.func("gated_rmsnorm_f32");
21915 let cfg = LaunchConfig {
21916 grid_dim: (nrows as u32, 1, 1),
21917 block_dim: (128, 1, 1),
21918 shared_mem_bytes: 0,
21919 };
21920 let (nc, e) = (ncols as i32, eps);
21921 let __s_b = self.gpu.stream();
21922 let mut b = __s_b.launch_builder(&f);
21923 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
21924 unsafe {
21925 b.launch(cfg)?;
21926 }
21927 Ok(())
21928 }
21929
21930 pub fn gated_rmsnorm_f16out(
21933 &self,
21934 o: &CudaSlice<f32>,
21935 w: &CudaSlice<f32>,
21936 z: &CudaSlice<f32>,
21937 dst: &mut CudaSlice<f32>,
21938 dst16: &mut CudaSlice<u8>,
21939 ncols: usize,
21940 nrows: usize,
21941 eps: f32,
21942 ) -> Result<(), Box<dyn std::error::Error>> {
21943 let f = self.func("gated_rmsnorm_f16out_f32");
21944 let cfg = LaunchConfig {
21946 grid_dim: (nrows as u32, 1, 1),
21947 block_dim: (128, 1, 1),
21948 shared_mem_bytes: 0,
21949 };
21950 let (nc, e) = (ncols as i32, eps);
21951 let __s_b = self.gpu.stream();
21952 let mut b = __s_b.launch_builder(&f);
21953 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
21954 unsafe {
21955 b.launch(cfg)?;
21956 }
21957 Ok(())
21958 }
21959
21960 #[allow(clippy::too_many_arguments)]
21964 pub fn add_rms_norm_zq8(
21965 &self,
21966 a: &CudaSlice<f32>,
21967 b_in: &CudaSlice<f32>,
21968 w: &CudaSlice<f32>,
21969 res: &mut CudaSlice<f32>,
21970 z: &mut CudaSlice<f32>,
21971 ncols: usize,
21972 nrows: usize,
21973 eps: f32,
21974 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
21975 assert!(ncols % 32 == 0);
21976 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
21977 let mut d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
21978 let f = self.func("add_rms_norm_zq8");
21979 let cfg = LaunchConfig {
21980 grid_dim: (nrows as u32, 1, 1),
21981 block_dim: (1024, 1, 1),
21982 shared_mem_bytes: 0,
21983 };
21984 let (nc, ep) = (ncols as i32, eps);
21985 let __s_b = self.gpu.stream();
21986 let mut b = __s_b.launch_builder(&f);
21987 b.arg(a)
21988 .arg(b_in)
21989 .arg(w)
21990 .arg(res)
21991 .arg(z)
21992 .arg(&mut q)
21993 .arg(&mut d)
21994 .arg(&nc)
21995 .arg(&ep);
21996 unsafe {
21997 b.launch(cfg)?;
21998 }
21999 Ok((q, d))
22000 }
22001
22002 pub fn gated_rmsnorm_zv(
22007 &self,
22008 o: &CudaSlice<f32>,
22009 w: &CudaSlice<f32>,
22010 z: &cudarc::driver::CudaView<f32>,
22011 dst: &mut CudaSlice<f32>,
22012 ncols: usize,
22013 nrows: usize,
22014 eps: f32,
22015 ) -> Result<(), Box<dyn std::error::Error>> {
22016 let f = self.func("gated_rmsnorm_f32");
22017 let cfg = LaunchConfig {
22018 grid_dim: (nrows as u32, 1, 1),
22019 block_dim: (128, 1, 1),
22020 shared_mem_bytes: 0,
22021 };
22022 let (nc, e) = (ncols as i32, eps);
22023 let __s_b = self.gpu.stream();
22024 let mut b = __s_b.launch_builder(&f);
22025 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
22026 unsafe {
22027 b.launch(cfg)?;
22028 }
22029 Ok(())
22030 }
22031
22032 pub fn gated_rmsnorm_f16out_zv(
22033 &self,
22034 o: &CudaSlice<f32>,
22035 w: &CudaSlice<f32>,
22036 z: &cudarc::driver::CudaView<f32>,
22037 dst: &mut CudaSlice<f32>,
22038 dst16: &mut CudaSlice<u8>,
22039 ncols: usize,
22040 nrows: usize,
22041 eps: f32,
22042 ) -> Result<(), Box<dyn std::error::Error>> {
22043 let f = self.func("gated_rmsnorm_f16out_f32");
22044 let cfg = LaunchConfig {
22046 grid_dim: (nrows as u32, 1, 1),
22047 block_dim: (128, 1, 1),
22048 shared_mem_bytes: 0,
22049 };
22050 let (nc, e) = (ncols as i32, eps);
22051 let __s_b = self.gpu.stream();
22052 let mut b = __s_b.launch_builder(&f);
22053 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
22054 unsafe {
22055 b.launch(cfg)?;
22056 }
22057 Ok(())
22058 }
22059
22060 pub fn gated_rmsnorm_q8_1(
22061 &self,
22062 o: &CudaSlice<f32>,
22063 w: &CudaSlice<f32>,
22064 z: &CudaSlice<f32>,
22065 ncols: usize,
22066 nrows: usize,
22067 eps: f32,
22068 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
22069 assert!(ncols % 32 == 0);
22070 let f = self.func("gated_rmsnorm_q8_1");
22071 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
22072 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
22073 let cfg = LaunchConfig {
22074 grid_dim: (nrows as u32, 1, 1),
22075 block_dim: (128, 1, 1),
22076 shared_mem_bytes: 0,
22077 };
22078 let (nc, ep) = (ncols as i32, eps);
22079 let __s_b = self.gpu.stream();
22080 let mut b = __s_b.launch_builder(&f);
22081 b.arg(o)
22082 .arg(w)
22083 .arg(z)
22084 .arg(&mut out_q)
22085 .arg(&mut out_d)
22086 .arg(&nc)
22087 .arg(&ep);
22088 unsafe {
22089 b.launch(cfg)?;
22090 }
22091 Ok((out_q, out_d))
22092 }
22093
22094 pub fn transpose(
22096 &self,
22097 inp: &CudaSlice<f32>,
22098 rows: usize,
22099 cols: usize,
22100 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22101 let f = self.func("transpose_f32");
22102 let mut out = self.zeros(rows * cols)?;
22103 let cfg = LaunchConfig::for_num_elems((rows * cols) as u32);
22104 let (r, c) = (rows as i32, cols as i32);
22105 let __s_b = self.gpu.stream();
22106 let mut b = __s_b.launch_builder(&f);
22107 b.arg(inp).arg(&mut out).arg(&r).arg(&c);
22108 unsafe {
22109 b.launch(cfg)?;
22110 }
22111 Ok(out)
22112 }
22113
22114 pub fn repeat_heads(
22116 &self,
22117 inp: &CudaSlice<f32>,
22118 out: &mut CudaSlice<f32>,
22119 head_dim: usize,
22120 n_in: usize,
22121 n_out: usize,
22122 t: usize,
22123 ) -> Result<(), Box<dyn std::error::Error>> {
22124 let f = self.func("repeat_heads_f32");
22125 let cfg = LaunchConfig::for_num_elems((head_dim * n_out * t) as u32);
22126 let (hd, ni, no, ti) = (head_dim as i32, n_in as i32, n_out as i32, t as i32);
22127 let __s_b = self.gpu.stream();
22128 let mut b = __s_b.launch_builder(&f);
22129 b.arg(inp).arg(out).arg(&hd).arg(&ni).arg(&no).arg(&ti);
22130 unsafe {
22131 b.launch(cfg)?;
22132 }
22133 Ok(())
22134 }
22135
22136 pub fn q_gate_split(
22143 &self,
22144 qf: &CudaSlice<f32>,
22145 q_out: &mut CudaSlice<f32>,
22146 gate_out: &mut CudaSlice<f32>,
22147 head_dim: usize,
22148 n_head: usize,
22149 t: usize,
22150 ) -> Result<(), Box<dyn std::error::Error>> {
22151 memra_gguf::config::check_fused_q_gate_extent(qf.len(), head_dim, n_head, t)?;
22152 let out_need = head_dim * n_head * t;
22153 if q_out.len() < out_need || gate_out.len() < out_need {
22154 return Err(format!(
22155 "q_gate_split destinations too small: need {out_need} each, have q={} gate={}",
22156 q_out.len(),
22157 gate_out.len()
22158 )
22159 .into());
22160 }
22161 let f = self.func("q_gate_split_f32");
22162 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
22163 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
22164 let __s_b = self.gpu.stream();
22165 let mut b = __s_b.launch_builder(&f);
22166 b.arg(qf)
22167 .arg(q_out)
22168 .arg(gate_out)
22169 .arg(&hd)
22170 .arg(&nh)
22171 .arg(&ti);
22172 unsafe {
22173 b.launch(cfg)?;
22174 }
22175 Ok(())
22176 }
22177
22178 pub fn qkv_to_gdn_repack(
22182 &self,
22183 conv_out: &CudaSlice<f32>,
22184 q_g: &mut CudaSlice<f32>,
22185 k_g: &mut CudaSlice<f32>,
22186 v_g: &mut CudaSlice<f32>,
22187 d_state: usize,
22188 num_v: usize,
22189 num_k: usize,
22190 key_dim: usize,
22191 t: usize,
22192 ) -> Result<(), Box<dyn std::error::Error>> {
22193 let f = self.func("qkv_to_gdn_repack_f32");
22194 let cfg = LaunchConfig::for_num_elems((d_state * num_v * t) as u32);
22195 let (ds, nv, nk, kd, ti) = (
22196 d_state as i32,
22197 num_v as i32,
22198 num_k as i32,
22199 key_dim as i32,
22200 t as i32,
22201 );
22202 let __s_b = self.gpu.stream();
22203 let mut b = __s_b.launch_builder(&f);
22204 b.arg(conv_out)
22205 .arg(q_g)
22206 .arg(k_g)
22207 .arg(v_g)
22208 .arg(&ds)
22209 .arg(&nv)
22210 .arg(&nk)
22211 .arg(&kd)
22212 .arg(&ti);
22213 unsafe {
22214 b.launch(cfg)?;
22215 }
22216 Ok(())
22217 }
22218
22219 pub fn conv_left_pad(
22222 &self,
22223 src: &CudaSlice<f32>,
22224 dst: &mut CudaSlice<f32>,
22225 conv_dim: usize,
22226 t: usize,
22227 pad: usize,
22228 ) -> Result<(), Box<dyn std::error::Error>> {
22229 let f = self.func("conv_left_pad_f32");
22230 let cfg = LaunchConfig::for_num_elems((conv_dim * t) as u32);
22231 let (cd, ti, p) = (conv_dim as i32, t as i32, pad as i32);
22232 let __s_b = self.gpu.stream();
22233 let mut b = __s_b.launch_builder(&f);
22234 b.arg(src).arg(dst).arg(&cd).arg(&ti).arg(&p);
22235 unsafe {
22236 b.launch(cfg)?;
22237 }
22238 Ok(())
22239 }
22240
22241 pub fn conv_assemble_and_roll(
22245 &self,
22246 qkv_col: &CudaSlice<f32>,
22247 conv_state: &mut CudaSlice<f32>,
22248 conv_in: &mut CudaSlice<f32>,
22249 conv_dim: usize,
22250 pad: usize,
22251 ) -> Result<(), Box<dyn std::error::Error>> {
22252 let f = self.func("conv_assemble_and_roll_f32");
22253 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
22254 let (cd, p) = (conv_dim as i32, pad as i32);
22255 let __s_b = self.gpu.stream();
22256 let mut b = __s_b.launch_builder(&f);
22257 b.arg(qkv_col).arg(conv_state).arg(conv_in).arg(&cd).arg(&p);
22258 unsafe {
22259 b.launch(cfg)?;
22260 }
22261 Ok(())
22262 }
22263
22264 pub fn ssm_conv1d_fused_decode(
22270 &self,
22271 qkv_col: &CudaSlice<f32>,
22272 conv_state: &mut CudaSlice<f32>,
22273 w: &CudaSlice<f32>,
22274 conv_out: &mut CudaSlice<f32>,
22275 conv_dim: usize,
22276 d_conv: usize,
22277 ) -> Result<(), Box<dyn std::error::Error>> {
22278 let f = self.func("ssm_conv1d_fused_decode_f32");
22279 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
22280 let (cd, dc) = (conv_dim as i32, d_conv as i32);
22281 let __s_b = self.gpu.stream();
22282 let mut b = __s_b.launch_builder(&f);
22283 b.arg(qkv_col)
22284 .arg(conv_state)
22285 .arg(w)
22286 .arg(conv_out)
22287 .arg(&cd)
22288 .arg(&dc);
22289 unsafe {
22290 b.launch(cfg)?;
22291 }
22292 Ok(())
22293 }
22294
22295 pub fn slice_range(
22298 &self,
22299 src: &CudaSlice<f32>,
22300 start: usize,
22301 len: usize,
22302 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22303 let host = self.gpu.stream().clone_dtoh(src)?;
22304 self.gpu.stream().synchronize()?;
22305 Ok(self.htod(&host[start..start + len])?)
22306 }
22307}
22308
22309#[cfg(test)]
22310mod target_dispatch_tests {
22311 use super::legacy_quant_gemm_allowed;
22312
22313 #[test]
22314 fn legacy_quant_gemm_arch_policy_honors_the_escape_hatch() {
22315 assert!(legacy_quant_gemm_allowed(false, false, false));
22317 assert!(!legacy_quant_gemm_allowed(false, false, true));
22318 assert!(!legacy_quant_gemm_allowed(true, false, false));
22320 assert!(!legacy_quant_gemm_allowed(true, false, true));
22321 assert!(legacy_quant_gemm_allowed(true, true, false));
22323 assert!(!legacy_quant_gemm_allowed(true, true, true));
22324 }
22325
22326 #[cfg(all(memra_portable_cuda, not(memra_hopper_mma)))]
22327 #[test]
22328 fn portable_build_disables_legacy_quant_gemm_without_an_env_override() {
22329 assert!(!legacy_quant_gemm_allowed(
22330 cfg!(memra_portable_cuda),
22331 cfg!(memra_hopper_mma),
22332 false
22333 ));
22334 }
22335
22336 #[cfg(memra_hopper_mma)]
22337 #[test]
22338 fn hopper_mma_build_re_admits_legacy_quant_gemm() {
22339 assert!(legacy_quant_gemm_allowed(
22340 cfg!(memra_portable_cuda),
22341 cfg!(memra_hopper_mma),
22342 false
22343 ));
22344 assert!(super::portable_mma_gated() == false);
22345 }
22346}
22347
22348impl memra_kv::KvDev for Engine {
22351 fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22352 Engine::zeros(self, n)
22353 }
22354 fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22355 Engine::uninit(self, n)
22356 }
22357 fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
22358 Engine::alloc_u8(self, n)
22359 }
22360 fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
22361 Engine::htod_i32(self, v)
22362 }
22363 fn clone_dtod(
22364 &self,
22365 src: &CudaSlice<f32>,
22366 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22367 Engine::clone_dtod(self, src)
22368 }
22369 fn copy_into(
22370 &self,
22371 dst: &mut CudaSlice<f32>,
22372 off: usize,
22373 src: &CudaSlice<f32>,
22374 len: usize,
22375 ) -> Result<(), Box<dyn std::error::Error>> {
22376 Engine::copy_into(self, dst, off, src, len)
22377 }
22378 fn set_i32_one(
22379 &self,
22380 d: &mut CudaSlice<i32>,
22381 v: i32,
22382 ) -> Result<(), Box<dyn std::error::Error>> {
22383 Engine::set_i32_one(self, d, v)
22384 }
22385}
22386
22387#[cfg(test)]
22388mod fused_gate_bounds_tests {
22389 use super::*;
22390
22391 #[test]
22404 #[ignore = "requires a CUDA GPU"]
22405 fn q_gate_split_refuses_a_separate_gate_wq_instead_of_reading_past_it() {
22406 let e = Engine::new(0).unwrap();
22407 let (head_dim, n_head, t) = (8usize, 4usize, 2usize);
22408 let fused = 2 * head_dim * n_head * t;
22409 let out_n = head_dim * n_head * t;
22410
22411 let narrow = e.htod(&vec![1.0f32; out_n]).unwrap();
22413 let mut q = e.uninit(out_n).unwrap();
22414 let mut gate = e.uninit(out_n).unwrap();
22415 let err = e
22416 .q_gate_split(&narrow, &mut q, &mut gate, head_dim, n_head, t)
22417 .expect_err("half-width wq must be refused, not read past")
22418 .to_string();
22419 assert!(err.contains("NO fused gate"), "{err}");
22420 assert!(err.contains(&format!("{fused}")), "{err}");
22421
22422 let host: Vec<f32> = (0..fused).map(|i| i as f32).collect();
22425 let wide = e.htod(&host).unwrap();
22426 e.q_gate_split(&wide, &mut q, &mut gate, head_dim, n_head, t)
22427 .expect("full-width wq splits");
22428 let (qh, gh) = (e.dtoh(&q).unwrap(), e.dtoh(&gate).unwrap());
22429 for tok in 0..t {
22430 for hh in 0..n_head {
22431 for d in 0..head_dim {
22432 let base = tok * (n_head * 2 * head_dim) + hh * (2 * head_dim);
22433 let idx = tok * (n_head * head_dim) + hh * head_dim + d;
22434 assert_eq!(qh[idx], host[base + d], "q t{tok} h{hh} d{d}");
22435 assert_eq!(gh[idx], host[base + head_dim + d], "gate t{tok} h{hh} d{d}");
22436 }
22437 }
22438 }
22439
22440 let mut small = e.uninit(out_n - 1).unwrap();
22442 assert!(
22443 e.q_gate_split(&wide, &mut small, &mut gate, head_dim, n_head, t)
22444 .is_err()
22445 );
22446 }
22447}