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 cache {
34 pub use memra_kv::*;
35}
36pub mod decode;
37pub mod decode_batch;
38pub mod dflash;
39pub mod eagle;
40pub mod gemma_spec;
41pub mod graph_update;
42pub mod mla;
46pub mod moesd;
47pub mod pp;
48pub mod round_stream;
49pub mod spec;
50pub use memra_sampling as sampler;
51
52pub fn moe_f16g_mode() -> u8 {
96 static M: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
97 *M.get_or_init(|| match std::env::var("MEMRA_MOE_F16G").as_deref() {
98 Ok("0") => 0,
99 Ok("2") => 2,
100 Ok("3") => 3,
101 Ok(_) => 1,
102 Err(_) => 2,
105 })
106}
107pub fn moe_f16g_sk_params() -> (i32, i32) {
121 static P: std::sync::OnceLock<(i32, i32)> = std::sync::OnceLock::new();
122 *P.get_or_init(|| match std::env::var("MEMRA_F16G_SK").as_deref() {
123 Ok("0") => (-1, 0),
124 Ok("32") => (0, i32::MAX),
125 Ok("128") => (0, 1),
126 _ => {
127 let cross = std::env::var("MEMRA_F16G_SK_CROSS")
128 .ok()
129 .and_then(|v| v.parse().ok())
130 .unwrap_or(64);
131 (0, cross)
132 }
133 })
134}
135pub fn moe_f16g_direct_on(qtype: i32) -> bool {
146 static M: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
147 let m = *M.get_or_init(|| match std::env::var("MEMRA_F16G_DIRECT").as_deref() {
148 Ok("0") => 0,
149 Ok("kq") => 1,
150 _ => 2,
151 });
152 match m {
153 0 => false,
154 1 => qtype == QT_Q4_K || qtype == QT_Q6_K,
155 _ => true,
156 }
157}
158pub fn moe_f16g_tail_on() -> bool {
167 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
168 *ON.get_or_init(|| std::env::var("MEMRA_F16G_TAIL").as_deref() != Ok("0"))
169}
170
171pub fn moe_f16g_gemma_on() -> bool {
178 static M: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
179 *M.get_or_init(|| !matches!(std::env::var("MEMRA_MOE_F16G").as_deref(), Ok("0") | Err(_)))
180}
181
182pub fn moe_fuse_actq_on() -> bool {
186 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
187 *ON.get_or_init(|| std::env::var("MEMRA_MOE_FUSE_ACTQ").as_deref() != Ok("0"))
188}
189
190pub fn router_prefill_exact_on() -> bool {
200 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
201 *ON.get_or_init(|| std::env::var("MEMRA_ROUTER_PREFILL_EXACT").as_deref() != Ok("0"))
202}
203
204pub fn router_kernel_on() -> bool {
205 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
206 *ON.get_or_init(|| {
207 let on = std::env::var("MEMRA_ROUTER_KERNEL").as_deref() != Ok("0");
208 if !on {
209 eprintln!("[memra] router kernel OFF (rollback: per-column cuBLAS gemv)");
210 }
211 on
212 })
213}
214
215pub const ROUTER_BATCH_MIN_T: usize = 8;
230pub fn router_batch_on() -> bool {
231 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
232 *ON.get_or_init(|| std::env::var("MEMRA_ROUTER_BATCH").as_deref() != Ok("0"))
233}
234mod cpu_experts;
235#[cfg(memra_cutlass)]
236pub mod cutlass_ffi;
237pub mod f16_ffi;
238pub mod fp8_ffi;
239pub mod mmq_ffi;
240pub mod moe_cache;
241pub mod prime_graph;
242pub mod spill;
243mod spill_pread;
244
245const FATBIN: &[u8] = include_bytes!(env!("MEMRA_ENGINE_FATBIN"));
252const HYBRID_FATBIN: &[u8] = include_bytes!(env!("MEMRA_HYBRID_FATBIN"));
253const QMATVEC_FATBIN: &[u8] = include_bytes!(env!("MEMRA_QMATVEC_FATBIN"));
254const FLASH_FATBIN: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN"));
255const GEMM_FATBIN: &[u8] = include_bytes!(env!("MEMRA_GEMM_FATBIN"));
256const ROUTER_FATBIN: &[u8] = include_bytes!(env!("MEMRA_ROUTER_FATBIN"));
257const SAMPLE_FATBIN: &[u8] = include_bytes!(env!("MEMRA_SAMPLE_FATBIN"));
259
260fn gemm_fatbin_bytes() -> std::borrow::Cow<'static, [u8]> {
266 assert!(
267 !(portable_mma_gated() && std::env::var_os("MEMRA_GEMM_FATBIN").is_some()),
268 "MEMRA_GEMM_FATBIN overrides are not allowed in the portable CUDA lane"
269 );
270 match std::env::var("MEMRA_GEMM_FATBIN") {
271 Ok(path) => std::borrow::Cow::Owned(
272 std::fs::read(&path).unwrap_or_else(|e| panic!("MEMRA_GEMM_FATBIN read {path}: {e}")),
273 ),
274 Err(_) => std::borrow::Cow::Borrowed(GEMM_FATBIN),
275 }
276}
277
278pub(crate) const fn portable_mma_gated() -> bool {
285 cfg!(memra_portable_cuda) && !cfg!(memra_hopper_mma)
286}
287
288const fn legacy_quant_gemm_allowed(portable_cuda: bool, hopper_mma: bool, no_gemm: bool) -> bool {
293 (!portable_cuda || hopper_mma) && !no_gemm
294}
295
296const FLASH_FATBIN_VQ4: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_VQ4"));
304const FLASH_FATBIN_VF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_VF8"));
305const FLASH_FATBIN_KF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8"));
306const FLASH_FATBIN_KF8VQ4: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8VQ4"));
307const FLASH_FATBIN_KF8VF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8VF8"));
308
309pub use memra_kv::{kv_blk_bytes, kv_cache_formats};
312
313fn flash_fatbin_bytes() -> &'static [u8] {
315 match kv_cache_formats() {
316 ("q8_0", "q5_1") => FLASH_FATBIN,
317 ("q8_0", "q4_0") => FLASH_FATBIN_VQ4,
318 ("q8_0", "fp8") => FLASH_FATBIN_VF8,
319 ("fp8", "q5_1") => FLASH_FATBIN_KF8,
320 ("fp8", "q4_0") => FLASH_FATBIN_KF8VQ4,
321 ("fp8", "fp8") => FLASH_FATBIN_KF8VF8,
322 other => unreachable!("kv_cache_formats returned {other:?}"),
323 }
324}
325
326fn k1_launch_override() -> Option<(u32, u32, u32)> {
333 static K1: std::sync::OnceLock<Option<(u32, u32, u32)>> = std::sync::OnceLock::new();
334 *K1.get_or_init(|| {
335 let v = std::env::var("MEMRA_GEMM_K1_LAUNCH").ok()?;
336 let p: Vec<u32> = v.split(',').filter_map(|s| s.trim().parse().ok()).collect();
337 match p.as_slice() {
338 [bm, bn, w] => Some((*bm, *bn, *w)),
339 _ => None,
340 }
341 })
342}
343
344pub(crate) fn wgmma_gemm_enabled() -> bool {
351 static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
352 *V.get_or_init(|| std::env::var("MEMRA_WGMMA").as_deref() == Ok("1"))
353}
354
355pub const FA_VEC_MIN_TKV: usize = 96;
370pub fn fa_vec_min_tkv() -> usize {
374 static V: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
375 *V.get_or_init(|| {
376 std::env::var("MEMRA_FA_VEC_MIN")
377 .ok()
378 .and_then(|v| v.parse().ok())
379 .unwrap_or_else(|| FA_VEC_MIN_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
380 })
381}
382
383pub fn fa_f16pv_on() -> bool {
394 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
395 *ON.get_or_init(|| {
396 std::env::var("MEMRA_FA_F16PV")
397 .map(|v| v != "0")
398 .unwrap_or_else(|_| std::env::var("MEMRA_DRAFT").is_err())
399 })
400}
401
402pub fn fa512_hp_on() -> bool {
406 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
407 *ON.get_or_init(|| std::env::var("MEMRA_FA512_HP").as_deref() != Ok("0"))
408}
409
410pub fn faw_hp_on() -> bool {
414 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
415 *ON.get_or_init(|| std::env::var("MEMRA_FAW_HP").as_deref() != Ok("0"))
416}
417
418pub fn fa512_wide_warps() -> usize {
422 static N: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
423 *N.get_or_init(|| match std::env::var("MEMRA_FA512_W4").as_deref() {
424 Ok("1") => 4,
425 _ => 2,
426 })
427}
428
429pub fn fa512_min_tkv() -> usize {
432 static FA512_MIN: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
433 *FA512_MIN.get_or_init(|| {
434 std::env::var("MEMRA_FA512_MIN")
435 .ok()
436 .and_then(|v| v.parse().ok())
437 .unwrap_or(512)
438 })
439}
440pub static FA_VEC_MIN_DEFAULT: std::sync::atomic::AtomicUsize =
444 std::sync::atomic::AtomicUsize::new(FA_VEC_MIN_TKV);
445pub static FA_SPW_DEFAULT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(32);
449pub static FUSED_MR1_DEFAULT: std::sync::atomic::AtomicBool =
455 std::sync::atomic::AtomicBool::new(false);
456pub static ROUTER_W8_DEFAULT: std::sync::atomic::AtomicBool =
463 std::sync::atomic::AtomicBool::new(true);
464pub static FA_SP512_DEFAULT: std::sync::atomic::AtomicUsize =
465 std::sync::atomic::AtomicUsize::new(16);
466pub static RMS_BLOCK_DEFAULT: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(256);
471pub static FA_SP_GEMMA: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
473pub static MMQ_SK_FORCE: std::sync::atomic::AtomicI8 = std::sync::atomic::AtomicI8::new(-1);
478pub use memra_kv::KV_FP8_FORCE;
481pub(crate) fn rms_block() -> u32 {
482 static V: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
483 *V.get_or_init(|| {
484 std::env::var("MEMRA_RMS_BLOCK")
485 .ok()
486 .and_then(|v| v.parse().ok())
487 .unwrap_or_else(|| RMS_BLOCK_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
488 })
489}
490
491pub(crate) fn fa_split_keys(t_kv: usize, n_head_kv: usize) -> usize {
492 static S: std::sync::OnceLock<Option<usize>> = std::sync::OnceLock::new();
493 if let Some(forced) = *S.get_or_init(|| {
494 std::env::var("MEMRA_FA_SPLIT")
495 .ok()
496 .and_then(|v| v.parse().ok())
497 .filter(|&s: &usize| s >= 8 && s % 8 == 0)
498 }) {
499 return forced;
500 }
501 if FA_SP_GEMMA.load(std::sync::atomic::Ordering::Relaxed)
519 && std::env::var("MEMRA_FA_SP16").as_deref() == Ok("1")
520 {
521 return if t_kv <= 8192 {
522 16
523 } else if t_kv <= 16384 {
524 64
525 } else {
526 128
527 };
528 }
529 let big_rig = fa_sm_count() >= 128;
530 if big_rig {
531 let _ = n_head_kv;
532 if t_kv <= 2048 {
533 16
534 } else if t_kv <= 16384 {
535 64
536 } else {
537 128
538 }
539 } else if n_head_kv <= 4 {
540 if t_kv <= 512 {
561 8
562 } else if t_kv <= 16384 {
563 64
564 } else {
565 128
566 }
567 } else {
568 if t_kv <= 8192 {
569 32
570 } else if t_kv <= 16384 {
571 64
572 } else {
573 128
574 }
575 }
576}
577
578fn fa_sm_count() -> i32 {
581 static N: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
582 *N.get_or_init(|| {
583 cudarc::driver::result::init().ok();
584 cudarc::driver::result::device::get(0)
585 .and_then(|d| unsafe { cudarc::driver::result::device::get_attribute(
586 d, cudarc::driver::sys::CUdevice_attribute_enum::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT) })
587 .unwrap_or(82)
588 })
589}
590
591fn fa_hd_suffix(head_dim: usize) -> Result<&'static str, Box<dyn std::error::Error>> {
595 match head_dim {
596 256 => Ok(""),
597 128 => Ok("_hd128"),
598 d => Err(format!(
599 "fa_prefill: no kernel stamped for head_dim={d} (only 256/128); \
600 callers must gate to sdpa_naive"
601 )
602 .into()),
603 }
604}
605
606pub const QT_Q8_0: i32 = 0;
608pub const QT_Q4_K: i32 = 1;
609pub const QT_Q6_K: i32 = 2;
610pub const QT_Q5_K: i32 = 3;
611pub const QT_Q3_K: i32 = 4;
612pub const QT_IQ4_XS: i32 = 5;
613pub const QT_IQ3_S: i32 = 6;
614pub const QT_NVFP4: i32 = 7;
615pub const QT_F8_E4M3: i32 = 10;
621pub const QT_NVFP4_RP: i32 = 9;
624pub const QT_F32: i32 = 8;
626pub const QT_BF16: i32 = 11;
627pub const QT_Q4_0: i32 = 12; pub const QT_Q2_K: i32 = 13;
632pub const QT_F8_E4M3_BLK: i32 = 14;
648
649pub struct Engine {
651 pub gpu: memra_runtime::Gpu,
652 module: Arc<CudaModule>,
653 hybrid: Arc<CudaModule>,
654 qmatvec: Arc<CudaModule>,
655 flash: Arc<CudaModule>,
656 flash_g: std::sync::OnceLock<Arc<CudaModule>>,
660 gemm: Arc<CudaModule>,
661 router: Arc<CudaModule>,
662 sample: Arc<CudaModule>,
664 moe_cache: Mutex<Option<crate::moe_cache::MoeSlotCache>>,
668 moe_cache_layout: Mutex<Option<Vec<usize>>>,
672 capture_keep_on: std::sync::atomic::AtomicBool,
678 verify_exact: std::sync::atomic::AtomicBool,
683 capture_keep: Mutex<Vec<Box<dyn std::any::Any + Send>>>,
684 pub copy_stream: Arc<CudaStream>,
686 #[cfg(memra_cutlass)]
693 cutlass_scratch: Mutex<Option<crate::cutlass_ffi::CutlassScratch>>,
694 fp8_scratch: Mutex<Option<crate::fp8_ffi::Fp8Scratch>>,
698 fa_vf16_scratch: Mutex<Option<CudaSlice<u8>>>,
701 fa_part_pool: Mutex<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>>,
705 fa_part_retired: Mutex<Vec<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>>,
709 fn_cache: Mutex<std::collections::HashMap<String, CudaFunction>>,
711 f16_scratch: Mutex<Option<crate::f16_ffi::F16Scratch>>,
712 argmax_partials: Mutex<Option<(CudaSlice<f32>, CudaSlice<i32>)>>,
717 prime_deqw_ws: Mutex<Option<(CudaSlice<u8>, CudaSlice<u8>)>>,
722 router_stage: Mutex<Option<PinnedStage>>,
726}
727
728fn fa_v2_on() -> bool {
738 std::env::var("MEMRA_FA_V2")
744 .map(|v| v != "0")
745 .unwrap_or(true)
746}
747
748fn fa_v3_on() -> bool {
756 std::env::var("MEMRA_FA_V3")
760 .map(|v| v != "0")
761 .unwrap_or(true)
762}
763
764fn fa_v4_mode() -> &'static str {
769 static M: std::sync::OnceLock<String> = std::sync::OnceLock::new();
770 M.get_or_init(|| std::env::var("MEMRA_FA_V4").unwrap_or_default())
771}
772fn fa_v4_on() -> bool {
773 fa_v4_mode() != "0"
774} pub static FA_SMEM_TKV_DEFAULT: std::sync::atomic::AtomicUsize =
783 std::sync::atomic::AtomicUsize::new(1024);
784pub static FA_V4_MAX_DEFAULT: std::sync::atomic::AtomicUsize =
785 std::sync::atomic::AtomicUsize::new(usize::MAX);
786pub fn fa_v4_at_pub(t_kv: usize) -> bool {
787 fa_v4_at(t_kv)
788}
789fn fa_v4_at(t_kv: usize) -> bool {
790 static M: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
791 let mx = *M.get_or_init(|| {
792 std::env::var("MEMRA_FA_V4_MAX")
793 .ok()
794 .and_then(|v| v.parse().ok())
795 .unwrap_or_else(|| FA_V4_MAX_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
796 });
797 fa_v4_on() && t_kv < mx
798}
799pub const FA_DEEP_MIN_DEFAULT: usize = 0;
813fn fa_deep_at(t_kv: usize) -> bool {
814 if std::env::var("MEMRA_FA_DEEP").as_deref() == Ok("0") {
815 return false;
816 }
817 let min = std::env::var("MEMRA_FA_DEEP_MIN")
818 .ok()
819 .and_then(|v| v.parse().ok())
820 .unwrap_or(FA_DEEP_MIN_DEFAULT);
821 t_kv >= min
822}
823pub fn fa_deep_at_pub(t_kv: usize) -> bool {
825 fa_deep_at(t_kv)
826}
827
828fn fa_v3_active(head_dim: usize) -> bool {
829 fa_v3_on()
832 && head_dim % 128 == 0
833 && kv_cache_formats() == ("q8_0", "q5_1")
834 && !Engine::kv_fp8_on()
835}
836
837pub fn fa_seqs_eligible(t_kv: usize, head_dim: usize) -> bool {
845 std::env::var("MEMRA_NO_FA_VEC").is_err()
846 && t_kv >= fa_vec_min_tkv()
847 && head_dim == 256
848 && fa_v4_at(t_kv)
849 && !matches!(fa_v4_mode(), "noB3" | "stage")
850 && !Engine::kv_fp8_on()
851}
852pub fn fa_split_keys_pub(t_kv: usize, n_head_kv: usize) -> usize {
854 fa_split_keys(t_kv, n_head_kv)
855}
856
857struct PinnedStage {
862 ptr: *mut u8,
863 cap: usize,
864}
865unsafe impl Send for PinnedStage {}
866impl PinnedStage {
867 fn new(cap: usize) -> Result<Self, Box<dyn std::error::Error>> {
868 let ptr = unsafe { cudarc::driver::result::malloc_host(cap, 0)? } as *mut u8;
869 Ok(PinnedStage { ptr, cap })
870 }
871}
872impl Drop for PinnedStage {
873 fn drop(&mut self) {
874 let _ = unsafe { cudarc::driver::result::free_host(self.ptr as _) };
875 }
876}
877
878pub const ARGMAX_NB: usize = 256;
881
882pub(crate) use memra_fa3_vl as fa3_vl_raw;
884
885unsafe extern "C" {
886 fn memra_fa3_prefill(
888 q16: *const core::ffi::c_void,
889 k16: *const core::ffi::c_void,
890 v16: *const core::ffi::c_void,
891 o: *mut f32,
892 t: i32,
893 h: i32,
894 hkv: i32,
895 d: i32,
896 scale: f32,
897 stream: *mut core::ffi::c_void,
898 ) -> i32;
899 pub(crate) fn memra_fa3_vl(
901 q16s: *const *const core::ffi::c_void,
902 k16s: *const *const core::ffi::c_void,
903 v16s: *const *const core::ffi::c_void,
904 os: *const *mut f32,
905 ts: *const i32,
906 b: i32,
907 h: i32,
908 hkv: i32,
909 d: i32,
910 scale: f32,
911 stream: *mut core::ffi::c_void,
912 ) -> i32;
913}
914
915#[repr(C)]
920#[derive(Clone, Copy)]
921pub struct WPtr8(pub [u64; 8]);
922unsafe impl cudarc::driver::DeviceRepr for WPtr8 {}
923
924#[repr(C)]
929#[derive(Clone, Copy, Default)]
930pub struct GdnSeqVl {
931 pub kb16: u64,
932 pub gcum: u64,
933 pub beta: u64,
934 pub u: u64,
935 pub wb16: u64,
936 pub y: u64,
937 pub ssnap: u64,
938 pub state_in: u64,
939 pub state_out: u64,
940 pub q: u64,
941 pub p: u64,
942 pub o: u64,
943 pub k: u64,
944 pub v: u64,
945 pub g: u64,
946 pub a: u64,
947 pub w: u64,
948 pub t: i32,
949 pub nc: i32,
950}
951unsafe impl cudarc::driver::DeviceRepr for GdnSeqVl {}
952#[repr(C)]
953#[derive(Clone, Copy)]
954pub struct GdnVl8(pub [GdnSeqVl; 8]);
955unsafe impl cudarc::driver::DeviceRepr for GdnVl8 {}
956
957#[repr(C)]
960#[derive(Clone, Copy, Default)]
961pub struct GdnWVl {
962 pub qb16: u64,
963 pub pb16: u64,
964}
965unsafe impl cudarc::driver::DeviceRepr for GdnWVl {}
966#[repr(C)]
967#[derive(Clone, Copy)]
968pub struct GdnWVl8(pub [GdnWVl; 8]);
969unsafe impl cudarc::driver::DeviceRepr for GdnWVl8 {}
970
971#[repr(C)]
973#[derive(Clone, Copy, Default)]
974pub struct GdnPrepVl {
975 pub qkv: u64,
976 pub conv_state: u64,
977 pub conv_out: u64,
978 pub q_g: u64,
979 pub k_g: u64,
980 pub v_g: u64,
981 pub q_l2: u64,
982 pub k_l2: u64,
983 pub beta_raw: u64,
984 pub alpha: u64,
985 pub beta: u64,
986 pub g_log: u64,
987 pub o: u64,
988 pub z: u64,
989 pub gn: u64,
990 pub gn16: u64,
991 pub kb16: u64,
992 pub qb16: u64,
993 pub t: i32,
994 pub pad: i32,
995}
996unsafe impl cudarc::driver::DeviceRepr for GdnPrepVl {}
997#[repr(C)]
998#[derive(Clone, Copy)]
999pub struct GdnPrepVl8(pub [GdnPrepVl; 8]);
1000unsafe impl cudarc::driver::DeviceRepr for GdnPrepVl8 {}
1001
1002#[repr(C)]
1004#[derive(Clone, Copy, Default)]
1005pub struct FaSeqVl {
1006 pub q: u64,
1007 pub k16: u64,
1008 pub v16: u64,
1009 pub o: u64,
1010 pub kf: u64,
1011 pub vf: u64,
1012 pub t: i32,
1013 pub pad: i32,
1014}
1015unsafe impl cudarc::driver::DeviceRepr for FaSeqVl {}
1016#[repr(C)]
1017#[derive(Clone, Copy)]
1018pub struct FaVl8(pub [FaSeqVl; 8]);
1019unsafe impl cudarc::driver::DeviceRepr for FaVl8 {}
1020
1021#[repr(C)]
1023#[derive(Clone, Copy, Default)]
1024pub struct AttnPreVl {
1025 pub qf: u64,
1026 pub kf: u64,
1027 pub vf: u64,
1028 pub q: u64,
1029 pub gate: u64,
1030 pub qn: u64,
1031 pub kn: u64,
1032 pub kc: u64,
1033 pub vc: u64,
1034 pub t: i32,
1035 pub pad: i32,
1036}
1037unsafe impl cudarc::driver::DeviceRepr for AttnPreVl {}
1038#[repr(C)]
1039#[derive(Clone, Copy)]
1040pub struct AttnPreVl8(pub [AttnPreVl; 8]);
1041unsafe impl cudarc::driver::DeviceRepr for AttnPreVl8 {}
1042
1043pub struct GdnChunkBufs {
1046 pub gcum: CudaSlice<f32>,
1047 pub a: CudaSlice<f32>,
1048 pub p: CudaSlice<f32>,
1049 pub u: CudaSlice<f32>,
1050 pub w: CudaSlice<f32>,
1051 pub kb16: CudaSlice<u8>,
1052 pub wb16: CudaSlice<u8>,
1053 pub y16: CudaSlice<u8>,
1054 pub ssnap16: CudaSlice<u8>,
1055 pub qb16: CudaSlice<u8>,
1056 pub pb16: CudaSlice<u8>,
1057 pub o: CudaSlice<f32>,
1058 pub t: usize,
1059 pub nc: usize,
1060}
1061
1062#[repr(C)]
1064#[derive(Clone, Copy)]
1065pub struct F32x8(pub [f32; 8]);
1066unsafe impl cudarc::driver::DeviceRepr for F32x8 {}
1067
1068pub static PRIME_NANOS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1072
1073impl Engine {
1074 pub fn new(ordinal: usize) -> Result<Self, Box<dyn std::error::Error>> {
1075 let gpu = memra_runtime::Gpu::new(ordinal)?;
1076 if std::env::var("MEMRA_ARCH_CHECK").as_deref() != Ok("0") {
1080 use cudarc::driver::sys::CUdevice_attribute_enum as A;
1081 let (maj, min) = cudarc::driver::result::device::get(ordinal as i32)
1082 .and_then(|d| unsafe {
1083 Ok((
1084 cudarc::driver::result::device::get_attribute(
1085 d,
1086 A::CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR,
1087 )?,
1088 cudarc::driver::result::device::get_attribute(
1089 d,
1090 A::CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR,
1091 )?,
1092 ))
1093 })
1094 .unwrap_or((0, 0));
1095 let built = env!("MEMRA_BUILT_CUDA_ARCH");
1096 let ok = matches!(
1097 (built, maj, min),
1098 ("120a", 12, 0) | ("120a", 12, 1) | ("100a", 10, 0) | ("90a", 9, 0) | ("89", 8, 9)
1099 );
1100 if !ok {
1101 return Err(format!(
1102 "memra was built for sm_{built} but device {ordinal} reports compute \
1103 capability {maj}.{min}. Rebuild on this machine (MEMRA_CUDA_ARCH \
1104 auto-detects the GPU) or set MEMRA_ARCH_CHECK=0 to bypass."
1105 )
1106 .into());
1107 }
1108 }
1109 unsafe {
1114 use cudarc::driver::sys;
1115 let dev: sys::CUdevice = ordinal as sys::CUdevice;
1116 let mut pool: sys::CUmemoryPool = std::ptr::null_mut();
1117 if sys::cuDeviceGetDefaultMemPool(&mut pool, dev) == sys::CUresult::CUDA_SUCCESS {
1118 let mut thresh: u64 = u64::MAX;
1119 let _ = sys::cuMemPoolSetAttribute(
1120 pool,
1121 sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_RELEASE_THRESHOLD,
1122 &mut thresh as *mut u64 as *mut core::ffi::c_void,
1123 );
1124 }
1125 }
1126 let module = gpu.ctx.load_module(Ptx::from_binary(FATBIN.to_vec()))?;
1127 let hybrid = gpu
1128 .ctx
1129 .load_module(Ptx::from_binary(HYBRID_FATBIN.to_vec()))?;
1130 let qmatvec = gpu
1131 .ctx
1132 .load_module(Ptx::from_binary(QMATVEC_FATBIN.to_vec()))?;
1133 let flash = gpu
1134 .ctx
1135 .load_module(Ptx::from_binary(flash_fatbin_bytes().to_vec()))?;
1136 let gemm = gpu
1137 .ctx
1138 .load_module(Ptx::from_binary(gemm_fatbin_bytes().into_owned()))?;
1139 let router = gpu
1140 .ctx
1141 .load_module(Ptx::from_binary(ROUTER_FATBIN.to_vec()))?;
1142 let sample = gpu
1143 .ctx
1144 .load_module(Ptx::from_binary(SAMPLE_FATBIN.to_vec()))?;
1145 let copy_stream = gpu.ctx.new_stream()?;
1146 if std::env::var("MEMRA_EVT")
1162 .map(|v| v == "1")
1163 .unwrap_or(false)
1164 {
1165 } else {
1167 unsafe {
1168 gpu.ctx.disable_event_tracking();
1169 }
1170 }
1171 Ok(Self {
1172 gpu,
1173 module,
1174 hybrid,
1175 qmatvec,
1176 flash,
1177 flash_g: std::sync::OnceLock::new(),
1178 gemm,
1179 router,
1180 sample,
1181 moe_cache: Mutex::new(None),
1182 moe_cache_layout: Mutex::new(None),
1183 copy_stream,
1184 capture_keep_on: std::sync::atomic::AtomicBool::new(false),
1185 verify_exact: std::sync::atomic::AtomicBool::new(false),
1186 capture_keep: Mutex::new(Vec::new()),
1187 argmax_partials: Mutex::new(None),
1188 prime_deqw_ws: Mutex::new(None),
1189 router_stage: Mutex::new(None),
1190 fp8_scratch: Mutex::new(None),
1191 fa_vf16_scratch: Mutex::new(None),
1192 fa_part_pool: Mutex::new(None),
1193 fa_part_retired: Mutex::new(Vec::new()),
1194 fn_cache: Mutex::new(Default::default()),
1195 f16_scratch: Mutex::new(None),
1196 #[cfg(memra_cutlass)]
1197 cutlass_scratch: Mutex::new(None),
1198 })
1199 }
1200
1201 pub fn ctx(&self) -> &Arc<CudaContext> {
1202 &self.gpu.ctx
1203 }
1204
1205 pub fn pool_cached_bytes(&self) -> usize {
1223 let (reserved, used) = self.pool_reserved_used();
1224 reserved.saturating_sub(used)
1225 }
1226
1227 pub fn pool_reserved_used(&self) -> (usize, usize) {
1234 use cudarc::driver::sys;
1235 unsafe {
1236 let mut pool: sys::CUmemoryPool = std::ptr::null_mut();
1237 if sys::cuDeviceGetDefaultMemPool(&mut pool, self.gpu.ctx.ordinal() as sys::CUdevice)
1238 != sys::CUresult::CUDA_SUCCESS
1239 {
1240 return (0, 0);
1241 }
1242 let (mut reserved, mut used) = (0u64, 0u64);
1243 if sys::cuMemPoolGetAttribute(
1244 pool,
1245 sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_RESERVED_MEM_CURRENT,
1246 &mut reserved as *mut u64 as *mut core::ffi::c_void,
1247 ) != sys::CUresult::CUDA_SUCCESS
1248 {
1249 return (0, 0);
1250 }
1251 if sys::cuMemPoolGetAttribute(
1252 pool,
1253 sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_USED_MEM_CURRENT,
1254 &mut used as *mut u64 as *mut core::ffi::c_void,
1255 ) != sys::CUresult::CUDA_SUCCESS
1256 {
1257 return (0, 0);
1258 }
1259 (reserved as usize, used as usize)
1260 }
1261 }
1262
1263 pub fn stream(&self) -> Arc<CudaStream> {
1266 self.gpu.stream()
1267 }
1268 pub fn gkv_on() -> bool {
1271 memra_kv::gkv_on()
1272 }
1273
1274 pub fn wkv_on() -> bool {
1286 memra_kv::wkv_on()
1287 }
1288
1289 pub fn kv_fp8_on() -> bool {
1295 memra_kv::kv_fp8_on()
1296 }
1297
1298 fn fa_func(&self, name: &str, head_dim: usize) -> CudaFunction {
1301 if head_dim == 512 && Self::gkv_on() {
1302 self.func_g(name)
1303 } else {
1304 self.func(name)
1305 }
1306 }
1307
1308 fn func_g(&self, name: &str) -> CudaFunction {
1312 let m = self.flash_g.get_or_init(|| {
1313 self.gpu
1314 .ctx
1315 .load_module(cudarc::nvrtc::Ptx::from_binary(
1316 FLASH_FATBIN_KF8VF8.to_vec(),
1317 ))
1318 .expect("load kf8vf8 flash fatbin (fp8-globals arm)")
1319 });
1320 let key = format!("g:{name}");
1321 if let Some(f) = self.fn_cache.lock().unwrap().get(&key) {
1322 return f.clone();
1323 }
1324 let f = match m.load_function(name) {
1325 Ok(f) => f,
1326 Err(_) => self.func(name),
1327 };
1328 self.fn_cache.lock().unwrap().insert(key, f.clone());
1329 f
1330 }
1331
1332 fn func(&self, name: &str) -> CudaFunction {
1333 if let Some(f) = self.fn_cache.lock().unwrap().get(name) {
1336 return f.clone();
1337 }
1338 let f = self
1339 .module
1340 .load_function(name)
1341 .or_else(|_| self.hybrid.load_function(name))
1342 .or_else(|_| self.qmatvec.load_function(name))
1343 .or_else(|_| self.flash.load_function(name))
1344 .or_else(|_| self.gemm.load_function(name))
1345 .or_else(|_| self.router.load_function(name))
1346 .or_else(|_| self.sample.load_function(name))
1347 .unwrap_or_else(|_| panic!("kernel {name} not in any fatbin"));
1348 self.fn_cache
1349 .lock()
1350 .unwrap()
1351 .insert(name.to_string(), f.clone());
1352 f
1353 }
1354
1355 pub fn scatter_trim_logits(
1358 &self,
1359 src: &CudaSlice<f32>,
1360 d2t: &CudaSlice<u32>,
1361 dst: &mut CudaSlice<f32>,
1362 d_vocab: usize,
1363 n_vocab: usize,
1364 ) -> Result<(), Box<dyn std::error::Error>> {
1365 let f1 = self.func("scatter_trim_logits_f32");
1366 let f2 = self.func("scatter_trim_logits_pass2_f32");
1367 let (dv, nv) = (d_vocab as i32, n_vocab as i32);
1368 let cfg1 = LaunchConfig {
1369 grid_dim: (256, 1, 1),
1370 block_dim: (256, 1, 1),
1371 shared_mem_bytes: 0,
1372 };
1373 let __s_b1 = self.gpu.stream();
1374 let mut b1 = __s_b1.launch_builder(&f1);
1375 b1.arg(src).arg(d2t).arg(&mut *dst).arg(&dv).arg(&nv);
1376 unsafe {
1377 b1.launch(cfg1)?;
1378 }
1379 let cfg2 = LaunchConfig {
1380 grid_dim: (d_vocab.div_ceil(256) as u32, 1, 1),
1381 block_dim: (256, 1, 1),
1382 shared_mem_bytes: 0,
1383 };
1384 let __s_b2 = self.gpu.stream();
1385 let mut b2 = __s_b2.launch_builder(&f2);
1386 b2.arg(src).arg(d2t).arg(&mut *dst).arg(&dv);
1387 unsafe {
1388 b2.launch(cfg2)?;
1389 }
1390 Ok(())
1391 }
1392
1393 #[allow(clippy::too_many_arguments)]
1399 pub fn filter_stats(
1400 &self,
1401 x: &CudaSlice<f32>,
1402 row_stride: usize,
1403 rows: &CudaSlice<i32>,
1404 out_th: &mut CudaSlice<f32>,
1405 out_z: &mut CudaSlice<f32>,
1406 out_max: &mut CudaSlice<f32>,
1407 n: usize,
1408 nrow: usize,
1409 temp: f32,
1410 top_k: i32,
1411 top_p: f32,
1412 min_p: f32,
1413 ) -> Result<(), Box<dyn std::error::Error>> {
1414 let f = self.func("filter_stats_f32");
1415 let (ni, nr, rs) = (n as i32, nrow as i32, row_stride as i64);
1416 let cfg = LaunchConfig {
1417 grid_dim: (nrow as u32, 1, 1),
1418 block_dim: (1024, 1, 1),
1419 shared_mem_bytes: 0,
1420 };
1421 let __s_b = self.gpu.stream();
1422 let mut b = __s_b.launch_builder(&f);
1423 b.arg(x)
1424 .arg(&rs)
1425 .arg(rows)
1426 .arg(&mut *out_th)
1427 .arg(&mut *out_z)
1428 .arg(&mut *out_max)
1429 .arg(&ni)
1430 .arg(&nr)
1431 .arg(&temp)
1432 .arg(&top_k)
1433 .arg(&top_p)
1434 .arg(&min_p);
1435 unsafe {
1436 b.launch(cfg)?;
1437 }
1438 Ok(())
1439 }
1440
1441 #[allow(clippy::too_many_arguments)]
1443 pub fn softmax_gather_filtered(
1444 &self,
1445 x: &CudaSlice<f32>,
1446 row_stride: usize,
1447 ids: &CudaSlice<u32>,
1448 rows: &CudaSlice<i32>,
1449 th: &CudaSlice<f32>,
1450 z: &CudaSlice<f32>,
1451 out: &mut CudaSlice<f32>,
1452 n: usize,
1453 npair: usize,
1454 temp: f32,
1455 ) -> Result<(), Box<dyn std::error::Error>> {
1456 let f = self.func("softmax_gather_filtered_f32");
1457 let (ni, np, rs) = (n as i32, npair as i32, row_stride as i64);
1458 let cfg = LaunchConfig {
1459 grid_dim: (npair as u32, 1, 1),
1460 block_dim: (256, 1, 1),
1461 shared_mem_bytes: 0,
1462 };
1463 let __s_b = self.gpu.stream();
1464 let mut b = __s_b.launch_builder(&f);
1465 b.arg(x)
1466 .arg(&rs)
1467 .arg(ids)
1468 .arg(rows)
1469 .arg(th)
1470 .arg(z)
1471 .arg(&mut *out)
1472 .arg(&ni)
1473 .arg(&np)
1474 .arg(&temp);
1475 unsafe {
1476 b.launch(cfg)?;
1477 }
1478 Ok(())
1479 }
1480
1481 #[allow(clippy::too_many_arguments)]
1483 pub fn residual_sample_filtered(
1484 &self,
1485 p: &CudaSlice<f32>,
1486 q: Option<&CudaSlice<f32>>,
1487 n: usize,
1488 temp: f32,
1489 seed: u64,
1490 stream_pos: u32,
1491 p_stats: (f32, f32, f32),
1492 q_stats: (f32, f32, f32),
1493 out_tok: &mut CudaSlice<u32>,
1494 ) -> Result<(), Box<dyn std::error::Error>> {
1495 let f = self.func("residual_sample_filtered_f32");
1496 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
1497 let has_q: i32 = q.is_some() as i32;
1498 let qbuf = q.unwrap_or(p);
1499 let (pm, pth, pz) = p_stats;
1500 let (qm, qth, qz) = q_stats;
1501 let cfg = LaunchConfig {
1502 grid_dim: (1, 1, 1),
1503 block_dim: (1024, 1, 1),
1504 shared_mem_bytes: 0,
1505 };
1506 let __s_b = self.gpu.stream();
1507 let mut b = __s_b.launch_builder(&f);
1508 b.arg(p)
1509 .arg(qbuf)
1510 .arg(&has_q)
1511 .arg(&ni)
1512 .arg(&temp)
1513 .arg(&slo)
1514 .arg(&shi)
1515 .arg(&stream_pos)
1516 .arg(&pm)
1517 .arg(&pth)
1518 .arg(&pz)
1519 .arg(&qm)
1520 .arg(&qth)
1521 .arg(&qz)
1522 .arg(&mut *out_tok);
1523 unsafe {
1524 b.launch(cfg)?;
1525 }
1526 Ok(())
1527 }
1528
1529 #[allow(clippy::too_many_arguments)]
1531 pub fn gumbel_perturb_filtered(
1532 &self,
1533 x: &CudaSlice<f32>,
1534 y: &mut CudaSlice<f32>,
1535 n: usize,
1536 seed: u64,
1537 stream_pos: u32,
1538 temp: f32,
1539 row_max: f32,
1540 th: f32,
1541 ) -> Result<(), Box<dyn std::error::Error>> {
1542 let f = self.func("gumbel_perturb_filtered_f32");
1543 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
1544 let cfg = LaunchConfig {
1545 grid_dim: (n.div_ceil(256) as u32, 1, 1),
1546 block_dim: (256, 1, 1),
1547 shared_mem_bytes: 0,
1548 };
1549 let __s_b = self.gpu.stream();
1550 let mut b = __s_b.launch_builder(&f);
1551 b.arg(x)
1552 .arg(&mut *y)
1553 .arg(&ni)
1554 .arg(&slo)
1555 .arg(&shi)
1556 .arg(&stream_pos)
1557 .arg(&temp)
1558 .arg(&row_max)
1559 .arg(&th);
1560 unsafe {
1561 b.launch(cfg)?;
1562 }
1563 Ok(())
1564 }
1565
1566 #[allow(clippy::too_many_arguments)]
1570 pub fn penalize_logits(
1571 &self,
1572 x: &mut CudaSlice<f32>,
1573 hist: &CudaSlice<u32>,
1574 n_hist: usize,
1575 rep: f32,
1576 freq: f32,
1577 present: f32,
1578 n: usize,
1579 ) -> Result<(), Box<dyn std::error::Error>> {
1580 if n_hist == 0 {
1581 return Ok(());
1582 }
1583 let f = self.func("penalize_logits_f32");
1584 let (nh, ni) = (n_hist as i32, n as i32);
1585 let cfg = LaunchConfig {
1586 grid_dim: (n_hist.div_ceil(128) as u32, 1, 1),
1587 block_dim: (128, 1, 1),
1588 shared_mem_bytes: 0,
1589 };
1590 let __s_b = self.gpu.stream();
1591 let mut b = __s_b.launch_builder(&f);
1592 b.arg(&mut *x)
1593 .arg(hist)
1594 .arg(&nh)
1595 .arg(&rep)
1596 .arg(&freq)
1597 .arg(&present)
1598 .arg(&ni);
1599 unsafe {
1600 b.launch(cfg)?;
1601 }
1602 Ok(())
1603 }
1604
1605 #[allow(clippy::too_many_arguments)]
1607 pub fn penalize_logits_rows(
1608 &self,
1609 x: &mut CudaSlice<f32>,
1610 hist: &CudaSlice<u32>,
1611 n_hist: usize,
1612 rep: f32,
1613 freq: f32,
1614 present: f32,
1615 n: usize,
1616 nrow: usize,
1617 ) -> Result<(), Box<dyn std::error::Error>> {
1618 if n_hist == 0 || nrow == 0 {
1619 return Ok(());
1620 }
1621 let f = self.func("penalize_logits_rows_f32");
1622 let (nh, ni, nr) = (n_hist as i32, n as i32, nrow as i32);
1623 let cfg = LaunchConfig {
1624 grid_dim: (n_hist.div_ceil(128) as u32, nrow as u32, 1),
1625 block_dim: (128, 1, 1),
1626 shared_mem_bytes: 0,
1627 };
1628 let __s_b = self.gpu.stream();
1629 let mut b = __s_b.launch_builder(&f);
1630 b.arg(&mut *x)
1631 .arg(hist)
1632 .arg(&nh)
1633 .arg(&rep)
1634 .arg(&freq)
1635 .arg(&present)
1636 .arg(&ni)
1637 .arg(&nr);
1638 unsafe {
1639 b.launch(cfg)?;
1640 }
1641 Ok(())
1642 }
1643
1644 pub fn wpf_level() -> u32 {
1652 static ON: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
1653 *ON.get_or_init(|| {
1654 std::env::var("MEMRA_WPF")
1655 .ok()
1656 .and_then(|v| v.parse().ok())
1657 .unwrap_or(1)
1658 })
1659 }
1660
1661 pub fn set_verify_exact(&self, on: bool) {
1673 self.verify_exact
1674 .store(on, std::sync::atomic::Ordering::Relaxed);
1675 }
1676 pub(crate) fn verify_exact_on(&self) -> bool {
1677 self.verify_exact.load(std::sync::atomic::Ordering::Relaxed)
1678 }
1679
1680 pub fn qkv_append_on() -> bool {
1683 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1684 *ON.get_or_init(|| {
1685 std::env::var("MEMRA_QKV_APPEND")
1686 .map(|v| v != "0")
1687 .unwrap_or(true)
1688 })
1689 }
1690
1691 pub fn pdl_wb_on() -> bool {
1694 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1695 *ON.get_or_init(|| {
1696 std::env::var("MEMRA_PDL_WB")
1697 .map(|v| v != "0")
1698 .unwrap_or(true)
1699 })
1700 }
1701
1702 pub fn pdl_mmvq_on() -> bool {
1706 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1707 *ON.get_or_init(|| {
1708 std::env::var("MEMRA_PDL_MMVQ")
1709 .map(|v| v != "0")
1710 .unwrap_or(true)
1711 })
1712 }
1713
1714 pub fn pdl_on() -> bool {
1715 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1716 *ON.get_or_init(|| std::env::var("MEMRA_PDL").map(|v| v != "0").unwrap_or(true))
1717 }
1718
1719 fn q40_mr1_on() -> bool {
1725 static Q40MR: std::sync::OnceLock<Option<u32>> = std::sync::OnceLock::new();
1726 match *Q40MR.get_or_init(|| {
1727 std::env::var("MEMRA_Q40_MR")
1728 .ok()
1729 .and_then(|v| v.parse().ok())
1730 }) {
1731 Some(v) => v == 1,
1732 None => crate::FUSED_MR1_DEFAULT.load(std::sync::atomic::Ordering::Relaxed),
1733 }
1734 }
1735
1736 fn pdl_func_flash(
1741 &self,
1742 g: bool,
1743 name: &'static str,
1744 ) -> Result<cudarc::driver::sys::CUfunction, Box<dyn std::error::Error>> {
1745 use cudarc::driver::sys as cu;
1746 static MODS: std::sync::Mutex<Option<std::collections::HashMap<(usize, bool), usize>>> =
1753 std::sync::Mutex::new(None);
1754 static FNS: std::sync::Mutex<
1755 Option<std::collections::HashMap<(usize, bool, &'static str), usize>>,
1756 > = std::sync::Mutex::new(None);
1757 let ctx_key = self.ctx().cu_ctx() as usize;
1758 if let Some(&f) = FNS
1759 .lock()
1760 .unwrap()
1761 .get_or_insert_with(Default::default)
1762 .get(&(ctx_key, g, name))
1763 {
1764 return Ok(f as cu::CUfunction);
1765 }
1766 let module = {
1767 let mut mods = MODS.lock().unwrap();
1768 let map = mods.get_or_insert_with(Default::default);
1769 match map.get(&(ctx_key, g)) {
1770 Some(&m) => m,
1771 None => {
1772 let m = self.pdl_load_module_in_ctx(if g {
1773 FLASH_FATBIN_KF8VF8
1774 } else {
1775 FLASH_FATBIN
1776 })?;
1777 map.insert((ctx_key, g), m);
1778 m
1779 }
1780 }
1781 };
1782 let cname = std::ffi::CString::new(name)?;
1783 let mut f: cu::CUfunction = std::ptr::null_mut();
1784 let r = unsafe { cu::cuModuleGetFunction(&mut f, module as cu::CUmodule, cname.as_ptr()) };
1785 if r != cu::CUresult::CUDA_SUCCESS {
1786 return Err(format!("pdl_func_flash {name} (g={g}): {r:?}").into());
1787 }
1788 FNS.lock()
1789 .unwrap()
1790 .get_or_insert_with(Default::default)
1791 .insert((ctx_key, g, name), f as usize);
1792 Ok(f)
1793 }
1794
1795 fn pdl_load_module_in_ctx(&self, bytes: &[u8]) -> Result<usize, Box<dyn std::error::Error>> {
1800 use cudarc::driver::sys as cu;
1801 let mut prev: cu::CUcontext = std::ptr::null_mut();
1802 unsafe {
1803 cu::cuCtxGetCurrent(&mut prev).result()?;
1804 }
1805 self.ctx().bind_to_thread()?;
1806 let mut m: cu::CUmodule = std::ptr::null_mut();
1807 let r = unsafe { cu::cuModuleLoadData(&mut m, bytes.as_ptr() as *const std::ffi::c_void) };
1808 let restore = if prev.is_null() {
1809 cu::CUresult::CUDA_SUCCESS
1810 } else {
1811 unsafe { cu::cuCtxSetCurrent(prev) }
1812 };
1813 if r != cu::CUresult::CUDA_SUCCESS {
1814 return Err(format!("pdl module load: {r:?}").into());
1815 }
1816 if restore != cu::CUresult::CUDA_SUCCESS {
1817 return Err(format!("pdl module load: ctx restore {restore:?}").into());
1818 }
1819 Ok(m as usize)
1820 }
1821
1822 fn pdl_func(
1823 &self,
1824 name: &'static str,
1825 ) -> Result<cudarc::driver::sys::CUfunction, Box<dyn std::error::Error>> {
1826 use cudarc::driver::sys as cu;
1827 static MODULES: std::sync::Mutex<Option<std::collections::HashMap<usize, usize>>> =
1830 std::sync::Mutex::new(None);
1831 static QMODULES: std::sync::Mutex<Option<std::collections::HashMap<usize, usize>>> =
1834 std::sync::Mutex::new(None);
1835 static FNS: std::sync::Mutex<
1836 Option<std::collections::HashMap<(usize, &'static str), usize>>,
1837 > = std::sync::Mutex::new(None);
1838 let ctx_key = self.ctx().cu_ctx() as usize;
1839 if let Some(&f) = FNS
1840 .lock()
1841 .unwrap()
1842 .get_or_insert_with(Default::default)
1843 .get(&(ctx_key, name))
1844 {
1845 return Ok(f as cu::CUfunction);
1846 }
1847 let module = {
1848 let mut mods = MODULES.lock().unwrap();
1849 let map = mods.get_or_insert_with(Default::default);
1850 match map.get(&ctx_key) {
1851 Some(&m) => m,
1852 None => {
1853 let m = self.pdl_load_module_in_ctx(FATBIN)?;
1854 map.insert(ctx_key, m);
1855 m
1856 }
1857 }
1858 };
1859 let cname = std::ffi::CString::new(name)?;
1860 let mut f: cu::CUfunction = std::ptr::null_mut();
1861 let mut r =
1862 unsafe { cu::cuModuleGetFunction(&mut f, module as cu::CUmodule, cname.as_ptr()) };
1863 if r == cu::CUresult::CUDA_ERROR_NOT_FOUND {
1864 let qmodule = {
1865 let mut mods = QMODULES.lock().unwrap();
1866 let map = mods.get_or_insert_with(Default::default);
1867 match map.get(&ctx_key) {
1868 Some(&m) => m,
1869 None => {
1870 let m = self.pdl_load_module_in_ctx(QMATVEC_FATBIN)?;
1871 map.insert(ctx_key, m);
1872 m
1873 }
1874 }
1875 };
1876 r = unsafe { cu::cuModuleGetFunction(&mut f, qmodule as cu::CUmodule, cname.as_ptr()) };
1877 }
1878 if r != cu::CUresult::CUDA_SUCCESS {
1879 return Err(format!("pdl_func {name}: {r:?}").into());
1880 }
1881 FNS.lock()
1882 .unwrap()
1883 .get_or_insert_with(Default::default)
1884 .insert((ctx_key, name), f as usize);
1885 Ok(f)
1886 }
1887
1888 unsafe fn launch_pdl_flash(
1900 &self,
1901 g: bool,
1902 name: &'static str,
1903 grid: (u32, u32, u32),
1904 block: (u32, u32, u32),
1905 smem: u32,
1906 params: &mut [*mut std::ffi::c_void],
1907 ) -> Result<(), Box<dyn std::error::Error>> {
1908 use cudarc::driver::sys as cu;
1909 let f = self.pdl_func_flash(g, name)?;
1910 if smem > 0 {
1911 let r =
1913 unsafe {
1914 cu::cuFuncSetAttribute(f,
1915 cu::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
1916 smem as i32)
1917 };
1918 if r != cu::CUresult::CUDA_SUCCESS {
1919 return Err(format!("pdl smem attr {name}: {r:?}").into());
1920 }
1921 }
1922 let mut attr = cu::CUlaunchAttribute {
1923 id: cu::CUlaunchAttributeID::CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION,
1924 pad: [0; 4],
1925 value: cu::CUlaunchAttributeValue {
1926 programmaticStreamSerializationAllowed: 1,
1927 },
1928 };
1929 let cfg = cu::CUlaunchConfig {
1930 gridDimX: grid.0,
1931 gridDimY: grid.1,
1932 gridDimZ: grid.2,
1933 blockDimX: block.0,
1934 blockDimY: block.1,
1935 blockDimZ: block.2,
1936 sharedMemBytes: smem,
1937 hStream: self.gpu.stream().cu_stream(),
1938 attrs: &mut attr,
1939 numAttrs: 1,
1940 };
1941 let r = unsafe { cu::cuLaunchKernelEx(&cfg, f, params.as_mut_ptr(), std::ptr::null_mut()) };
1942 if r != cu::CUresult::CUDA_SUCCESS {
1943 return Err(format!("launch_pdl_flash {name}: {r:?}").into());
1944 }
1945 Ok(())
1946 }
1947
1948 unsafe fn launch_pdl(
1949 &self,
1950 name: &'static str,
1951 grid: (u32, u32, u32),
1952 block: (u32, u32, u32),
1953 params: &mut [*mut std::ffi::c_void],
1954 ) -> Result<(), Box<dyn std::error::Error>> {
1955 use cudarc::driver::sys as cu;
1956 let f = self.pdl_func(name)?;
1957 let mut attr = cu::CUlaunchAttribute {
1958 id: cu::CUlaunchAttributeID::CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION,
1959 pad: [0; 4],
1960 value: cu::CUlaunchAttributeValue {
1961 programmaticStreamSerializationAllowed: 1,
1962 },
1963 };
1964 let cfg = cu::CUlaunchConfig {
1965 gridDimX: grid.0,
1966 gridDimY: grid.1,
1967 gridDimZ: grid.2,
1968 blockDimX: block.0,
1969 blockDimY: block.1,
1970 blockDimZ: block.2,
1971 sharedMemBytes: 0,
1972 hStream: self.gpu.stream().cu_stream(),
1973 attrs: &mut attr,
1974 numAttrs: 1,
1975 };
1976 let r = unsafe { cu::cuLaunchKernelEx(&cfg, f, params.as_mut_ptr(), std::ptr::null_mut()) };
1977 if r != cu::CUresult::CUDA_SUCCESS {
1978 return Err(format!("launch_pdl {name}: {r:?}").into());
1979 }
1980 Ok(())
1981 }
1982
1983 pub fn prefetch_weight_l2(
1986 &self,
1987 w: &crate::model::GpuTensor,
1988 ) -> Result<(), Box<dyn std::error::Error>> {
1989 if let crate::model::GpuTensor::Quant { bytes, rp4, .. } = w {
1990 let p = rp4.as_ref().unwrap_or(bytes);
1991 self.prefetch_l2(p, p.len())?;
1992 }
1993 Ok(())
1994 }
1995
1996 pub fn gather_row_bf16(
1999 &self,
2000 table: &CudaSlice<u8>,
2001 tok: &CudaSlice<u32>,
2002 idx: usize,
2003 dst: &mut CudaSlice<f32>,
2004 ncols: usize,
2005 ) -> Result<(), Box<dyn std::error::Error>> {
2006 let f = self.func("gather_row_bf16_f32");
2007 let cfg = LaunchConfig {
2008 grid_dim: (ncols.div_ceil(256) as u32, 1, 1),
2009 block_dim: (256, 1, 1),
2010 shared_mem_bytes: 0,
2011 };
2012 let (nc, ix) = (ncols as i32, idx as i32);
2013 let __s_b = self.gpu.stream();
2014 let mut b = __s_b.launch_builder(&f);
2015 b.arg(table).arg(tok).arg(&ix).arg(dst).arg(&nc);
2016 unsafe {
2017 b.launch(cfg)?;
2018 }
2019 Ok(())
2020 }
2021
2022 pub fn add_row_inplace(
2024 &self,
2025 logits: &mut CudaSlice<f32>,
2026 bias: &CudaSlice<f32>,
2027 n: usize,
2028 row_off: usize,
2029 ) -> Result<(), Box<dyn std::error::Error>> {
2030 let f = self.func("add_row_inplace_f32");
2031 let cfg = LaunchConfig {
2032 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2033 block_dim: (256, 1, 1),
2034 shared_mem_bytes: 0,
2035 };
2036 let (ni, off) = (n as i32, row_off as i64);
2037 let __s_b = self.gpu.stream();
2038 let mut b = __s_b.launch_builder(&f);
2039 b.arg(logits).arg(bias).arg(&ni).arg(&off);
2040 unsafe {
2041 b.launch(cfg)?;
2042 }
2043 Ok(())
2044 }
2045
2046 pub fn prefetch_l2(
2048 &self,
2049 p: &CudaSlice<u8>,
2050 n: usize,
2051 ) -> Result<(), Box<dyn std::error::Error>> {
2052 let f = self.func("prefetch_l2_bytes");
2053 let lines = n.div_ceil(128);
2054 let ni = n as i64;
2055 let cfg = LaunchConfig {
2056 grid_dim: (lines.div_ceil(256) as u32, 1, 1),
2057 block_dim: (256, 1, 1),
2058 shared_mem_bytes: 0,
2059 };
2060 let __s_b = self.gpu.stream();
2061 let mut b = __s_b.launch_builder(&f);
2062 b.arg(p).arg(&ni);
2063 unsafe {
2064 b.launch(cfg)?;
2065 }
2066 Ok(())
2067 }
2068
2069 pub fn router_gemv(
2072 &self,
2073 w: &CudaSlice<f32>,
2074 x: &CudaSlice<f32>,
2075 n_embd: usize,
2076 n_experts: usize,
2077 t: usize,
2078 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2079 let w8 = match std::env::var("MEMRA_ROUTER_V2").as_deref() {
2085 Ok("0") => false,
2086 Ok(_) => true,
2087 Err(_) => ROUTER_W8_DEFAULT.load(std::sync::atomic::Ordering::Relaxed),
2088 };
2089 let batch = w8 && t >= ROUTER_BATCH_MIN_T && router_batch_on();
2099 self.router_gemv_form(w, x, n_embd, n_experts, t, w8, batch)
2100 }
2101
2102 pub fn router_gemv_form(
2105 &self,
2106 w: &CudaSlice<f32>,
2107 x: &CudaSlice<f32>,
2108 n_embd: usize,
2109 n_experts: usize,
2110 t: usize,
2111 w8: bool,
2112 batch: bool,
2113 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2114 debug_assert!(!batch || w8, "batch twin exists for the w8 form only");
2115 let mut y = self.alloc_uninit::<f32>(t * n_experts)?;
2116 let f = if batch {
2117 self.func("router_gemv_f32_w8_batch")
2118 } else if w8 {
2119 self.func("router_gemv_f32_w8")
2120 } else {
2121 self.func("router_gemv_f32")
2122 };
2123 let (ne, nx, ti) = (n_embd as i32, n_experts as i32, t as i32);
2124 let cfg = if batch {
2125 LaunchConfig {
2126 grid_dim: (n_experts.div_ceil(8) as u32, t.div_ceil(8) as u32, 1),
2127 block_dim: (32, 8, 1),
2128 shared_mem_bytes: 0,
2129 }
2130 } else {
2131 LaunchConfig {
2132 grid_dim: (n_experts as u32, t as u32, 1),
2133 block_dim: (32, if w8 { 8 } else { 1 }, 1),
2134 shared_mem_bytes: 0,
2135 }
2136 };
2137 let __s_b = self.gpu.stream();
2138 let mut b = __s_b.launch_builder(&f);
2139 b.arg(w).arg(x).arg(&mut y).arg(&ne).arg(&nx).arg(&ti);
2140 unsafe {
2141 b.launch(cfg)?;
2142 }
2143 Ok(y)
2144 }
2145
2146 pub fn rows_permute(
2148 &self,
2149 src: &CudaSlice<f32>,
2150 idx: &CudaSlice<i32>,
2151 nrows: usize,
2152 ncols: usize,
2153 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2154 let mut dst = self.alloc_uninit::<f32>(nrows * ncols)?;
2155 let f = self.func("rows_permute_f32");
2156 let (nc, nr) = (ncols as i32, nrows as i32);
2157 let cfg = LaunchConfig {
2158 grid_dim: (nrows as u32, 1, 1),
2159 block_dim: (256, 1, 1),
2160 shared_mem_bytes: 0,
2161 };
2162 let __s_b = self.gpu.stream();
2163 let mut b = __s_b.launch_builder(&f);
2164 b.arg(src).arg(idx).arg(&mut dst).arg(&nc).arg(&nr);
2165 unsafe {
2166 b.launch(cfg)?;
2167 }
2168 Ok(dst)
2169 }
2170
2171 pub fn sigmoid_dot_rows(
2176 &self,
2177 x: &CudaSlice<f32>,
2178 w: &CudaSlice<f32>,
2179 n_embd: usize,
2180 t: usize,
2181 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2182 static OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2185 if *OFF.get_or_init(|| std::env::var("MEMRA_SHEXP_DOT").as_deref() == Ok("0")) {
2186 let gs = self.linear(x, w, t, n_embd, 1)?;
2187 let mut g = self.uninit(t)?;
2188 self.sigmoid(&gs, &mut g, t)?;
2189 return Ok(g);
2190 }
2191 let mut g = self.alloc_uninit::<f32>(t)?;
2197 let f = self.func("sigmoid_dot_rows_f32");
2198 let (ne, ti) = (n_embd as i32, t as i32);
2199 let cfg = LaunchConfig {
2200 grid_dim: (t as u32, 1, 1),
2201 block_dim: (32, 8, 1),
2202 shared_mem_bytes: 0,
2203 };
2204 let __s_b = self.gpu.stream();
2205 let mut b = __s_b.launch_builder(&f);
2206 b.arg(x).arg(w).arg(&mut g).arg(&ne).arg(&ti);
2207 unsafe {
2208 b.launch(cfg)?;
2209 }
2210 Ok(g)
2211 }
2212
2213 pub fn spec_rollback_stream(
2215 &self,
2216 len_ptrs: &CudaSlice<u64>,
2217 pos_start: &CudaSlice<i32>,
2218 acc: &CudaSlice<u32>,
2219 base: usize,
2220 n_rows: usize,
2221 ) -> Result<(), Box<dyn std::error::Error>> {
2222 let f = self.func("spec_rollback_stream");
2223 let (b, nr) = (base as i32, n_rows as i32);
2224 let cfg = LaunchConfig {
2225 grid_dim: (n_rows.div_ceil(64) as u32, 1, 1),
2226 block_dim: (64, 1, 1),
2227 shared_mem_bytes: 0,
2228 };
2229 let __s_bl = self.gpu.stream();
2230 let mut bl = __s_bl.launch_builder(&f);
2231 bl.arg(len_ptrs).arg(pos_start).arg(acc).arg(&b).arg(&nr);
2232 unsafe {
2233 bl.launch(cfg)?;
2234 }
2235 Ok(())
2236 }
2237
2238 pub fn plain_tok_ring(
2240 &self,
2241 vam: &CudaSlice<u32>,
2242 pos_start: &CudaSlice<i32>,
2243 base: usize,
2244 ring: &mut CudaSlice<u32>,
2245 ) -> Result<(), Box<dyn std::error::Error>> {
2246 let f = self.func("plain_tok_ring");
2247 let (b, cap) = (base as i32, ring.len() as i32);
2248 let cfg = LaunchConfig {
2249 grid_dim: (1, 1, 1),
2250 block_dim: (32, 1, 1),
2251 shared_mem_bytes: 0,
2252 };
2253 let __s_bl = self.gpu.stream();
2254 let mut bl = __s_bl.launch_builder(&f);
2255 bl.arg(vam).arg(pos_start).arg(&b).arg(&mut *ring).arg(&cap);
2256 unsafe {
2257 bl.launch(cfg)?;
2258 }
2259 Ok(())
2260 }
2261
2262 pub fn spec_ring_commit(
2264 &self,
2265 vtok: &CudaSlice<u32>,
2266 acc: &CudaSlice<u32>,
2267 brk: &CudaSlice<u32>,
2268 ring: &mut CudaSlice<u32>,
2269 pend: &mut CudaSlice<u32>,
2270 ) -> Result<(), Box<dyn std::error::Error>> {
2271 let f = self.func("spec_ring_commit");
2272 let cfg = LaunchConfig {
2273 grid_dim: (1, 1, 1),
2274 block_dim: (32, 1, 1),
2275 shared_mem_bytes: 0,
2276 };
2277 let __s_b = self.gpu.stream();
2278 let mut b = __s_b.launch_builder(&f);
2279 b.arg(vtok).arg(acc).arg(brk).arg(ring).arg(pend);
2280 unsafe {
2281 b.launch(cfg)?;
2282 }
2283 Ok(())
2284 }
2285 pub fn i32_copy_add(
2286 &self,
2287 src: &CudaSlice<i32>,
2288 dst: &mut CudaSlice<i32>,
2289 delta: i32,
2290 ) -> Result<(), Box<dyn std::error::Error>> {
2291 let f = self.func("i32_copy_add");
2292 let cfg = LaunchConfig {
2293 grid_dim: (1, 1, 1),
2294 block_dim: (32, 1, 1),
2295 shared_mem_bytes: 0,
2296 };
2297 let __s_b = self.gpu.stream();
2298 let mut b = __s_b.launch_builder(&f);
2299 b.arg(src).arg(dst).arg(&delta);
2300 unsafe {
2301 b.launch(cfg)?;
2302 }
2303 Ok(())
2304 }
2305 pub fn u32_copy(
2306 &self,
2307 src: &CudaSlice<u32>,
2308 dst: &mut CudaSlice<u32>,
2309 ) -> Result<(), Box<dyn std::error::Error>> {
2310 let f = self.func("u32_copy");
2311 let cfg = LaunchConfig {
2312 grid_dim: (1, 1, 1),
2313 block_dim: (32, 1, 1),
2314 shared_mem_bytes: 0,
2315 };
2316 let __s_b = self.gpu.stream();
2317 let mut b = __s_b.launch_builder(&f);
2318 b.arg(src).arg(dst);
2319 unsafe {
2320 b.launch(cfg)?;
2321 }
2322 Ok(())
2323 }
2324
2325 pub fn spec_adapt_k(
2329 &self,
2330 acc: &CudaSlice<u32>,
2331 brk: &mut CudaSlice<u32>,
2332 floor: usize,
2333 cap: usize,
2334 ) -> Result<(), Box<dyn std::error::Error>> {
2335 let f = self.func("spec_adapt_k");
2336 let (fl, cp) = (floor as i32, cap as i32);
2337 let cfg = LaunchConfig {
2338 grid_dim: (1, 1, 1),
2339 block_dim: (32, 1, 1),
2340 shared_mem_bytes: 0,
2341 };
2342 let __s_b = self.gpu.stream();
2343 let mut b = __s_b.launch_builder(&f);
2344 b.arg(acc).arg(brk).arg(&fl).arg(&cp);
2345 unsafe {
2346 b.launch(cfg)?;
2347 }
2348 Ok(())
2349 }
2350
2351 pub fn spec_accept_greedy_dc(
2353 &self,
2354 preds: &CudaSlice<u32>,
2355 vtok: &CudaSlice<u32>,
2356 last_pred: &CudaSlice<u32>,
2357 brk: &CudaSlice<u32>,
2358 out: &mut CudaSlice<u32>,
2359 ) -> Result<(), Box<dyn std::error::Error>> {
2360 let f = self.func("spec_accept_greedy_dc");
2361 let cfg = LaunchConfig {
2362 grid_dim: (1, 1, 1),
2363 block_dim: (32, 1, 1),
2364 shared_mem_bytes: 0,
2365 };
2366 let __s_b = self.gpu.stream();
2367 let mut b = __s_b.launch_builder(&f);
2368 b.arg(preds).arg(vtok).arg(last_pred).arg(brk).arg(out);
2369 unsafe {
2370 b.launch(cfg)?;
2371 }
2372 Ok(())
2373 }
2374
2375 pub fn pos_iota(
2377 &self,
2378 pos0: &CudaSlice<i32>,
2379 out: &mut CudaSlice<i32>,
2380 t: usize,
2381 ) -> Result<(), Box<dyn std::error::Error>> {
2382 let f = self.func("pos_iota_i32");
2383 let ti = t as i32;
2384 let cfg = LaunchConfig {
2385 grid_dim: (1, 1, 1),
2386 block_dim: (t.max(1) as u32, 1, 1),
2387 shared_mem_bytes: 0,
2388 };
2389 let __s_b = self.gpu.stream();
2390 let mut b = __s_b.launch_builder(&f);
2391 b.arg(pos0).arg(out).arg(&ti);
2392 unsafe {
2393 b.launch(cfg)?;
2394 }
2395 Ok(())
2396 }
2397 #[allow(clippy::too_many_arguments)]
2398 pub fn append_kv_quantized_rows_dc(
2399 &self,
2400 k_rows: &CudaSlice<f32>,
2401 v_rows: &CudaSlice<f32>,
2402 kc: &mut CudaSlice<u8>,
2403 vc: &mut CudaSlice<u8>,
2404 t0_dev: &CudaSlice<i32>,
2405 t: usize,
2406 kv_dim_k: usize,
2407 kv_dim_v: usize,
2408 k_tok_bytes: usize,
2409 v_tok_bytes: usize,
2410 g: bool,
2411 ) -> Result<(), Box<dyn std::error::Error>> {
2412 let f = if g {
2413 self.func_g("append_quantize_kv_q8_0_q5_1_rows_dc")
2414 } else {
2415 self.func("append_quantize_kv_q8_0_q5_1_rows_dc")
2416 };
2417 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
2418 let cfg = LaunchConfig {
2419 grid_dim: (nblk, t as u32, 1),
2420 block_dim: (32, 1, 1),
2421 shared_mem_bytes: 0,
2422 };
2423 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
2424 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
2425 let __s_b = self.gpu.stream();
2426 let mut b = __s_b.launch_builder(&f);
2427 b.arg(k_rows)
2428 .arg(v_rows)
2429 .arg(kc)
2430 .arg(vc)
2431 .arg(t0_dev)
2432 .arg(&kdk)
2433 .arg(&kdv)
2434 .arg(&ktb)
2435 .arg(&vtb);
2436 unsafe {
2437 b.launch(cfg)?;
2438 }
2439 Ok(())
2440 }
2441
2442 #[allow(clippy::too_many_arguments)]
2445 pub fn append_kv_quantized_row_dc_inc(
2446 &self,
2447 k_row: &CudaSlice<f32>,
2448 v_row: &CudaSlice<f32>,
2449 kc: &mut CudaSlice<u8>,
2450 vc: &mut CudaSlice<u8>,
2451 t0_dev: &mut CudaSlice<i32>,
2452 kv_dim_k: usize,
2453 kv_dim_v: usize,
2454 k_tok_bytes: usize,
2455 v_tok_bytes: usize,
2456 g: bool,
2457 ) -> Result<(), Box<dyn std::error::Error>> {
2458 let f = if g {
2459 self.func_g("append_quantize_kv_q8_0_q5_1_dc_inc")
2460 } else {
2461 self.func("append_quantize_kv_q8_0_q5_1_dc_inc")
2462 };
2463 let nthreads = ((kv_dim_k.max(kv_dim_v) / 32) * 32).min(1024) as u32;
2464 let cfg = LaunchConfig {
2465 grid_dim: (1, 1, 1),
2466 block_dim: (nthreads, 1, 1),
2467 shared_mem_bytes: 0,
2468 };
2469 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
2470 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
2471 let __s_b = self.gpu.stream();
2472 let mut b = __s_b.launch_builder(&f);
2473 b.arg(k_row)
2474 .arg(v_row)
2475 .arg(kc)
2476 .arg(vc)
2477 .arg(t0_dev)
2478 .arg(&kdk)
2479 .arg(&kdv)
2480 .arg(&ktb)
2481 .arg(&vtb);
2482 unsafe {
2483 b.launch(cfg)?;
2484 }
2485 Ok(())
2486 }
2487
2488 pub fn pack_tok_p(
2490 &self,
2491 tok: &CudaSlice<u32>,
2492 p: &CudaSlice<f32>,
2493 out: &mut CudaSlice<u32>,
2494 slot: usize,
2495 ) -> Result<(), Box<dyn std::error::Error>> {
2496 let f = self.func("pack_tok_p");
2497 let sl = slot as i32;
2498 let cfg = LaunchConfig {
2499 grid_dim: (1, 1, 1),
2500 block_dim: (32, 1, 1),
2501 shared_mem_bytes: 0,
2502 };
2503 let __s_b = self.gpu.stream();
2504 let mut b = __s_b.launch_builder(&f);
2505 b.arg(tok).arg(p).arg(out).arg(&sl);
2506 unsafe {
2507 b.launch(cfg)?;
2508 }
2509 Ok(())
2510 }
2511 pub fn tok_map_u32(
2512 &self,
2513 tok: &mut CudaSlice<u32>,
2514 map: &CudaSlice<u32>,
2515 ) -> Result<(), Box<dyn std::error::Error>> {
2516 let f = self.func("tok_map_u32");
2517 let cfg = LaunchConfig {
2518 grid_dim: (1, 1, 1),
2519 block_dim: (32, 1, 1),
2520 shared_mem_bytes: 0,
2521 };
2522 let __s_b = self.gpu.stream();
2523 let mut b = __s_b.launch_builder(&f);
2524 b.arg(tok).arg(map);
2525 unsafe {
2526 b.launch(cfg)?;
2527 }
2528 Ok(())
2529 }
2530
2531 #[allow(clippy::too_many_arguments)]
2533 pub fn spec_assemble_verify(
2534 &self,
2535 tokp: &CudaSlice<u32>,
2536 pend: &CudaSlice<u32>,
2537 d2t: Option<&CudaSlice<u32>>,
2538 vtok: &mut CudaSlice<u32>,
2539 brk: &mut CudaSlice<u32>,
2540 p_min: f32,
2541 k: usize,
2542 pmin0: bool,
2543 ) -> Result<(), Box<dyn std::error::Error>> {
2544 let f = self.func("spec_assemble_verify");
2545 let (ki, pm) = (k as i32, if pmin0 { 1i32 } else { 0i32 });
2546 let cfg = LaunchConfig {
2547 grid_dim: (1, 1, 1),
2548 block_dim: (32, 1, 1),
2549 shared_mem_bytes: 0,
2550 };
2551 let __s_b = self.gpu.stream();
2552 let mut b = __s_b.launch_builder(&f);
2553 match d2t {
2554 Some(m) => {
2555 b.arg(tokp)
2556 .arg(pend)
2557 .arg(m)
2558 .arg(vtok)
2559 .arg(brk)
2560 .arg(&p_min)
2561 .arg(&ki)
2562 .arg(&pm);
2563 unsafe {
2564 b.launch(cfg)?;
2565 }
2566 }
2567 None => {
2568 let null: u64 = 0;
2569 b.arg(tokp)
2570 .arg(pend)
2571 .arg(&null)
2572 .arg(vtok)
2573 .arg(brk)
2574 .arg(&p_min)
2575 .arg(&ki)
2576 .arg(&pm);
2577 unsafe {
2578 b.launch(cfg)?;
2579 }
2580 }
2581 }
2582 Ok(())
2583 }
2584
2585 #[allow(clippy::too_many_arguments)]
2587 pub fn ssm_conv_ring_rebuild_dc(
2588 &self,
2589 qkv_tm: &CudaSlice<f32>,
2590 ring_old: &CudaSlice<f32>,
2591 conv_state: &mut CudaSlice<f32>,
2592 conv_dim: usize,
2593 acc: &CudaSlice<u32>,
2594 base: usize,
2595 t_v: usize,
2596 d_conv: usize,
2597 ) -> Result<(), Box<dyn std::error::Error>> {
2598 let f = self.func("ssm_conv_ring_rebuild_f32_dc");
2599 let n = conv_dim * (d_conv - 1);
2600 let cfg = LaunchConfig::for_num_elems(n as u32);
2601 let (cd, b0, tv, dc) = (conv_dim as i32, base as i32, t_v as i32, d_conv as i32);
2602 let __s_b = self.gpu.stream();
2603 let mut b = __s_b.launch_builder(&f);
2604 b.arg(qkv_tm)
2605 .arg(ring_old)
2606 .arg(conv_state)
2607 .arg(&cd)
2608 .arg(acc)
2609 .arg(&b0)
2610 .arg(&tv)
2611 .arg(&dc);
2612 unsafe {
2613 b.launch(cfg)?;
2614 }
2615 Ok(())
2616 }
2617 #[allow(clippy::too_many_arguments)]
2618 pub fn gdn_scan_s128_dc(
2619 &self,
2620 q: &CudaSlice<f32>,
2621 k: &CudaSlice<f32>,
2622 v: &CudaSlice<f32>,
2623 g: &CudaSlice<f32>,
2624 beta: &CudaSlice<f32>,
2625 state_in: &CudaSlice<f32>,
2626 state_out: &mut CudaSlice<f32>,
2627 o: &mut CudaSlice<f32>,
2628 n_head: usize,
2629 acc: &CudaSlice<u32>,
2630 base: usize,
2631 t_v: usize,
2632 scale: f32,
2633 ) -> Result<(), Box<dyn std::error::Error>> {
2634 let f = self.func("gdn_scan_s128_dc");
2635 const S_V: u32 = 128;
2636 const WARP: u32 = 32;
2637 const COLS_PER_BLOCK: u32 = 4;
2638 let cfg = LaunchConfig {
2639 grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
2640 block_dim: (WARP, COLS_PER_BLOCK, 1),
2641 shared_mem_bytes: 0,
2642 };
2643 let (h, b0, tv) = (n_head as i32, base as i32, t_v as i32);
2644 let __s_b = self.gpu.stream();
2645 let mut b = __s_b.launch_builder(&f);
2646 b.arg(q)
2647 .arg(k)
2648 .arg(v)
2649 .arg(g)
2650 .arg(beta)
2651 .arg(state_in)
2652 .arg(state_out)
2653 .arg(o)
2654 .arg(&h)
2655 .arg(acc)
2656 .arg(&b0)
2657 .arg(&tv)
2658 .arg(&scale);
2659 unsafe {
2660 b.launch(cfg)?;
2661 }
2662 Ok(())
2663 }
2664
2665 pub fn spec_rollback_kv(
2667 &self,
2668 len_ptrs: &CudaSlice<u64>,
2669 saved: &CudaSlice<i32>,
2670 acc: &CudaSlice<u32>,
2671 base: usize,
2672 n_layer: usize,
2673 ) -> Result<(), Box<dyn std::error::Error>> {
2674 let f = self.func("spec_rollback_kv");
2675 let (b, nl) = (base as i32, n_layer as i32);
2676 let cfg = LaunchConfig {
2677 grid_dim: (n_layer.div_ceil(64) as u32, 1, 1),
2678 block_dim: (64, 1, 1),
2679 shared_mem_bytes: 0,
2680 };
2681 let __s_bl = self.gpu.stream();
2682 let mut bl = __s_bl.launch_builder(&f);
2683 bl.arg(len_ptrs).arg(saved).arg(acc).arg(&b).arg(&nl);
2684 unsafe {
2685 bl.launch(cfg)?;
2686 }
2687 Ok(())
2688 }
2689
2690 pub fn spec_fork_valid(
2692 &self,
2693 acc: &CudaSlice<u32>,
2694 optimistic_pending: u32,
2695 valid: &mut CudaSlice<u32>,
2696 ) -> Result<(), Box<dyn std::error::Error>> {
2697 let f = self.func("spec_fork_valid");
2698 let cfg = LaunchConfig {
2699 grid_dim: (1, 1, 1),
2700 block_dim: (1, 1, 1),
2701 shared_mem_bytes: 0,
2702 };
2703 let __s_bl = self.gpu.stream();
2704 let mut bl = __s_bl.launch_builder(&f);
2705 bl.arg(acc).arg(&optimistic_pending).arg(valid);
2706 unsafe {
2707 bl.launch(cfg)?;
2708 }
2709 Ok(())
2710 }
2711
2712 pub fn spec_fork_reconcile_kv(
2714 &self,
2715 len_ptrs: &CudaSlice<u64>,
2716 saved: &CudaSlice<i32>,
2717 acc: &CudaSlice<u32>,
2718 valid: &CudaSlice<u32>,
2719 base: usize,
2720 n_layer: usize,
2721 ) -> Result<(), Box<dyn std::error::Error>> {
2722 let f = self.func("spec_fork_reconcile_kv");
2723 let (b, nl) = (base as i32, n_layer as i32);
2724 let cfg = LaunchConfig {
2725 grid_dim: (n_layer.div_ceil(64) as u32, 1, 1),
2726 block_dim: (64, 1, 1),
2727 shared_mem_bytes: 0,
2728 };
2729 let __s_bl = self.gpu.stream();
2730 let mut bl = __s_bl.launch_builder(&f);
2731 bl.arg(len_ptrs)
2732 .arg(saved)
2733 .arg(acc)
2734 .arg(valid)
2735 .arg(&b)
2736 .arg(&nl);
2737 unsafe {
2738 bl.launch(cfg)?;
2739 }
2740 Ok(())
2741 }
2742
2743 pub fn spec_fork_restore_f32(
2745 &self,
2746 snapshot: &CudaSlice<f32>,
2747 state: &mut CudaSlice<f32>,
2748 valid: &CudaSlice<u32>,
2749 ) -> Result<(), Box<dyn std::error::Error>> {
2750 assert_eq!(
2751 snapshot.len(),
2752 state.len(),
2753 "fork recurrent snapshot shape mismatch"
2754 );
2755 let f = self.func("spec_fork_restore_f32");
2756 let n = state.len() as i32;
2757 let blocks = state.len().div_ceil(256).min(65535).max(1) as u32;
2758 let cfg = LaunchConfig {
2759 grid_dim: (blocks, 1, 1),
2760 block_dim: (256, 1, 1),
2761 shared_mem_bytes: 0,
2762 };
2763 let __s_bl = self.gpu.stream();
2764 let mut bl = __s_bl.launch_builder(&f);
2765 bl.arg(snapshot).arg(state).arg(valid).arg(&n);
2766 unsafe {
2767 bl.launch(cfg)?;
2768 }
2769 Ok(())
2770 }
2771
2772 pub fn spec_seed_gather(
2775 &self,
2776 vx: &CudaSlice<f32>,
2777 fill_prev: &CudaSlice<f32>,
2778 acc: &CudaSlice<u32>,
2779 h_seed: &mut CudaSlice<f32>,
2780 base: usize,
2781 n_embd: usize,
2782 ) -> Result<(), Box<dyn std::error::Error>> {
2783 let f = self.func("spec_seed_gather");
2784 let (b, ne) = (base as i32, n_embd as i32);
2785 let cfg = LaunchConfig {
2786 grid_dim: (n_embd.div_ceil(256) as u32, 1, 1),
2787 block_dim: (256, 1, 1),
2788 shared_mem_bytes: 0,
2789 };
2790 let __s_bl = self.gpu.stream();
2791 let mut bl = __s_bl.launch_builder(&f);
2792 bl.arg(vx)
2793 .arg(fill_prev)
2794 .arg(acc)
2795 .arg(h_seed)
2796 .arg(&b)
2797 .arg(&ne);
2798 unsafe {
2799 bl.launch(cfg)?;
2800 }
2801 Ok(())
2802 }
2803
2804 pub fn spec_accept_greedy(
2806 &self,
2807 preds: &CudaSlice<u32>,
2808 draft: &CudaSlice<u32>,
2809 last_pred: u32,
2810 base: usize,
2811 k_round: usize,
2812 out: &mut CudaSlice<u32>,
2813 ) -> Result<(), Box<dyn std::error::Error>> {
2814 let f = self.func("spec_accept_greedy");
2815 let (b, k) = (base as i32, k_round as i32);
2816 let cfg = LaunchConfig {
2817 grid_dim: (1, 1, 1),
2818 block_dim: (32, 1, 1),
2819 shared_mem_bytes: 0,
2820 };
2821 let __s_bl = self.gpu.stream();
2822 let mut bl = __s_bl.launch_builder(&f);
2823 bl.arg(preds)
2824 .arg(draft)
2825 .arg(&last_pred)
2826 .arg(&b)
2827 .arg(&k)
2828 .arg(out);
2829 unsafe {
2830 bl.launch(cfg)?;
2831 }
2832 Ok(())
2833 }
2834
2835 pub fn gumbel_perturb(
2842 &self,
2843 x: &CudaSlice<f32>,
2844 y: &mut CudaSlice<f32>,
2845 n: usize,
2846 seed: u64,
2847 stream_pos: u32,
2848 temp: f32,
2849 ) -> Result<(), Box<dyn std::error::Error>> {
2850 let f = self.func("gumbel_perturb_f32");
2851 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2852 let cfg = LaunchConfig {
2853 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2854 block_dim: (256, 1, 1),
2855 shared_mem_bytes: 0,
2856 };
2857 let __s_b = self.gpu.stream();
2858 let mut b = __s_b.launch_builder(&f);
2859 b.arg(x)
2860 .arg(&mut *y)
2861 .arg(&ni)
2862 .arg(&slo)
2863 .arg(&shi)
2864 .arg(&stream_pos)
2865 .arg(&temp);
2866 unsafe {
2867 b.launch(cfg)?;
2868 }
2869 Ok(())
2870 }
2871
2872 pub fn mask_logits_col(
2880 &self,
2881 logits: &mut CudaSlice<f32>,
2882 mask: &CudaSlice<u32>,
2883 col: usize,
2884 n: usize,
2885 mask_words: usize,
2886 ) -> Result<(), Box<dyn std::error::Error>> {
2887 let f = self.func("mask_logits_f32");
2888 let (ci, ni, mw) = (col as i32, n as i32, mask_words as i32);
2889 let cfg = LaunchConfig {
2890 grid_dim: (n.div_ceil(256).min(1024) as u32, 1, 1),
2891 block_dim: (256, 1, 1),
2892 shared_mem_bytes: 0,
2893 };
2894 let __s_b = self.gpu.stream();
2895 let mut b = __s_b.launch_builder(&f);
2896 b.arg(&mut *logits).arg(mask).arg(&ci).arg(&ni).arg(&mw);
2897 unsafe {
2898 b.launch(cfg)?;
2899 }
2900 Ok(())
2901 }
2902
2903 pub fn gumbel_perturb_col(
2910 &self,
2911 x: &CudaSlice<f32>,
2912 col: usize,
2913 y: &mut CudaSlice<f32>,
2914 n: usize,
2915 seed: u64,
2916 stream_pos: u32,
2917 temp: f32,
2918 ) -> Result<(), Box<dyn std::error::Error>> {
2919 let f = self.func("gumbel_perturb_f32");
2920 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2921 let col_view = x.slice(col * n..(col + 1) * n);
2922 let cfg = LaunchConfig {
2923 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2924 block_dim: (256, 1, 1),
2925 shared_mem_bytes: 0,
2926 };
2927 let __s_b = self.gpu.stream();
2928 let mut b = __s_b.launch_builder(&f);
2929 b.arg(&col_view)
2930 .arg(&mut *y)
2931 .arg(&ni)
2932 .arg(&slo)
2933 .arg(&shi)
2934 .arg(&stream_pos)
2935 .arg(&temp);
2936 unsafe {
2937 b.launch(cfg)?;
2938 }
2939 Ok(())
2940 }
2941
2942 pub fn sctr_inc(&self, ctr: &mut CudaSlice<u32>) -> Result<(), Box<dyn std::error::Error>> {
2947 let f = self.func("memra_sctr_inc");
2948 let cfg = LaunchConfig {
2949 grid_dim: (1, 1, 1),
2950 block_dim: (1, 1, 1),
2951 shared_mem_bytes: 0,
2952 };
2953 let __s_b = self.gpu.stream();
2954 let mut b = __s_b.launch_builder(&f);
2955 b.arg(&mut *ctr);
2956 unsafe {
2957 b.launch(cfg)?;
2958 }
2959 Ok(())
2960 }
2961
2962 pub fn gumbel_perturb_ctr(
2967 &self,
2968 x: &CudaSlice<f32>,
2969 y: &mut CudaSlice<f32>,
2970 n: usize,
2971 seed: u64,
2972 ctr: &CudaSlice<u32>,
2973 temp: f32,
2974 ) -> Result<(), Box<dyn std::error::Error>> {
2975 let f = self.func("gumbel_perturb_ctr_f32");
2976 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2977 let cfg = LaunchConfig {
2978 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2979 block_dim: (256, 1, 1),
2980 shared_mem_bytes: 0,
2981 };
2982 let __s_b = self.gpu.stream();
2983 let mut b = __s_b.launch_builder(&f);
2984 b.arg(x)
2985 .arg(&mut *y)
2986 .arg(&ni)
2987 .arg(&slo)
2988 .arg(&shi)
2989 .arg(ctr)
2990 .arg(&temp);
2991 unsafe {
2992 b.launch(cfg)?;
2993 }
2994 Ok(())
2995 }
2996
2997 pub fn softmax_gather(
3001 &self,
3002 x: &CudaSlice<f32>,
3003 row_stride: usize,
3004 ids: &CudaSlice<u32>,
3005 rows: &CudaSlice<i32>,
3006 out: &mut CudaSlice<f32>,
3007 n: usize,
3008 npair: usize,
3009 temp: f32,
3010 ) -> Result<(), Box<dyn std::error::Error>> {
3011 let f = self.func("softmax_gather_f32");
3012 let (ni, rs) = (n as i32, row_stride as i64);
3013 let np = npair as i32;
3014 let cfg = LaunchConfig {
3015 grid_dim: (npair as u32, 1, 1),
3016 block_dim: (256, 1, 1),
3017 shared_mem_bytes: 0,
3018 };
3019 let __s_b = self.gpu.stream();
3020 let mut b = __s_b.launch_builder(&f);
3021 b.arg(x)
3022 .arg(&rs)
3023 .arg(ids)
3024 .arg(rows)
3025 .arg(&mut *out)
3026 .arg(&ni)
3027 .arg(&np)
3028 .arg(&temp);
3029 unsafe {
3030 b.launch(cfg)?;
3031 }
3032 Ok(())
3033 }
3034
3035 pub fn residual_sample(
3039 &self,
3040 p: &CudaSlice<f32>,
3041 q: Option<&CudaSlice<f32>>,
3042 n: usize,
3043 temp: f32,
3044 seed: u64,
3045 stream_pos: u32,
3046 out_tok: &mut CudaSlice<u32>,
3047 ) -> Result<(), Box<dyn std::error::Error>> {
3048 let f = self.func("residual_sample_f32");
3049 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3050 let nth = 1024u32;
3051 let cfg = LaunchConfig {
3052 grid_dim: (1, 1, 1),
3053 block_dim: (nth, 1, 1),
3054 shared_mem_bytes: 0,
3055 };
3056 let has_q: i32 = q.is_some() as i32;
3057 let qbuf = q.unwrap_or(p); let __s_b = self.gpu.stream();
3059 let mut b = __s_b.launch_builder(&f);
3060 b.arg(p)
3061 .arg(qbuf)
3062 .arg(&has_q)
3063 .arg(&ni)
3064 .arg(&temp)
3065 .arg(&slo)
3066 .arg(&shi)
3067 .arg(&stream_pos)
3068 .arg(&mut *out_tok);
3069 unsafe {
3070 b.launch(cfg)?;
3071 }
3072 Ok(())
3073 }
3074
3075 pub fn with_moe_cache<R>(
3080 &self,
3081 max_block_bytes: usize,
3082 f: impl FnOnce(
3083 &mut crate::moe_cache::MoeSlotCache,
3084 &Engine,
3085 ) -> Result<R, Box<dyn std::error::Error>>,
3086 ) -> Result<R, Box<dyn std::error::Error>> {
3087 let mut guard = self.moe_cache.lock().unwrap();
3088 if guard.is_none() {
3089 *guard = Some(crate::moe_cache::MoeSlotCache::new(self, max_block_bytes)?);
3090 }
3091 let cache = guard.as_mut().unwrap();
3092 f(cache, self)
3093 }
3094
3095 pub fn freeze_moe_cache(&self) {
3098 if let Some(cache) = self.moe_cache.lock().unwrap().as_mut() {
3099 cache.freeze();
3100 }
3101 }
3102
3103 pub fn export_moe_residency(&self) -> Option<Vec<(u16, u8, u16)>> {
3106 self.moe_cache
3107 .lock()
3108 .unwrap()
3109 .as_ref()
3110 .map(crate::moe_cache::MoeSlotCache::export_residency)
3111 }
3112
3113 pub(crate) fn moe_cache_frozen(&self) -> bool {
3114 self.moe_cache
3115 .lock()
3116 .unwrap()
3117 .as_ref()
3118 .is_some_and(crate::moe_cache::MoeSlotCache::is_frozen)
3119 }
3120
3121 pub fn frozen_cpu_experts_prefer_tokenwise_prime(&self) -> bool {
3128 crate::cpu_experts::configured()
3129 && self.moe_cache_frozen()
3130 && std::env::var("MEMRA_CPU_EXPERT_BATCHED_PRIME").as_deref() != Ok("1")
3131 }
3132
3133 pub(crate) fn configure_moe_cache_layout(&self, block_bytes: Vec<usize>) {
3135 assert!(
3136 self.moe_cache.lock().unwrap().is_none(),
3137 "MoE cache layout configured after cache construction"
3138 );
3139 *self.moe_cache_layout.lock().unwrap() = Some(block_bytes);
3140 }
3141
3142 pub(crate) fn moe_cache_layout(&self) -> Option<Vec<usize>> {
3143 self.moe_cache_layout.lock().unwrap().clone()
3144 }
3145
3146 pub fn moe_cache_enabled() -> bool {
3148 std::env::var("MEMRA_MOE_CACHE").as_deref() != Ok("0")
3149 }
3150
3151 pub fn moe_cache_stats(&self) -> Option<(u64, u64, u64, usize)> {
3154 let guard = self.moe_cache.lock().unwrap();
3155 guard
3156 .as_ref()
3157 .map(|c| (c.hits, c.misses, c.staged_bytes, c.n_slots()))
3158 }
3159
3160 pub fn cpu_expert_stats(
3164 &self,
3165 ) -> Option<(u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64)> {
3166 crate::cpu_experts::configured().then(crate::cpu_experts::stats)
3167 }
3168
3169 pub fn cpu_expert_predictor_stats(&self) -> (u64, u64) {
3172 crate::cpu_experts::predictor_stats()
3173 }
3174
3175 pub fn cpu_expert_exposed_wait_ns(&self) -> Option<u64> {
3176 crate::cpu_experts::configured().then(crate::cpu_experts::exposed_wait_ns)
3177 }
3178
3179 pub fn cpu_expert_gpu_residency_stats(&self) -> Option<(u64, u64, u64)> {
3182 crate::cpu_experts::configured().then(crate::cpu_experts::incomplete_gpu_residency_stats)
3183 }
3184
3185 pub fn moe_pread_stats(&self) -> Option<(u64, u64, u64, u64, u64, u64, u64)> {
3188 let guard = self.moe_cache.lock().unwrap();
3189 guard
3190 .as_ref()
3191 .and_then(|cache| cache.pread_stats())
3192 .map(|stats| {
3193 (
3194 stats.reads,
3195 stats.bytes,
3196 stats.read_errors,
3197 stats.short_reads,
3198 stats.fallbacks,
3199 stats.buffer_waits,
3200 stats.ring_full,
3201 )
3202 })
3203 }
3204
3205 pub fn spill_config_fallbacks(&self) -> u64 {
3207 crate::spill_pread::config_fallbacks()
3208 }
3209
3210 pub fn moe_cache_reset_counters(&self) {
3212 if let Some(c) = self.moe_cache.lock().unwrap().as_mut() {
3213 c.reset_counters();
3214 }
3215 }
3216
3217 pub fn htod_bytes(&self, v: &[u8]) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3218 Ok(self.gpu.stream().clone_htod(v)?)
3219 }
3220
3221 pub fn htod_bytes_padded(
3225 &self,
3226 v: &[u8],
3227 pad: usize,
3228 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3229 let mut d = self.alloc_u8_uninit(v.len() + pad)?;
3230 {
3231 let mut view = d.slice_mut(0..v.len());
3232 self.gpu.stream().memcpy_htod(v, &mut view)?;
3233 }
3234 Ok(d)
3235 }
3236
3237 pub fn copy_into(
3239 &self,
3240 dst: &mut CudaSlice<f32>,
3241 off: usize,
3242 src: &CudaSlice<f32>,
3243 len: usize,
3244 ) -> Result<(), Box<dyn std::error::Error>> {
3245 let mut view = dst.slice_mut(off..off + len);
3246 self.gpu
3247 .stream()
3248 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3249 Ok(())
3250 }
3251
3252 pub fn copy_u8_into(
3255 &self,
3256 dst: &mut CudaSlice<u8>,
3257 off: usize,
3258 src: &CudaSlice<u8>,
3259 len: usize,
3260 ) -> Result<(), Box<dyn std::error::Error>> {
3261 let mut view = dst.slice_mut(off..off + len);
3262 self.gpu
3263 .stream()
3264 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3265 Ok(())
3266 }
3267
3268 pub fn copy_u8_range_into(
3270 &self,
3271 dst: &mut CudaSlice<u8>,
3272 dst_off: usize,
3273 src: &CudaSlice<u8>,
3274 src_off: usize,
3275 len: usize,
3276 ) -> Result<(), Box<dyn std::error::Error>> {
3277 let mut dst_view = dst.slice_mut(dst_off..dst_off + len);
3278 self.gpu
3279 .stream()
3280 .memcpy_dtod(&src.slice(src_off..src_off + len), &mut dst_view)?;
3281 Ok(())
3282 }
3283
3284 pub fn prepare_kv_append(
3288 &self,
3289 kv: &mut crate::cache::KvLayer,
3290 retain_from: usize,
3291 append_rows: usize,
3292 ) -> Result<usize, Box<dyn std::error::Error>> {
3293 let Some(plan) = kv
3294 .ring
3295 .as_ref()
3296 .map(|ring| ring.append_plan(kv.len, retain_from, append_rows))
3297 .transpose()?
3298 else {
3299 return Ok(kv.len);
3300 };
3301 match plan {
3302 crate::cache::KvRingAppend::Contiguous { write_row } => Ok(write_row),
3303 crate::cache::KvRingAppend::Rebase {
3304 src_row,
3305 keep_rows,
3306 new_base,
3307 write_row,
3308 } => {
3309 if keep_rows > 0 {
3310 let k_len = keep_rows * kv.k_tok_bytes;
3311 let v_len = keep_rows * kv.v_tok_bytes;
3312 let mut k_tmp = self.alloc_u8_uninit(k_len)?;
3313 let mut v_tmp = self.alloc_u8_uninit(v_len)?;
3314 self.copy_u8_range_into(&mut k_tmp, 0, &kv.k, src_row * kv.k_tok_bytes, k_len)?;
3315 self.copy_u8_range_into(&mut v_tmp, 0, &kv.v, src_row * kv.v_tok_bytes, v_len)?;
3316 self.copy_u8_into(&mut kv.k, 0, &k_tmp, k_len)?;
3317 self.copy_u8_into(&mut kv.v, 0, &v_tmp, v_len)?;
3318 }
3319 kv.ring.as_mut().unwrap().apply_rebase(new_base);
3320 Ok(write_row)
3321 }
3322 }
3323 }
3324
3325 pub fn htod_u8_into(
3328 &self,
3329 dst: &mut CudaSlice<u8>,
3330 off: usize,
3331 src: &[u8],
3332 ) -> Result<(), Box<dyn std::error::Error>> {
3333 let mut view = dst.slice_mut(off..off + src.len());
3334 self.gpu.stream().memcpy_htod(src, &mut view)?;
3335 Ok(())
3336 }
3337
3338 pub fn view<'a>(&self, b: &'a CudaSlice<f32>, len: usize) -> cudarc::driver::CudaView<'a, f32> {
3339 b.slice(0..len)
3340 }
3341
3342 pub fn view_u8_range<'a>(
3345 &self,
3346 b: &'a CudaSlice<u8>,
3347 start: usize,
3348 end: usize,
3349 ) -> cudarc::driver::CudaView<'a, u8> {
3350 b.slice(start..end)
3351 }
3352 pub fn view_u8<'a>(
3353 &self,
3354 b: &'a CudaSlice<u8>,
3355 len: usize,
3356 ) -> cudarc::driver::CudaView<'a, u8> {
3357 b.slice(0..len)
3358 }
3359
3360 pub fn append_kv_quantized(
3364 &self,
3365 k_row: &CudaSlice<f32>,
3366 v_row: &CudaSlice<f32>,
3367 kc: &mut CudaSlice<u8>,
3368 vc: &mut CudaSlice<u8>,
3369 t: usize,
3370 kv_dim_k: usize,
3371 kv_dim_v: usize,
3372 k_tok_bytes: usize,
3373 v_tok_bytes: usize,
3374 g: bool,
3375 ) -> Result<(), Box<dyn std::error::Error>> {
3376 let f = if g {
3377 self.func_g("append_quantize_kv_q8_0_q5_1")
3378 } else {
3379 self.func("append_quantize_kv_q8_0_q5_1")
3380 };
3381 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3382 let cfg = LaunchConfig {
3383 grid_dim: (nblk, 1, 1),
3384 block_dim: (32, 1, 1),
3385 shared_mem_bytes: 0,
3386 };
3387 let (ti, kdk, kdv) = (t as i32, kv_dim_k as i32, kv_dim_v as i32);
3388 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3389 let __s_b = self.gpu.stream();
3390 let mut b = __s_b.launch_builder(&f);
3391 b.arg(k_row)
3392 .arg(v_row)
3393 .arg(kc)
3394 .arg(vc)
3395 .arg(&ti)
3396 .arg(&kdk)
3397 .arg(&kdv)
3398 .arg(&ktb)
3399 .arg(&vtb);
3400 unsafe {
3401 b.launch(cfg)?;
3402 }
3403 Ok(())
3404 }
3405
3406 pub fn append_kv_quantized_dc(
3410 &self,
3411 k_row: &CudaSlice<f32>,
3412 v_row: &CudaSlice<f32>,
3413 kc: &mut CudaSlice<u8>,
3414 vc: &mut CudaSlice<u8>,
3415 t_dev: &CudaSlice<i32>,
3416 kv_dim_k: usize,
3417 kv_dim_v: usize,
3418 k_tok_bytes: usize,
3419 v_tok_bytes: usize,
3420 g: bool,
3421 ) -> Result<(), Box<dyn std::error::Error>> {
3422 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3423 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
3424 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3425 if Self::pdl_on() && Self::pdl_wb_on() {
3427 use cudarc::driver::{DevicePtr, DevicePtrMut};
3428 let s = &self.gpu.stream();
3429 let (pk, _g0) = k_row.device_ptr(s);
3430 let (pv, _g1) = v_row.device_ptr(s);
3431 let (pkc, _g2) = kc.device_ptr_mut(s);
3432 let (pvc, _g3) = vc.device_ptr_mut(s);
3433 let (pt, _g4) = t_dev.device_ptr(s);
3434 let mut ps = [
3435 &pk as *const _ as *mut std::ffi::c_void,
3436 &pv as *const _ as *mut _,
3437 &pkc as *const _ as *mut _,
3438 &pvc as *const _ as *mut _,
3439 &pt as *const _ as *mut _,
3440 &kdk as *const _ as *mut _,
3441 &kdv as *const _ as *mut _,
3442 &ktb as *const _ as *mut _,
3443 &vtb as *const _ as *mut _,
3444 ];
3445 unsafe {
3446 self.launch_pdl_flash(
3447 g,
3448 "append_quantize_kv_q8_0_q5_1_dc",
3449 (nblk, 1, 1),
3450 (32, 1, 1),
3451 0,
3452 &mut ps,
3453 )?;
3454 }
3455 return Ok(());
3456 }
3457 let f = if g {
3458 self.func_g("append_quantize_kv_q8_0_q5_1_dc")
3459 } else {
3460 self.func("append_quantize_kv_q8_0_q5_1_dc")
3461 };
3462 let cfg = LaunchConfig {
3463 grid_dim: (nblk, 1, 1),
3464 block_dim: (32, 1, 1),
3465 shared_mem_bytes: 0,
3466 };
3467 let __s_b = self.gpu.stream();
3468 let mut b = __s_b.launch_builder(&f);
3469 b.arg(k_row)
3470 .arg(v_row)
3471 .arg(kc)
3472 .arg(vc)
3473 .arg(t_dev)
3474 .arg(&kdk)
3475 .arg(&kdv)
3476 .arg(&ktb)
3477 .arg(&vtb);
3478 unsafe {
3479 b.launch(cfg)?;
3480 }
3481 Ok(())
3482 }
3483
3484 #[allow(clippy::too_many_arguments)]
3491 pub fn append_kv_quantized_rows(
3492 &self,
3493 k_rows: &CudaSlice<f32>,
3494 v_rows: &CudaSlice<f32>,
3495 kc: &mut CudaSlice<u8>,
3496 vc: &mut CudaSlice<u8>,
3497 t0: usize,
3498 t: usize,
3499 kv_dim_k: usize,
3500 kv_dim_v: usize,
3501 k_tok_bytes: usize,
3502 v_tok_bytes: usize,
3503 g: bool,
3504 ) -> Result<(), Box<dyn std::error::Error>> {
3505 if std::env::var("MEMRA_PRIME_APPEND_LOOP").is_ok() {
3506 for i in 0..t {
3507 let k_row = k_rows.slice(i * kv_dim_k..(i + 1) * kv_dim_k);
3508 let v_row = v_rows.slice(i * kv_dim_v..(i + 1) * kv_dim_v);
3509 self.append_kv_quantized_view(
3510 &k_row,
3511 &v_row,
3512 kc,
3513 vc,
3514 t0 + i,
3515 kv_dim_k,
3516 kv_dim_v,
3517 k_tok_bytes,
3518 v_tok_bytes,
3519 g,
3520 )?;
3521 }
3522 return Ok(());
3523 }
3524 let f = if g {
3525 self.func_g("append_quantize_kv_q8_0_q5_1_rows")
3526 } else {
3527 self.func("append_quantize_kv_q8_0_q5_1_rows")
3528 };
3529 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3530 let cfg = LaunchConfig {
3531 grid_dim: (nblk, t as u32, 1),
3532 block_dim: (32, 1, 1),
3533 shared_mem_bytes: 0,
3534 };
3535 let (t0i, kdk, kdv) = (t0 as i32, kv_dim_k as i32, kv_dim_v as i32);
3536 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3537 let __s_b = self.gpu.stream();
3538 let mut b = __s_b.launch_builder(&f);
3539 b.arg(k_rows)
3540 .arg(v_rows)
3541 .arg(kc)
3542 .arg(vc)
3543 .arg(&t0i)
3544 .arg(&kdk)
3545 .arg(&kdv)
3546 .arg(&ktb)
3547 .arg(&vtb);
3548 unsafe {
3549 b.launch(cfg)?;
3550 }
3551 Ok(())
3552 }
3553
3554 pub fn inc_seqlen(&self, p: &mut CudaSlice<i32>) -> Result<(), Box<dyn std::error::Error>> {
3558 let f = self.func("inc_i32");
3559 let cfg = LaunchConfig {
3560 grid_dim: (1, 1, 1),
3561 block_dim: (1, 1, 1),
3562 shared_mem_bytes: 0,
3563 };
3564 let __s_b = self.gpu.stream();
3565 let mut b = __s_b.launch_builder(&f);
3566 b.arg(p);
3567 unsafe {
3568 b.launch(cfg)?;
3569 }
3570 Ok(())
3571 }
3572
3573 pub fn append_kv_quantized_view(
3576 &self,
3577 k_row: &cudarc::driver::CudaView<f32>,
3578 v_row: &cudarc::driver::CudaView<f32>,
3579 kc: &mut CudaSlice<u8>,
3580 vc: &mut CudaSlice<u8>,
3581 t: usize,
3582 kv_dim_k: usize,
3583 kv_dim_v: usize,
3584 k_tok_bytes: usize,
3585 v_tok_bytes: usize,
3586 g: bool,
3587 ) -> Result<(), Box<dyn std::error::Error>> {
3588 let f = if g {
3589 self.func_g("append_quantize_kv_q8_0_q5_1")
3590 } else {
3591 self.func("append_quantize_kv_q8_0_q5_1")
3592 };
3593 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3594 let cfg = LaunchConfig {
3595 grid_dim: (nblk, 1, 1),
3596 block_dim: (32, 1, 1),
3597 shared_mem_bytes: 0,
3598 };
3599 let (ti, kdk, kdv) = (t 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_row)
3604 .arg(v_row)
3605 .arg(kc)
3606 .arg(vc)
3607 .arg(&ti)
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 copy_view_into(
3621 &self,
3622 dst: &mut CudaSlice<f32>,
3623 off: usize,
3624 src: &cudarc::driver::CudaView<f32>,
3625 len: usize,
3626 ) -> Result<(), Box<dyn std::error::Error>> {
3627 let mut view = dst.slice_mut(off..off + len);
3628 self.gpu
3629 .stream()
3630 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3631 Ok(())
3632 }
3633
3634 pub fn clone_dtod(
3638 &self,
3639 src: &CudaSlice<f32>,
3640 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3641 let mut dst = self.gpu.stream().alloc_zeros::<f32>(src.len())?;
3642 self.gpu.stream().memcpy_dtod(src, &mut dst)?;
3643 Ok(dst)
3644 }
3645
3646 pub fn dtod_copy_view(
3649 &self,
3650 src: &cudarc::driver::CudaView<f32>,
3651 dst: &mut CudaSlice<f32>,
3652 ) -> Result<(), Box<dyn std::error::Error>> {
3653 self.gpu.stream().memcpy_dtod(src, dst)?;
3654 Ok(())
3655 }
3656
3657 pub fn dtod_copy_view_i8(
3659 &self,
3660 src: &cudarc::driver::CudaView<i8>,
3661 dst: &mut CudaSlice<i8>,
3662 ) -> Result<(), Box<dyn std::error::Error>> {
3663 self.gpu.stream().memcpy_dtod(src, dst)?;
3664 Ok(())
3665 }
3666
3667 pub fn dtod_copy_into(
3669 &self,
3670 src: &CudaSlice<f32>,
3671 dst: &mut CudaSlice<f32>,
3672 offset: usize,
3673 ) -> Result<(), Box<dyn std::error::Error>> {
3674 let n = src.len();
3675 let mut dv = dst.slice_mut(offset..offset + n);
3676 self.gpu.stream().memcpy_dtod(src, &mut dv)?;
3677 Ok(())
3678 }
3679
3680 pub fn uninit_i8(&self, n: usize) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
3682 self.alloc_uninit::<i8>(n)
3683 }
3684
3685 pub fn qmatvec(
3687 &self,
3688 w: &CudaSlice<u8>,
3689 x: &CudaSlice<f32>,
3690 m: usize,
3691 in_f: usize,
3692 out_f: usize,
3693 qtype: i32,
3694 row_bytes: usize,
3695 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3696 let f = self.func("qmatvec_f32");
3697 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
3699 grid_dim: (out_f as u32, m as u32, 1),
3700 block_dim: (256, 1, 1),
3701 shared_mem_bytes: 0,
3702 };
3703 let (inf, outf, mi, qt, rb) =
3704 (in_f as i32, out_f as i32, m as i32, qtype, row_bytes as i64);
3705 let __s_b = self.gpu.stream();
3706 let mut b = __s_b.launch_builder(&f);
3707 b.arg(w)
3708 .arg(x)
3709 .arg(&mut y)
3710 .arg(&inf)
3711 .arg(&outf)
3712 .arg(&mi)
3713 .arg(&qt)
3714 .arg(&rb);
3715 unsafe {
3716 b.launch(cfg)?;
3717 }
3718 Ok(y)
3719 }
3720
3721 pub fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3723 let s = self.gpu.stream().alloc_zeros::<u8>(n)?;
3724 self.keep_if_capturing(&s);
3725 Ok(s)
3726 }
3727
3728 pub fn alloc_u8_uninit(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3732 let s = unsafe { self.gpu.stream().alloc::<u8>(n)? };
3733 self.keep_if_capturing(&s);
3734 Ok(s)
3735 }
3736
3737 pub fn memset_zeros_view(
3740 &self,
3741 dst: &mut cudarc::driver::CudaViewMut<f32>,
3742 ) -> Result<(), Box<dyn std::error::Error>> {
3743 self.gpu.stream().memset_zeros(dst)?;
3744 Ok(())
3745 }
3746
3747 pub fn stage_expert(
3753 &self,
3754 host_bytes: &[u8],
3755 scratch: &mut CudaSlice<u8>,
3756 off: usize,
3757 ) -> Result<(), Box<dyn std::error::Error>> {
3758 let mut dst = scratch.slice_mut(off..off + host_bytes.len()); self.gpu.stream().memcpy_htod(host_bytes, &mut dst)?; Ok(())
3761 }
3762
3763 pub fn moe_router_topk(
3769 &self,
3770 logits: &CudaSlice<f32>,
3771 t: usize,
3772 n_expert: usize,
3773 n_used: usize,
3774 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
3775 let f = self.func("moe_router_topk_f32");
3776 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 {
3779 grid_dim: (t as u32, 1, 1),
3780 block_dim: (n_expert as u32, 1, 1),
3781 shared_mem_bytes: 0,
3782 };
3783 let (ne, nu) = (n_expert as i32, n_used as i32);
3784 let __s_b = self.gpu.stream();
3785 let mut b = __s_b.launch_builder(&f);
3786 b.arg(logits)
3787 .arg(&mut sel_idx)
3788 .arg(&mut sel_w)
3789 .arg(&ne)
3790 .arg(&nu);
3791 unsafe {
3792 b.launch(cfg)?;
3793 }
3794 Ok((sel_idx, sel_w))
3795 }
3796
3797 pub fn moe_router_topk_scaled(
3800 &self,
3801 logits: &CudaSlice<f32>,
3802 t: usize,
3803 n_expert: usize,
3804 n_used: usize,
3805 ex_scale: &CudaSlice<f32>,
3806 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
3807 let f = self.func("moe_router_topk_scaled_f32");
3812 let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?;
3813 let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?;
3814 let cfg = LaunchConfig {
3815 grid_dim: (t as u32, 1, 1),
3816 block_dim: (n_expert as u32, 1, 1),
3817 shared_mem_bytes: 0,
3818 };
3819 let (ne, nu) = (n_expert as i32, n_used as i32);
3820 let __s_b = self.gpu.stream();
3821 let mut b = __s_b.launch_builder(&f);
3822 b.arg(logits)
3823 .arg(&mut sel_idx)
3824 .arg(&mut sel_w)
3825 .arg(&ne)
3826 .arg(&nu)
3827 .arg(ex_scale);
3828 unsafe {
3829 b.launch(cfg)?;
3830 }
3831 Ok((sel_idx, sel_w))
3832 }
3833
3834 pub fn moe_router_topk_host(
3842 &self,
3843 logits: &CudaSlice<f32>,
3844 t: usize,
3845 n_expert: usize,
3846 n_used: usize,
3847 ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
3848 let f = self.func("moe_router_topk_f32");
3849 let n = t * n_used;
3850 let mut sel_idx = self.alloc_uninit::<i32>(n)?;
3851 let mut sel_w = self.alloc_uninit::<f32>(n)?;
3852 let cfg = LaunchConfig {
3853 grid_dim: (t as u32, 1, 1),
3854 block_dim: (n_expert as u32, 1, 1),
3855 shared_mem_bytes: 0,
3856 };
3857 let (ne, nu) = (n_expert as i32, n_used as i32);
3858 let __s_b = self.gpu.stream();
3859 let mut b = __s_b.launch_builder(&f);
3860 b.arg(logits)
3861 .arg(&mut sel_idx)
3862 .arg(&mut sel_w)
3863 .arg(&ne)
3864 .arg(&nu);
3865 unsafe {
3866 b.launch(cfg)?;
3867 }
3868 let bytes = n * 8;
3870 let mut guard = self.router_stage.lock().unwrap();
3871 if guard.as_ref().map(|p| p.cap < bytes).unwrap_or(true) {
3872 *guard = Some(PinnedStage::new(bytes.max(4096))?);
3873 }
3874 let stage = guard.as_mut().unwrap();
3875 let (si, sw) = unsafe {
3876 (
3877 std::slice::from_raw_parts_mut(stage.ptr as *mut i32, n),
3878 std::slice::from_raw_parts_mut(stage.ptr.add(n * 4) as *mut f32, n),
3879 )
3880 };
3881 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()))
3885 }
3886
3887 #[allow(clippy::too_many_arguments)]
3891 pub fn moe_router_sigmoid_topk(
3892 &self,
3893 logits: &CudaSlice<f32>,
3894 t: usize,
3895 n_expert: usize,
3896 n_used: usize,
3897 active_count: usize,
3898 correction_bias: &CudaSlice<f32>,
3899 active: &CudaSlice<u8>,
3900 scaling_factor: f32,
3901 route_norm: bool,
3902 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
3903 crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
3904 if n_expert == 0 || n_expert > 1024 || n_used == 0 || n_used > n_expert {
3905 return Err(format!(
3906 "sigmoid router shape unsupported: n_expert={n_expert}, n_used={n_used}",
3907 )
3908 .into());
3909 }
3910 if logits.len() < t * n_expert
3911 || correction_bias.len() != n_expert
3912 || active.len() != n_expert
3913 {
3914 return Err(format!(
3915 "sigmoid router buffer mismatch: logits={} bias={} active={} expected logits>={} row={}",
3916 logits.len(), correction_bias.len(), active.len(), t * n_expert, n_expert,
3917 ).into());
3918 }
3919 let f = self.func("moe_router_sigmoid_topk_f32");
3920 let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?;
3921 let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?;
3922 let threads = n_expert.div_ceil(32) * 32;
3923 let cfg = LaunchConfig {
3924 grid_dim: (t as u32, 1, 1),
3925 block_dim: (threads as u32, 1, 1),
3926 shared_mem_bytes: 0,
3927 };
3928 let (ne, nu, rn) = (n_expert as i32, n_used as i32, i32::from(route_norm));
3929 let __s_b = self.gpu.stream();
3930 let mut b = __s_b.launch_builder(&f);
3931 b.arg(logits)
3932 .arg(correction_bias)
3933 .arg(active)
3934 .arg(&mut sel_idx)
3935 .arg(&mut sel_w)
3936 .arg(&ne)
3937 .arg(&nu)
3938 .arg(&scaling_factor)
3939 .arg(&rn);
3940 unsafe {
3941 b.launch(cfg)?;
3942 }
3943 Ok((sel_idx, sel_w))
3944 }
3945
3946 #[allow(clippy::too_many_arguments)]
3949 pub fn moe_router_sigmoid_topk_host(
3950 &self,
3951 logits: &CudaSlice<f32>,
3952 t: usize,
3953 n_expert: usize,
3954 n_used: usize,
3955 active_count: usize,
3956 correction_bias: &CudaSlice<f32>,
3957 active: &CudaSlice<u8>,
3958 scaling_factor: f32,
3959 route_norm: bool,
3960 ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
3961 let (sel_idx, sel_w) = self.moe_router_sigmoid_topk(
3962 logits,
3963 t,
3964 n_expert,
3965 n_used,
3966 active_count,
3967 correction_bias,
3968 active,
3969 scaling_factor,
3970 route_norm,
3971 )?;
3972 let n = t * n_used;
3973 let bytes = n * 8;
3974 let mut guard = self.router_stage.lock().unwrap();
3975 if guard.as_ref().map(|p| p.cap < bytes).unwrap_or(true) {
3976 *guard = Some(PinnedStage::new(bytes.max(4096))?);
3977 }
3978 let stage = guard.as_mut().unwrap();
3979 let (si, sw) = unsafe {
3980 (
3981 std::slice::from_raw_parts_mut(stage.ptr as *mut i32, n),
3982 std::slice::from_raw_parts_mut(stage.ptr.add(n * 4) as *mut f32, n),
3983 )
3984 };
3985 self.gpu.stream().memcpy_dtoh(&sel_idx, si)?;
3986 self.gpu.stream().memcpy_dtoh(&sel_w, sw)?;
3987 self.gpu.stream().synchronize()?;
3988 Ok((si.iter().map(|&i| i as u32).collect(), sw.to_vec()))
3989 }
3990
3991 pub fn stage_expert_async(
3995 &self,
3996 host_bytes: &[u8],
3997 scratch: &mut CudaSlice<u8>,
3998 off: usize,
3999 ) -> Result<cudarc::driver::CudaEvent, Box<dyn std::error::Error>> {
4000 let mut dst = scratch.slice_mut(off..off + host_bytes.len());
4001 self.copy_stream.memcpy_htod(host_bytes, &mut dst)?;
4002 Ok(self.copy_stream.record_event(None)?)
4003 }
4004
4005 pub fn compute_wait(
4007 &self,
4008 ev: &cudarc::driver::CudaEvent,
4009 ) -> Result<(), Box<dyn std::error::Error>> {
4010 self.gpu.stream().wait(ev)?;
4011 Ok(())
4012 }
4013
4014 pub fn qmatvec_view(
4019 &self,
4020 w: &CudaSlice<u8>,
4021 range: std::ops::Range<usize>,
4022 x: &cudarc::driver::CudaView<f32>,
4023 m: usize,
4024 in_f: usize,
4025 out_f: usize,
4026 qtype: i32,
4027 row_bytes: usize,
4028 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4029 let f = self.func("qmatvec_f32");
4030 let wv = w.slice(range); let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
4033 grid_dim: (out_f as u32, m as u32, 1),
4034 block_dim: (256, 1, 1),
4035 shared_mem_bytes: 0,
4036 };
4037 let (inf, outf, mi, qt, rb) =
4038 (in_f as i32, out_f as i32, m as i32, qtype, row_bytes as i64);
4039 let __s_b = self.gpu.stream();
4040 let mut b = __s_b.launch_builder(&f);
4041 b.arg(&wv)
4042 .arg(x)
4043 .arg(&mut y)
4044 .arg(&inf)
4045 .arg(&outf)
4046 .arg(&mi)
4047 .arg(&qt)
4048 .arg(&rb);
4049 unsafe {
4050 b.launch(cfg)?;
4051 }
4052 Ok(y)
4053 }
4054
4055 #[allow(clippy::too_many_arguments)]
4062 pub fn moe_gate_up_silu8_q8(
4066 &self,
4067 gp: WPtr8,
4068 up: WPtr8,
4069 aq: &CudaSlice<i8>,
4070 ad: &CudaSlice<f32>,
4071 in_f: usize,
4072 n_ff: usize,
4073 n_used: usize,
4074 qt_g: i32,
4075 qt_u: i32,
4076 rb_g: usize,
4077 rb_u: usize,
4078 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4079 let f = self.func("moe_gate_up_silu8_q8");
4080 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
4081 let cfg = LaunchConfig {
4082 grid_dim: (n_ff as u32, n_used as u32, 1),
4083 block_dim: (32, 1, 1),
4084 shared_mem_bytes: 0,
4085 };
4086 let (inf, nff, rbg, rbu) = (in_f as i32, n_ff as i32, rb_g as i64, rb_u as i64);
4087 let __s_b = self.gpu.stream();
4088 let mut b = __s_b.launch_builder(&f);
4089 b.arg(&gp)
4090 .arg(&up)
4091 .arg(aq)
4092 .arg(ad)
4093 .arg(&mut act)
4094 .arg(&inf)
4095 .arg(&nff)
4096 .arg(&qt_g)
4097 .arg(&qt_u)
4098 .arg(&rbg)
4099 .arg(&rbu);
4100 unsafe {
4101 b.launch(cfg)?;
4102 }
4103 Ok(act)
4104 }
4105
4106 #[allow(clippy::too_many_arguments)]
4107 pub fn moe_down8_fma_q8(
4108 &self,
4109 dp: WPtr8,
4110 w: F32x8,
4111 aq2: &CudaSlice<i8>,
4112 ad2: &CudaSlice<f32>,
4113 dst: &mut cudarc::driver::CudaViewMut<f32>,
4114 in_f: usize,
4115 out_f: usize,
4116 n_used: usize,
4117 qt: i32,
4118 rb: usize,
4119 ) -> Result<(), Box<dyn std::error::Error>> {
4120 let f = self.func("moe_down8_fma_q8");
4121 let cfg = LaunchConfig {
4122 grid_dim: (out_f as u32, 1, 1),
4123 block_dim: (32, 1, 1),
4124 shared_mem_bytes: 0,
4125 };
4126 let (inf, outf, nu, rbi) = (in_f as i32, out_f as i32, n_used as i32, rb as i64);
4127 let __s_b = self.gpu.stream();
4128 let mut b = __s_b.launch_builder(&f);
4129 b.arg(&dp)
4130 .arg(&w)
4131 .arg(aq2)
4132 .arg(ad2)
4133 .arg(dst)
4134 .arg(&inf)
4135 .arg(&outf)
4136 .arg(&nu)
4137 .arg(&qt)
4138 .arg(&rbi);
4139 unsafe {
4140 b.launch(cfg)?;
4141 }
4142 Ok(())
4143 }
4144
4145 pub fn qmatvec_expert_q8(
4147 &self,
4148 w: &CudaSlice<u8>,
4149 range: std::ops::Range<usize>,
4150 aq: &CudaSlice<i8>,
4151 ad: &CudaSlice<f32>,
4152 m: usize,
4153 in_f: usize,
4154 out_f: usize,
4155 qtype: i32,
4156 row_bytes: usize,
4157 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4158 let f = self.func("qmatvec_expert_q8");
4159 let wv = w.slice(range);
4160 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
4161 const ROWS: u32 = 4; let cfg = LaunchConfig {
4163 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, m as u32, 1),
4164 block_dim: (32, ROWS, 1),
4165 shared_mem_bytes: 0,
4166 };
4167 let (inf, outf, mi, rbi) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
4168 let __s_b = self.gpu.stream();
4169 let mut b = __s_b.launch_builder(&f);
4170 b.arg(&wv)
4171 .arg(aq)
4172 .arg(ad)
4173 .arg(&mut y)
4174 .arg(&inf)
4175 .arg(&outf)
4176 .arg(&mi)
4177 .arg(&qtype)
4178 .arg(&rbi);
4179 unsafe {
4180 b.launch(cfg)?;
4181 }
4182 Ok(y)
4183 }
4184
4185 pub fn moe_gate_up_silu8(
4186 &self,
4187 gp: WPtr8,
4188 up: WPtr8,
4189 x: &cudarc::driver::CudaView<f32>,
4190 in_f: usize,
4191 n_ff: usize,
4192 n_used: usize,
4193 qt_g: i32,
4194 qt_u: i32,
4195 rb_g: usize,
4196 rb_u: usize,
4197 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4198 let f = self.func("moe_gate_up_silu8_f32");
4199 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?; let cfg = LaunchConfig {
4201 grid_dim: (n_ff as u32, n_used as u32, 1),
4202 block_dim: (256, 1, 1),
4203 shared_mem_bytes: 0,
4204 };
4205 let (inf, nff, rbg, rbu) = (in_f as i32, n_ff as i32, rb_g as i64, rb_u as i64);
4206 let __s_b = self.gpu.stream();
4207 let mut b = __s_b.launch_builder(&f);
4208 b.arg(&gp)
4209 .arg(&up)
4210 .arg(x)
4211 .arg(&mut act)
4212 .arg(&inf)
4213 .arg(&nff)
4214 .arg(&qt_g)
4215 .arg(&qt_u)
4216 .arg(&rbg)
4217 .arg(&rbu);
4218 unsafe {
4219 b.launch(cfg)?;
4220 }
4221 Ok(act)
4222 }
4223
4224 #[allow(clippy::too_many_arguments)]
4230 pub fn moe_down8_fma_into(
4231 &self,
4232 dp: WPtr8,
4233 w: F32x8,
4234 act: &CudaSlice<f32>,
4235 dst: &mut cudarc::driver::CudaViewMut<f32>,
4236 in_f: usize,
4237 out_f: usize,
4238 n_used: usize,
4239 qt: i32,
4240 rb: usize,
4241 ) -> Result<(), Box<dyn std::error::Error>> {
4242 let f = self.func("moe_down8_fma_f32");
4243 let cfg = LaunchConfig {
4244 grid_dim: (out_f as u32, 1, 1),
4245 block_dim: (256, 1, 1),
4246 shared_mem_bytes: 0,
4247 };
4248 let (inf, outf, nu, rbv) = (in_f as i32, out_f as i32, n_used as i32, rb as i64);
4249 let __s_b = self.gpu.stream();
4250 let mut b = __s_b.launch_builder(&f);
4251 b.arg(&dp)
4252 .arg(&w)
4253 .arg(act)
4254 .arg(dst)
4255 .arg(&inf)
4256 .arg(&outf)
4257 .arg(&nu)
4258 .arg(&qt)
4259 .arg(&rbv);
4260 unsafe {
4261 b.launch(cfg)?;
4262 }
4263 Ok(())
4264 }
4265
4266 #[allow(clippy::too_many_arguments)]
4271 #[allow(clippy::too_many_arguments)]
4286 #[allow(clippy::too_many_arguments)]
4288 pub fn moe_pairs_matvec_q8(
4289 &self,
4290 table: &CudaSlice<u64>,
4291 proj: i32,
4292 pair_tok: &CudaSlice<i32>,
4293 pair_ex: &CudaSlice<i32>,
4294 aq: &CudaSlice<i8>,
4295 ad: &CudaSlice<f32>,
4296 in_f: usize,
4297 out_f: usize,
4298 n_expert: usize,
4299 n_pairs: usize,
4300 qtype: i32,
4301 row_bytes: usize,
4302 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4303 let f = self.func("moe_pairs_matvec_q8");
4304 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4305 const ROWS: u32 = 4;
4306 let cfg = LaunchConfig {
4307 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_pairs as u32, 1),
4308 block_dim: (32, ROWS, 1),
4309 shared_mem_bytes: 0,
4310 };
4311 let (inf, outf, ne, np, rbi) = (
4312 in_f as i32,
4313 out_f as i32,
4314 n_expert as i32,
4315 n_pairs as i32,
4316 row_bytes as i64,
4317 );
4318 let __s_b = self.gpu.stream();
4319 let mut b = __s_b.launch_builder(&f);
4320 b.arg(table)
4321 .arg(&proj)
4322 .arg(pair_tok)
4323 .arg(pair_ex)
4324 .arg(aq)
4325 .arg(ad)
4326 .arg(&mut y)
4327 .arg(&inf)
4328 .arg(&outf)
4329 .arg(&ne)
4330 .arg(&np)
4331 .arg(&qtype)
4332 .arg(&rbi);
4333 unsafe {
4334 b.launch(cfg)?;
4335 }
4336 Ok(y)
4337 }
4338
4339 #[allow(clippy::too_many_arguments)]
4341 pub fn moe_pairs_matvec_q8_em(
4342 &self,
4343 table: &CudaSlice<u64>,
4344 proj: i32,
4345 ex_ids: &CudaSlice<i32>,
4346 ex_off: &CudaSlice<i32>,
4347 ex_pairs: &CudaSlice<i32>,
4348 pair_tok: &CudaSlice<i32>,
4349 aq: &CudaSlice<i8>,
4350 ad: &CudaSlice<f32>,
4351 in_f: usize,
4352 out_f: usize,
4353 n_expert: usize,
4354 n_active: usize,
4355 n_pairs: usize,
4356 qtype: i32,
4357 row_bytes: usize,
4358 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4359 let f = self.func("moe_pairs_matvec_q8_em");
4360 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4361 const ROWS: u32 = 4;
4362 let cfg = LaunchConfig {
4363 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_active as u32, 1),
4364 block_dim: (32, ROWS, 1),
4365 shared_mem_bytes: 0,
4366 };
4367 let (inf, outf, ne, na, rbi) = (
4368 in_f as i32,
4369 out_f as i32,
4370 n_expert as i32,
4371 n_active as i32,
4372 row_bytes as i64,
4373 );
4374 let __s_b = self.gpu.stream();
4375 let mut b = __s_b.launch_builder(&f);
4376 b.arg(table)
4377 .arg(&proj)
4378 .arg(ex_ids)
4379 .arg(ex_off)
4380 .arg(ex_pairs)
4381 .arg(pair_tok)
4382 .arg(aq)
4383 .arg(ad)
4384 .arg(&mut y)
4385 .arg(&inf)
4386 .arg(&outf)
4387 .arg(&ne)
4388 .arg(&na)
4389 .arg(&qtype)
4390 .arg(&rbi);
4391 unsafe {
4392 b.launch(cfg)?;
4393 }
4394 Ok(y)
4395 }
4396
4397 #[allow(clippy::too_many_arguments)]
4400 pub fn moe_pairs_matvec_q8_dec(
4401 &self,
4402 table: &CudaSlice<u64>,
4403 proj: i32,
4404 ex_ids: &CudaSlice<i32>,
4405 ex_off: &CudaSlice<i32>,
4406 ex_pairs: &CudaSlice<i32>,
4407 pair_tok: &CudaSlice<i32>,
4408 aq: &CudaSlice<i8>,
4409 ad: &CudaSlice<f32>,
4410 in_f: usize,
4411 out_f: usize,
4412 n_expert: usize,
4413 n_active: usize,
4414 n_pairs: usize,
4415 qtype: i32,
4416 row_bytes: usize,
4417 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4418 let f = self.func("moe_pairs_matvec_q8_dec");
4419 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4420 const ROWS: u32 = 4;
4421 let cfg = LaunchConfig {
4422 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_active as u32, 1),
4423 block_dim: (32, ROWS, 1),
4424 shared_mem_bytes: 0,
4425 };
4426 let (inf, outf, ne, na, rbi) = (
4427 in_f as i32,
4428 out_f as i32,
4429 n_expert as i32,
4430 n_active as i32,
4431 row_bytes as i64,
4432 );
4433 let __s_b = self.gpu.stream();
4434 let mut b = __s_b.launch_builder(&f);
4435 b.arg(table)
4436 .arg(&proj)
4437 .arg(ex_ids)
4438 .arg(ex_off)
4439 .arg(ex_pairs)
4440 .arg(pair_tok)
4441 .arg(aq)
4442 .arg(ad)
4443 .arg(&mut y)
4444 .arg(&inf)
4445 .arg(&outf)
4446 .arg(&ne)
4447 .arg(&na)
4448 .arg(&qtype)
4449 .arg(&rbi);
4450 unsafe {
4451 b.launch(cfg)?;
4452 }
4453 Ok(y)
4454 }
4455
4456 pub fn moe_pairs_gelu_mul(
4457 &self,
4458 gate: &CudaSlice<f32>,
4459 up: &CudaSlice<f32>,
4460 n: usize,
4461 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4462 let f = self.func("moe_pairs_gelu_mul");
4463 let mut act = self.alloc_uninit::<f32>(n)?;
4464 let cfg = LaunchConfig::for_num_elems(n as u32);
4465 let nl = n as i64;
4466 let __s_b = self.gpu.stream();
4467 let mut b = __s_b.launch_builder(&f);
4468 b.arg(gate).arg(up).arg(&mut act).arg(&nl);
4469 unsafe {
4470 b.launch(cfg)?;
4471 }
4472 Ok(act)
4473 }
4474
4475 pub fn moe_pairs_silu_mul(
4476 &self,
4477 gate: &CudaSlice<f32>,
4478 up: &CudaSlice<f32>,
4479 n: usize,
4480 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4481 let f = self.func("moe_pairs_silu_mul");
4482 let mut act = self.alloc_uninit::<f32>(n)?;
4483 let cfg = LaunchConfig::for_num_elems(n as u32);
4484 let nl = n as i64;
4485 let __s_b = self.gpu.stream();
4486 let mut b = __s_b.launch_builder(&f);
4487 b.arg(gate).arg(up).arg(&mut act).arg(&nl);
4488 unsafe {
4489 b.launch(cfg)?;
4490 }
4491 Ok(act)
4492 }
4493
4494 #[allow(clippy::too_many_arguments)]
4495 pub fn moe_pairs_scatter(
4496 &self,
4497 y_down: &CudaSlice<f32>,
4498 pair_w: &CudaSlice<f32>,
4499 tok_pair_off: &CudaSlice<i32>,
4500 tok_pair_ids: &CudaSlice<i32>,
4501 moe_out: &mut CudaSlice<f32>,
4502 t: usize,
4503 n_embd: usize,
4504 ) -> Result<(), Box<dyn std::error::Error>> {
4505 let f = self.func("moe_pairs_scatter");
4506 let cfg = LaunchConfig {
4507 grid_dim: (((n_embd + 255) / 256) as u32, t as u32, 1),
4508 block_dim: (256, 1, 1),
4509 shared_mem_bytes: 0,
4510 };
4511 let ne = n_embd as i32;
4512 let __s_b = self.gpu.stream();
4513 let mut b = __s_b.launch_builder(&f);
4514 b.arg(y_down)
4515 .arg(pair_w)
4516 .arg(tok_pair_off)
4517 .arg(tok_pair_ids)
4518 .arg(moe_out)
4519 .arg(&ne);
4520 unsafe {
4521 b.launch(cfg)?;
4522 }
4523 Ok(())
4524 }
4525
4526 #[allow(clippy::too_many_arguments)]
4530 pub fn moe_gate_up_gelu8_dev_q8(
4531 &self,
4532 table: &CudaSlice<u64>,
4533 sel: &cudarc::driver::CudaView<i32>,
4534 aq: &CudaSlice<i8>,
4535 ad: &CudaSlice<f32>,
4536 in_f: usize,
4537 n_ff: usize,
4538 n_used: usize,
4539 n_expert: usize,
4540 qt_g: i32,
4541 qt_u: i32,
4542 rb_g: usize,
4543 rb_u: usize,
4544 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4545 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
4546 let (inf, nff, ne, rbg, rbu) = (
4547 in_f as i32,
4548 n_ff as i32,
4549 n_expert as i32,
4550 rb_g as i64,
4551 rb_u as i64,
4552 );
4553 let f = self.func("moe_gate_up_gelu8_dev_q8");
4554 let cfg = LaunchConfig {
4555 grid_dim: (n_ff as u32, n_used as u32, 1),
4556 block_dim: (32, 1, 1),
4557 shared_mem_bytes: 0,
4558 };
4559 let __s_b = self.gpu.stream();
4560 let mut b = __s_b.launch_builder(&f);
4561 b.arg(table)
4562 .arg(sel)
4563 .arg(aq)
4564 .arg(ad)
4565 .arg(&mut act)
4566 .arg(&inf)
4567 .arg(&nff)
4568 .arg(&ne)
4569 .arg(&qt_g)
4570 .arg(&qt_u)
4571 .arg(&rbg)
4572 .arg(&rbu);
4573 unsafe {
4574 b.launch(cfg)?;
4575 }
4576 Ok(act)
4577 }
4578
4579 #[allow(clippy::too_many_arguments)]
4581 pub fn moe_gate_up_gelu8_dev_q8_rows(
4582 &self,
4583 table: &CudaSlice<u64>,
4584 sel: &CudaSlice<i32>,
4585 aq: &CudaSlice<i8>,
4586 ad: &CudaSlice<f32>,
4587 t: usize,
4588 in_f: usize,
4589 n_ff: usize,
4590 n_used: usize,
4591 n_expert: usize,
4592 qt_g: i32,
4593 qt_u: i32,
4594 rb_g: usize,
4595 rb_u: usize,
4596 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4597 let mut act = self.alloc_uninit::<f32>(t * n_used * n_ff)?;
4598 let (inf, nff, ne, rbg, rbu, nu) = (
4599 in_f as i32,
4600 n_ff as i32,
4601 n_expert as i32,
4602 rb_g as i64,
4603 rb_u as i64,
4604 n_used as i32,
4605 );
4606 let f = self.func("moe_gate_up_gelu8_dev_q8_rows");
4607 let cfg = LaunchConfig {
4608 grid_dim: (n_ff as u32, n_used as u32, t as u32),
4609 block_dim: (32, 1, 1),
4610 shared_mem_bytes: 0,
4611 };
4612 let __s_b = self.gpu.stream();
4613 let mut b = __s_b.launch_builder(&f);
4614 b.arg(table)
4615 .arg(sel)
4616 .arg(aq)
4617 .arg(ad)
4618 .arg(&mut act)
4619 .arg(&inf)
4620 .arg(&nff)
4621 .arg(&ne)
4622 .arg(&qt_g)
4623 .arg(&qt_u)
4624 .arg(&rbg)
4625 .arg(&rbu)
4626 .arg(&nu);
4627 unsafe {
4628 b.launch(cfg)?;
4629 }
4630 Ok(act)
4631 }
4632
4633 #[allow(clippy::too_many_arguments)]
4635 pub fn moe_gate_up_gelu8_dev_q8_csr(
4636 &self,
4637 table: &CudaSlice<u64>,
4638 sel: &CudaSlice<i32>,
4639 aq: &CudaSlice<i8>,
4640 ad: &CudaSlice<f32>,
4641 n_pairs: usize,
4642 in_f: usize,
4643 n_ff: usize,
4644 n_used: usize,
4645 n_expert: usize,
4646 qt_g: i32,
4647 qt_u: i32,
4648 rb_g: usize,
4649 rb_u: usize,
4650 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4651 let mut act = self.alloc_uninit::<f32>(n_pairs * n_ff)?;
4652 let (inf, nff, ne, rbg, rbu, nu, npi) = (
4653 in_f as i32,
4654 n_ff as i32,
4655 n_expert as i32,
4656 rb_g as i64,
4657 rb_u as i64,
4658 n_used as i32,
4659 n_pairs as i32,
4660 );
4661 let f = self.func("moe_gate_up_gelu8_dev_q8_csr");
4662 let cfg = LaunchConfig {
4663 grid_dim: (n_ff as u32, n_pairs as u32, 1),
4664 block_dim: (32, 1, 1),
4665 shared_mem_bytes: 0,
4666 };
4667 let __s_b = self.gpu.stream();
4668 let mut b = __s_b.launch_builder(&f);
4669 b.arg(table)
4670 .arg(sel)
4671 .arg(aq)
4672 .arg(ad)
4673 .arg(&mut act)
4674 .arg(&inf)
4675 .arg(&nff)
4676 .arg(&ne)
4677 .arg(&qt_g)
4678 .arg(&qt_u)
4679 .arg(&rbg)
4680 .arg(&rbu)
4681 .arg(&nu)
4682 .arg(&npi);
4683 unsafe {
4684 b.launch(cfg)?;
4685 }
4686 Ok(act)
4687 }
4688
4689 #[allow(clippy::too_many_arguments)]
4691 pub fn moe_down8_fma_dev_q8_rows_g(
4692 &self,
4693 table: &CudaSlice<u64>,
4694 sel: &CudaSlice<i32>,
4695 w: &CudaSlice<f32>,
4696 aq2: &CudaSlice<i8>,
4697 ad2: &CudaSlice<f32>,
4698 dst: &mut CudaSlice<f32>,
4699 t: usize,
4700 in_f: usize,
4701 out_f: usize,
4702 n_used: usize,
4703 n_expert: usize,
4704 qt: i32,
4705 rb: usize,
4706 ) -> Result<(), Box<dyn std::error::Error>> {
4707 let (inf, outf, nu, ne, rbi) = (
4708 in_f as i32,
4709 out_f as i32,
4710 n_used as i32,
4711 n_expert as i32,
4712 rb as i64,
4713 );
4714 let step_b1_w8 = t == 1 && in_f == 1280 && out_f == 4096 && n_used == 8 && qt == QT_IQ4_XS;
4718 let f = self.func(if step_b1_w8 {
4719 "moe_down8_fma_dev_q8_rows_w8"
4720 } else {
4721 "moe_down8_fma_dev_q8_rows_g"
4722 });
4723 let cfg = LaunchConfig {
4724 grid_dim: (out_f as u32, 1, t as u32),
4725 block_dim: (32, if step_b1_w8 { 8 } else { 1 }, 1),
4726 shared_mem_bytes: 0,
4727 };
4728 let __s_b = self.gpu.stream();
4729 let mut b = __s_b.launch_builder(&f);
4730 b.arg(table)
4731 .arg(sel)
4732 .arg(w)
4733 .arg(aq2)
4734 .arg(ad2)
4735 .arg(dst)
4736 .arg(&inf)
4737 .arg(&outf)
4738 .arg(&nu)
4739 .arg(&ne)
4740 .arg(&qt)
4741 .arg(&rbi);
4742 unsafe {
4743 b.launch(cfg)?;
4744 }
4745 Ok(())
4746 }
4747
4748 pub fn rp_probe_q4(&self, m: usize) -> Result<(f64, f64), Box<dyn std::error::Error>> {
4752 let (out_f, in_f) = (2048usize, 2816usize);
4753 let nblk = in_f / 32;
4754 let mut seed = 0x9E3779B97F4A7C15u64;
4755 let mut rng = move || {
4756 seed = seed
4757 .wrapping_mul(6364136223846793005)
4758 .wrapping_add(1442695040888963407);
4759 (seed >> 33) as u8
4760 };
4761 let mut w = vec![0u8; out_f * nblk * 18];
4762 for b in w.iter_mut() {
4763 *b = rng();
4764 }
4765 for r in 0..out_f {
4766 for g in 0..nblk {
4767 let off = (r * nblk + g) * 18;
4768 w[off] = 0x00;
4769 w[off + 1] = 0x2C; }
4771 }
4772 let qplane = out_f * nblk * 16;
4773 let mut wrp = vec![0u8; w.len()];
4774 for r in 0..out_f {
4775 for g in 0..nblk {
4776 let src = &w[(r * nblk + g) * 18..(r * nblk + g) * 18 + 18];
4777 wrp[qplane + (r * nblk + g) * 2..qplane + (r * nblk + g) * 2 + 2]
4778 .copy_from_slice(&src[0..2]);
4779 wrp[(r * nblk + g) * 16..(r * nblk + g) * 16 + 16].copy_from_slice(&src[2..18]);
4780 }
4781 }
4782 let w_d = self.htod_bytes(&w)?;
4783 let wrp_d = self.htod_bytes(&wrp)?;
4784 let mut aq = vec![0i8; m * in_f];
4785 for v in aq.iter_mut() {
4786 *v = rng() as i8;
4787 }
4788 let aq_d = self.htod_i8(&aq)?;
4789 let ad_d = self.htod(&vec![0.03125f32; m * nblk])?;
4790 let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
4791 let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
4792 const RPB: u32 = 4;
4793 let cfg = LaunchConfig {
4794 grid_dim: ((out_f as u32).div_ceil(RPB), 1, 1),
4795 block_dim: (32, RPB, 1),
4796 shared_mem_bytes: 0,
4797 };
4798 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
4799 let (rb, qp) = ((nblk * 18) as i64, qplane as i64);
4800 let fb = self.func("qmatvec_q4_0_mmvq_b4");
4801 let fr = self.func("qmatvec_q4_0_mmvq_b4_rp");
4802 {
4803 let __s_b = self.gpu.stream();
4804 let mut b = __s_b.launch_builder(&fb);
4805 b.arg(&w_d)
4806 .arg(&aq_d)
4807 .arg(&ad_d)
4808 .arg(&mut y0)
4809 .arg(&inf)
4810 .arg(&outf)
4811 .arg(&mi)
4812 .arg(&rb);
4813 unsafe {
4814 b.launch(cfg)?;
4815 }
4816 let __s_b = self.gpu.stream();
4817 let mut b = __s_b.launch_builder(&fr);
4818 b.arg(&wrp_d)
4819 .arg(&aq_d)
4820 .arg(&ad_d)
4821 .arg(&mut y1)
4822 .arg(&inf)
4823 .arg(&outf)
4824 .arg(&mi)
4825 .arg(&qp);
4826 unsafe {
4827 b.launch(cfg)?;
4828 }
4829 }
4830 self.gpu.stream().synchronize()?;
4831 let (h0, h1) = (self.dtoh(&y0)?, self.dtoh(&y1)?);
4832 let nd = h0
4833 .iter()
4834 .zip(&h1)
4835 .filter(|(a, b)| a.to_bits() != b.to_bits())
4836 .count();
4837 if nd != 0 {
4838 return Err(format!("rp twin not bitwise: {nd}/{} diffs", h0.len()).into());
4839 }
4840 let mut time = |rp: bool| -> Result<f64, Box<dyn std::error::Error>> {
4841 self.gpu.stream().synchronize()?;
4842 let t0 = std::time::Instant::now();
4843 for _ in 0..500 {
4844 if rp {
4845 let __s_b = self.gpu.stream();
4846 let mut b = __s_b.launch_builder(&fr);
4847 b.arg(&wrp_d)
4848 .arg(&aq_d)
4849 .arg(&ad_d)
4850 .arg(&mut y1)
4851 .arg(&inf)
4852 .arg(&outf)
4853 .arg(&mi)
4854 .arg(&qp);
4855 unsafe {
4856 b.launch(cfg)?;
4857 }
4858 } else {
4859 let __s_b = self.gpu.stream();
4860 let mut b = __s_b.launch_builder(&fb);
4861 b.arg(&w_d)
4862 .arg(&aq_d)
4863 .arg(&ad_d)
4864 .arg(&mut y0)
4865 .arg(&inf)
4866 .arg(&outf)
4867 .arg(&mi)
4868 .arg(&rb);
4869 unsafe {
4870 b.launch(cfg)?;
4871 }
4872 }
4873 }
4874 self.gpu.stream().synchronize()?;
4875 Ok(t0.elapsed().as_secs_f64() * 1e6 / 500.0)
4876 };
4877 let _ = time(false)?;
4878 let _ = time(true)?; Ok((time(false)?, time(true)?))
4880 }
4881
4882 pub fn build_q4_rp4(
4887 &self,
4888 t: &mut crate::model::GpuTensor,
4889 ) -> Result<(), Box<dyn std::error::Error>> {
4890 use crate::model::GpuTensor;
4891 let GpuTensor::Quant {
4892 bytes,
4893 qtype,
4894 row_bytes,
4895 ne,
4896 rp4,
4897 ..
4898 } = t
4899 else {
4900 return Ok(());
4901 };
4902 if *qtype != QT_Q4_0 || rp4.is_some() || ne.len() != 2 {
4903 return Ok(());
4904 }
4905 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
4906 if in_f % 32 != 0 || *row_bytes != (in_f / 32) * 18 {
4907 return Ok(());
4908 }
4909 let nblk = in_f / 32;
4910 let mut dst = self.alloc_uninit::<u8>(out_f * nblk * 18)?;
4911 let f = self.func("q4_0_split_rp_build");
4912 let n = (out_f * nblk) as i32;
4913 let cfg = LaunchConfig {
4914 grid_dim: (((out_f * nblk) as u32).div_ceil(256), 1, 1),
4915 block_dim: (256, 1, 1),
4916 shared_mem_bytes: 0,
4917 };
4918 let (of, nb) = (out_f as i32, nblk as i32);
4919 let _ = n;
4920 let __s_b = self.gpu.stream();
4921 let mut b = __s_b.launch_builder(&f);
4922 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
4923 unsafe {
4924 b.launch(cfg)?;
4925 }
4926 *rp4 = Some(dst);
4927 Ok(())
4928 }
4929
4930 pub fn build_q8_rp4(
4935 &self,
4936 t: &mut crate::model::GpuTensor,
4937 ) -> Result<(), Box<dyn std::error::Error>> {
4938 use crate::model::GpuTensor;
4939 let GpuTensor::Quant {
4940 bytes,
4941 qtype,
4942 row_bytes,
4943 ne,
4944 rp4,
4945 ..
4946 } = t
4947 else {
4948 return Ok(());
4949 };
4950 if *qtype != QT_Q8_0 || rp4.is_some() || ne.len() != 2 {
4951 return Ok(());
4952 }
4953 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
4954 if in_f % 32 != 0 || *row_bytes != (in_f / 32) * 34 {
4955 return Ok(());
4956 }
4957 *rp4 = Some(self.build_q8_rp4_raw(bytes, in_f, out_f)?);
4958 Ok(())
4959 }
4960
4961 pub fn build_q8_rp4_raw(
4964 &self,
4965 bytes: &CudaSlice<u8>,
4966 in_f: usize,
4967 out_f: usize,
4968 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
4969 assert!(in_f % 32 == 0);
4970 let nblk = in_f / 32;
4971 let mut dst = self.alloc_uninit::<u8>(out_f * nblk * 34)?;
4972 let f = self.func("q8_0_split_rp_build");
4973 let cfg = LaunchConfig {
4974 grid_dim: (((out_f * nblk) as u32).div_ceil(256), 1, 1),
4975 block_dim: (256, 1, 1),
4976 shared_mem_bytes: 0,
4977 };
4978 let (of, nb) = (out_f as i32, nblk as i32);
4979 let __s_b = self.gpu.stream();
4980 let mut b = __s_b.launch_builder(&f);
4981 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
4982 unsafe {
4983 b.launch(cfg)?;
4984 }
4985 Ok(dst)
4986 }
4987
4988 pub fn build_q4k_rp4(
4996 &self,
4997 t: &mut crate::model::GpuTensor,
4998 ) -> Result<(), Box<dyn std::error::Error>> {
4999 use crate::model::GpuTensor;
5000 let GpuTensor::Quant {
5001 bytes,
5002 qtype,
5003 row_bytes,
5004 ne,
5005 rp4,
5006 ..
5007 } = t
5008 else {
5009 return Ok(());
5010 };
5011 if *qtype != QT_Q4_K || rp4.is_some() || ne.len() != 2 {
5012 return Ok(());
5013 }
5014 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5015 if in_f % 256 != 0 || *row_bytes != (in_f / 256) * 144 {
5016 return Ok(());
5017 }
5018 *rp4 = Some(self.build_kq_rp4_raw(bytes, in_f, out_f, QT_Q4_K)?);
5019 Ok(())
5020 }
5021
5022 pub fn build_q6k_rp4(
5023 &self,
5024 t: &mut crate::model::GpuTensor,
5025 ) -> Result<(), Box<dyn std::error::Error>> {
5026 use crate::model::GpuTensor;
5027 let GpuTensor::Quant {
5028 bytes,
5029 qtype,
5030 row_bytes,
5031 ne,
5032 rp4,
5033 ..
5034 } = t
5035 else {
5036 return Ok(());
5037 };
5038 if *qtype != QT_Q6_K || rp4.is_some() || ne.len() != 2 {
5039 return Ok(());
5040 }
5041 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5042 if in_f % 256 != 0 || *row_bytes != (in_f / 256) * 210 {
5043 return Ok(());
5044 }
5045 *rp4 = Some(self.build_kq_rp4_raw(bytes, in_f, out_f, QT_Q6_K)?);
5046 Ok(())
5047 }
5048
5049 pub fn build_kq_rp4_raw(
5051 &self,
5052 bytes: &CudaSlice<u8>,
5053 in_f: usize,
5054 out_f: usize,
5055 qtype: i32,
5056 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
5057 assert!(in_f % 256 == 0);
5058 let nsbk = in_f / 256;
5059 let (sb_bytes, kname) = match qtype {
5060 QT_Q4_K => (144usize, "q4_K_split_rp_build"),
5061 QT_Q6_K => (210usize, "q6_K_split_rp_build"),
5062 _ => return Err(format!("build_kq_rp4_raw: qtype {qtype} has no rp mirror").into()),
5063 };
5064 let mut dst = self.alloc_uninit::<u8>(out_f * nsbk * sb_bytes)?;
5065 let f = self.func(kname);
5066 let cfg = LaunchConfig {
5067 grid_dim: (((out_f * nsbk) as u32).div_ceil(256), 1, 1),
5068 block_dim: (256, 1, 1),
5069 shared_mem_bytes: 0,
5070 };
5071 let (of, nb) = (out_f as i32, nsbk as i32);
5072 let __s_b = self.gpu.stream();
5073 let mut b = __s_b.launch_builder(&f);
5074 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
5075 unsafe {
5076 b.launch(cfg)?;
5077 }
5078 Ok(dst)
5079 }
5080
5081 pub fn kqrp_enabled() -> bool {
5085 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5086 *ON.get_or_init(|| match std::env::var("MEMRA_KQRP").as_deref() {
5087 Ok("0") => false,
5088 Ok(_) => true,
5089 Err(_) => cfg!(memra_hopper_mma),
5090 })
5091 }
5092
5093 pub fn build_q4_rp_swap(
5099 &self,
5100 t: &mut crate::model::GpuTensor,
5101 ) -> Result<bool, Box<dyn std::error::Error>> {
5102 self.build_q4_rp4(t)?;
5103 self.gpu.stream().synchronize()?; use crate::model::GpuTensor;
5105 let GpuTensor::Quant { bytes, rp4, rp, .. } = t else {
5106 return Ok(false);
5107 };
5108 match rp4.take() {
5109 Some(split) => {
5110 *bytes = split; *rp = true;
5112 Ok(true)
5113 }
5114 None => Ok(false),
5115 }
5116 }
5117
5118 pub fn q4rp_enabled() -> bool {
5120 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5121 *ON.get_or_init(|| {
5122 std::env::var("MEMRA_Q4RP")
5123 .map(|v| v != "0")
5124 .unwrap_or(true)
5125 })
5126 }
5127
5128 pub fn copy_rows_strided(
5131 &self,
5132 src: &CudaSlice<f32>,
5133 dst: &mut CudaSlice<f32>,
5134 row_elems: usize,
5135 n_rows: usize,
5136 src_stride: usize,
5137 src_off: usize,
5138 ) -> Result<(), Box<dyn std::error::Error>> {
5139 let f = self.func("copy_rows_strided_f32");
5140 let cfg = LaunchConfig {
5141 grid_dim: (((row_elems as u32 + 255) / 256).max(1), n_rows as u32, 1),
5142 block_dim: (256, 1, 1),
5143 shared_mem_bytes: 0,
5144 };
5145 let (re, nr) = (row_elems as i32, n_rows as i32);
5146 let (st, off) = (src_stride as i64, src_off as i64);
5147 let __s_b = self.gpu.stream();
5148 let mut b = __s_b.launch_builder(&f);
5149 b.arg(src)
5150 .arg(&mut *dst)
5151 .arg(&re)
5152 .arg(&nr)
5153 .arg(&st)
5154 .arg(&off);
5155 unsafe {
5156 b.launch(cfg)?;
5157 }
5158 Ok(())
5159 }
5160
5161 pub fn u32_set_k(
5163 &self,
5164 dst: &mut CudaSlice<u32>,
5165 v: u32,
5166 idx: usize,
5167 ) -> Result<(), Box<dyn std::error::Error>> {
5168 let f = self.func("u32_set_k");
5169 let cfg = LaunchConfig {
5170 grid_dim: (1, 1, 1),
5171 block_dim: (1, 1, 1),
5172 shared_mem_bytes: 0,
5173 };
5174 let ii = idx as i32;
5175 let __s_b = self.gpu.stream();
5176 let mut b = __s_b.launch_builder(&f);
5177 b.arg(dst).arg(&v).arg(&ii);
5178 unsafe {
5179 b.launch(cfg)?;
5180 }
5181 Ok(())
5182 }
5183
5184 pub fn i32_add_k(
5186 &self,
5187 d: &mut CudaSlice<i32>,
5188 v: i32,
5189 ) -> Result<(), Box<dyn std::error::Error>> {
5190 let f = self.func("i32_add_k");
5191 let cfg = LaunchConfig {
5192 grid_dim: (1, 1, 1),
5193 block_dim: (32, 1, 1),
5194 shared_mem_bytes: 0,
5195 };
5196 let __s_b = self.gpu.stream();
5197 let mut b = __s_b.launch_builder(&f);
5198 b.arg(d).arg(&v);
5199 unsafe {
5200 b.launch(cfg)?;
5201 }
5202 Ok(())
5203 }
5204
5205 pub fn i32_iota_from(
5207 &self,
5208 ctr: &CudaSlice<i32>,
5209 dst: &mut CudaSlice<i32>,
5210 n: usize,
5211 ) -> Result<(), Box<dyn std::error::Error>> {
5212 let f = self.func("i32_iota_from");
5213 let cfg = LaunchConfig::for_num_elems(n as u32);
5214 let ni = n as i32;
5215 let __s_b = self.gpu.stream();
5216 let mut b = __s_b.launch_builder(&f);
5217 b.arg(ctr).arg(dst).arg(&ni);
5218 unsafe {
5219 b.launch(cfg)?;
5220 }
5221 Ok(())
5222 }
5223
5224 pub fn u32_map_k(
5226 &self,
5227 buf: &mut CudaSlice<u32>,
5228 map: &CudaSlice<u32>,
5229 idx: usize,
5230 ) -> Result<(), Box<dyn std::error::Error>> {
5231 let f = self.func("u32_map_k");
5232 let cfg = LaunchConfig {
5233 grid_dim: (1, 1, 1),
5234 block_dim: (1, 1, 1),
5235 shared_mem_bytes: 0,
5236 };
5237 let ii = idx as i32;
5238 let __s_b = self.gpu.stream();
5239 let mut b = __s_b.launch_builder(&f);
5240 b.arg(buf).arg(map).arg(&ii);
5241 unsafe {
5242 b.launch(cfg)?;
5243 }
5244 Ok(())
5245 }
5246
5247 #[allow(clippy::too_many_arguments)]
5249 pub fn u32_pack2(
5250 &self,
5251 a: &CudaSlice<u32>,
5252 off_a: usize,
5253 n1: usize,
5254 b_in: &CudaSlice<u32>,
5255 n2: usize,
5256 out: &mut CudaSlice<u32>,
5257 ) -> Result<(), Box<dyn std::error::Error>> {
5258 let f = self.func("u32_pack2");
5259 let cfg = LaunchConfig::for_num_elems((n1 + n2) as u32);
5260 let (oa, i1, i2) = (off_a as i32, n1 as i32, n2 as i32);
5261 let __s_b = self.gpu.stream();
5262 let mut b = __s_b.launch_builder(&f);
5263 b.arg(a).arg(&oa).arg(&i1).arg(b_in).arg(&i2).arg(out);
5264 unsafe {
5265 b.launch(cfg)?;
5266 }
5267 Ok(())
5268 }
5269
5270 pub fn moe_w_exscale(
5272 &self,
5273 w: &mut CudaSlice<f32>,
5274 sel: &CudaSlice<i32>,
5275 s: &CudaSlice<f32>,
5276 n: usize,
5277 ) -> Result<(), Box<dyn std::error::Error>> {
5278 let f = self.func("moe_w_exscale");
5279 let cfg = LaunchConfig::for_num_elems(n as u32);
5280 let ni = n as i32;
5281 let __s_b = self.gpu.stream();
5282 let mut b = __s_b.launch_builder(&f);
5283 b.arg(w).arg(sel).arg(s).arg(&ni);
5284 unsafe {
5285 b.launch(cfg)?;
5286 }
5287 Ok(())
5288 }
5289
5290 pub fn moe_w_scale_by_expert(
5293 &self,
5294 w: &mut CudaSlice<f32>,
5295 sel: &CudaSlice<i32>,
5296 macros: &CudaSlice<f32>,
5297 n_expert: usize,
5298 n: usize,
5299 ) -> Result<(), Box<dyn std::error::Error>> {
5300 let f = self.func("moe_w_scale_by_expert");
5301 let cfg = LaunchConfig {
5302 grid_dim: (n.div_ceil(64) as u32, 1, 1),
5303 block_dim: (64, 1, 1),
5304 shared_mem_bytes: 0,
5305 };
5306 let (ne, nn) = (n_expert as i32, n as i32);
5307 let __s_b = self.gpu.stream();
5308 let mut b = __s_b.launch_builder(&f);
5309 b.arg(w).arg(sel).arg(macros).arg(&ne).arg(&nn);
5310 unsafe {
5311 b.launch(cfg)?;
5312 }
5313 Ok(())
5314 }
5315
5316 pub fn moe_gate_up_silu8_dev_q8(
5317 &self,
5318 table: &CudaSlice<u64>,
5319 sel: &cudarc::driver::CudaView<i32>,
5320 aq: &CudaSlice<i8>,
5321 ad: &CudaSlice<f32>,
5322 in_f: usize,
5323 n_ff: usize,
5324 n_used: usize,
5325 n_expert: usize,
5326 qt_g: i32,
5327 qt_u: i32,
5328 rb_g: usize,
5329 rb_u: usize,
5330 macros: &CudaSlice<f32>,
5331 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5332 static GU: std::sync::OnceLock<(String, u32)> = std::sync::OnceLock::new();
5333 let (mode, wpb) = GU.get_or_init(|| {
5334 let mode = std::env::var("MEMRA_MOE_DEVQ8_GU").unwrap_or_default();
5335 let wpb = std::env::var("MEMRA_MOE_DEVQ8_WPB")
5336 .ok()
5337 .and_then(|v| v.parse().ok())
5338 .unwrap_or(4u32)
5339 .clamp(1, 16);
5340 (mode, wpb)
5341 });
5342 let (mode, wpb) = (mode.as_str(), *wpb);
5343 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
5344 let (inf, nff, ne, rbg, rbu) = (
5345 in_f as i32,
5346 n_ff as i32,
5347 n_expert as i32,
5348 rb_g as i64,
5349 rb_u as i64,
5350 );
5351 let (f, cfg) = match mode {
5352 "1" | "2" | "4" => {
5353 let rpw: u32 = mode.parse().unwrap();
5354 let f = self.func(match rpw {
5355 1 => "moe_gate_up_silu8_dev_q8_r1",
5356 2 => "moe_gate_up_silu8_dev_q8_r2",
5357 _ => "moe_gate_up_silu8_dev_q8_r4",
5358 });
5359 let rows_per_block = (rpw * wpb) as usize;
5360 let gx = n_ff.div_ceil(rows_per_block) as u32;
5361 (
5362 f,
5363 LaunchConfig {
5364 grid_dim: (gx, n_used as u32, 1),
5365 block_dim: (32, wpb, 1),
5366 shared_mem_bytes: 0,
5367 },
5368 )
5369 }
5370 "j8" if n_used <= 32 => (
5371 self.func("moe_gate_up_silu8_dev_q8_j8"),
5372 LaunchConfig {
5373 grid_dim: (n_ff as u32, 1, 1),
5374 block_dim: (32, n_used as u32, 1),
5375 shared_mem_bytes: 0,
5376 },
5377 ),
5378 "vsm2" => {
5380 let f = self.func("moe_gate_up_silu8_dev_q8_vsm2");
5381 let sh = (rb_g + rb_u) as u32;
5382 use cudarc::driver::sys::CUfunction_attribute_enum as A;
5383 f.set_attribute(
5384 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
5385 sh as i32,
5386 )?;
5387 (
5388 f,
5389 LaunchConfig {
5390 grid_dim: (n_ff as u32, n_used as u32, 1),
5391 block_dim: (32, 1, 1),
5392 shared_mem_bytes: sh,
5393 },
5394 )
5395 }
5396 "vsm" => {
5397 let f = self.func("moe_gate_up_silu8_dev_q8_vsm");
5398 let sh = (rb_g + rb_u) as u32;
5399 use cudarc::driver::sys::CUfunction_attribute_enum as A;
5400 f.set_attribute(
5401 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
5402 sh as i32,
5403 )?;
5404 (
5405 f,
5406 LaunchConfig {
5407 grid_dim: (n_ff as u32, n_used as u32, 1),
5408 block_dim: (32, 1, 1),
5409 shared_mem_bytes: sh,
5410 },
5411 )
5412 }
5413 "sg" => (
5414 self.func("moe_gate_up_silu8_dev_q8_sg"),
5415 LaunchConfig {
5416 grid_dim: (n_ff as u32, n_used as u32, 1),
5417 block_dim: (32, 1, 1),
5418 shared_mem_bytes: 0,
5419 },
5420 ),
5421 "j8sg" if n_used <= 32 => (
5422 self.func("moe_gate_up_silu8_dev_q8_j8sg"),
5423 LaunchConfig {
5424 grid_dim: (n_ff as u32, 1, 1),
5425 block_dim: (32, n_used as u32, 1),
5426 shared_mem_bytes: 0,
5427 },
5428 ),
5429 "u64" if in_f == 2048 => (
5430 self.func("moe_gate_up_silu8_dev_q8_u64"),
5431 LaunchConfig {
5432 grid_dim: (n_ff as u32, n_used as u32, 1),
5433 block_dim: (32, 1, 1),
5434 shared_mem_bytes: 0,
5435 },
5436 ),
5437 "gs4" if in_f == 2048 => (
5438 self.func("moe_gate_up_silu8_dev_q8_gs4"),
5439 LaunchConfig {
5440 grid_dim: (n_ff as u32, n_used as u32, 1),
5441 block_dim: (32, 4, 1),
5442 shared_mem_bytes: 0,
5443 },
5444 ),
5445 "v" | "" => (
5447 self.func("moe_gate_up_silu8_dev_q8_v"),
5448 LaunchConfig {
5449 grid_dim: (n_ff as u32, n_used as u32, 1),
5450 block_dim: (32, 1, 1),
5451 shared_mem_bytes: 0,
5452 },
5453 ),
5454 "s2" => (
5455 self.func("moe_gate_up_silu8_dev_q8_s2"),
5456 LaunchConfig {
5457 grid_dim: (n_ff as u32, n_used as u32, 1),
5458 block_dim: (32, 2, 1),
5459 shared_mem_bytes: 0,
5460 },
5461 ),
5462 "s2z" => {
5463 let rz = wpb.min(16); (
5465 self.func("moe_gate_up_silu8_dev_q8_s2z"),
5466 LaunchConfig {
5467 grid_dim: (n_ff.div_ceil(rz as usize) as u32, n_used as u32, 1),
5468 block_dim: (32, 2, rz),
5469 shared_mem_bytes: 0,
5470 },
5471 )
5472 }
5473 _ => (
5474 self.func("moe_gate_up_silu8_dev_q8"),
5475 LaunchConfig {
5476 grid_dim: (n_ff as u32, n_used as u32, 1),
5477 block_dim: (32, 1, 1),
5478 shared_mem_bytes: 0,
5479 },
5480 ),
5481 };
5482 let __s_b = self.gpu.stream();
5483 let mut b = __s_b.launch_builder(&f);
5484 b.arg(table)
5485 .arg(sel)
5486 .arg(aq)
5487 .arg(ad)
5488 .arg(&mut act)
5489 .arg(&inf)
5490 .arg(&nff)
5491 .arg(&ne)
5492 .arg(&qt_g)
5493 .arg(&qt_u)
5494 .arg(&rbg)
5495 .arg(&rbu)
5496 .arg(macros);
5497 unsafe {
5498 b.launch(cfg)?;
5499 }
5500 Ok(act)
5501 }
5502
5503 #[allow(clippy::too_many_arguments)]
5504 pub fn moe_down8_fma_dev_q8(
5505 &self,
5506 table: &CudaSlice<u64>,
5507 sel: &cudarc::driver::CudaView<i32>,
5508 w: &cudarc::driver::CudaView<f32>,
5509 aq2: &CudaSlice<i8>,
5510 ad2: &CudaSlice<f32>,
5511 dst: &mut cudarc::driver::CudaViewMut<f32>,
5512 in_f: usize,
5513 out_f: usize,
5514 n_used: usize,
5515 n_expert: usize,
5516 qt: i32,
5517 rb: usize,
5518 ) -> Result<(), Box<dyn std::error::Error>> {
5519 static DOWN: std::sync::OnceLock<String> = std::sync::OnceLock::new();
5520 let mode = DOWN.get_or_init(|| std::env::var("MEMRA_MOE_DEVQ8_DOWN").unwrap_or_default());
5521 let (inf, outf, nu, ne, rbi) = (
5522 in_f as i32,
5523 out_f as i32,
5524 n_used as i32,
5525 n_expert as i32,
5526 rb as i64,
5527 );
5528 let (f, cfg) = match mode.as_str() {
5531 m @ ("1" | "2" | "4") if n_used <= 8 => {
5532 let rpw: usize = m.parse().unwrap();
5533 let f = self.func(match rpw {
5534 1 => "moe_down8_fma_dev_q8_w8r1",
5535 2 => "moe_down8_fma_dev_q8_w8r2",
5536 _ => "moe_down8_fma_dev_q8_w8r4",
5537 });
5538 (
5539 f,
5540 LaunchConfig {
5541 grid_dim: (out_f.div_ceil(rpw) as u32, 1, 1),
5542 block_dim: (32, n_used as u32, 1),
5543 shared_mem_bytes: 0,
5544 },
5545 )
5546 }
5547 "h2" if in_f == 512 => (
5548 self.func("moe_down8_fma_dev_q8_h2"),
5549 LaunchConfig {
5550 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5551 block_dim: (32, 1, 1),
5552 shared_mem_bytes: 0,
5553 },
5554 ),
5555 "" if in_f == 704 && n_used <= 8 => (
5558 self.func("moe_down8_fma_dev_q8_w8r2"),
5559 LaunchConfig {
5560 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5561 block_dim: (32, n_used as u32, 1),
5562 shared_mem_bytes: 0,
5563 },
5564 ),
5565 "w8h2v" | "" if in_f == 512 && n_used <= 8 => (
5569 self.func("moe_down8_fma_dev_q8_w8h2v"),
5570 LaunchConfig {
5571 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5572 block_dim: (32, n_used as u32, 1),
5573 shared_mem_bytes: 0,
5574 },
5575 ),
5576 "w8h2r2v" if in_f == 512 && n_used <= 8 => (
5577 self.func("moe_down8_fma_dev_q8_w8h2r2v"),
5578 LaunchConfig {
5579 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
5580 block_dim: (32, n_used as u32, 1),
5581 shared_mem_bytes: 0,
5582 },
5583 ),
5584 "w8h2r2" if in_f == 512 && n_used <= 8 => (
5585 self.func("moe_down8_fma_dev_q8_w8h2r2"),
5586 LaunchConfig {
5587 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
5588 block_dim: (32, n_used as u32, 1),
5589 shared_mem_bytes: 0,
5590 },
5591 ),
5592 "w8h2" if in_f == 512 && n_used <= 8 => (
5593 self.func("moe_down8_fma_dev_q8_w8h2"),
5594 LaunchConfig {
5595 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5596 block_dim: (32, n_used as u32, 1),
5597 shared_mem_bytes: 0,
5598 },
5599 ),
5600 _ => (
5601 self.func("moe_down8_fma_dev_q8"),
5602 LaunchConfig {
5603 grid_dim: (out_f as u32, 1, 1),
5604 block_dim: (32, 1, 1),
5605 shared_mem_bytes: 0,
5606 },
5607 ),
5608 };
5609 let __s_b = self.gpu.stream();
5610 let mut b = __s_b.launch_builder(&f);
5611 b.arg(table)
5612 .arg(sel)
5613 .arg(w)
5614 .arg(aq2)
5615 .arg(ad2)
5616 .arg(dst)
5617 .arg(&inf)
5618 .arg(&outf)
5619 .arg(&nu)
5620 .arg(&ne)
5621 .arg(&qt)
5622 .arg(&rbi);
5623 unsafe {
5624 b.launch(cfg)?;
5625 }
5626 Ok(())
5627 }
5628
5629 #[allow(clippy::too_many_arguments)]
5636 pub fn moe_gate_up_silu8_dev_q8_rows(
5637 &self,
5638 table: &CudaSlice<u64>,
5639 sel: &CudaSlice<i32>,
5640 aq: &CudaSlice<i8>,
5641 ad: &CudaSlice<f32>,
5642 t: usize,
5643 in_f: usize,
5644 n_ff: usize,
5645 n_used: usize,
5646 n_expert: usize,
5647 qt_g: i32,
5648 qt_u: i32,
5649 rb_g: usize,
5650 rb_u: usize,
5651 macros: &CudaSlice<f32>,
5652 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5653 let f = self.func("moe_gate_up_silu8_dev_q8_v_rows");
5654 let mut act = self.alloc_uninit::<f32>(t * n_used * n_ff)?;
5655 let cfg = LaunchConfig {
5656 grid_dim: (n_ff as u32, n_used as u32, t as u32),
5657 block_dim: (32, 1, 1),
5658 shared_mem_bytes: 0,
5659 };
5660 let (inf, nff, ne, nu, rbg, rbu) = (
5661 in_f as i32,
5662 n_ff as i32,
5663 n_expert as i32,
5664 n_used as i32,
5665 rb_g as i64,
5666 rb_u as i64,
5667 );
5668 let __s_b = self.gpu.stream();
5669 let mut b = __s_b.launch_builder(&f);
5670 b.arg(table)
5671 .arg(sel)
5672 .arg(aq)
5673 .arg(ad)
5674 .arg(&mut act)
5675 .arg(&inf)
5676 .arg(&nff)
5677 .arg(&ne)
5678 .arg(&qt_g)
5679 .arg(&qt_u)
5680 .arg(&rbg)
5681 .arg(&rbu)
5682 .arg(&nu)
5683 .arg(macros);
5684 unsafe {
5685 b.launch(cfg)?;
5686 }
5687 Ok(act)
5688 }
5689
5690 #[allow(clippy::too_many_arguments)]
5695 pub fn moe_down8_fma_dev_q8_rows(
5696 &self,
5697 table: &CudaSlice<u64>,
5698 sel: &CudaSlice<i32>,
5699 w: &CudaSlice<f32>,
5700 aq2: &CudaSlice<i8>,
5701 ad2: &CudaSlice<f32>,
5702 dst: &mut CudaSlice<f32>,
5703 t: usize,
5704 in_f: usize,
5705 out_f: usize,
5706 n_used: usize,
5707 n_expert: usize,
5708 qt: i32,
5709 rb: usize,
5710 ) -> Result<(), Box<dyn std::error::Error>> {
5711 assert!(
5712 in_f == 512 && n_used <= 8,
5713 "down rows twin is w8h2v shape-gated"
5714 );
5715 let f = self.func("moe_down8_fma_dev_q8_w8h2v_rows");
5716 let cfg = LaunchConfig {
5717 grid_dim: (out_f.div_ceil(2) as u32, 1, t as u32),
5718 block_dim: (32, n_used as u32, 1),
5719 shared_mem_bytes: 0,
5720 };
5721 let (inf, outf, nu, ne, rbi) = (
5722 in_f as i32,
5723 out_f as i32,
5724 n_used as i32,
5725 n_expert as i32,
5726 rb as i64,
5727 );
5728 let __s_b = self.gpu.stream();
5729 let mut b = __s_b.launch_builder(&f);
5730 b.arg(table)
5731 .arg(sel)
5732 .arg(w)
5733 .arg(aq2)
5734 .arg(ad2)
5735 .arg(dst)
5736 .arg(&inf)
5737 .arg(&outf)
5738 .arg(&nu)
5739 .arg(&ne)
5740 .arg(&qt)
5741 .arg(&rbi);
5742 unsafe {
5743 b.launch(cfg)?;
5744 }
5745 Ok(())
5746 }
5747
5748 #[allow(clippy::too_many_arguments)]
5752 pub fn moe_gate_up_silu8_dev_q8_csr(
5753 &self,
5754 table: &CudaSlice<u64>,
5755 sel: &CudaSlice<i32>,
5756 aq: &CudaSlice<i8>,
5757 ad: &CudaSlice<f32>,
5758 n_pairs: usize,
5759 in_f: usize,
5760 n_ff: usize,
5761 n_used: usize,
5762 n_expert: usize,
5763 qt_g: i32,
5764 qt_u: i32,
5765 rb_g: usize,
5766 rb_u: usize,
5767 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5768 let f = self.func("moe_gate_up_silu8_dev_q8_csr_iq4");
5769 let mut act = self.alloc_uninit::<f32>(n_pairs * n_ff)?;
5770 let cfg = LaunchConfig {
5771 grid_dim: (n_ff as u32, n_pairs as u32, 1),
5772 block_dim: (32, 1, 1),
5773 shared_mem_bytes: 0,
5774 };
5775 let (inf, nff, ne, nu, npi, rbg, rbu) = (
5776 in_f as i32,
5777 n_ff as i32,
5778 n_expert as i32,
5779 n_used as i32,
5780 n_pairs as i32,
5781 rb_g as i64,
5782 rb_u as i64,
5783 );
5784 let __s_b = self.gpu.stream();
5785 let mut b = __s_b.launch_builder(&f);
5786 b.arg(table)
5787 .arg(sel)
5788 .arg(aq)
5789 .arg(ad)
5790 .arg(&mut act)
5791 .arg(&inf)
5792 .arg(&nff)
5793 .arg(&ne)
5794 .arg(&qt_g)
5795 .arg(&qt_u)
5796 .arg(&rbg)
5797 .arg(&rbu)
5798 .arg(&nu)
5799 .arg(&npi);
5800 unsafe {
5801 b.launch(cfg)?;
5802 }
5803 Ok(act)
5804 }
5805
5806 #[allow(clippy::too_many_arguments)]
5810 pub fn moe_down8_fma_dev_q8_variant(
5811 &self,
5812 variant: &str,
5813 table: &CudaSlice<u64>,
5814 sel: &cudarc::driver::CudaView<i32>,
5815 w: &cudarc::driver::CudaView<f32>,
5816 aq2: &CudaSlice<i8>,
5817 ad2: &CudaSlice<f32>,
5818 dst: &mut cudarc::driver::CudaViewMut<f32>,
5819 in_f: usize,
5820 out_f: usize,
5821 n_used: usize,
5822 n_expert: usize,
5823 qt: i32,
5824 rb: usize,
5825 ) -> Result<(), Box<dyn std::error::Error>> {
5826 let (inf, outf, nu, ne, rbi) = (
5827 in_f as i32,
5828 out_f as i32,
5829 n_used as i32,
5830 n_expert as i32,
5831 rb as i64,
5832 );
5833 let (f, cfg) = match variant {
5834 "w8h2" | "w8h2v" => (
5835 self.func(if variant == "w8h2" {
5836 "moe_down8_fma_dev_q8_w8h2"
5837 } else {
5838 "moe_down8_fma_dev_q8_w8h2v"
5839 }),
5840 LaunchConfig {
5841 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5842 block_dim: (32, n_used as u32, 1),
5843 shared_mem_bytes: 0,
5844 },
5845 ),
5846 "w8h2r2" | "w8h2r2v" => (
5847 self.func(if variant == "w8h2r2" {
5848 "moe_down8_fma_dev_q8_w8h2r2"
5849 } else {
5850 "moe_down8_fma_dev_q8_w8h2r2v"
5851 }),
5852 LaunchConfig {
5853 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
5854 block_dim: (32, n_used as u32, 1),
5855 shared_mem_bytes: 0,
5856 },
5857 ),
5858 _ => (
5859 self.func("moe_down8_fma_dev_q8"),
5860 LaunchConfig {
5861 grid_dim: (out_f as u32, 1, 1),
5862 block_dim: (32, 1, 1),
5863 shared_mem_bytes: 0,
5864 },
5865 ),
5866 };
5867 let __s_b = self.gpu.stream();
5868 let mut b = __s_b.launch_builder(&f);
5869 b.arg(table)
5870 .arg(sel)
5871 .arg(w)
5872 .arg(aq2)
5873 .arg(ad2)
5874 .arg(dst)
5875 .arg(&inf)
5876 .arg(&outf)
5877 .arg(&nu)
5878 .arg(&ne)
5879 .arg(&qt)
5880 .arg(&rbi);
5881 unsafe {
5882 b.launch(cfg)?;
5883 }
5884 Ok(())
5885 }
5886
5887 #[allow(clippy::too_many_arguments)]
5889 pub fn moe_gate_up_silu8_dev_q8_variant(
5890 &self,
5891 variant: &str,
5892 table: &CudaSlice<u64>,
5893 sel: &cudarc::driver::CudaView<i32>,
5894 aq: &CudaSlice<i8>,
5895 ad: &CudaSlice<f32>,
5896 in_f: usize,
5897 n_ff: usize,
5898 n_used: usize,
5899 n_expert: usize,
5900 qt_g: i32,
5901 qt_u: i32,
5902 rb_g: usize,
5903 rb_u: usize,
5904 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5905 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
5906 let (inf, nff, ne, rbg, rbu) = (
5907 in_f as i32,
5908 n_ff as i32,
5909 n_expert as i32,
5910 rb_g as i64,
5911 rb_u as i64,
5912 );
5913 let f = self.func(if variant == "v" {
5914 "moe_gate_up_silu8_dev_q8_v"
5915 } else {
5916 "moe_gate_up_silu8_dev_q8"
5917 });
5918 let cfg = LaunchConfig {
5919 grid_dim: (n_ff as u32, n_used as u32, 1),
5920 block_dim: (32, 1, 1),
5921 shared_mem_bytes: 0,
5922 };
5923 let __s_b = self.gpu.stream();
5924 let mut b = __s_b.launch_builder(&f);
5925 b.arg(table)
5926 .arg(sel)
5927 .arg(aq)
5928 .arg(ad)
5929 .arg(&mut act)
5930 .arg(&inf)
5931 .arg(&nff)
5932 .arg(&ne)
5933 .arg(&qt_g)
5934 .arg(&qt_u)
5935 .arg(&rbg)
5936 .arg(&rbu);
5937 unsafe {
5938 b.launch(cfg)?;
5939 }
5940 Ok(act)
5941 }
5942
5943 pub fn moe_gate_up_silu8_dev(
5944 &self,
5945 table: &CudaSlice<u64>,
5946 sel: &cudarc::driver::CudaView<i32>,
5947 x: &cudarc::driver::CudaView<f32>,
5948 in_f: usize,
5949 n_ff: usize,
5950 n_used: usize,
5951 n_expert: usize,
5952 qt_g: i32,
5953 qt_u: i32,
5954 rb_g: usize,
5955 rb_u: usize,
5956 macros: &CudaSlice<f32>,
5957 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5958 let f = self.func("moe_gate_up_silu8_dev");
5959 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?; let cfg = LaunchConfig {
5961 grid_dim: (n_ff as u32, n_used as u32, 1),
5962 block_dim: (256, 1, 1),
5963 shared_mem_bytes: 0,
5964 };
5965 let (inf, nff, ne, rbg, rbu) = (
5966 in_f as i32,
5967 n_ff as i32,
5968 n_expert as i32,
5969 rb_g as i64,
5970 rb_u as i64,
5971 );
5972 let __s_b = self.gpu.stream();
5973 let mut b = __s_b.launch_builder(&f);
5974 b.arg(table)
5975 .arg(sel)
5976 .arg(x)
5977 .arg(&mut act)
5978 .arg(&inf)
5979 .arg(&nff)
5980 .arg(&ne)
5981 .arg(&qt_g)
5982 .arg(&qt_u)
5983 .arg(&rbg)
5984 .arg(&rbu)
5985 .arg(macros);
5986 unsafe {
5987 b.launch(cfg)?;
5988 }
5989 Ok(act)
5990 }
5991
5992 #[allow(clippy::too_many_arguments)]
5995 pub fn moe_down8_fma_dev(
5996 &self,
5997 table: &CudaSlice<u64>,
5998 sel: &cudarc::driver::CudaView<i32>,
5999 w: &cudarc::driver::CudaView<f32>,
6000 act: &CudaSlice<f32>,
6001 dst: &mut cudarc::driver::CudaViewMut<f32>,
6002 in_f: usize,
6003 out_f: usize,
6004 n_used: usize,
6005 n_expert: usize,
6006 qt: i32,
6007 rb: usize,
6008 ) -> Result<(), Box<dyn std::error::Error>> {
6009 let f = self.func("moe_down8_fma_dev");
6010 let cfg = LaunchConfig {
6011 grid_dim: (out_f as u32, 1, 1),
6012 block_dim: (256, 1, 1),
6013 shared_mem_bytes: 0,
6014 };
6015 let (inf, outf, nu, ne, rbv) = (
6016 in_f as i32,
6017 out_f as i32,
6018 n_used as i32,
6019 n_expert as i32,
6020 rb as i64,
6021 );
6022 let __s_b = self.gpu.stream();
6023 let mut b = __s_b.launch_builder(&f);
6024 b.arg(table)
6025 .arg(sel)
6026 .arg(w)
6027 .arg(act)
6028 .arg(dst)
6029 .arg(&inf)
6030 .arg(&outf)
6031 .arg(&nu)
6032 .arg(&ne)
6033 .arg(&qt)
6034 .arg(&rbv);
6035 unsafe {
6036 b.launch(cfg)?;
6037 }
6038 Ok(())
6039 }
6040
6041 pub fn axpy_into(
6043 &self,
6044 src: &CudaSlice<f32>,
6045 alpha: f32,
6046 dst: &mut cudarc::driver::CudaViewMut<f32>,
6047 n: usize,
6048 ) -> Result<(), Box<dyn std::error::Error>> {
6049 let f = self.func("axpy_f32");
6050 let cfg = LaunchConfig::for_num_elems(n as u32);
6051 let (a, ni) = (alpha, n as i32);
6052 let __s_b = self.gpu.stream();
6053 let mut b = __s_b.launch_builder(&f);
6054 b.arg(src).arg(dst).arg(&a).arg(&ni);
6055 unsafe {
6056 b.launch(cfg)?;
6057 }
6058 Ok(())
6059 }
6060
6061 pub fn add_scaled_rows(
6063 &self,
6064 src: &CudaSlice<f32>,
6065 scale: &CudaSlice<f32>,
6066 dst: &mut CudaSlice<f32>,
6067 ncols: usize,
6068 nrows: usize,
6069 ) -> Result<(), Box<dyn std::error::Error>> {
6070 let f = self.func("add_scaled_rows_f32");
6071 let cfg = LaunchConfig::for_num_elems((ncols * nrows) as u32);
6072 let (nc, nr) = (ncols as i32, nrows as i32);
6073 let __s_b = self.gpu.stream();
6074 let mut b = __s_b.launch_builder(&f);
6075 b.arg(src).arg(scale).arg(dst).arg(&nc).arg(&nr);
6076 unsafe {
6077 b.launch(cfg)?;
6078 }
6079 Ok(())
6080 }
6081
6082 pub fn gather_rows(
6086 &self,
6087 src: &CudaSlice<f32>,
6088 idx: &CudaSlice<i32>,
6089 dst: &mut CudaSlice<f32>,
6090 ncols: usize,
6091 m_e: usize,
6092 ) -> Result<(), Box<dyn std::error::Error>> {
6093 let f = self.func("gather_rows_f32");
6094 let cfg = LaunchConfig::for_num_elems((m_e * ncols) as u32);
6095 let (nc, me) = (ncols as i32, m_e as i32);
6096 let __s_b = self.gpu.stream();
6097 let mut b = __s_b.launch_builder(&f);
6098 b.arg(src).arg(idx).arg(dst).arg(&nc).arg(&me);
6099 unsafe {
6100 b.launch(cfg)?;
6101 }
6102 Ok(())
6103 }
6104
6105 pub fn scatter_slot(
6110 &self,
6111 src: &CudaSlice<f32>,
6112 tok_idx: &CudaSlice<i32>,
6113 slot_idx: &CudaSlice<i32>,
6114 weight: &CudaSlice<f32>,
6115 dst: &mut CudaSlice<f32>,
6116 wbuf: &mut CudaSlice<f32>,
6117 ncols: usize,
6118 n_used: usize,
6119 m_e: usize,
6120 ) -> Result<(), Box<dyn std::error::Error>> {
6121 let f = self.func("scatter_add_slot_f32");
6122 let cfg = LaunchConfig::for_num_elems((m_e * ncols) as u32);
6123 let (nc, nu, me) = (ncols as i32, n_used as i32, m_e as i32);
6124 let __s_b = self.gpu.stream();
6125 let mut b = __s_b.launch_builder(&f);
6126 b.arg(src)
6127 .arg(tok_idx)
6128 .arg(slot_idx)
6129 .arg(weight)
6130 .arg(dst)
6131 .arg(wbuf)
6132 .arg(&nc)
6133 .arg(&nu)
6134 .arg(&me);
6135 unsafe {
6136 b.launch(cfg)?;
6137 }
6138 Ok(())
6139 }
6140
6141 pub fn reduce_slots(
6145 &self,
6146 slots: &CudaSlice<f32>,
6147 wbuf: &CudaSlice<f32>,
6148 dst: &mut CudaSlice<f32>,
6149 ncols: usize,
6150 n_used: usize,
6151 t: usize,
6152 ) -> Result<(), Box<dyn std::error::Error>> {
6153 let f = self.func("reduce_slots_f32");
6154 let cfg = LaunchConfig::for_num_elems((t * ncols) as u32);
6155 let (nc, nu, ti) = (ncols as i32, n_used as i32, t as i32);
6156 let __s_b = self.gpu.stream();
6157 let mut b = __s_b.launch_builder(&f);
6158 b.arg(slots).arg(wbuf).arg(dst).arg(&nc).arg(&nu).arg(&ti);
6159 unsafe {
6160 b.launch(cfg)?;
6161 }
6162 Ok(())
6163 }
6164
6165 pub fn quantize_q8_1_view(
6172 &self,
6173 x: &cudarc::driver::CudaView<f32>,
6174 m: usize,
6175 in_f: usize,
6176 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
6177 let f = self.func("quantize_q8_1");
6178 let nblk = in_f / 32;
6179 let mut q = self.alloc_uninit::<i8>(m * in_f)?;
6180 let mut d = self.alloc_uninit::<f32>(m * nblk)?;
6181 let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
6182 let (inf, mi) = (in_f as i32, m as i32);
6183 let __s_b = self.gpu.stream();
6184 let mut b = __s_b.launch_builder(&f);
6185 b.arg(x).arg(&mut q).arg(&mut d).arg(&inf).arg(&mi);
6186 unsafe {
6187 b.launch(cfg)?;
6188 }
6189 Ok((q, d))
6190 }
6191
6192 pub fn quantize_q8_1(
6193 &self,
6194 x: &CudaSlice<f32>,
6195 m: usize,
6196 in_f: usize,
6197 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
6198 let nblk = in_f / 32;
6199 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);
6203 let (inf, mi) = (in_f as i32, m as i32);
6204 if Self::pdl_on() && Self::pdl_wb_on() {
6205 {
6206 use cudarc::driver::{DevicePtr, DevicePtrMut};
6207 let s = &self.gpu.stream();
6208 let (px, _g0) = x.device_ptr(s);
6209 let (pq, _g1) = q.device_ptr_mut(s);
6210 let (pd, _g2) = d.device_ptr_mut(s);
6211 let mut ps = [
6212 &px as *const _ as *mut std::ffi::c_void,
6213 &pq as *const _ as *mut _,
6214 &pd as *const _ as *mut _,
6215 &inf as *const _ as *mut _,
6216 &mi as *const _ as *mut _,
6217 ];
6218 unsafe {
6219 self.launch_pdl("quantize_q8_1", cfg.grid_dim, cfg.block_dim, &mut ps)?;
6220 }
6221 }
6222 return Ok((q, d));
6223 }
6224 let f = self.func("quantize_q8_1");
6225 let __s_b = self.gpu.stream();
6226 let mut b = __s_b.launch_builder(&f);
6227 b.arg(x).arg(&mut q).arg(&mut d).arg(&inf).arg(&mi);
6228 unsafe {
6229 b.launch(cfg)?;
6230 }
6231 Ok((q, d))
6232 }
6233
6234 pub fn quantize_fp4_act(
6238 &self,
6239 x: &CudaSlice<f32>,
6240 m: usize,
6241 in_f: usize,
6242 ) -> Result<(CudaSlice<u32>, CudaSlice<u8>), Box<dyn std::error::Error>> {
6243 let f = self.func("quantize_fp4_act");
6244 let nb16 = in_f / 16;
6245 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);
6248 let (inf, mi) = (in_f as i32, m as i32);
6249 let __s_b = self.gpu.stream();
6250 let mut b = __s_b.launch_builder(&f);
6251 b.arg(x).arg(&mut aq4).arg(&mut ad4).arg(&inf).arg(&mi);
6252 unsafe {
6253 b.launch(cfg)?;
6254 }
6255 Ok((aq4, ad4))
6256 }
6257
6258 pub fn qmatvec_gemm_nvfp4_fp4(
6263 &self,
6264 bytes: &CudaSlice<u8>,
6265 x: &CudaSlice<f32>,
6266 m: usize,
6267 in_f: usize,
6268 out_f: usize,
6269 row_bytes: usize,
6270 scale: f32,
6271 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6272 assert!(
6273 in_f % 64 == 0,
6274 "FP4 GEMM requires in_f % 64 == 0, got {in_f}"
6275 );
6276 let (aq4, ad4) = self.quantize_fp4_act(x, m, in_f)?;
6277 let mut y = self.fp4_gemm_launch(bytes, &aq4, &ad4, m, in_f, out_f, row_bytes)?;
6278 if scale != 1.0 {
6279 self.scale_inplace(&mut y, scale, m * out_f)?;
6280 }
6281 Ok(y)
6282 }
6283
6284 fn fp4_gemm_launch(
6287 &self,
6288 bytes: &CudaSlice<u8>,
6289 aq4: &CudaSlice<u32>,
6290 ad4: &CudaSlice<u8>,
6291 m: usize,
6292 in_f: usize,
6293 out_f: usize,
6294 row_bytes: usize,
6295 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6296 let f = self.func("qmatvec_gemm_nvfp4_fp4");
6297 let mut y = self.alloc_uninit::<f32>(m * out_f)?; const BM: u32 = 64;
6299 const BN: u32 = 256;
6300 let cfg = LaunchConfig {
6301 grid_dim: ((out_f as u32 + BM - 1) / BM, (m as u32 + BN - 1) / BN, 1),
6302 block_dim: (32, 4, 1),
6303 shared_mem_bytes: 0,
6304 };
6305 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6306 let __s_b = self.gpu.stream();
6307 let mut b = __s_b.launch_builder(&f);
6308 b.arg(bytes)
6309 .arg(aq4)
6310 .arg(ad4)
6311 .arg(&mut y)
6312 .arg(&inf)
6313 .arg(&outf)
6314 .arg(&mi)
6315 .arg(&rb);
6316 unsafe {
6317 b.launch(cfg)?;
6318 }
6319 Ok(y)
6320 }
6321
6322 pub fn qmatvec_gemm_nvfp4_fp4_raw(
6324 &self,
6325 bytes: &CudaSlice<u8>,
6326 x: &CudaSlice<f32>,
6327 m: usize,
6328 in_f: usize,
6329 out_f: usize,
6330 row_bytes: usize,
6331 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6332 assert!(
6333 in_f % 64 == 0,
6334 "FP4 GEMM requires in_f % 64 == 0, got {in_f}"
6335 );
6336 let (aq4, ad4) = self.quantize_fp4_act(x, m, in_f)?;
6337 self.fp4_gemm_launch(bytes, &aq4, &ad4, m, in_f, out_f, row_bytes)
6338 }
6339
6340 pub fn qmatvec_q8_0_fast(
6342 &self,
6343 w: &CudaSlice<u8>,
6344 x: &CudaSlice<f32>,
6345 m: usize,
6346 in_f: usize,
6347 out_f: usize,
6348 row_bytes: usize,
6349 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6350 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6351 let f = self.func("qmatvec_q8_0_dp4a");
6352 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6354 grid_dim: (out_f as u32, m as u32, 1),
6355 block_dim: (128, 1, 1),
6356 shared_mem_bytes: 0,
6357 };
6358 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6359 let __s_b = self.gpu.stream();
6360 let mut b = __s_b.launch_builder(&f);
6361 b.arg(w)
6362 .arg(&aq)
6363 .arg(&ad)
6364 .arg(&mut y)
6365 .arg(&inf)
6366 .arg(&outf)
6367 .arg(&mi)
6368 .arg(&rb);
6369 unsafe {
6370 b.launch(cfg)?;
6371 }
6372 Ok(y)
6373 }
6374
6375 #[allow(non_snake_case)] pub fn qmatvec_q4_K_fast(
6378 &self,
6379 w: &CudaSlice<u8>,
6380 x: &CudaSlice<f32>,
6381 m: usize,
6382 in_f: usize,
6383 out_f: usize,
6384 row_bytes: usize,
6385 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6386 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6387 let f = self.func("qmatvec_q4_K_dp4a");
6388 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6390 grid_dim: (out_f as u32, m as u32, 1),
6391 block_dim: (128, 1, 1),
6392 shared_mem_bytes: 0,
6393 };
6394 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6395 let __s_b = self.gpu.stream();
6396 let mut b = __s_b.launch_builder(&f);
6397 b.arg(w)
6398 .arg(&aq)
6399 .arg(&ad)
6400 .arg(&mut y)
6401 .arg(&inf)
6402 .arg(&outf)
6403 .arg(&mi)
6404 .arg(&rb);
6405 unsafe {
6406 b.launch(cfg)?;
6407 }
6408 Ok(y)
6409 }
6410
6411 #[allow(non_snake_case)] pub fn qmatvec_q6_K_fast(
6414 &self,
6415 w: &CudaSlice<u8>,
6416 x: &CudaSlice<f32>,
6417 m: usize,
6418 in_f: usize,
6419 out_f: usize,
6420 row_bytes: usize,
6421 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6422 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6423 let f = self.func("qmatvec_q6_K_dp4a");
6424 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6426 grid_dim: (out_f as u32, m as u32, 1),
6427 block_dim: (128, 1, 1),
6428 shared_mem_bytes: 0,
6429 };
6430 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6431 let __s_b = self.gpu.stream();
6432 let mut b = __s_b.launch_builder(&f);
6433 b.arg(w)
6434 .arg(&aq)
6435 .arg(&ad)
6436 .arg(&mut y)
6437 .arg(&inf)
6438 .arg(&outf)
6439 .arg(&mi)
6440 .arg(&rb);
6441 unsafe {
6442 b.launch(cfg)?;
6443 }
6444 Ok(y)
6445 }
6446
6447 #[allow(non_snake_case)] pub fn qmatvec_q5_K_fast(
6450 &self,
6451 w: &CudaSlice<u8>,
6452 x: &CudaSlice<f32>,
6453 m: usize,
6454 in_f: usize,
6455 out_f: usize,
6456 row_bytes: usize,
6457 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6458 self.qmatvec_dp4a_named("qmatvec_q5_K_dp4a", w, x, m, in_f, out_f, row_bytes)
6459 }
6460 #[allow(non_snake_case)] pub fn qmatvec_q3_K_fast(
6463 &self,
6464 w: &CudaSlice<u8>,
6465 x: &CudaSlice<f32>,
6466 m: usize,
6467 in_f: usize,
6468 out_f: usize,
6469 row_bytes: usize,
6470 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6471 self.qmatvec_dp4a_named("qmatvec_q3_K_dp4a", w, x, m, in_f, out_f, row_bytes)
6472 }
6473 pub fn qmatvec_nvfp4_fast_rp(
6475 &self,
6476 w: &CudaSlice<u8>,
6477 x: &CudaSlice<f32>,
6478 m: usize,
6479 in_f: usize,
6480 out_f: usize,
6481 row_bytes: usize,
6482 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6483 assert!(
6484 in_f % 64 == 0,
6485 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
6486 );
6487 self.qmatvec_dp4a_named("qmatvec_nvfp4_dp4a_rp", w, x, m, in_f, out_f, row_bytes)
6488 }
6489 pub fn qmatvec_nvfp4_fast(
6491 &self,
6492 w: &CudaSlice<u8>,
6493 x: &CudaSlice<f32>,
6494 m: usize,
6495 in_f: usize,
6496 out_f: usize,
6497 row_bytes: usize,
6498 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6499 assert!(
6502 in_f % 64 == 0,
6503 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
6504 );
6505 self.qmatvec_dp4a_named("qmatvec_nvfp4_dp4a", w, x, m, in_f, out_f, row_bytes)
6506 }
6507 #[allow(non_snake_case)] pub fn qmatvec_iq4_XS_fast(
6510 &self,
6511 w: &CudaSlice<u8>,
6512 x: &CudaSlice<f32>,
6513 m: usize,
6514 in_f: usize,
6515 out_f: usize,
6516 row_bytes: usize,
6517 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6518 self.qmatvec_dp4a_named("qmatvec_iq4_XS_dp4a", w, x, m, in_f, out_f, row_bytes)
6519 }
6520
6521 fn qmatvec_dp4a_named(
6523 &self,
6524 name: &str,
6525 w: &CudaSlice<u8>,
6526 x: &CudaSlice<f32>,
6527 m: usize,
6528 in_f: usize,
6529 out_f: usize,
6530 row_bytes: usize,
6531 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6532 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6533 let f = self.func(name);
6534 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6536 grid_dim: (out_f as u32, m as u32, 1),
6537 block_dim: (128, 1, 1),
6538 shared_mem_bytes: 0,
6539 };
6540 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6541 let __s_b = self.gpu.stream();
6542 let mut b = __s_b.launch_builder(&f);
6543 b.arg(w)
6544 .arg(&aq)
6545 .arg(&ad)
6546 .arg(&mut y)
6547 .arg(&inf)
6548 .arg(&outf)
6549 .arg(&mi)
6550 .arg(&rb);
6551 unsafe {
6552 b.launch(cfg)?;
6553 }
6554 Ok(y)
6555 }
6556
6557 pub fn htod(&self, v: &[f32]) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6558 Ok(self.gpu.stream().clone_htod(v)?)
6559 }
6560 pub fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
6561 Ok(self.gpu.stream().clone_htod(v)?)
6562 }
6563 pub fn htod_i8(&self, v: &[i8]) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
6565 Ok(self.gpu.stream().clone_htod(v)?)
6566 }
6567 pub fn htod_u64(&self, v: &[u64]) -> Result<CudaSlice<u64>, Box<dyn std::error::Error>> {
6568 Ok(self.gpu.stream().clone_htod(v)?)
6569 }
6570 pub fn dtoh_view(
6572 &self,
6573 d: &cudarc::driver::CudaView<f32>,
6574 ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6575 let v = self.gpu.stream().clone_dtoh(d)?;
6576 self.gpu.stream().synchronize()?;
6577 Ok(v)
6578 }
6579 pub fn dtoh(&self, d: &CudaSlice<f32>) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6580 let v = self.gpu.stream().clone_dtoh(d)?;
6581 self.gpu.stream().synchronize()?;
6582 Ok(v)
6583 }
6584 pub fn dtoh_pair(
6588 &self,
6589 a: &CudaSlice<f32>,
6590 b: &CudaSlice<f32>,
6591 ) -> Result<(Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
6592 let av = self.gpu.stream().clone_dtoh(a)?;
6593 let bv = self.gpu.stream().clone_dtoh(b)?;
6594 self.gpu.stream().synchronize()?;
6595 Ok((av, bv))
6596 }
6597 pub fn dtoh_i32(&self, d: &CudaSlice<i32>) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
6599 let v = self.gpu.stream().clone_dtoh(d)?;
6600 self.gpu.stream().synchronize()?;
6601 Ok(v)
6602 }
6603 pub fn dtoh_u8(&self, d: &CudaSlice<u8>) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
6605 let v = self.gpu.stream().clone_dtoh(d)?;
6606 self.gpu.stream().synchronize()?;
6607 Ok(v)
6608 }
6609 pub fn dtoh_u8_view(
6610 &self,
6611 d: &cudarc::driver::CudaView<u8>,
6612 ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
6613 let v = self.gpu.stream().clone_dtoh(d)?;
6614 self.gpu.stream().synchronize()?;
6615 Ok(v)
6616 }
6617 pub fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6618 let s = self.gpu.stream().alloc_zeros::<f32>(n)?;
6619 self.keep_if_capturing(&s);
6620 Ok(s)
6621 }
6622
6623 pub fn prob_of_token_device(
6632 &self,
6633 logits: &CudaSlice<f32>,
6634 tok: &CudaSlice<u32>,
6635 n_vocab: usize,
6636 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6637 let nb = ARGMAX_NB;
6638 let mut part = self.alloc_uninit::<f32>(nb)?;
6639 let mut p = self.alloc_uninit::<f32>(1)?;
6640 let f1 = self.func("prob_of_token_partial_f32");
6641 let cfg1 = LaunchConfig {
6642 grid_dim: (nb as u32, 1, 1),
6643 block_dim: (256, 1, 1),
6644 shared_mem_bytes: 0,
6645 };
6646 let nv = n_vocab as i32;
6647 let __s_b1 = self.gpu.stream();
6648 let mut b1 = __s_b1.launch_builder(&f1);
6649 b1.arg(logits).arg(tok).arg(&mut part).arg(&nv);
6650 unsafe {
6651 b1.launch(cfg1)?;
6652 }
6653 let f2 = self.func("prob_of_token_final_f32");
6654 let cfg2 = LaunchConfig {
6655 grid_dim: (1, 1, 1),
6656 block_dim: (256, 1, 1),
6657 shared_mem_bytes: 0,
6658 };
6659 let nbi = nb as i32;
6660 let __s_b2 = self.gpu.stream();
6661 let mut b2 = __s_b2.launch_builder(&f2);
6662 b2.arg(&part).arg(&mut p).arg(&nbi);
6663 unsafe {
6664 b2.launch(cfg2)?;
6665 }
6666 Ok(p)
6667 }
6668
6669 pub fn prob_of_token_device_col(
6676 &self,
6677 logits: &CudaSlice<f32>,
6678 tok_all: &CudaSlice<u32>,
6679 tok_idx: usize,
6680 p_out: &mut CudaSlice<f32>,
6681 p_idx: usize,
6682 n_vocab: usize,
6683 ) -> Result<(), Box<dyn std::error::Error>> {
6684 let tok_v = tok_all.slice(tok_idx..tok_idx + 1);
6685 let mut p_v = p_out.slice_mut(p_idx..p_idx + 1);
6686 let nb = ARGMAX_NB;
6687 let mut part = self.alloc_uninit::<f32>(nb)?;
6688 let f1 = self.func("prob_of_token_partial_f32");
6689 let cfg1 = LaunchConfig {
6690 grid_dim: (nb as u32, 1, 1),
6691 block_dim: (256, 1, 1),
6692 shared_mem_bytes: 0,
6693 };
6694 let nv = n_vocab as i32;
6695 let __s_b1 = self.gpu.stream();
6696 let mut b1 = __s_b1.launch_builder(&f1);
6697 b1.arg(logits).arg(&tok_v).arg(&mut part).arg(&nv);
6698 unsafe {
6699 b1.launch(cfg1)?;
6700 }
6701 let f2 = self.func("prob_of_token_final_f32");
6702 let cfg2 = LaunchConfig {
6703 grid_dim: (1, 1, 1),
6704 block_dim: (256, 1, 1),
6705 shared_mem_bytes: 0,
6706 };
6707 let nbi = nb as i32;
6708 let __s_b2 = self.gpu.stream();
6709 let mut b2 = __s_b2.launch_builder(&f2);
6710 b2.arg(&part).arg(&mut p_v).arg(&nbi);
6711 unsafe {
6712 b2.launch(cfg2)?;
6713 }
6714 Ok(())
6715 }
6716
6717 pub fn prob_of_token_device_into(
6718 &self,
6719 logits: &CudaSlice<f32>,
6720 tok: &CudaSlice<u32>,
6721 p_out: &mut CudaSlice<f32>,
6722 n_vocab: usize,
6723 ) -> Result<(), Box<dyn std::error::Error>> {
6724 let nb = ARGMAX_NB;
6725 let mut part = self.alloc_uninit::<f32>(nb)?;
6726 let f1 = self.func("prob_of_token_partial_f32");
6727 let cfg1 = LaunchConfig {
6728 grid_dim: (nb as u32, 1, 1),
6729 block_dim: (256, 1, 1),
6730 shared_mem_bytes: 0,
6731 };
6732 let nv = n_vocab as i32;
6733 let __s_b1 = self.gpu.stream();
6734 let mut b1 = __s_b1.launch_builder(&f1);
6735 b1.arg(logits).arg(tok).arg(&mut part).arg(&nv);
6736 unsafe {
6737 b1.launch(cfg1)?;
6738 }
6739 let f2 = self.func("prob_of_token_final_f32");
6740 let cfg2 = LaunchConfig {
6741 grid_dim: (1, 1, 1),
6742 block_dim: (256, 1, 1),
6743 shared_mem_bytes: 0,
6744 };
6745 let nbi = nb as i32;
6746 let __s_b2 = self.gpu.stream();
6747 let mut b2 = __s_b2.launch_builder(&f2);
6748 b2.arg(&part).arg(p_out).arg(&nbi);
6749 unsafe {
6750 b2.launch(cfg2)?;
6751 }
6752 Ok(())
6753 }
6754
6755 pub fn argmax_token_device(
6756 &self,
6757 logits: &CudaSlice<f32>,
6758 n_vocab: usize,
6759 ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
6760 let mut tok = unsafe { self.gpu.stream().alloc::<u32>(1)? };
6761 self.argmax_token_device_into(logits, &mut tok, n_vocab)?;
6762 Ok(tok)
6763 }
6764 pub fn argmax_token_device_into(
6771 &self,
6772 logits: &CudaSlice<f32>,
6773 tok: &mut CudaSlice<u32>,
6774 n_vocab: usize,
6775 ) -> Result<(), Box<dyn std::error::Error>> {
6776 let nb = ARGMAX_NB;
6777 let f1 = self.func("argmax_partial_f32");
6778 let f2 = self.func("argmax_final_f32");
6779 let mut guard = self.argmax_partials.lock().unwrap();
6780 if guard.is_none() {
6781 let pv = self.gpu.stream().alloc_zeros::<f32>(nb)?;
6784 let pi = self.gpu.stream().alloc_zeros::<i32>(nb)?;
6785 *guard = Some((pv, pi));
6786 }
6787 let (part_v, part_i) = guard.as_mut().unwrap();
6788 let nv = n_vocab as i32;
6789 let nbi = nb as i32;
6790 let cfg1 = LaunchConfig {
6792 grid_dim: (nb as u32, 1, 1),
6793 block_dim: (256, 1, 1),
6794 shared_mem_bytes: 0,
6795 };
6796 let __s_b1 = self.gpu.stream();
6797 let mut b1 = __s_b1.launch_builder(&f1);
6798 b1.arg(logits).arg(&mut *part_v).arg(&mut *part_i).arg(&nv);
6799 unsafe {
6800 b1.launch(cfg1)?;
6801 }
6802 let cfg2 = LaunchConfig {
6804 grid_dim: (1, 1, 1),
6805 block_dim: (256, 1, 1),
6806 shared_mem_bytes: 0,
6807 };
6808 let __s_b2 = self.gpu.stream();
6809 let mut b2 = __s_b2.launch_builder(&f2);
6810 b2.arg(&*part_v).arg(&*part_i).arg(tok).arg(&nbi);
6811 unsafe {
6812 b2.launch(cfg2)?;
6813 }
6814 Ok(())
6815 }
6816 pub fn argmax_token_device_col(
6822 &self,
6823 logits: &CudaSlice<f32>,
6824 col: usize,
6825 n_vocab: usize,
6826 toks: &mut CudaSlice<u32>,
6827 out_idx: usize,
6828 ) -> Result<(), Box<dyn std::error::Error>> {
6829 let nb = ARGMAX_NB;
6830 let f1 = self.func("argmax_partial_f32");
6831 let f2 = self.func("argmax_final_f32");
6832 let mut guard = self.argmax_partials.lock().unwrap();
6833 if guard.is_none() {
6834 let pv = self.gpu.stream().alloc_zeros::<f32>(nb)?;
6835 let pi = self.gpu.stream().alloc_zeros::<i32>(nb)?;
6836 *guard = Some((pv, pi));
6837 }
6838 let (part_v, part_i) = guard.as_mut().unwrap();
6839 let col_view = logits.slice(col * n_vocab..(col + 1) * n_vocab);
6840 let nv = n_vocab as i32;
6841 let nbi = nb as i32;
6842 let cfg1 = LaunchConfig {
6843 grid_dim: (nb as u32, 1, 1),
6844 block_dim: (256, 1, 1),
6845 shared_mem_bytes: 0,
6846 };
6847 let __s_b1 = self.gpu.stream();
6848 let mut b1 = __s_b1.launch_builder(&f1);
6849 b1.arg(&col_view)
6850 .arg(&mut *part_v)
6851 .arg(&mut *part_i)
6852 .arg(&nv);
6853 unsafe {
6854 b1.launch(cfg1)?;
6855 }
6856 let mut tok_view = toks.slice_mut(out_idx..out_idx + 1);
6857 let cfg2 = LaunchConfig {
6858 grid_dim: (1, 1, 1),
6859 block_dim: (256, 1, 1),
6860 shared_mem_bytes: 0,
6861 };
6862 let __s_b2 = self.gpu.stream();
6863 let mut b2 = __s_b2.launch_builder(&f2);
6864 b2.arg(&*part_v).arg(&*part_i).arg(&mut tok_view).arg(&nbi);
6865 unsafe {
6866 b2.launch(cfg2)?;
6867 }
6868 Ok(())
6869 }
6870 pub fn htod_u32_v(&self, v: &[u32]) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
6872 Ok(self.gpu.stream().clone_htod(v)?)
6873 }
6874 pub fn dtoh_u32(&self, d: &CudaSlice<u32>) -> Result<Vec<u32>, Box<dyn std::error::Error>> {
6875 let v = self.gpu.stream().clone_dtoh(d)?;
6876 self.gpu.stream().synchronize()?;
6877 Ok(v)
6878 }
6879 pub fn htod_u32_into(
6883 &self,
6884 dst: &mut CudaSlice<u32>,
6885 src: &[u32],
6886 ) -> Result<(), Box<dyn std::error::Error>> {
6887 let mut view = dst.slice_mut(0..src.len());
6888 self.gpu.stream().memcpy_htod(src, &mut view)?;
6889 Ok(())
6890 }
6891
6892 pub fn htod_i32_into(
6895 &self,
6896 dst: &mut CudaSlice<i32>,
6897 src: &[i32],
6898 ) -> Result<(), Box<dyn std::error::Error>> {
6899 let mut view = dst.slice_mut(0..src.len());
6900 self.gpu.stream().memcpy_htod(src, &mut view)?;
6901 Ok(())
6902 }
6903
6904 pub fn alloc_u32_zeroed(&self, n: usize) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
6905 let s = self.gpu.stream().alloc_zeros::<u32>(n)?;
6906 self.keep_if_capturing(&s);
6907 Ok(s)
6908 }
6909 pub fn embed_gather_device_into(
6912 &self,
6913 embd: &CudaSlice<u8>,
6914 token_d: &CudaSlice<u32>,
6915 x_out: &mut CudaSlice<f32>,
6916 n_embd: usize,
6917 qtype: i32,
6918 row_bytes: usize,
6919 ) -> Result<(), Box<dyn std::error::Error>> {
6920 let f = self.func("embed_gather_u32");
6921 let cfg = LaunchConfig {
6922 grid_dim: (((n_embd as u32 + 255) / 256).max(1), 1, 1),
6923 block_dim: (256, 1, 1),
6924 shared_mem_bytes: 0,
6925 };
6926 let (ne, qt, rb) = (n_embd as i32, qtype, row_bytes as i64);
6927 let __s_b = self.gpu.stream();
6928 let mut b = __s_b.launch_builder(&f);
6929 b.arg(embd)
6930 .arg(token_d)
6931 .arg(x_out)
6932 .arg(&ne)
6933 .arg(&qt)
6934 .arg(&rb);
6935 unsafe {
6936 b.launch(cfg)?;
6937 }
6938 Ok(())
6939 }
6940 pub fn dtoh_i32_one(&self, d: &CudaSlice<i32>) -> Result<i32, Box<dyn std::error::Error>> {
6942 let v = self.gpu.stream().clone_dtoh(d)?;
6943 self.gpu.stream().synchronize()?;
6944 Ok(v[0])
6945 }
6946 pub fn i32_set_k(
6953 &self,
6954 dst: &mut CudaSlice<i32>,
6955 v: i32,
6956 ) -> Result<(), Box<dyn std::error::Error>> {
6957 let f = self.func("i32_set_k");
6958 let cfg = LaunchConfig {
6959 grid_dim: (1, 1, 1),
6960 block_dim: (1, 1, 1),
6961 shared_mem_bytes: 0,
6962 };
6963 let idx = 0i32;
6964 let __s_b = self.gpu.stream();
6965 let mut b = __s_b.launch_builder(&f);
6966 b.arg(dst).arg(&v).arg(&idx);
6967 unsafe {
6968 b.launch(cfg)?;
6969 }
6970 Ok(())
6971 }
6972
6973 pub fn set_i32_one(
6974 &self,
6975 d: &mut CudaSlice<i32>,
6976 v: i32,
6977 ) -> Result<(), Box<dyn std::error::Error>> {
6978 self.gpu.stream().memcpy_htod(&[v], d)?;
6979 Ok(())
6980 }
6981 pub fn set_u32_one(
6984 &self,
6985 d: &mut CudaSlice<u32>,
6986 v: u32,
6987 ) -> Result<(), Box<dyn std::error::Error>> {
6988 self.gpu.stream().memcpy_htod(&[v], d)?;
6989 Ok(())
6990 }
6991 pub fn dtoh_u32_one(&self, d: &CudaSlice<u32>) -> Result<u32, Box<dyn std::error::Error>> {
6993 let v = self.gpu.stream().clone_dtoh(d)?;
6994 self.gpu.stream().synchronize()?;
6995 Ok(v[0])
6996 }
6997 pub fn upload_u8(&self, bytes: &[u8]) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
6999 Ok(self.gpu.stream().clone_htod(bytes)?)
7000 }
7001 pub fn embed_gather_device(
7005 &self,
7006 embd: &CudaSlice<u8>,
7007 token_d: &CudaSlice<u32>,
7008 n_embd: usize,
7009 qtype: i32,
7010 row_bytes: usize,
7011 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7012 let f = self.func("embed_gather_u32");
7013 let mut x = self.alloc_uninit::<f32>(n_embd)?;
7014 let cfg = LaunchConfig {
7015 grid_dim: (((n_embd as u32 + 255) / 256).max(1), 1, 1),
7016 block_dim: (256, 1, 1),
7017 shared_mem_bytes: 0,
7018 };
7019 let (ne, qt, rb) = (n_embd as i32, qtype, row_bytes as i64);
7020 let __s_b = self.gpu.stream();
7021 let mut b = __s_b.launch_builder(&f);
7022 b.arg(embd)
7023 .arg(token_d)
7024 .arg(&mut x)
7025 .arg(&ne)
7026 .arg(&qt)
7027 .arg(&rb);
7028 unsafe {
7029 b.launch(cfg)?;
7030 }
7031 Ok(x)
7032 }
7033
7034 pub fn embed_gather_device_t(
7038 &self,
7039 embd: &CudaSlice<u8>,
7040 tokens: &[u32],
7041 n_embd: usize,
7042 qtype: i32,
7043 row_bytes: usize,
7044 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7045 let t = tokens.len();
7046 let tok_d = self.gpu.stream().clone_htod(tokens)?;
7047 let f = self.func("embed_gather_u32_t");
7048 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7049 let cfg = LaunchConfig {
7050 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7051 block_dim: (256, 1, 1),
7052 shared_mem_bytes: 0,
7053 };
7054 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7055 let __s_b = self.gpu.stream();
7056 let mut b = __s_b.launch_builder(&f);
7057 b.arg(embd)
7058 .arg(&tok_d)
7059 .arg(&mut x)
7060 .arg(&ne)
7061 .arg(&qt)
7062 .arg(&rb)
7063 .arg(&ti);
7064 unsafe {
7065 b.launch(cfg)?;
7066 }
7067 Ok(x)
7068 }
7069
7070 pub fn embed_gather_device_tv(
7075 &self,
7076 embd: &CudaSlice<u8>,
7077 tok_v: &cudarc::driver::CudaView<u32>,
7078 t: usize,
7079 n_embd: usize,
7080 qtype: i32,
7081 row_bytes: usize,
7082 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7083 let f = self.func("embed_gather_u32_t");
7084 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7085 let cfg = LaunchConfig {
7086 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7087 block_dim: (256, 1, 1),
7088 shared_mem_bytes: 0,
7089 };
7090 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7091 let __s_b = self.gpu.stream();
7092 let mut b = __s_b.launch_builder(&f);
7093 b.arg(embd)
7094 .arg(tok_v)
7095 .arg(&mut x)
7096 .arg(&ne)
7097 .arg(&qt)
7098 .arg(&rb)
7099 .arg(&ti);
7100 unsafe {
7101 b.launch(cfg)?;
7102 }
7103 Ok(x)
7104 }
7105
7106 pub fn embed_gather_device_td(
7107 &self,
7108 embd: &CudaSlice<u8>,
7109 tok_d: &CudaSlice<u32>,
7110 t: usize,
7111 n_embd: usize,
7112 qtype: i32,
7113 row_bytes: usize,
7114 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7115 let f = self.func("embed_gather_u32_t");
7116 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7117 let cfg = LaunchConfig {
7118 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7119 block_dim: (256, 1, 1),
7120 shared_mem_bytes: 0,
7121 };
7122 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7123 let __s_b = self.gpu.stream();
7124 let mut b = __s_b.launch_builder(&f);
7125 b.arg(embd)
7126 .arg(tok_d)
7127 .arg(&mut x)
7128 .arg(&ne)
7129 .arg(&qt)
7130 .arg(&rb)
7131 .arg(&ti);
7132 unsafe {
7133 b.launch(cfg)?;
7134 }
7135 Ok(x)
7136 }
7137
7138 #[inline]
7144 fn keep_if_capturing<T: cudarc::driver::DeviceRepr + Send + 'static>(&self, s: &CudaSlice<T>) {
7146 if self
7147 .capture_keep_on
7148 .load(std::sync::atomic::Ordering::Relaxed)
7149 {
7150 self.capture_keep.lock().unwrap().push(Box::new(s.clone()));
7151 }
7152 }
7153
7154 fn alloc_uninit<T: cudarc::driver::DeviceRepr + Send + 'static>(
7155 &self,
7156 n: usize,
7157 ) -> Result<CudaSlice<T>, Box<dyn std::error::Error>> {
7158 let mut s = unsafe { self.gpu.stream().alloc::<T>(n)? };
7159 {
7163 static Z: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7164 if *Z.get_or_init(|| std::env::var("MEMRA_DEBUG_ZERO_ALLOCS").as_deref() == Ok("1")) {
7165 use cudarc::driver::DevicePtrMut;
7167 let n_bytes = s.len() * std::mem::size_of::<T>();
7168 let stream = self.gpu.stream();
7169 let (p_, _g) = s.device_ptr_mut(&stream);
7170 unsafe {
7171 cudarc::driver::sys::cuMemsetD8Async(p_, 0, n_bytes, stream.cu_stream())
7172 .result()?;
7173 }
7174 }
7175 }
7176 self.keep_if_capturing(&s);
7177 Ok(s)
7178 }
7179
7180 pub fn uninit_q8_pair(
7185 &self,
7186 n: usize,
7187 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7188 Ok((
7189 self.alloc_uninit::<i8>(n)?,
7190 self.alloc_uninit::<f32>(n / 32)?,
7191 ))
7192 }
7193
7194 pub fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7195 self.alloc_uninit::<f32>(n)
7196 }
7197
7198 pub fn alloc_i8_uninit(&self, n: usize) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
7200 self.alloc_uninit::<i8>(n)
7201 }
7202
7203 #[allow(clippy::too_many_arguments)]
7207 pub fn rms_norm3(
7208 &self,
7209 x: &CudaSlice<f32>,
7210 w0: &CudaSlice<f32>,
7211 w1: &CudaSlice<f32>,
7212 w2: &CudaSlice<f32>,
7213 d0: &mut CudaSlice<f32>,
7214 d1: &mut CudaSlice<f32>,
7215 d2: &mut CudaSlice<f32>,
7216 ncols: usize,
7217 nrows: usize,
7218 eps: f32,
7219 ) -> Result<(), Box<dyn std::error::Error>> {
7220 let f = self.func("rms_norm3_f32");
7221 let cfg = LaunchConfig {
7222 grid_dim: (nrows as u32, 1, 1),
7223 block_dim: (rms_block(), 1, 1),
7224 shared_mem_bytes: 0,
7225 };
7226 let (nc, e) = (ncols as i32, eps);
7227 let __s_b = self.gpu.stream();
7228 let mut b = __s_b.launch_builder(&f);
7229 b.arg(x)
7230 .arg(w0)
7231 .arg(w1)
7232 .arg(w2)
7233 .arg(d0)
7234 .arg(d1)
7235 .arg(d2)
7236 .arg(&nc)
7237 .arg(&e);
7238 unsafe {
7239 b.launch(cfg)?;
7240 }
7241 Ok(())
7242 }
7243
7244 #[allow(clippy::too_many_arguments)]
7246 pub fn qkvnorm_w_on_prefill(rows: usize, ncols: usize) -> bool {
7249 static WARP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7250 *WARP_ON.get_or_init(|| {
7251 std::env::var("MEMRA_QKVNORM_W")
7252 .map(|v| v != "0")
7253 .unwrap_or(true)
7254 }) && ncols % 4 == 0
7255 && rows >= 64
7256 }
7257
7258 #[allow(clippy::too_many_arguments)]
7261 pub fn rms_norm_qkv_w4b(
7262 &self,
7263 q: &CudaSlice<f32>,
7264 k: &CudaSlice<f32>,
7265 v: &CudaSlice<f32>,
7266 wq: &CudaSlice<f32>,
7267 wk: &CudaSlice<f32>,
7268 wv: &CudaSlice<f32>,
7269 dq: &mut CudaSlice<f32>,
7270 dk: &mut CudaSlice<f32>,
7271 dv: &mut CudaSlice<f32>,
7272 dvb: &mut CudaSlice<u8>,
7273 ncols: usize,
7274 rq: usize,
7275 rk: usize,
7276 eps: f32,
7277 vf16: bool,
7278 ) -> Result<(), Box<dyn std::error::Error>> {
7279 assert!(ncols % 4 == 0 && rq + 2 * rk >= 64);
7280 let f = self.func("rms_norm_qkv_w4b_f32");
7281 let rows = (rq + 2 * rk) as u32;
7282 let cfg = LaunchConfig {
7283 grid_dim: (rows.div_ceil(8), 1, 1),
7284 block_dim: (256, 1, 1),
7285 shared_mem_bytes: 0,
7286 };
7287 let (nc, rqi, rki, rvi, e) = (ncols as i32, rq as i32, rk as i32, rk as i32, eps);
7288 let vf = vf16 as i32;
7289 let __s_b = self.gpu.stream();
7290 let mut b = __s_b.launch_builder(&f);
7291 b.arg(q)
7292 .arg(k)
7293 .arg(v)
7294 .arg(wq)
7295 .arg(wk)
7296 .arg(wv)
7297 .arg(dq)
7298 .arg(dk)
7299 .arg(dv)
7300 .arg(&mut *dvb)
7301 .arg(&nc)
7302 .arg(&rqi)
7303 .arg(&rki)
7304 .arg(&rvi)
7305 .arg(&e)
7306 .arg(&vf);
7307 unsafe {
7308 b.launch(cfg)?;
7309 }
7310 Ok(())
7311 }
7312
7313 pub fn rms_norm_qkv(
7314 &self,
7315 q: &CudaSlice<f32>,
7316 k: &CudaSlice<f32>,
7317 v: &CudaSlice<f32>,
7318 wq: &CudaSlice<f32>,
7319 wk: &CudaSlice<f32>,
7320 wv: &CudaSlice<f32>,
7321 dq: &mut CudaSlice<f32>,
7322 dk: &mut CudaSlice<f32>,
7323 dv: &mut CudaSlice<f32>,
7324 ncols: usize,
7325 rq: usize,
7326 rk: usize,
7327 eps: f32,
7328 ) -> Result<(), Box<dyn std::error::Error>> {
7329 static WARP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7333 let warp_on = *WARP_ON.get_or_init(|| {
7334 std::env::var("MEMRA_QKVNORM_W")
7335 .map(|v| v != "0")
7336 .unwrap_or(true)
7337 });
7338 if warp_on && ncols % 4 == 0 && rq + 2 * rk >= 64 {
7341 let f = self.func("rms_norm_qkv_w4_f32");
7342 let rows = (rq + 2 * rk) as u32;
7343 let cfg = LaunchConfig {
7344 grid_dim: (rows.div_ceil(8), 1, 1),
7345 block_dim: (256, 1, 1),
7346 shared_mem_bytes: 0,
7347 };
7348 let (nc, rqi, rki, rvi, e) = (ncols as i32, rq as i32, rk as i32, rk as i32, eps);
7349 let __s_b = self.gpu.stream();
7350 let mut b = __s_b.launch_builder(&f);
7351 b.arg(q)
7352 .arg(k)
7353 .arg(v)
7354 .arg(wq)
7355 .arg(wk)
7356 .arg(wv)
7357 .arg(dq)
7358 .arg(dk)
7359 .arg(dv)
7360 .arg(&nc)
7361 .arg(&rqi)
7362 .arg(&rki)
7363 .arg(&rvi)
7364 .arg(&e);
7365 unsafe {
7366 b.launch(cfg)?;
7367 }
7368 return Ok(());
7369 }
7370 let f = self.func("rms_norm_qkv_f32");
7371 let grid = (rq + 2 * rk) as u32;
7372 let cfg = LaunchConfig {
7373 grid_dim: (grid, 1, 1),
7374 block_dim: (rms_block(), 1, 1),
7375 shared_mem_bytes: 0,
7376 };
7377 let (nc, rqi, rki, e) = (ncols as i32, rq as i32, rk as i32, eps);
7378 let __s_b = self.gpu.stream();
7379 let mut b = __s_b.launch_builder(&f);
7380 b.arg(q)
7381 .arg(k)
7382 .arg(v)
7383 .arg(wq)
7384 .arg(wk)
7385 .arg(wv)
7386 .arg(dq)
7387 .arg(dk)
7388 .arg(dv)
7389 .arg(&nc)
7390 .arg(&rqi)
7391 .arg(&rki)
7392 .arg(&e);
7393 unsafe {
7394 b.launch(cfg)?;
7395 }
7396 Ok(())
7397 }
7398
7399 #[allow(clippy::too_many_arguments)]
7401 pub fn rms_norm2x(
7402 &self,
7403 a: &CudaSlice<f32>,
7404 bb: &CudaSlice<f32>,
7405 wa: &CudaSlice<f32>,
7406 wb: &CudaSlice<f32>,
7407 da: &mut CudaSlice<f32>,
7408 db: &mut CudaSlice<f32>,
7409 ncols: usize,
7410 nrows: usize,
7411 eps: f32,
7412 ) -> Result<(), Box<dyn std::error::Error>> {
7413 let f = self.func("rms_norm2x_f32");
7414 let cfg = LaunchConfig {
7415 grid_dim: (2 * nrows as u32, 1, 1),
7416 block_dim: (rms_block(), 1, 1),
7417 shared_mem_bytes: 0,
7418 };
7419 let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
7420 let __s_b = self.gpu.stream();
7421 let mut b = __s_b.launch_builder(&f);
7422 b.arg(a)
7423 .arg(bb)
7424 .arg(wa)
7425 .arg(wb)
7426 .arg(da)
7427 .arg(db)
7428 .arg(&nc)
7429 .arg(&nr)
7430 .arg(&e);
7431 unsafe {
7432 b.launch(cfg)?;
7433 }
7434 Ok(())
7435 }
7436
7437 pub fn softcap(
7439 &self,
7440 y: &mut CudaSlice<f32>,
7441 cap: f32,
7442 n: usize,
7443 ) -> Result<(), Box<dyn std::error::Error>> {
7444 let f = self.func("softcap_f32");
7445 let cfg = LaunchConfig::for_num_elems(n as u32);
7446 let ni = n as i32;
7447 let __s_b = self.gpu.stream();
7448 let mut b = __s_b.launch_builder(&f);
7449 b.arg(y).arg(&cap).arg(&ni);
7450 unsafe {
7451 b.launch(cfg)?;
7452 }
7453 Ok(())
7454 }
7455
7456 pub fn mask_ids_rows(
7459 &self,
7460 y: &mut CudaSlice<f32>,
7461 ids: &CudaSlice<i32>,
7462 n_ids: usize,
7463 n_vocab: usize,
7464 t: usize,
7465 ) -> Result<(), Box<dyn std::error::Error>> {
7466 let f = self.func("mask_ids_rows_f32");
7467 let cfg = LaunchConfig::for_num_elems((n_ids * t) as u32);
7468 let (ni, nv, ti) = (n_ids as i32, n_vocab as i32, t as i32);
7469 let __s_b = self.gpu.stream();
7470 let mut b = __s_b.launch_builder(&f);
7471 b.arg(y).arg(ids).arg(&ni).arg(&nv).arg(&ti);
7472 unsafe {
7473 b.launch(cfg)?;
7474 }
7475 Ok(())
7476 }
7477
7478 #[allow(clippy::too_many_arguments)]
7480 pub fn add_scale_rms_norm(
7481 &self,
7482 a: &CudaSlice<f32>,
7483 b_in: &CudaSlice<f32>,
7484 c: f32,
7485 w: &CudaSlice<f32>,
7486 res: &mut CudaSlice<f32>,
7487 dst: &mut CudaSlice<f32>,
7488 ncols: usize,
7489 nrows: usize,
7490 eps: f32,
7491 ) -> Result<(), Box<dyn std::error::Error>> {
7492 let f = self.func("add_scale_rms_norm_f32");
7493 let cfg = LaunchConfig {
7494 grid_dim: (nrows as u32, 1, 1),
7495 block_dim: (rms_block(), 1, 1),
7496 shared_mem_bytes: 0,
7497 };
7498 let (nc, e2) = (ncols as i32, eps);
7499 let __s_b = self.gpu.stream();
7500 let mut b = __s_b.launch_builder(&f);
7501 b.arg(a)
7502 .arg(b_in)
7503 .arg(&c)
7504 .arg(w)
7505 .arg(res)
7506 .arg(dst)
7507 .arg(&nc)
7508 .arg(&e2);
7509 unsafe {
7510 b.launch(cfg)?;
7511 }
7512 Ok(())
7513 }
7514
7515 #[allow(clippy::too_many_arguments)]
7518 pub fn add_scale_rms_norm_q8_1(
7519 &self,
7520 a: &CudaSlice<f32>,
7521 b_in: &CudaSlice<f32>,
7522 c: f32,
7523 w: &CudaSlice<f32>,
7524 res: &mut CudaSlice<f32>,
7525 ncols: usize,
7526 nrows: usize,
7527 eps: f32,
7528 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7529 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7530 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7531 let (nc, e2) = (ncols as i32, eps);
7532 if Self::pdl_on() && Self::pdl_wb_on() {
7533 {
7534 use cudarc::driver::{DevicePtr, DevicePtrMut};
7535 let s = &self.gpu.stream();
7536 let (pa, _g0) = a.device_ptr(s);
7537 let (pb, _g1) = b_in.device_ptr(s);
7538 let (pw, _g2) = w.device_ptr(s);
7539 let (pr, _g3) = res.device_ptr_mut(s);
7540 let (pq, _g4) = out_q.device_ptr_mut(s);
7541 let (pd, _g5) = out_d.device_ptr_mut(s);
7542 let mut ps = [
7543 &pa as *const _ as *mut std::ffi::c_void,
7544 &pb as *const _ as *mut _,
7545 &c as *const _ as *mut _,
7546 &pw as *const _ as *mut _,
7547 &pr as *const _ as *mut _,
7548 &pq as *const _ as *mut _,
7549 &pd as *const _ as *mut _,
7550 &nc as *const _ as *mut _,
7551 &e2 as *const _ as *mut _,
7552 ];
7553 unsafe {
7554 self.launch_pdl(
7555 "add_scale_rms_norm_q8_1",
7556 (nrows as u32, 1, 1),
7557 (rms_block(), 1, 1),
7558 &mut ps,
7559 )?;
7560 }
7561 }
7562 return Ok((out_q, out_d));
7563 }
7564 let f = self.func("add_scale_rms_norm_q8_1");
7565 let cfg = LaunchConfig {
7566 grid_dim: (nrows as u32, 1, 1),
7567 block_dim: (rms_block(), 1, 1),
7568 shared_mem_bytes: 0,
7569 };
7570 let __s_b = self.gpu.stream();
7571 let mut b = __s_b.launch_builder(&f);
7572 b.arg(a)
7573 .arg(b_in)
7574 .arg(&c)
7575 .arg(w)
7576 .arg(res)
7577 .arg(&mut out_q)
7578 .arg(&mut out_d)
7579 .arg(&nc)
7580 .arg(&e2);
7581 unsafe {
7582 b.launch(cfg)?;
7583 }
7584 Ok((out_q, out_d))
7585 }
7586
7587 #[allow(clippy::too_many_arguments)]
7589 pub fn add_scale_rms_norm_q8_1_into(
7590 &self,
7591 a: &CudaSlice<f32>,
7592 b_in: &CudaSlice<f32>,
7593 c: f32,
7594 w: &CudaSlice<f32>,
7595 res: &mut CudaSlice<f32>,
7596 ncols: usize,
7597 nrows: usize,
7598 eps: f32,
7599 out_q: &mut CudaSlice<i8>,
7600 out_d: &mut CudaSlice<f32>,
7601 ) -> Result<(), Box<dyn std::error::Error>> {
7602 debug_assert!(out_q.len() >= nrows * ncols && out_d.len() >= nrows * (ncols / 32));
7603 let (nc, e2) = (ncols as i32, eps);
7604 if Self::pdl_on() && Self::pdl_wb_on() {
7605 use cudarc::driver::{DevicePtr, DevicePtrMut};
7606 let s = &self.gpu.stream();
7607 let (pa, _g0) = a.device_ptr(s);
7608 let (pb, _g1) = b_in.device_ptr(s);
7609 let (pw, _g2) = w.device_ptr(s);
7610 let (pr, _g3) = res.device_ptr_mut(s);
7611 let (pq, _g4) = out_q.device_ptr_mut(s);
7612 let (pd, _g5) = out_d.device_ptr_mut(s);
7613 let mut ps = [
7614 &pa as *const _ as *mut std::ffi::c_void,
7615 &pb as *const _ as *mut _,
7616 &c as *const _ as *mut _,
7617 &pw as *const _ as *mut _,
7618 &pr as *const _ as *mut _,
7619 &pq as *const _ as *mut _,
7620 &pd as *const _ as *mut _,
7621 &nc as *const _ as *mut _,
7622 &e2 as *const _ as *mut _,
7623 ];
7624 unsafe {
7625 self.launch_pdl(
7626 "add_scale_rms_norm_q8_1",
7627 (nrows as u32, 1, 1),
7628 (rms_block(), 1, 1),
7629 &mut ps,
7630 )?;
7631 }
7632 return Ok(());
7633 }
7634 let f = self.func("add_scale_rms_norm_q8_1");
7635 let cfg = LaunchConfig {
7636 grid_dim: (nrows as u32, 1, 1),
7637 block_dim: (rms_block(), 1, 1),
7638 shared_mem_bytes: 0,
7639 };
7640 let __s_b = self.gpu.stream();
7641 let mut b = __s_b.launch_builder(&f);
7642 b.arg(a)
7643 .arg(b_in)
7644 .arg(&c)
7645 .arg(w)
7646 .arg(res)
7647 .arg(&mut *out_q)
7648 .arg(&mut *out_d)
7649 .arg(&nc)
7650 .arg(&e2);
7651 unsafe {
7652 b.launch(cfg)?;
7653 }
7654 Ok(())
7655 }
7656
7657 #[allow(clippy::too_many_arguments)]
7660 pub fn rms_pre_add_scale_rms_norm_q8_1(
7661 &self,
7662 a: &CudaSlice<f32>,
7663 wa: &CudaSlice<f32>,
7664 b_in: &CudaSlice<f32>,
7665 c: f32,
7666 w: &CudaSlice<f32>,
7667 res: &mut CudaSlice<f32>,
7668 ncols: usize,
7669 nrows: usize,
7670 eps: f32,
7671 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7672 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7673 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7674 let (nc, e2) = (ncols as i32, eps);
7675 if Self::pdl_on() {
7676 {
7677 use cudarc::driver::{DevicePtr, DevicePtrMut};
7678 let s = &self.gpu.stream();
7679 let (pa, _g0) = a.device_ptr(s);
7680 let (pwa, _g1) = wa.device_ptr(s);
7681 let (pb, _g2) = b_in.device_ptr(s);
7682 let (pw, _g3) = w.device_ptr(s);
7683 let (pr, _g4) = res.device_ptr_mut(s);
7684 let (pq, _g5) = out_q.device_ptr_mut(s);
7685 let (pd, _g6) = out_d.device_ptr_mut(s);
7686 let mut ps = [
7687 &pa as *const _ as *mut std::ffi::c_void,
7688 &pwa as *const _ as *mut _,
7689 &pb as *const _ as *mut _,
7690 &c as *const _ as *mut _,
7691 &pw as *const _ as *mut _,
7692 &pr as *const _ as *mut _,
7693 &pq as *const _ as *mut _,
7694 &pd as *const _ as *mut _,
7695 &nc as *const _ as *mut _,
7696 &e2 as *const _ as *mut _,
7697 ];
7698 unsafe {
7699 self.launch_pdl(
7700 "rms_pre_add_scale_rms_norm_q8_1",
7701 (nrows as u32, 1, 1),
7702 (rms_block(), 1, 1),
7703 &mut ps,
7704 )?;
7705 }
7706 }
7707 return Ok((out_q, out_d));
7708 }
7709 let f = self.func("rms_pre_add_scale_rms_norm_q8_1");
7710 let cfg = LaunchConfig {
7711 grid_dim: (nrows as u32, 1, 1),
7712 block_dim: (rms_block(), 1, 1),
7713 shared_mem_bytes: 0,
7714 };
7715 let __s_b = self.gpu.stream();
7716 let mut b = __s_b.launch_builder(&f);
7717 b.arg(a)
7718 .arg(wa)
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((out_q, out_d))
7731 }
7732
7733 pub fn gelu_tanh_mul_q8_1(
7736 &self,
7737 gate: &CudaSlice<f32>,
7738 up: &cudarc::driver::CudaView<f32>,
7739 act: &mut CudaSlice<f32>,
7740 ncols: usize,
7741 nrows: usize,
7742 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7743 debug_assert!(ncols % 128 == 0);
7744 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7745 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7746 let nc = ncols as i32;
7747 if Self::pdl_on() {
7748 {
7749 use cudarc::driver::{DevicePtr, DevicePtrMut};
7750 let s = &self.gpu.stream();
7751 let (pg, _g0) = gate.device_ptr(s);
7752 let (pu, _g1) = up.device_ptr(s);
7753 let (pact, _g2) = act.device_ptr_mut(s);
7754 let (pq, _g3) = out_q.device_ptr_mut(s);
7755 let (pd, _g4) = out_d.device_ptr_mut(s);
7756 let mut ps = [
7757 &pg as *const _ as *mut std::ffi::c_void,
7758 &pu as *const _ as *mut _,
7759 &pact as *const _ as *mut _,
7760 &pq as *const _ as *mut _,
7761 &pd as *const _ as *mut _,
7762 &nc as *const _ as *mut _,
7763 ];
7764 unsafe {
7765 self.launch_pdl(
7766 "gelu_tanh_mul_q8_1",
7767 (nrows as u32, 1, 1),
7768 (rms_block(), 1, 1),
7769 &mut ps,
7770 )?;
7771 }
7772 }
7773 return Ok((out_q, out_d));
7774 }
7775 let f = self.func("gelu_tanh_mul_q8_1");
7776 let cfg = LaunchConfig {
7777 grid_dim: (nrows as u32, 1, 1),
7778 block_dim: (rms_block(), 1, 1),
7779 shared_mem_bytes: 0,
7780 };
7781 let __s_b = self.gpu.stream();
7782 let mut b = __s_b.launch_builder(&f);
7783 b.arg(gate)
7784 .arg(up)
7785 .arg(act)
7786 .arg(&mut out_q)
7787 .arg(&mut out_d)
7788 .arg(&nc);
7789 unsafe {
7790 b.launch(cfg)?;
7791 }
7792 Ok((out_q, out_d))
7793 }
7794
7795 #[allow(clippy::too_many_arguments)]
7797 pub fn gelu_tanh_mul_q8_1_into(
7798 &self,
7799 gate: &CudaSlice<f32>,
7800 up: &cudarc::driver::CudaView<f32>,
7801 act: &mut CudaSlice<f32>,
7802 ncols: usize,
7803 nrows: usize,
7804 out_q: &mut CudaSlice<i8>,
7805 out_d: &mut CudaSlice<f32>,
7806 ) -> Result<(), Box<dyn std::error::Error>> {
7807 debug_assert!(ncols % 128 == 0);
7808 debug_assert!(out_q.len() >= nrows * ncols && out_d.len() >= nrows * (ncols / 32));
7809 let nc = ncols as i32;
7810 if Self::pdl_on() {
7811 use cudarc::driver::{DevicePtr, DevicePtrMut};
7812 let s = &self.gpu.stream();
7813 let (pg, _g0) = gate.device_ptr(s);
7814 let (pu, _g1) = up.device_ptr(s);
7815 let (pact, _g2) = act.device_ptr_mut(s);
7816 let (pq, _g3) = out_q.device_ptr_mut(s);
7817 let (pd, _g4) = out_d.device_ptr_mut(s);
7818 let mut ps = [
7819 &pg as *const _ as *mut std::ffi::c_void,
7820 &pu as *const _ as *mut _,
7821 &pact as *const _ as *mut _,
7822 &pq as *const _ as *mut _,
7823 &pd as *const _ as *mut _,
7824 &nc as *const _ as *mut _,
7825 ];
7826 unsafe {
7827 self.launch_pdl(
7828 "gelu_tanh_mul_q8_1",
7829 (nrows as u32, 1, 1),
7830 (rms_block(), 1, 1),
7831 &mut ps,
7832 )?;
7833 }
7834 return Ok(());
7835 }
7836 let f = self.func("gelu_tanh_mul_q8_1");
7837 let cfg = LaunchConfig {
7838 grid_dim: (nrows as u32, 1, 1),
7839 block_dim: (rms_block(), 1, 1),
7840 shared_mem_bytes: 0,
7841 };
7842 let __s_b = self.gpu.stream();
7843 let mut b = __s_b.launch_builder(&f);
7844 b.arg(gate)
7845 .arg(up)
7846 .arg(&mut *act)
7847 .arg(&mut *out_q)
7848 .arg(&mut *out_d)
7849 .arg(&nc);
7850 unsafe {
7851 b.launch(cfg)?;
7852 }
7853 Ok(())
7854 }
7855
7856 #[allow(clippy::too_many_arguments)]
7858 pub fn add_rms_norm3_q8z(
7859 &self,
7860 a: &CudaSlice<f32>,
7861 b_in: &CudaSlice<f32>,
7862 w0: &CudaSlice<f32>,
7863 w1: &CudaSlice<f32>,
7864 w2: &CudaSlice<f32>,
7865 res: &mut CudaSlice<f32>,
7866 out1: &mut CudaSlice<f32>,
7867 ncols: usize,
7868 nrows: usize,
7869 eps: f32,
7870 ) -> Result<
7871 (
7872 (CudaSlice<i8>, CudaSlice<f32>),
7873 (CudaSlice<i8>, CudaSlice<f32>),
7874 ),
7875 Box<dyn std::error::Error>,
7876 > {
7877 let mut q0 = self.alloc_uninit::<i8>(nrows * ncols)?;
7878 let mut d0 = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7879 let mut q2 = self.alloc_uninit::<i8>(nrows * ncols)?;
7880 let mut d2 = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7881 let f = self.func("add_rms_norm3_q8z_f32");
7882 let cfg = LaunchConfig {
7883 grid_dim: (nrows as u32, 1, 1),
7884 block_dim: (rms_block(), 1, 1),
7885 shared_mem_bytes: 0,
7886 };
7887 let (nc, e2) = (ncols as i32, eps);
7888 let __s_b = self.gpu.stream();
7889 let mut b = __s_b.launch_builder(&f);
7890 b.arg(a)
7891 .arg(b_in)
7892 .arg(w0)
7893 .arg(w1)
7894 .arg(w2)
7895 .arg(res)
7896 .arg(&mut q0)
7897 .arg(&mut d0)
7898 .arg(out1)
7899 .arg(&mut q2)
7900 .arg(&mut d2)
7901 .arg(&nc)
7902 .arg(&e2);
7903 unsafe {
7904 b.launch(cfg)?;
7905 }
7906 Ok(((q0, d0), (q2, d2)))
7907 }
7908
7909 #[allow(clippy::too_many_arguments)]
7911 pub fn add_rms_norm3(
7912 &self,
7913 a: &CudaSlice<f32>,
7914 b_in: &CudaSlice<f32>,
7915 w0: &CudaSlice<f32>,
7916 w1: &CudaSlice<f32>,
7917 w2: &CudaSlice<f32>,
7918 res: &mut CudaSlice<f32>,
7919 d0: &mut CudaSlice<f32>,
7920 d1: &mut CudaSlice<f32>,
7921 d2: &mut CudaSlice<f32>,
7922 ncols: usize,
7923 nrows: usize,
7924 eps: f32,
7925 ) -> Result<(), Box<dyn std::error::Error>> {
7926 let f = self.func("add_rms_norm3_f32");
7927 let cfg = LaunchConfig {
7928 grid_dim: (nrows as u32, 1, 1),
7929 block_dim: (rms_block(), 1, 1),
7930 shared_mem_bytes: 0,
7931 };
7932 let (nc, e2) = (ncols as i32, eps);
7933 let __s_b = self.gpu.stream();
7934 let mut b = __s_b.launch_builder(&f);
7935 b.arg(a)
7936 .arg(b_in)
7937 .arg(w0)
7938 .arg(w1)
7939 .arg(w2)
7940 .arg(res)
7941 .arg(d0)
7942 .arg(d1)
7943 .arg(d2)
7944 .arg(&nc)
7945 .arg(&e2);
7946 unsafe {
7947 b.launch(cfg)?;
7948 }
7949 Ok(())
7950 }
7951
7952 pub fn add_scale(
7954 &self,
7955 a: &CudaSlice<f32>,
7956 b_in: &CudaSlice<f32>,
7957 c: f32,
7958 dst: &mut CudaSlice<f32>,
7959 n: usize,
7960 ) -> Result<(), Box<dyn std::error::Error>> {
7961 let f = self.func("add_scale_f32");
7962 let cfg = LaunchConfig::for_num_elems(n as u32);
7963 let ni = n as i32;
7964 let __s_b = self.gpu.stream();
7965 let mut b = __s_b.launch_builder(&f);
7966 b.arg(a).arg(b_in).arg(&c).arg(dst).arg(&ni);
7967 unsafe {
7968 b.launch(cfg)?;
7969 }
7970 Ok(())
7971 }
7972
7973 pub fn rms_norm(
7974 &self,
7975 x: &CudaSlice<f32>,
7976 w: &CudaSlice<f32>,
7977 dst: &mut CudaSlice<f32>,
7978 ncols: usize,
7979 nrows: usize,
7980 eps: f32,
7981 ) -> Result<(), Box<dyn std::error::Error>> {
7982 let (nc, e) = (ncols as i32, eps);
7983 if Self::pdl_on() && Self::pdl_wb_on() {
7984 use cudarc::driver::{DevicePtr, DevicePtrMut};
7985 let s = &self.gpu.stream();
7986 let (px, _g0) = x.device_ptr(s);
7987 let (pw, _g1) = w.device_ptr(s);
7988 let (pd, _g2) = dst.device_ptr_mut(s);
7989 let mut ps = [
7990 &px as *const _ as *mut std::ffi::c_void,
7991 &pw as *const _ as *mut _,
7992 &pd as *const _ as *mut _,
7993 &nc as *const _ as *mut _,
7994 &e as *const _ as *mut _,
7995 ];
7996 unsafe {
7997 self.launch_pdl(
7998 "rms_norm_f32",
7999 (nrows as u32, 1, 1),
8000 (rms_block(), 1, 1),
8001 &mut ps,
8002 )?;
8003 }
8004 return Ok(());
8005 }
8006 let f = self.func("rms_norm_f32");
8007 let cfg = LaunchConfig {
8008 grid_dim: (nrows as u32, 1, 1),
8009 block_dim: (rms_block(), 1, 1),
8010 shared_mem_bytes: 0,
8011 };
8012 let __s_b = self.gpu.stream();
8013 let mut b = __s_b.launch_builder(&f);
8014 b.arg(x).arg(w).arg(dst).arg(&nc).arg(&e);
8015 unsafe {
8016 b.launch(cfg)?;
8017 }
8018 Ok(())
8019 }
8020
8021 pub fn rms_norm_decode(
8029 &self,
8030 x: &CudaSlice<f32>,
8031 w: &CudaSlice<f32>,
8032 dst: &mut CudaSlice<f32>,
8033 ncols: usize,
8034 nrows: usize,
8035 eps: f32,
8036 ) -> Result<(), Box<dyn std::error::Error>> {
8037 let f = self.func("rms_norm_f32");
8038 let cfg = LaunchConfig {
8039 grid_dim: (nrows as u32, 1, 1),
8040 block_dim: (1024, 1, 1),
8041 shared_mem_bytes: 0,
8042 };
8043 let (nc, e) = (ncols as i32, eps);
8044 let __s_b = self.gpu.stream();
8045 let mut b = __s_b.launch_builder(&f);
8046 b.arg(x).arg(w).arg(dst).arg(&nc).arg(&e);
8047 unsafe {
8048 b.launch(cfg)?;
8049 }
8050 Ok(())
8051 }
8052
8053 pub fn rms_norm_q8_1(
8057 &self,
8058 x: &CudaSlice<f32>,
8059 w: &CudaSlice<f32>,
8060 ncols: usize,
8061 nrows: usize,
8062 eps: f32,
8063 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8064 let nblk = ncols / 32;
8065 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
8066 let mut d = self.alloc_uninit::<f32>(nrows * nblk)?;
8067 let (nc, e) = (ncols as i32, eps);
8068 if Self::pdl_on() {
8069 {
8070 use cudarc::driver::{DevicePtr, DevicePtrMut};
8071 let s = &self.gpu.stream();
8072 let (px, _g0) = x.device_ptr(s);
8073 let (pw, _g1) = w.device_ptr(s);
8074 let (pq, _g2) = q.device_ptr_mut(s);
8075 let (pd, _g3) = d.device_ptr_mut(s);
8076 let mut ps = [
8077 &px as *const _ as *mut std::ffi::c_void,
8078 &pw as *const _ as *mut _,
8079 &pq as *const _ as *mut _,
8080 &pd as *const _ as *mut _,
8081 &nc as *const _ as *mut _,
8082 &e as *const _ as *mut _,
8083 ];
8084 unsafe {
8085 self.launch_pdl("rms_norm_q8_1", (nrows as u32, 1, 1), (1024, 1, 1), &mut ps)?;
8086 }
8087 }
8088 return Ok((q, d));
8089 }
8090 let f = self.func("rms_norm_q8_1");
8091 let cfg = LaunchConfig {
8094 grid_dim: (nrows as u32, 1, 1),
8095 block_dim: (1024, 1, 1),
8096 shared_mem_bytes: 0,
8097 };
8098 let __s_b = self.gpu.stream();
8099 let mut b = __s_b.launch_builder(&f);
8100 b.arg(x).arg(w).arg(&mut q).arg(&mut d).arg(&nc).arg(&e);
8101 unsafe {
8102 b.launch(cfg)?;
8103 }
8104 Ok((q, d))
8105 }
8106
8107 pub fn rms_norm_q8_1_into(
8110 &self,
8111 x: &CudaSlice<f32>,
8112 w: &CudaSlice<f32>,
8113 ncols: usize,
8114 nrows: usize,
8115 eps: f32,
8116 q: &mut CudaSlice<i8>,
8117 d: &mut CudaSlice<f32>,
8118 ) -> Result<(), Box<dyn std::error::Error>> {
8119 let nblk = ncols / 32;
8120 debug_assert!(q.len() >= nrows * ncols && d.len() >= nrows * nblk);
8121 let (nc, e) = (ncols as i32, eps);
8122 if Self::pdl_on() {
8123 use cudarc::driver::{DevicePtr, DevicePtrMut};
8124 let s = &self.gpu.stream();
8125 let (px, _g0) = x.device_ptr(s);
8126 let (pw, _g1) = w.device_ptr(s);
8127 let (pq, _g2) = q.device_ptr_mut(s);
8128 let (pd, _g3) = d.device_ptr_mut(s);
8129 let mut ps = [
8130 &px as *const _ as *mut std::ffi::c_void,
8131 &pw as *const _ as *mut _,
8132 &pq as *const _ as *mut _,
8133 &pd as *const _ as *mut _,
8134 &nc as *const _ as *mut _,
8135 &e as *const _ as *mut _,
8136 ];
8137 unsafe {
8138 self.launch_pdl("rms_norm_q8_1", (nrows as u32, 1, 1), (1024, 1, 1), &mut ps)?;
8139 }
8140 return Ok(());
8141 }
8142 let f = self.func("rms_norm_q8_1");
8143 let cfg = LaunchConfig {
8144 grid_dim: (nrows as u32, 1, 1),
8145 block_dim: (1024, 1, 1),
8146 shared_mem_bytes: 0,
8147 };
8148 let __s_b = self.gpu.stream();
8149 let mut b = __s_b.launch_builder(&f);
8150 b.arg(x).arg(w).arg(&mut *q).arg(&mut *d).arg(&nc).arg(&e);
8151 unsafe {
8152 b.launch(cfg)?;
8153 }
8154 Ok(())
8155 }
8156
8157 pub fn quantize_q8_1_into(
8159 &self,
8160 x: &CudaSlice<f32>,
8161 m: usize,
8162 in_f: usize,
8163 q: &mut CudaSlice<i8>,
8164 d: &mut CudaSlice<f32>,
8165 ) -> Result<(), Box<dyn std::error::Error>> {
8166 let nblk = in_f / 32;
8167 debug_assert!(q.len() >= m * in_f && d.len() >= m * nblk);
8168 let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
8169 let (inf, mi) = (in_f as i32, m as i32);
8170 if Self::pdl_on() && Self::pdl_wb_on() {
8171 use cudarc::driver::{DevicePtr, DevicePtrMut};
8172 let s = &self.gpu.stream();
8173 let (px, _g0) = x.device_ptr(s);
8174 let (pq, _g1) = q.device_ptr_mut(s);
8175 let (pd, _g2) = d.device_ptr_mut(s);
8176 let mut ps = [
8177 &px as *const _ as *mut std::ffi::c_void,
8178 &pq as *const _ as *mut _,
8179 &pd as *const _ as *mut _,
8180 &inf as *const _ as *mut _,
8181 &mi as *const _ as *mut _,
8182 ];
8183 unsafe {
8184 self.launch_pdl("quantize_q8_1", cfg.grid_dim, cfg.block_dim, &mut ps)?;
8185 }
8186 return Ok(());
8187 }
8188 let f = self.func("quantize_q8_1");
8189 let __s_b = self.gpu.stream();
8190 let mut b = __s_b.launch_builder(&f);
8191 b.arg(x).arg(&mut *q).arg(&mut *d).arg(&inf).arg(&mi);
8192 unsafe {
8193 b.launch(cfg)?;
8194 }
8195 Ok(())
8196 }
8197
8198 pub fn add_rms_norm_q8_1(
8202 &self,
8203 a: &CudaSlice<f32>,
8204 b_in: &CudaSlice<f32>,
8205 w: &CudaSlice<f32>,
8206 res: &mut CudaSlice<f32>,
8207 ncols: usize,
8208 nrows: usize,
8209 eps: f32,
8210 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8211 let nblk = ncols / 32;
8212 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
8213 let mut d = self.alloc_uninit::<f32>(nrows * nblk)?;
8214 let f = self.func("add_rms_norm_q8_1");
8215 let cfg = LaunchConfig {
8217 grid_dim: (nrows as u32, 1, 1),
8218 block_dim: (1024, 1, 1),
8219 shared_mem_bytes: 0,
8220 };
8221 let (nc, e) = (ncols as i32, eps);
8222 let __s_bld = self.gpu.stream();
8223 let mut bld = __s_bld.launch_builder(&f);
8224 bld.arg(a)
8225 .arg(b_in)
8226 .arg(w)
8227 .arg(res)
8228 .arg(&mut q)
8229 .arg(&mut d)
8230 .arg(&nc)
8231 .arg(&e);
8232 unsafe {
8233 bld.launch(cfg)?;
8234 }
8235 Ok((q, d))
8236 }
8237
8238 pub fn add_rms_norm(
8242 &self,
8243 a: &CudaSlice<f32>,
8244 b: &CudaSlice<f32>,
8245 w: &CudaSlice<f32>,
8246 res: &mut CudaSlice<f32>,
8247 dst: &mut CudaSlice<f32>,
8248 ncols: usize,
8249 nrows: usize,
8250 eps: f32,
8251 ) -> Result<(), Box<dyn std::error::Error>> {
8252 let (nc, e) = (ncols as i32, eps);
8253 if Self::pdl_on() && Self::pdl_wb_on() {
8254 use cudarc::driver::{DevicePtr, DevicePtrMut};
8255 let s = &self.gpu.stream();
8256 let (pa, _g0) = a.device_ptr(s);
8257 let (pb, _g1) = b.device_ptr(s);
8258 let (pw, _g2) = w.device_ptr(s);
8259 let (pr, _g3) = res.device_ptr_mut(s);
8260 let (pd, _g4) = dst.device_ptr_mut(s);
8261 let mut ps = [
8262 &pa as *const _ as *mut std::ffi::c_void,
8263 &pb as *const _ as *mut _,
8264 &pw as *const _ as *mut _,
8265 &pr as *const _ as *mut _,
8266 &pd as *const _ as *mut _,
8267 &nc as *const _ as *mut _,
8268 &e as *const _ as *mut _,
8269 ];
8270 unsafe {
8271 self.launch_pdl(
8272 "add_rms_norm_f32",
8273 (nrows as u32, 1, 1),
8274 (rms_block(), 1, 1),
8275 &mut ps,
8276 )?;
8277 }
8278 return Ok(());
8279 }
8280 let f = self.func("add_rms_norm_f32");
8281 let cfg = LaunchConfig {
8282 grid_dim: (nrows as u32, 1, 1),
8283 block_dim: (rms_block(), 1, 1),
8284 shared_mem_bytes: 0,
8285 };
8286 let __s_b2 = self.gpu.stream();
8287 let mut b2 = __s_b2.launch_builder(&f);
8288 b2.arg(a)
8289 .arg(b)
8290 .arg(w)
8291 .arg(&mut *res)
8292 .arg(&mut *dst)
8293 .arg(&nc)
8294 .arg(&e);
8295 unsafe {
8296 b2.launch(cfg)?;
8297 }
8298 Ok(())
8299 }
8300
8301 #[allow(clippy::too_many_arguments)]
8304 pub fn rms_pre_add_rms_norm(
8305 &self,
8306 a: &CudaSlice<f32>,
8307 wa: &CudaSlice<f32>,
8308 b: &CudaSlice<f32>,
8309 w: &CudaSlice<f32>,
8310 res: &mut CudaSlice<f32>,
8311 dst: &mut CudaSlice<f32>,
8312 ncols: usize,
8313 nrows: usize,
8314 eps: f32,
8315 ) -> Result<(), Box<dyn std::error::Error>> {
8316 let f = self.func("rms_pre_add_rms_norm_f32");
8317 let cfg = LaunchConfig {
8318 grid_dim: (nrows as u32, 1, 1),
8319 block_dim: (rms_block(), 1, 1),
8320 shared_mem_bytes: 0,
8321 };
8322 let (nc, e) = (ncols as i32, eps);
8323 let __s_b2 = self.gpu.stream();
8324 let mut b2 = __s_b2.launch_builder(&f);
8325 b2.arg(a)
8326 .arg(wa)
8327 .arg(b)
8328 .arg(w)
8329 .arg(&mut *res)
8330 .arg(&mut *dst)
8331 .arg(&nc)
8332 .arg(&e);
8333 unsafe {
8334 b2.launch(cfg)?;
8335 }
8336 Ok(())
8337 }
8338
8339 #[allow(clippy::too_many_arguments)]
8341 pub fn rms_pre_add_rms_norm_q8z(
8342 &self,
8343 a: &CudaSlice<f32>,
8344 wa: &CudaSlice<f32>,
8345 b: &CudaSlice<f32>,
8346 w: &CudaSlice<f32>,
8347 res: &mut CudaSlice<f32>,
8348 dst: &mut CudaSlice<f32>,
8349 ncols: usize,
8350 nrows: usize,
8351 eps: f32,
8352 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8353 debug_assert!(ncols % 128 == 0);
8354 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
8355 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
8356 let (nc, e) = (ncols as i32, eps);
8357 if Self::pdl_on() {
8358 {
8359 use cudarc::driver::{DevicePtr, DevicePtrMut};
8360 let s = &self.gpu.stream();
8361 let (pa, _g0) = a.device_ptr(s);
8362 let (pwa, _g1) = wa.device_ptr(s);
8363 let (pb, _g2) = b.device_ptr(s);
8364 let (pw, _g3) = w.device_ptr(s);
8365 let (pr, _g4) = res.device_ptr_mut(s);
8366 let (pdst, _g5) = dst.device_ptr_mut(s);
8367 let (pq, _g6) = out_q.device_ptr_mut(s);
8368 let (pd, _g7) = out_d.device_ptr_mut(s);
8369 let mut ps = [
8370 &pa as *const _ as *mut std::ffi::c_void,
8371 &pwa as *const _ as *mut _,
8372 &pb as *const _ as *mut _,
8373 &pw as *const _ as *mut _,
8374 &pr as *const _ as *mut _,
8375 &pdst as *const _ as *mut _,
8376 &pq as *const _ as *mut _,
8377 &pd as *const _ as *mut _,
8378 &nc as *const _ as *mut _,
8379 &e as *const _ as *mut _,
8380 ];
8381 unsafe {
8382 self.launch_pdl(
8383 "rms_pre_add_rms_norm_q8z_f32",
8384 (nrows as u32, 1, 1),
8385 (rms_block(), 1, 1),
8386 &mut ps,
8387 )?;
8388 }
8389 }
8390 return Ok((out_q, out_d));
8391 }
8392 let f = self.func("rms_pre_add_rms_norm_q8z_f32");
8393 let cfg = LaunchConfig {
8394 grid_dim: (nrows as u32, 1, 1),
8395 block_dim: (rms_block(), 1, 1),
8396 shared_mem_bytes: 0,
8397 };
8398 let __s_b2 = self.gpu.stream();
8399 let mut b2 = __s_b2.launch_builder(&f);
8400 b2.arg(a)
8401 .arg(wa)
8402 .arg(b)
8403 .arg(w)
8404 .arg(&mut *res)
8405 .arg(&mut *dst)
8406 .arg(&mut out_q)
8407 .arg(&mut out_d)
8408 .arg(&nc)
8409 .arg(&e);
8410 unsafe {
8411 b2.launch(cfg)?;
8412 }
8413 Ok((out_q, out_d))
8414 }
8415
8416 pub fn build_q4_out_concat3(
8420 &self,
8421 w0: &crate::model::GpuTensor,
8422 w1: &crate::model::GpuTensor,
8423 w2: &crate::model::GpuTensor,
8424 ) -> Result<Option<crate::model::GpuTensor>, Box<dyn std::error::Error>> {
8425 use crate::model::GpuTensor;
8426 let part = |w: &GpuTensor| -> Option<(usize, usize)> {
8427 match w {
8428 GpuTensor::Quant {
8429 qtype,
8430 row_bytes,
8431 rp,
8432 ..
8433 } if *qtype == QT_Q4_0 && !*rp => Some((*row_bytes, w.out_features())),
8434 _ => None,
8435 }
8436 };
8437 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (part(w0), part(w1), part(w2))
8438 else {
8439 return Ok(None);
8440 };
8441 if rb0 != rb1
8442 || rb0 != rb2
8443 || w0.in_features() != w1.in_features()
8444 || w0.in_features() != w2.in_features()
8445 {
8446 return Ok(None);
8447 }
8448 fn bytes_of(w: &crate::model::GpuTensor) -> &CudaSlice<u8> {
8449 match w {
8450 crate::model::GpuTensor::Quant { bytes, .. } => bytes,
8451 _ => unreachable!(),
8452 }
8453 }
8454 let (b0, b1, b2) = (bytes_of(w0), bytes_of(w1), bytes_of(w2));
8455 let total = rb0 * (o0 + o1 + o2);
8456 let mut cat = self.alloc_u8(total)?;
8457 self.copy_u8_into(&mut cat, 0, b0, rb0 * o0)?;
8458 self.copy_u8_into(&mut cat, rb0 * o0, b1, rb1 * o1)?;
8459 self.copy_u8_into(&mut cat, rb0 * (o0 + o1), b2, rb2 * o2)?;
8460 Ok(Some(GpuTensor::Quant {
8461 bytes: cat,
8462 qtype: QT_Q4_0,
8463 row_bytes: rb0,
8464 ne: vec![w0.in_features() as u64, (o0 + o1 + o2) as u64],
8465 scale: 1.0,
8466 rp: false,
8467 #[cfg(memra_cutlass)]
8468 cutlass: None,
8469 fp8: None,
8470 blk: None,
8471 rp4: None,
8472 f16: None,
8473 }))
8474 }
8475
8476 #[allow(clippy::too_many_arguments)]
8478 pub fn rms_norm_qkv_rope_cat(
8479 &self,
8480 qkv: &CudaSlice<f32>,
8481 wq: &CudaSlice<f32>,
8482 wk: &CudaSlice<f32>,
8483 wv: &CudaSlice<f32>,
8484 q: &mut CudaSlice<f32>,
8485 k: &mut CudaSlice<f32>,
8486 v: &mut CudaSlice<f32>,
8487 head_dim: usize,
8488 rq: usize,
8489 rk: usize,
8490 pos: &CudaSlice<i32>,
8491 nh_q: usize,
8492 nh_k: usize,
8493 base: f32,
8494 freq_scale: f32,
8495 ff: Option<&CudaSlice<f32>>,
8496 eps: f32,
8497 ) -> Result<(), Box<dyn std::error::Error>> {
8498 let rows = rq + rk + rk;
8499 let theta_scale = base.powf(-2.0 / head_dim as f32);
8500 let (nc, rqi, rki, nhq, nhk) = (
8501 head_dim as i32,
8502 rq as i32,
8503 rk as i32,
8504 nh_q as i32,
8505 nh_k as i32,
8506 );
8507 if Self::pdl_on() {
8508 use cudarc::driver::{DevicePtr, DevicePtrMut};
8509 let s = &self.gpu.stream();
8510 let (pqkv, _g0) = qkv.device_ptr(s);
8511 let (pwq, _g1) = wq.device_ptr(s);
8512 let (pwk, _g2) = wk.device_ptr(s);
8513 let (pwv, _g3) = wv.device_ptr(s);
8514 let (pq, _g4) = q.device_ptr_mut(s);
8515 let (pk, _g5) = k.device_ptr_mut(s);
8516 let (pv, _g6) = v.device_ptr_mut(s);
8517 let (ppos, _g7) = pos.device_ptr(s);
8518 let (pff, _g8) = match ff {
8519 Some(t) => {
8520 let (p, g) = t.device_ptr(s);
8521 (p, Some(g))
8522 }
8523 None => (0, None),
8524 };
8525 let mut ps = [
8526 &pqkv as *const _ as *mut std::ffi::c_void,
8527 &pwq as *const _ as *mut _,
8528 &pwk as *const _ as *mut _,
8529 &pwv as *const _ as *mut _,
8530 &pq as *const _ as *mut _,
8531 &pk as *const _ as *mut _,
8532 &pv as *const _ as *mut _,
8533 &nc as *const _ as *mut _,
8534 &rqi as *const _ as *mut _,
8535 &rki as *const _ as *mut _,
8536 &ppos as *const _ as *mut _,
8537 &nhq as *const _ as *mut _,
8538 &nhk as *const _ as *mut _,
8539 &theta_scale as *const _ as *mut _,
8540 &freq_scale as *const _ as *mut _,
8541 &pff as *const _ as *mut _,
8542 &eps as *const _ as *mut _,
8543 ];
8544 unsafe {
8545 self.launch_pdl(
8546 "rms_norm_qkv_rope_cat_f32",
8547 (rows as u32, 1, 1),
8548 (rms_block(), 1, 1),
8549 &mut ps,
8550 )?;
8551 }
8552 return Ok(());
8553 }
8554 let f = self.func("rms_norm_qkv_rope_cat_f32");
8555 let cfg = LaunchConfig {
8556 grid_dim: (rows as u32, 1, 1),
8557 block_dim: (rms_block(), 1, 1),
8558 shared_mem_bytes: 0,
8559 };
8560 let __s_b = self.gpu.stream();
8561 let mut b = __s_b.launch_builder(&f);
8562 match ff {
8563 Some(t) => {
8564 b.arg(qkv)
8565 .arg(wq)
8566 .arg(wk)
8567 .arg(wv)
8568 .arg(&mut *q)
8569 .arg(&mut *k)
8570 .arg(&mut *v)
8571 .arg(&nc)
8572 .arg(&rqi)
8573 .arg(&rki)
8574 .arg(pos)
8575 .arg(&nhq)
8576 .arg(&nhk)
8577 .arg(&theta_scale)
8578 .arg(&freq_scale)
8579 .arg(t)
8580 .arg(&eps);
8581 unsafe {
8582 b.launch(cfg)?;
8583 }
8584 }
8585 None => {
8586 let null: u64 = 0;
8587 b.arg(qkv)
8588 .arg(wq)
8589 .arg(wk)
8590 .arg(wv)
8591 .arg(&mut *q)
8592 .arg(&mut *k)
8593 .arg(&mut *v)
8594 .arg(&nc)
8595 .arg(&rqi)
8596 .arg(&rki)
8597 .arg(pos)
8598 .arg(&nhq)
8599 .arg(&nhk)
8600 .arg(&theta_scale)
8601 .arg(&freq_scale)
8602 .arg(&null)
8603 .arg(&eps);
8604 unsafe {
8605 b.launch(cfg)?;
8606 }
8607 }
8608 }
8609 Ok(())
8610 }
8611
8612 #[allow(clippy::too_many_arguments)]
8614 pub fn rms_norm_qkv_rope(
8615 &self,
8616 q0: &CudaSlice<f32>,
8617 k0: &CudaSlice<f32>,
8618 v0: &CudaSlice<f32>,
8619 wq: &CudaSlice<f32>,
8620 wk: &CudaSlice<f32>,
8621 wv: &CudaSlice<f32>,
8622 q: &mut CudaSlice<f32>,
8623 k: &mut CudaSlice<f32>,
8624 v: &mut CudaSlice<f32>,
8625 head_dim: usize,
8626 rq: usize,
8627 rk: usize,
8628 pos: &CudaSlice<i32>,
8629 nh_q: usize,
8630 nh_k: usize,
8631 base: f32,
8632 freq_scale: f32,
8633 ff: Option<&CudaSlice<f32>>,
8634 eps: f32,
8635 ) -> Result<(), Box<dyn std::error::Error>> {
8636 let f = self.func("rms_norm_qkv_rope_f32");
8637 let rows = rq + rk + rk; let cfg = LaunchConfig {
8639 grid_dim: (rows as u32, 1, 1),
8640 block_dim: (rms_block(), 1, 1),
8641 shared_mem_bytes: 0,
8642 };
8643 let theta_scale = base.powf(-2.0 / head_dim as f32);
8644 let (nc, rqi, rki, nhq, nhk) = (
8645 head_dim as i32,
8646 rq as i32,
8647 rk as i32,
8648 nh_q as i32,
8649 nh_k as i32,
8650 );
8651 let __s_b = self.gpu.stream();
8652 let mut b = __s_b.launch_builder(&f);
8653 match ff {
8654 Some(t) => {
8655 b.arg(q0)
8656 .arg(k0)
8657 .arg(v0)
8658 .arg(wq)
8659 .arg(wk)
8660 .arg(wv)
8661 .arg(&mut *q)
8662 .arg(&mut *k)
8663 .arg(&mut *v)
8664 .arg(&nc)
8665 .arg(&rqi)
8666 .arg(&rki)
8667 .arg(pos)
8668 .arg(&nhq)
8669 .arg(&nhk)
8670 .arg(&theta_scale)
8671 .arg(&freq_scale)
8672 .arg(t)
8673 .arg(&eps);
8674 unsafe {
8675 b.launch(cfg)?;
8676 }
8677 }
8678 None => {
8679 let null: u64 = 0;
8680 b.arg(q0)
8681 .arg(k0)
8682 .arg(v0)
8683 .arg(wq)
8684 .arg(wk)
8685 .arg(wv)
8686 .arg(&mut *q)
8687 .arg(&mut *k)
8688 .arg(&mut *v)
8689 .arg(&nc)
8690 .arg(&rqi)
8691 .arg(&rki)
8692 .arg(pos)
8693 .arg(&nhq)
8694 .arg(&nhk)
8695 .arg(&theta_scale)
8696 .arg(&freq_scale)
8697 .arg(&null)
8698 .arg(&eps);
8699 unsafe {
8700 b.launch(cfg)?;
8701 }
8702 }
8703 }
8704 Ok(())
8705 }
8706
8707 #[allow(clippy::too_many_arguments)]
8711 pub fn rms_norm_qkv_rope_append_dc(
8712 &self,
8713 q0: &CudaSlice<f32>,
8714 k0: &CudaSlice<f32>,
8715 v0: &CudaSlice<f32>,
8716 wq: &CudaSlice<f32>,
8717 wk: &CudaSlice<f32>,
8718 wv: &CudaSlice<f32>,
8719 q: &mut CudaSlice<f32>,
8720 k: &mut CudaSlice<f32>,
8721 v: &mut CudaSlice<f32>,
8722 head_dim: usize,
8723 rq: usize,
8724 rk: usize,
8725 pos: &CudaSlice<i32>,
8726 nh_q: usize,
8727 nh_k: usize,
8728 base: f32,
8729 freq_scale: f32,
8730 ff: Option<&CudaSlice<f32>>,
8731 eps: f32,
8732 kc: &mut CudaSlice<u8>,
8733 vc: &mut CudaSlice<u8>,
8734 t_dev: &CudaSlice<i32>,
8735 k_tok_bytes: usize,
8736 v_tok_bytes: usize,
8737 g: bool,
8738 ) -> Result<(), Box<dyn std::error::Error>> {
8739 let rows = rq + rk + rk;
8740 let theta_scale = base.powf(-2.0 / head_dim as f32);
8741 let (nc, rqi, rki, nhq, nhk) = (
8742 head_dim as i32,
8743 rq as i32,
8744 rk as i32,
8745 nh_q as i32,
8746 nh_k as i32,
8747 );
8748 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
8749 if Self::pdl_on() && Self::pdl_wb_on() {
8750 use cudarc::driver::{DevicePtr, DevicePtrMut};
8751 let s = &self.gpu.stream();
8752 let (p0, _a0) = q0.device_ptr(s);
8753 let (p1, _a1) = k0.device_ptr(s);
8754 let (p2, _a2) = v0.device_ptr(s);
8755 let (pwq, _a3) = wq.device_ptr(s);
8756 let (pwk, _a4) = wk.device_ptr(s);
8757 let (pwv, _a5) = wv.device_ptr(s);
8758 let (pq, _a6) = q.device_ptr_mut(s);
8759 let (pk, _a7) = k.device_ptr_mut(s);
8760 let (pv, _a8) = v.device_ptr_mut(s);
8761 let (pp, _a9) = pos.device_ptr(s);
8762 let pff: u64 = match ff {
8763 Some(t) => {
8764 let (p, _gg) = t.device_ptr(s);
8765 p as u64
8766 }
8767 None => 0,
8768 };
8769 let (pkc, _a10) = kc.device_ptr_mut(s);
8770 let (pvc, _a11) = vc.device_ptr_mut(s);
8771 let (pt, _a12) = t_dev.device_ptr(s);
8772 let mut ps = [
8773 &p0 as *const _ as *mut std::ffi::c_void,
8774 &p1 as *const _ as *mut _,
8775 &p2 as *const _ as *mut _,
8776 &pwq as *const _ as *mut _,
8777 &pwk as *const _ as *mut _,
8778 &pwv as *const _ as *mut _,
8779 &pq as *const _ as *mut _,
8780 &pk as *const _ as *mut _,
8781 &pv as *const _ as *mut _,
8782 &nc as *const _ as *mut _,
8783 &rqi as *const _ as *mut _,
8784 &rki as *const _ as *mut _,
8785 &pp as *const _ as *mut _,
8786 &nhq as *const _ as *mut _,
8787 &nhk as *const _ as *mut _,
8788 &theta_scale as *const _ as *mut _,
8789 &freq_scale as *const _ as *mut _,
8790 &pff as *const _ as *mut _,
8791 &eps as *const _ as *mut _,
8792 &pkc as *const _ as *mut _,
8793 &pvc as *const _ as *mut _,
8794 &pt as *const _ as *mut _,
8795 &ktb as *const _ as *mut _,
8796 &vtb as *const _ as *mut _,
8797 ];
8798 unsafe {
8799 self.launch_pdl_flash(
8800 g,
8801 "rms_norm_qkv_rope_append_dc_f32",
8802 (rows as u32, 1, 1),
8803 (rms_block(), 1, 1),
8804 0,
8805 &mut ps,
8806 )?;
8807 }
8808 return Ok(());
8809 }
8810 let f = if g {
8811 self.func_g("rms_norm_qkv_rope_append_dc_f32")
8812 } else {
8813 self.func("rms_norm_qkv_rope_append_dc_f32")
8814 };
8815 let cfg = LaunchConfig {
8816 grid_dim: (rows as u32, 1, 1),
8817 block_dim: (rms_block(), 1, 1),
8818 shared_mem_bytes: 0,
8819 };
8820 let __s_b = self.gpu.stream();
8821 let mut b = __s_b.launch_builder(&f);
8822 match ff {
8823 Some(t) => {
8824 b.arg(q0)
8825 .arg(k0)
8826 .arg(v0)
8827 .arg(wq)
8828 .arg(wk)
8829 .arg(wv)
8830 .arg(&mut *q)
8831 .arg(&mut *k)
8832 .arg(&mut *v)
8833 .arg(&nc)
8834 .arg(&rqi)
8835 .arg(&rki)
8836 .arg(pos)
8837 .arg(&nhq)
8838 .arg(&nhk)
8839 .arg(&theta_scale)
8840 .arg(&freq_scale)
8841 .arg(t)
8842 .arg(&eps)
8843 .arg(&mut *kc)
8844 .arg(&mut *vc)
8845 .arg(t_dev)
8846 .arg(&ktb)
8847 .arg(&vtb);
8848 unsafe {
8849 b.launch(cfg)?;
8850 }
8851 }
8852 None => {
8853 let null: u64 = 0;
8854 b.arg(q0)
8855 .arg(k0)
8856 .arg(v0)
8857 .arg(wq)
8858 .arg(wk)
8859 .arg(wv)
8860 .arg(&mut *q)
8861 .arg(&mut *k)
8862 .arg(&mut *v)
8863 .arg(&nc)
8864 .arg(&rqi)
8865 .arg(&rki)
8866 .arg(pos)
8867 .arg(&nhq)
8868 .arg(&nhk)
8869 .arg(&theta_scale)
8870 .arg(&freq_scale)
8871 .arg(&null)
8872 .arg(&eps)
8873 .arg(&mut *kc)
8874 .arg(&mut *vc)
8875 .arg(t_dev)
8876 .arg(&ktb)
8877 .arg(&vtb);
8878 unsafe {
8879 b.launch(cfg)?;
8880 }
8881 }
8882 }
8883 Ok(())
8884 }
8885
8886 pub fn add_q8_1(
8888 &self,
8889 a: &CudaSlice<f32>,
8890 b: &CudaSlice<f32>,
8891 res: &mut CudaSlice<f32>,
8892 ncols: usize,
8893 nrows: usize,
8894 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8895 debug_assert!(ncols % 128 == 0);
8896 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
8897 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
8898 let f = self.func("add_q8_1_f32");
8899 let cfg = LaunchConfig {
8900 grid_dim: (nrows as u32, 1, 1),
8901 block_dim: (rms_block(), 1, 1),
8902 shared_mem_bytes: 0,
8903 };
8904 let nc = ncols as i32;
8905 let __s_b2 = self.gpu.stream();
8906 let mut b2 = __s_b2.launch_builder(&f);
8907 b2.arg(a)
8908 .arg(b)
8909 .arg(&mut *res)
8910 .arg(&mut out_q)
8911 .arg(&mut out_d)
8912 .arg(&nc);
8913 unsafe {
8914 b2.launch(cfg)?;
8915 }
8916 Ok((out_q, out_d))
8917 }
8918
8919 pub fn rms_pre_add_q8_1(
8923 &self,
8924 a: &CudaSlice<f32>,
8925 wa: &CudaSlice<f32>,
8926 b: &CudaSlice<f32>,
8927 res: &mut CudaSlice<f32>,
8928 ncols: usize,
8929 nrows: usize,
8930 eps: f32,
8931 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8932 debug_assert!(ncols % 128 == 0);
8933 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
8934 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
8935 let f = self.func("rms_pre_add_q8_1_f32");
8936 let cfg = LaunchConfig {
8937 grid_dim: (nrows as u32, 1, 1),
8938 block_dim: (rms_block(), 1, 1),
8939 shared_mem_bytes: 0,
8940 };
8941 let (nc, ep) = (ncols as i32, eps);
8942 let __s_b2 = self.gpu.stream();
8943 let mut b2 = __s_b2.launch_builder(&f);
8944 b2.arg(a)
8945 .arg(wa)
8946 .arg(b)
8947 .arg(&mut *res)
8948 .arg(&mut out_q)
8949 .arg(&mut out_d)
8950 .arg(&nc)
8951 .arg(&ep);
8952 unsafe {
8953 b2.launch(cfg)?;
8954 }
8955 Ok((out_q, out_d))
8956 }
8957
8958 pub fn l2_v2_on(ncols: usize) -> bool {
8962 ncols == 128 && std::env::var("MEMRA_L2_V2").as_deref() != Ok("0")
8963 }
8964
8965 pub fn l2_norm_pp(
8966 &self,
8967 x: &CudaSlice<f32>,
8968 dst: &mut CudaSlice<f32>,
8969 dst16: Option<&mut CudaSlice<u8>>,
8970 ncols: usize,
8971 nrows: usize,
8972 eps: f32,
8973 ) -> Result<(), Box<dyn std::error::Error>> {
8974 if Self::l2_v2_on(ncols) {
8975 let f = self.func("l2_norm_pp_v2_f32");
8976 let rows_per_block = 8u32; let cfg = LaunchConfig {
8978 grid_dim: ((nrows as u32).div_ceil(rows_per_block), 1, 1),
8979 block_dim: (256, 1, 1),
8980 shared_mem_bytes: 0,
8981 };
8982 let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
8983 let d16: u64 = match dst16 {
8985 Some(d) => self.addr_u8(d),
8986 None => 0,
8987 };
8988 let __s_b = self.gpu.stream();
8989 let mut b = __s_b.launch_builder(&f);
8990 b.arg(x).arg(dst).arg(&d16).arg(&nc).arg(&nr).arg(&e);
8991 unsafe {
8992 b.launch(cfg)?;
8993 }
8994 return Ok(());
8995 }
8996 self.l2_norm(x, dst, ncols, nrows, eps)
8997 }
8998
8999 pub fn l2_norm(
9000 &self,
9001 x: &CudaSlice<f32>,
9002 dst: &mut CudaSlice<f32>,
9003 ncols: usize,
9004 nrows: usize,
9005 eps: f32,
9006 ) -> Result<(), Box<dyn std::error::Error>> {
9007 let f = self.func("l2_norm_f32");
9008 let cfg = LaunchConfig {
9009 grid_dim: (nrows as u32, 1, 1),
9010 block_dim: (256, 1, 1),
9011 shared_mem_bytes: 0,
9012 };
9013 let (nc, e) = (ncols as i32, eps);
9014 let __s_b = self.gpu.stream();
9015 let mut b = __s_b.launch_builder(&f);
9016 b.arg(x).arg(dst).arg(&nc).arg(&e);
9017 unsafe {
9018 b.launch(cfg)?;
9019 }
9020 Ok(())
9021 }
9022
9023 pub fn l2_norm_decode(
9029 &self,
9030 x: &CudaSlice<f32>,
9031 dst: &mut CudaSlice<f32>,
9032 ncols: usize,
9033 nrows: usize,
9034 eps: f32,
9035 ) -> Result<(), Box<dyn std::error::Error>> {
9036 let f = self.func("l2_norm_f32");
9037 let cfg = LaunchConfig {
9038 grid_dim: (nrows as u32, 1, 1),
9039 block_dim: (32, 1, 1),
9040 shared_mem_bytes: 0,
9041 };
9042 let (nc, e) = (ncols as i32, eps);
9043 let __s_b = self.gpu.stream();
9044 let mut b = __s_b.launch_builder(&f);
9045 b.arg(x).arg(dst).arg(&nc).arg(&e);
9046 unsafe {
9047 b.launch(cfg)?;
9048 }
9049 Ok(())
9050 }
9051
9052 pub fn rope_neox(
9054 &self,
9055 x: &mut CudaSlice<f32>,
9056 pos: &CudaSlice<i32>,
9057 head_dim: usize,
9058 n_dims: usize,
9059 n_heads: usize,
9060 n_tokens: usize,
9061 freq_base: f32,
9062 freq_scale: f32,
9063 ) -> Result<(), Box<dyn std::error::Error>> {
9064 let f = self.func("rope_neox_f32");
9065 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9066 let grid = (n_heads * n_tokens) as u32;
9067 let cfg = LaunchConfig {
9068 grid_dim: (grid, 1, 1),
9069 block_dim: ((head_dim / 2) as u32, 1, 1),
9070 shared_mem_bytes: 0,
9071 };
9072 let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
9073 let __s_b = self.gpu.stream();
9074 let mut b = __s_b.launch_builder(&f);
9075 b.arg(x)
9076 .arg(pos)
9077 .arg(&hd)
9078 .arg(&nd)
9079 .arg(&nh)
9080 .arg(&theta_scale)
9081 .arg(&freq_scale);
9082 unsafe {
9083 b.launch(cfg)?;
9084 }
9085 Ok(())
9086 }
9087
9088 pub fn rope_neox_ff(
9090 &self,
9091 x: &mut CudaSlice<f32>,
9092 pos: &CudaSlice<i32>,
9093 head_dim: usize,
9094 n_dims: usize,
9095 n_heads: usize,
9096 n_tokens: usize,
9097 freq_base: f32,
9098 freq_scale: f32,
9099 ff: &CudaSlice<f32>,
9100 ) -> Result<(), Box<dyn std::error::Error>> {
9101 let f = self.func("rope_neox_ff_f32");
9102 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9103 let grid = (n_heads * n_tokens) as u32;
9104 let cfg = LaunchConfig {
9105 grid_dim: (grid, 1, 1),
9106 block_dim: ((head_dim / 2) as u32, 1, 1),
9107 shared_mem_bytes: 0,
9108 };
9109 let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
9110 let __s_b = self.gpu.stream();
9111 let mut b = __s_b.launch_builder(&f);
9112 b.arg(x)
9113 .arg(pos)
9114 .arg(&hd)
9115 .arg(&nd)
9116 .arg(&nh)
9117 .arg(&theta_scale)
9118 .arg(&freq_scale)
9119 .arg(ff);
9120 unsafe {
9121 b.launch(cfg)?;
9122 }
9123 Ok(())
9124 }
9125
9126 #[allow(clippy::too_many_arguments)]
9128 pub fn rope_neox2(
9129 &self,
9130 q: &mut CudaSlice<f32>,
9131 k: &mut CudaSlice<f32>,
9132 pos: &CudaSlice<i32>,
9133 head_dim: usize,
9134 n_dims: usize,
9135 nh_q: usize,
9136 nh_k: usize,
9137 n_tokens: usize,
9138 freq_base: f32,
9139 freq_scale: f32,
9140 ff: Option<&CudaSlice<f32>>,
9141 ) -> Result<(), Box<dyn std::error::Error>> {
9142 let f = self.func("rope_neox2_f32");
9143 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9144 let grid = ((nh_q + nh_k) * n_tokens) as u32;
9145 let cfg = LaunchConfig {
9146 grid_dim: (grid, 1, 1),
9147 block_dim: ((head_dim / 2) as u32, 1, 1),
9148 shared_mem_bytes: 0,
9149 };
9150 let (hd, nd, nq, nk, nt) = (
9151 head_dim as i32,
9152 n_dims as i32,
9153 nh_q as i32,
9154 nh_k as i32,
9155 n_tokens as i32,
9156 );
9157 let __s_b = self.gpu.stream();
9158 let mut b = __s_b.launch_builder(&f);
9159 b.arg(q)
9160 .arg(k)
9161 .arg(pos)
9162 .arg(&hd)
9163 .arg(&nd)
9164 .arg(&nq)
9165 .arg(&nk)
9166 .arg(&nt)
9167 .arg(&theta_scale)
9168 .arg(&freq_scale);
9169 match ff {
9170 Some(ffv) => {
9171 b.arg(ffv);
9172 unsafe {
9173 b.launch(cfg)?;
9174 }
9175 }
9176 None => {
9177 let null: u64 = 0;
9178 b.arg(&null);
9179 unsafe {
9180 b.launch(cfg)?;
9181 }
9182 }
9183 }
9184 Ok(())
9185 }
9186
9187 pub fn gelu_tanh_mul(
9189 &self,
9190 gate: &CudaSlice<f32>,
9191 up: &CudaSlice<f32>,
9192 dst: &mut CudaSlice<f32>,
9193 n: usize,
9194 ) -> Result<(), Box<dyn std::error::Error>> {
9195 let f = self.func("gelu_tanh_mul_f32");
9196 let cfg = LaunchConfig::for_num_elems(n as u32);
9197 let ni = n as i32;
9198 let __s_b = self.gpu.stream();
9199 let mut b = __s_b.launch_builder(&f);
9200 b.arg(gate).arg(up).arg(dst).arg(&ni);
9201 unsafe {
9202 b.launch(cfg)?;
9203 }
9204 Ok(())
9205 }
9206
9207 pub fn silu_mul(
9208 &self,
9209 gate: &CudaSlice<f32>,
9210 up: &CudaSlice<f32>,
9211 dst: &mut CudaSlice<f32>,
9212 n: usize,
9213 ) -> Result<(), Box<dyn std::error::Error>> {
9214 let f = self.func("silu_mul_f32");
9215 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9217 let ni = n as i32;
9218 let __s_b = self.gpu.stream();
9219 let mut b = __s_b.launch_builder(&f);
9220 b.arg(gate).arg(up).arg(dst).arg(&ni);
9221 unsafe {
9222 b.launch(cfg)?;
9223 }
9224 Ok(())
9225 }
9226
9227 pub fn silu_mul_f16out(
9230 &self,
9231 gate: &CudaSlice<f32>,
9232 up: &CudaSlice<f32>,
9233 dst: &mut CudaSlice<f32>,
9234 dst16: &mut CudaSlice<u8>,
9235 n: usize,
9236 ) -> Result<(), Box<dyn std::error::Error>> {
9237 let f = self.func("silu_mul_f16out_f32");
9238 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9239 let ni = n as i32;
9240 let __s_b = self.gpu.stream();
9241 let mut b = __s_b.launch_builder(&f);
9242 b.arg(gate).arg(up).arg(dst).arg(dst16).arg(&ni);
9243 unsafe {
9244 b.launch(cfg)?;
9245 }
9246 Ok(())
9247 }
9248
9249 pub fn silu_mul_scaled(
9256 &self,
9257 gate: &CudaSlice<f32>,
9258 up: &CudaSlice<f32>,
9259 gs: f32,
9260 us: f32,
9261 dst: &mut CudaSlice<f32>,
9262 n: usize,
9263 ) -> Result<(), Box<dyn std::error::Error>> {
9264 let f = self.func("silu_mul_scaled_f32");
9265 let cfg = LaunchConfig::for_num_elems(n as u32);
9266 let ni = n as i32;
9267 let (gsf, usf) = (gs, us);
9268 let __s_b = self.gpu.stream();
9269 let mut b = __s_b.launch_builder(&f);
9270 b.arg(gate).arg(up).arg(&gsf).arg(&usf).arg(dst).arg(&ni);
9271 unsafe {
9272 b.launch(cfg)?;
9273 }
9274 Ok(())
9275 }
9276
9277 #[allow(clippy::too_many_arguments)]
9281 pub fn swigluoai_mul_scaled(
9282 &self,
9283 gate: &CudaSlice<f32>,
9284 up: &CudaSlice<f32>,
9285 gs: f32,
9286 us: f32,
9287 alpha: f32,
9288 limit: f32,
9289 dst: &mut CudaSlice<f32>,
9290 n: usize,
9291 ) -> Result<(), Box<dyn std::error::Error>> {
9292 let f = self.func("swigluoai_mul_scaled_f32");
9293 let cfg = LaunchConfig::for_num_elems(n as u32);
9294 let ni = n as i32;
9295 let __s_b = self.gpu.stream();
9296 let mut b = __s_b.launch_builder(&f);
9297 b.arg(gate)
9298 .arg(up)
9299 .arg(&gs)
9300 .arg(&us)
9301 .arg(&alpha)
9302 .arg(&limit)
9303 .arg(dst)
9304 .arg(&ni);
9305 unsafe {
9306 b.launch(cfg)?;
9307 }
9308 Ok(())
9309 }
9310
9311 pub fn silu_mul_scaled_q8_1(
9319 &self,
9320 gate: &CudaSlice<f32>,
9321 up: &CudaSlice<f32>,
9322 gs: f32,
9323 us: f32,
9324 n: usize,
9325 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9326 let f = self.func("silu_mul_scaled_q8_1");
9327 let nblk = n / 32;
9328 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);
9332 let (gsf, usf, ni) = (gs, us, n as i32);
9333 let __s_b = self.gpu.stream();
9334 let mut b = __s_b.launch_builder(&f);
9335 b.arg(gate)
9336 .arg(up)
9337 .arg(&gsf)
9338 .arg(&usf)
9339 .arg(&mut aq)
9340 .arg(&mut ad)
9341 .arg(&ni);
9342 unsafe {
9343 b.launch(cfg)?;
9344 }
9345 Ok((aq, ad))
9346 }
9347
9348 pub fn add(
9349 &self,
9350 a: &CudaSlice<f32>,
9351 b_in: &CudaSlice<f32>,
9352 dst: &mut CudaSlice<f32>,
9353 n: usize,
9354 ) -> Result<(), Box<dyn std::error::Error>> {
9355 let f = self.func("add_f32");
9356 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9358 let ni = n as i32;
9359 let __s_bld = self.gpu.stream();
9360 let mut bld = __s_bld.launch_builder(&f);
9361 bld.arg(a).arg(b_in).arg(dst).arg(&ni);
9362 unsafe {
9363 bld.launch(cfg)?;
9364 }
9365 Ok(())
9366 }
9367
9368 pub fn mul(
9369 &self,
9370 a: &CudaSlice<f32>,
9371 b_in: &CudaSlice<f32>,
9372 dst: &mut CudaSlice<f32>,
9373 n: usize,
9374 ) -> Result<(), Box<dyn std::error::Error>> {
9375 let f = self.func("mul_f32");
9376 let cfg = LaunchConfig::for_num_elems(n as u32);
9377 let ni = n as i32;
9378 let __s_bld = self.gpu.stream();
9379 let mut bld = __s_bld.launch_builder(&f);
9380 bld.arg(a).arg(b_in).arg(dst).arg(&ni);
9381 unsafe {
9382 bld.launch(cfg)?;
9383 }
9384 Ok(())
9385 }
9386
9387 pub fn matmul(
9390 &self,
9391 w: &crate::model::GpuTensor,
9392 x: &CudaSlice<f32>,
9393 m: usize,
9394 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9395 use crate::model::GpuTensor;
9396 let in_f = w.in_features();
9397 let out_f = w.out_features();
9398 #[allow(non_snake_case)]
9406 let GEMM_M_THRESHOLD = if self.verify_exact_on() {
9409 usize::MAX
9410 } else {
9411 16usize
9412 };
9413
9414 const GEMM_MIN_OUT_F: usize = 128; if m >= GEMM_M_THRESHOLD {
9439 if let Some(y) = self.try_fp8_gemm(w, x, m)? {
9440 return Ok(y);
9441 }
9442 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
9449 return Ok(y);
9450 }
9451 if let Some(y) = self.try_f16_gemm(w, x, m)? {
9454 return Ok(y);
9455 }
9456 }
9457 if let GpuTensor::Quant { qtype, .. } = w {
9472 if *qtype == QT_F8_E4M3_BLK {
9473 if m >= GEMM_M_THRESHOLD {
9474 if let Some(y) = self.try_e4m3_blk_prefill(w, x, m)? {
9475 return Ok(y);
9476 }
9477 }
9478 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9479 if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
9480 return Ok(y);
9481 }
9482 }
9483 }
9484 if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.mmq_supports(w) {
9485 return self.qmatvec_mmq(w, x, m);
9486 }
9487 if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.gemm_supports(w) {
9488 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9489 return self.qmatvec_gemm(w, &aq, &ad, m);
9490 }
9491 if m >= GEMM_M_THRESHOLD {
9494 if let Some(y) = self.try_fp4_gemm(w, x, m, in_f, out_f)? {
9495 return Ok(y);
9496 }
9497 }
9498 let fast = std::env::var("MEMRA_FAST").as_deref() != Ok("0");
9502 if m == 1 && fast {
9507 if let GpuTensor::Quant {
9508 bytes,
9509 qtype,
9510 row_bytes,
9511 rp,
9512 rp4,
9513 scale,
9514 ..
9515 } = w
9516 {
9517 if self.mmvq_supports(*qtype) {
9518 let (bytes, rp) = match rp4 {
9522 Some(m4) => (m4, true),
9523 None => (bytes, *rp),
9524 };
9525 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9526 return self.qmatvec_mmvq(
9527 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, rp,
9528 );
9529 }
9530 }
9531 }
9532 if (2..=16).contains(&m)
9548 && fast
9549 && std::env::var("MEMRA_NO_BATCHED").is_err()
9550 && (m <= 4 || Self::b8_enabled())
9551 {
9552 let m_ok = m <= 8
9562 || matches!(w, GpuTensor::Quant { qtype, .. }
9563 if *qtype == QT_Q4_0 || *qtype == QT_Q6_K || *qtype == QT_F8_E4M3
9564 || *qtype == QT_NVFP4 || *qtype == QT_Q4_K || *qtype == QT_Q5_K || *qtype == QT_Q8_0);
9565 if m_ok {
9566 if let GpuTensor::Quant {
9567 bytes,
9568 qtype,
9569 row_bytes,
9570 rp,
9571 rp4,
9572 ..
9573 } = w
9574 {
9575 if self.batched_supports(*qtype) && self.mmvq_supports(*qtype) {
9576 let (bytes, rp) = match rp4 {
9577 Some(m4) => (m4, true),
9578 None => (bytes, *rp),
9579 };
9580 let mcols = Self::batched_mcols(m);
9581 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9582 let mut y = self.qmatvec_mmvq_batched(
9583 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, mcols, 1.0, rp,
9584 )?;
9585 if let GpuTensor::Quant { scale, .. } = w {
9586 if *scale != 1.0 {
9587 self.scale_inplace(&mut y, *scale, m * out_f)?;
9588 }
9589 }
9590 return Ok(y);
9591 }
9592 }
9593 }
9594 }
9595 if fast {
9601 if let GpuTensor::Quant {
9602 bytes,
9603 qtype,
9604 row_bytes,
9605 scale,
9606 ..
9607 } = w
9608 {
9609 if *qtype == QT_F8_E4M3 {
9610 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9611 return self.qmatvec_mmvq(
9612 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, false,
9613 );
9614 }
9615 }
9616 }
9617 let mut y = match w {
9618 GpuTensor::Quant {
9619 bytes,
9620 qtype,
9621 row_bytes,
9622 ..
9623 } if fast && *qtype == QT_Q8_0 => {
9624 self.qmatvec_q8_0_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9625 }
9626 GpuTensor::Quant {
9627 bytes,
9628 qtype,
9629 row_bytes,
9630 ..
9631 } if fast && *qtype == QT_Q4_K => {
9632 self.qmatvec_q4_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9633 }
9634 GpuTensor::Quant {
9635 bytes,
9636 qtype,
9637 row_bytes,
9638 ..
9639 } if fast && *qtype == QT_Q6_K => {
9640 self.qmatvec_q6_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9641 }
9642 GpuTensor::Quant {
9643 bytes,
9644 qtype,
9645 row_bytes,
9646 ..
9647 } if fast && *qtype == QT_Q5_K => {
9648 self.qmatvec_q5_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9649 }
9650 GpuTensor::Quant {
9651 bytes,
9652 qtype,
9653 row_bytes,
9654 ..
9655 } if fast && *qtype == QT_Q3_K => {
9656 self.qmatvec_q3_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9657 }
9658 GpuTensor::Quant {
9659 bytes,
9660 qtype,
9661 row_bytes,
9662 rp,
9663 ..
9664 } if fast && *qtype == QT_NVFP4 => self.qmatvec_dp4a_named(
9665 if *rp {
9666 "qmatvec_nvfp4_dp4a_rp"
9667 } else {
9668 "qmatvec_nvfp4_dp4a"
9669 },
9670 bytes,
9671 x,
9672 m,
9673 in_f,
9674 out_f,
9675 *row_bytes,
9676 )?,
9677 GpuTensor::Quant {
9681 bytes,
9682 qtype,
9683 row_bytes,
9684 ..
9685 } if fast && *qtype == QT_IQ4_XS && Self::iq_fast_enabled() => {
9686 self.qmatvec_iq4_XS_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9687 }
9688 GpuTensor::Quant {
9693 bytes,
9694 qtype,
9695 row_bytes,
9696 rp,
9697 ..
9698 } =>
9699 {
9702 self.qmatvec(
9703 bytes,
9704 x,
9705 m,
9706 in_f,
9707 out_f,
9708 if *rp && *qtype == QT_NVFP4 {
9709 QT_NVFP4_RP
9710 } else {
9711 *qtype
9712 },
9713 *row_bytes,
9714 )?
9715 }
9716 GpuTensor::Float { data, .. } => self.linear(x, data, m, in_f, out_f)?,
9717 GpuTensor::FloatBf16 { data, .. } => {
9720 self.linear_bf16_chunked(x, data, m, in_f, out_f, false)?
9721 }
9722 };
9723 if let GpuTensor::Quant { scale, .. } = w {
9725 if *scale != 1.0 {
9726 self.scale_inplace(&mut y, *scale, m * out_f)?;
9727 }
9728 }
9729 Ok(y)
9730 }
9731
9732 pub fn uses_q8_1_fast(&self, w: &crate::model::GpuTensor) -> bool {
9735 use crate::model::GpuTensor;
9736 if std::env::var("MEMRA_FAST").as_deref() == Ok("0") {
9737 return false;
9738 }
9739 match w {
9740 GpuTensor::Quant { qtype, .. } => {
9747 matches!(
9748 *qtype,
9749 QT_Q8_0
9750 | QT_Q4_K
9751 | QT_Q6_K
9752 | QT_Q5_K
9753 | QT_Q3_K
9754 | QT_NVFP4
9755 | QT_F8_E4M3
9756 | QT_F8_E4M3_BLK
9757 | QT_Q4_0
9758 ) || (*qtype == QT_IQ4_XS && Self::iq_fast_enabled())
9759 }
9760 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
9761 }
9762 }
9763
9764 pub fn matmul_pre(
9769 &self,
9770 w: &crate::model::GpuTensor,
9771 aq: &CudaSlice<i8>,
9772 ad: &CudaSlice<f32>,
9773 x_fallback: &CudaSlice<f32>,
9774 m: usize,
9775 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9776 use crate::model::GpuTensor;
9777 let x_raw_ok = x_fallback.len() >= m * w.in_features();
9783 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
9786 if let Some(y) = self.try_fp8_gemm(w, x_fallback, m)? {
9787 return Ok(y);
9788 }
9789 if let Some(y) = self.try_fp8_blk_mmq(w, x_fallback, m)? {
9792 return Ok(y);
9793 }
9794 if let Some(y) = self.try_f16_gemm(w, x_fallback, m)? {
9796 return Ok(y);
9797 }
9798 }
9799 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
9805 if let Some(y) = self.try_e4m3_blk_prefill(w, x_fallback, m)? {
9806 return Ok(y);
9807 }
9808 }
9809 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
9810 return Ok(y);
9811 }
9812 if m >= 16
9817 && w.out_features() >= 128
9818 && self.mmq_supports(w)
9819 && !self.verify_exact_on()
9820 && x_raw_ok
9821 {
9822 return self.qmatvec_mmq(w, x_fallback, m);
9823 }
9824 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
9827 if let Some(y) =
9828 self.try_fp4_gemm(w, x_fallback, m, w.in_features(), w.out_features())?
9829 {
9830 return Ok(y);
9831 }
9832 }
9833 if m >= 16 && self.gemm_supports(w) && !self.verify_exact_on() {
9836 return self.qmatvec_gemm(w, aq, ad, m);
9837 }
9838 if !self.uses_q8_1_fast(w) {
9839 return self.matmul(w, x_fallback, m);
9840 }
9841 let in_f = w.in_features();
9842 let out_f = w.out_features();
9843 let (bytes, qtype, row_bytes, scale, rp) = match w {
9844 GpuTensor::Quant {
9845 bytes,
9846 qtype,
9847 row_bytes,
9848 scale,
9849 rp,
9850 ..
9851 } => (bytes, *qtype, *row_bytes, *scale, *rp),
9852 _ => unreachable!("uses_q8_1_fast guaranteed Quant"),
9853 };
9854 let (mbytes, mrp) = match w {
9857 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
9858 _ => (bytes, rp),
9859 };
9860 if m == 1 && self.mmvq_supports(qtype) {
9864 return self.qmatvec_mmvq(mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, mrp);
9865 }
9866 if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
9879 && std::env::var("MEMRA_NO_BATCHED").is_err()
9880 && (m <= 4 || Self::b8_enabled())
9881 && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_NVFP4
9885 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_F8_E4M3 || qtype == QT_Q8_0)
9886 {
9887 let mcols = Self::batched_mcols(m);
9888 return self.qmatvec_mmvq_batched(
9889 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, mrp,
9890 );
9891 }
9892 if qtype == QT_F8_E4M3 || qtype == QT_Q4_0 {
9898 let (b2, r2) = if qtype == QT_Q4_0 {
9899 (mbytes, mrp)
9900 } else {
9901 (bytes, rp)
9902 };
9903 return self.qmatvec_mmvq(b2, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, r2);
9904 }
9905 let name = match qtype {
9906 QT_Q8_0 => "qmatvec_q8_0_dp4a",
9907 QT_Q4_K => "qmatvec_q4_K_dp4a",
9908 QT_Q6_K => "qmatvec_q6_K_dp4a",
9909 QT_Q5_K => "qmatvec_q5_K_dp4a",
9910 QT_Q3_K => "qmatvec_q3_K_dp4a",
9911 QT_NVFP4 => {
9912 if rp {
9913 "qmatvec_nvfp4_dp4a_rp"
9914 } else {
9915 "qmatvec_nvfp4_dp4a"
9916 }
9917 }
9918 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
9919 _ => unreachable!(),
9920 };
9921 let f = self.func(name);
9922 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
9924 grid_dim: (out_f as u32, m as u32, 1),
9925 block_dim: (128, 1, 1),
9926 shared_mem_bytes: 0,
9927 };
9928 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
9929 let __s_b = self.gpu.stream();
9930 let mut b = __s_b.launch_builder(&f);
9931 b.arg(bytes)
9932 .arg(aq)
9933 .arg(ad)
9934 .arg(&mut y)
9935 .arg(&inf)
9936 .arg(&outf)
9937 .arg(&mi)
9938 .arg(&rb);
9939 unsafe {
9940 b.launch(cfg)?;
9941 }
9942 if scale != 1.0 {
9943 self.scale_inplace(&mut y, scale, m * out_f)?;
9944 }
9945 Ok(y)
9946 }
9947
9948 pub fn matmul_decode_exact(
9956 &self,
9957 w: &crate::model::GpuTensor,
9958 x: &CudaSlice<f32>,
9959 m: usize,
9960 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9961 use crate::model::GpuTensor;
9962 if let GpuTensor::Float { data, .. } = w {
9970 return self.linear_decode_exact(x, data, m, w.in_features(), w.out_features());
9971 }
9972 if let GpuTensor::FloatBf16 { data, .. } = w {
9975 let (in_f, out_f) = (w.in_features(), w.out_features());
9976 return self.linear_bf16_chunked(x, data, m, in_f, out_f, true);
9977 }
9978 if !self.uses_q8_1_fast(w) {
9979 return self.matmul(w, x, m);
9980 }
9981 let in_f = w.in_features();
9982 let out_f = w.out_features();
9983 let (bytes, qtype, row_bytes, scale, rp) = match w {
9984 GpuTensor::Quant {
9985 bytes,
9986 qtype,
9987 row_bytes,
9988 scale,
9989 rp,
9990 ..
9991 } => (bytes, *qtype, *row_bytes, *scale, *rp),
9992 _ => return self.matmul(w, x, m),
9993 };
9994 let (bytes, rp) = match w {
9997 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
9998 _ => (bytes, rp),
9999 };
10000 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10001 if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
10005 return Ok(y);
10006 }
10007 if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
10016 && std::env::var("MEMRA_NO_BATCHED").is_err()
10017 && (m <= 4 || Self::b8_enabled())
10018 && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_F8_E4M3
10021 || qtype == QT_NVFP4 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_Q8_0)
10022 {
10023 let mcols = Self::batched_mcols(m);
10024 return self.qmatvec_mmvq_batched(
10025 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
10026 );
10027 }
10028 if self.mmvq_supports(qtype) {
10029 return self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
10032 }
10033 self.matmul_pre(w, &aq, &ad, x, m)
10036 }
10037
10038 pub fn matmul_decode_exact_pre(
10048 &self,
10049 w: &crate::model::GpuTensor,
10050 aq: &CudaSlice<i8>,
10051 ad: &CudaSlice<f32>,
10052 m: usize,
10053 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10054 use crate::model::GpuTensor;
10055 debug_assert!(
10056 self.uses_q8_1_fast(w),
10057 "matmul_decode_exact_pre: caller must guarantee q8_1-fast"
10058 );
10059 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
10061 return Ok(y);
10062 }
10063 let in_f = w.in_features();
10064 let out_f = w.out_features();
10065 let (bytes, qtype, row_bytes, scale, rp) = match w {
10066 GpuTensor::Quant {
10067 bytes,
10068 qtype,
10069 row_bytes,
10070 scale,
10071 rp,
10072 ..
10073 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10074 _ => {
10075 return Err(
10076 "matmul_decode_exact_pre: Quant tensor required (q8_1-fast contract)".into(),
10077 );
10078 }
10079 };
10080 let (bytes, rp) = match w {
10082 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10083 _ => (bytes, rp),
10084 };
10085 if (2..=16).contains(&m)
10087 && self.batched_supports(qtype)
10088 && self.mmvq_supports(qtype)
10089 && std::env::var("MEMRA_NO_BATCHED").is_err()
10090 && (m <= 4 || Self::b8_enabled())
10091 && (m <= 8
10092 || qtype == QT_Q4_0
10093 || qtype == QT_Q6_K
10094 || qtype == QT_F8_E4M3
10095 || qtype == QT_NVFP4
10096 || qtype == QT_Q4_K
10097 || qtype == QT_Q5_K
10098 || qtype == QT_Q8_0)
10099 {
10100 let mcols = Self::batched_mcols(m);
10101 return self.qmatvec_mmvq_batched(
10102 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
10103 );
10104 }
10105 if self.mmvq_supports(qtype) {
10106 return self.qmatvec_mmvq(bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
10107 }
10108 let x0 = self.zeros(0)?;
10111 self.matmul_pre(w, aq, ad, &x0, m)
10112 }
10113
10114 pub fn matmul_decode_exact_dual_pre(
10123 &self,
10124 w0: &crate::model::GpuTensor,
10125 w1: &crate::model::GpuTensor,
10126 aq: &CudaSlice<i8>,
10127 ad: &CudaSlice<f32>,
10128 m: usize,
10129 ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
10130 {
10131 use crate::model::GpuTensor;
10132 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10133 let on = *ON.get_or_init(|| {
10134 std::env::var("MEMRA_SPEC_DUAL_T")
10135 .map(|v| v != "0")
10136 .unwrap_or(true)
10137 });
10138 if !on
10139 || !(2..=7).contains(&m)
10140 || std::env::var("MEMRA_NO_BATCHED").is_ok()
10141 || !self.uses_q8_1_fast(w0)
10142 || !self.uses_q8_1_fast(w1)
10143 {
10144 return Ok(None);
10145 }
10146 if !self.mmvq_supports(QT_NVFP4) {
10151 return Ok(None);
10152 }
10153 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10154 if w1.in_features() != in_f || w1.out_features() != out_f {
10155 return Ok(None);
10156 }
10157 let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
10158 (
10159 GpuTensor::Quant {
10160 bytes: b0,
10161 qtype: q0,
10162 row_bytes: rb0,
10163 scale: s0,
10164 rp: rp0,
10165 rp4: None,
10166 ..
10167 },
10168 GpuTensor::Quant {
10169 bytes: b1,
10170 qtype: q1,
10171 row_bytes: rb1,
10172 scale: s1,
10173 rp: rp1,
10174 rp4: None,
10175 ..
10176 },
10177 ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
10178 (b0, b1, *rb0, *s0, *s1, *rp0)
10179 }
10180 _ => return Ok(None),
10181 };
10182 if m > 4 && !(rp && Self::b8_enabled() && std::env::var("MEMRA_B567").as_deref() != Ok("0"))
10185 {
10186 return Ok(None);
10187 }
10188 let (y0, y1) =
10189 self.qmatvec_batched_dual_raw(b0, b1, aq, ad, m, in_f, out_f, row_bytes, rp)?;
10190 Ok(Some(((y0, s0), (y1, s1))))
10191 }
10192
10193 pub fn matmul_decode_exact_dual(
10209 &self,
10210 w0: &crate::model::GpuTensor,
10211 w1: &crate::model::GpuTensor,
10212 x: &CudaSlice<f32>,
10213 m: usize,
10214 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10215 use crate::model::GpuTensor;
10216 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10217 let on = *ON.get_or_init(|| {
10218 std::env::var("MEMRA_SPEC_DUAL_T")
10219 .map(|v| v != "0")
10220 .unwrap_or(true)
10221 });
10222 if !on
10223 || !(2..=4).contains(&m)
10224 || std::env::var("MEMRA_NO_BATCHED").is_ok()
10225 || !self.uses_q8_1_fast(w0)
10226 || !self.uses_q8_1_fast(w1)
10227 {
10228 return Ok(None);
10229 }
10230 if !self.mmvq_supports(QT_NVFP4) {
10235 return Ok(None);
10236 }
10237 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10238 if w1.in_features() != in_f || w1.out_features() != out_f {
10239 return Ok(None);
10240 }
10241 let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
10242 (
10243 GpuTensor::Quant {
10244 bytes: b0,
10245 qtype: q0,
10246 row_bytes: rb0,
10247 scale: s0,
10248 rp: rp0,
10249 rp4: None,
10250 ..
10251 },
10252 GpuTensor::Quant {
10253 bytes: b1,
10254 qtype: q1,
10255 row_bytes: rb1,
10256 scale: s1,
10257 rp: rp1,
10258 rp4: None,
10259 ..
10260 },
10261 ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
10262 (b0, b1, *rb0, *s0, *s1, *rp0)
10263 }
10264 _ => return Ok(None),
10265 };
10266 if std::env::var("MEMRA_DEBUG").is_ok() {
10269 static ONCE: std::sync::Once = std::sync::Once::new();
10270 ONCE.call_once(|| eprintln!("[memra] dual gate+up batched ENGAGED (m={m} rp={rp})"));
10271 }
10272 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10273 let (y0, y1) =
10274 self.qmatvec_batched_dual_raw(b0, b1, &aq, &ad, m, in_f, out_f, row_bytes, rp)?;
10275 let mut y0 = y0;
10276 let mut y1 = y1;
10277 if s0 != 1.0 {
10278 self.scale_inplace(&mut y0, s0, m * out_f)?;
10279 }
10280 if s1 != 1.0 {
10281 self.scale_inplace(&mut y1, s1, m * out_f)?;
10282 }
10283 Ok(Some((y0, y1)))
10284 }
10285
10286 #[allow(clippy::too_many_arguments)]
10291 pub fn qmatvec_batched_dual_raw(
10292 &self,
10293 b0: &CudaSlice<u8>,
10294 b1: &CudaSlice<u8>,
10295 aq: &CudaSlice<i8>,
10296 ad: &CudaSlice<f32>,
10297 m: usize,
10298 in_f: usize,
10299 out_f: usize,
10300 row_bytes: usize,
10301 rp: bool,
10302 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10303 const ROWS_PER_BLOCK: u32 = 4;
10304 let mcols = Self::batched_mcols(m);
10305 let tiny_rp1 = rp
10308 && mcols == 4
10309 && out_f <= 128
10310 && std::env::var("MEMRA_NVFP4_AUX_DUAL").as_deref() != Ok("0");
10311 let (name, rows_per_block) = if tiny_rp1 {
10312 ("qmatvec_nvfp4_mmvq_dual_b4_rp", ROWS_PER_BLOCK)
10313 } else {
10314 match (mcols, rp, m) {
10315 (2, false, _) => ("qmatvec_nvfp4_mmvq_dual_b2", ROWS_PER_BLOCK),
10316 (4, false, _) => ("qmatvec_nvfp4_mmvq_dual_b4_r2", ROWS_PER_BLOCK * 2),
10317 (2, true, _) => ("qmatvec_nvfp4_mmvq_dual_b2_rp", ROWS_PER_BLOCK),
10318 (4, true, _) => ("qmatvec_nvfp4_mmvq_dual_b4_rpr2", ROWS_PER_BLOCK * 2),
10319 (8, true, 5) => ("qmatvec_nvfp4_mmvq_dual_b5_rpr2", ROWS_PER_BLOCK * 2),
10320 (8, true, 6) => ("qmatvec_nvfp4_mmvq_dual_b6_rpr2", ROWS_PER_BLOCK * 2),
10321 (8, true, 7) => ("qmatvec_nvfp4_mmvq_dual_b7_rpr2", ROWS_PER_BLOCK * 2),
10322 _ => {
10323 return Err(
10324 format!("qmatvec_batched_dual_raw: no dual kernel for m {m}").into(),
10325 );
10326 }
10327 }
10328 };
10329 let f = self.func(name);
10330 let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
10331 let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
10332 let cfg = LaunchConfig {
10333 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
10334 block_dim: (32, ROWS_PER_BLOCK, 1),
10335 shared_mem_bytes: 0,
10336 };
10337 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
10338 let __s_b = self.gpu.stream();
10339 let mut b = __s_b.launch_builder(&f);
10340 b.arg(b0)
10341 .arg(b1)
10342 .arg(aq)
10343 .arg(ad)
10344 .arg(&mut y0)
10345 .arg(&mut y1)
10346 .arg(&inf)
10347 .arg(&outf)
10348 .arg(&mi)
10349 .arg(&rb);
10350 unsafe {
10351 b.launch(cfg)?;
10352 }
10353 Ok((y0, y1))
10354 }
10355
10356 pub fn matmul_pre_dual_noscale(
10368 &self,
10369 w0: &crate::model::GpuTensor,
10370 w1: &crate::model::GpuTensor,
10371 aq: &CudaSlice<i8>,
10372 ad: &CudaSlice<f32>,
10373 m: usize,
10374 ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
10375 {
10376 use crate::model::GpuTensor;
10377 if m != 1 || !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
10378 return Ok(None);
10379 }
10380 if !self.mmvq_supports(QT_NVFP4) {
10390 return Ok(None);
10391 }
10392 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10393 if w1.in_features() != in_f || w1.out_features() != out_f {
10394 return Ok(None);
10395 }
10396 let no_mirror =
10409 |w: &crate::model::GpuTensor| !matches!(w, GpuTensor::Quant { rp4: Some(_), .. });
10410 if self.q8_ffn_fuse2_on()
10411 && no_mirror(w0)
10412 && no_mirror(w1)
10413 && let Some([p0, p1]) = self.q8_fused_params(&[w0, w1])
10414 {
10415 let (y0, y1) = self.q8_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2)?;
10416 return Ok(Some(((y0, 1.0), (y1, 1.0))));
10417 }
10418 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
10428 let (y0, y1) =
10429 self.e4m3_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2, 1.0, 1.0)?;
10430 return Ok(Some(((y0, p0.3), (y1, p1.3))));
10431 }
10432 let (b0, q0, rb0, s0, rp0) = match w0 {
10433 GpuTensor::Quant {
10434 bytes,
10435 qtype,
10436 row_bytes,
10437 scale,
10438 rp,
10439 ..
10440 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10441 _ => return Ok(None),
10442 };
10443 let (b1, q1, rb1, s1, rp1) = match w1 {
10444 GpuTensor::Quant {
10445 bytes,
10446 qtype,
10447 row_bytes,
10448 scale,
10449 rp,
10450 ..
10451 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10452 _ => return Ok(None),
10453 };
10454 if q0 != QT_NVFP4 || q1 != QT_NVFP4 || rb0 != rb1 || rp0 != rp1 {
10455 return Ok(None);
10456 }
10457 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
10459 let rows_per_block = ROWS_PER_BLOCK * RPW;
10460 let f = self.func(if rp0 {
10461 "qmatvec_nvfp4_mmvq_dual_mr2_rp"
10462 } else {
10463 "qmatvec_nvfp4_mmvq_dual_mr2"
10464 });
10465 let mut y0 = self.alloc_uninit::<f32>(out_f)?;
10466 let mut y1 = self.alloc_uninit::<f32>(out_f)?;
10467 let cfg = LaunchConfig {
10468 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
10469 block_dim: (32, ROWS_PER_BLOCK, 1),
10470 shared_mem_bytes: 0,
10471 };
10472 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, rb0 as i64);
10473 let one = 1.0f32;
10476 let __s_b = self.gpu.stream();
10477 let mut b = __s_b.launch_builder(&f);
10478 b.arg(b0)
10479 .arg(b1)
10480 .arg(aq)
10481 .arg(ad)
10482 .arg(&mut y0)
10483 .arg(&mut y1)
10484 .arg(&inf)
10485 .arg(&outf)
10486 .arg(&mi)
10487 .arg(&rb)
10488 .arg(&one)
10489 .arg(&one);
10490 unsafe {
10491 b.launch(cfg)?;
10492 }
10493 Ok(Some(((y0, s0), (y1, s1))))
10494 }
10495
10496 pub fn matmul_q8_fused2(
10504 &self,
10505 w0: &crate::model::GpuTensor,
10506 w1: &crate::model::GpuTensor,
10507 aq: &CudaSlice<i8>,
10508 ad: &CudaSlice<f32>,
10509 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10510 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
10516 return Ok(Some(self.e4m3_fused2_core(
10517 p0.0,
10518 p1.0,
10519 aq,
10520 ad,
10521 w0.in_features(),
10522 p0.1,
10523 p1.1,
10524 p0.2,
10525 p0.3,
10526 p1.3,
10527 )?));
10528 }
10529 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
10530 return Ok(None);
10531 };
10532 Ok(Some(self.q8_fused2_core(
10533 p0.0,
10534 p1.0,
10535 aq,
10536 ad,
10537 w0.in_features(),
10538 p0.1,
10539 p1.1,
10540 p0.2,
10541 )?))
10542 }
10543
10544 #[allow(clippy::too_many_arguments)]
10545 fn q8_fused2_core(
10546 &self,
10547 b0: &CudaSlice<u8>,
10548 b1: &CudaSlice<u8>,
10549 aq: &CudaSlice<i8>,
10550 ad: &CudaSlice<f32>,
10551 in_f: usize,
10552 out0: usize,
10553 out1: usize,
10554 row_bytes: usize,
10555 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10556 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
10558 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
10559 let f = self.func("qmatvec_q8_0_mmvq_fused2");
10560 let mut y0 = self.alloc_uninit::<f32>(out0)?;
10561 let mut y1 = self.alloc_uninit::<f32>(out1)?;
10562 let cfg = LaunchConfig {
10563 grid_dim: (nb0 + nb1, 1, 1),
10564 block_dim: (32, ROWS_PER_BLOCK, 1),
10565 shared_mem_bytes: 0,
10566 };
10567 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
10568 let __s_b = self.gpu.stream();
10569 let mut b = __s_b.launch_builder(&f);
10570 b.arg(b0)
10571 .arg(b1)
10572 .arg(aq)
10573 .arg(ad)
10574 .arg(&mut y0)
10575 .arg(&mut y1)
10576 .arg(&inf)
10577 .arg(&o0)
10578 .arg(&o1)
10579 .arg(&rbl);
10580 unsafe {
10581 b.launch(cfg)?;
10582 }
10583 Ok((y0, y1))
10584 }
10585
10586 pub fn matmul_q8_fused2_x(
10592 &self,
10593 w0: &crate::model::GpuTensor,
10594 w1: &crate::model::GpuTensor,
10595 x: &CudaSlice<f32>,
10596 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10597 if !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
10598 return Ok(None);
10599 }
10600 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
10601 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
10602 return Ok(Some(self.e4m3_fused2_core(
10603 p0.0,
10604 p1.0,
10605 &aq,
10606 &ad,
10607 w0.in_features(),
10608 p0.1,
10609 p1.1,
10610 p0.2,
10611 p0.3,
10612 p1.3,
10613 )?));
10614 }
10615 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
10616 return Ok(None);
10617 };
10618 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
10619 Ok(Some(self.q8_fused2_core(
10620 p0.0,
10621 p1.0,
10622 &aq,
10623 &ad,
10624 w0.in_features(),
10625 p0.1,
10626 p1.1,
10627 p0.2,
10628 )?))
10629 }
10630
10631 #[allow(clippy::too_many_arguments)]
10634 pub fn qmatvec_q8_fused2_raw(
10635 &self,
10636 b0: &CudaSlice<u8>,
10637 b1: &CudaSlice<u8>,
10638 x: &CudaSlice<f32>,
10639 in_f: usize,
10640 out0: usize,
10641 out1: usize,
10642 row_bytes: usize,
10643 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10644 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
10645 self.q8_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes)
10646 }
10647
10648 pub fn matmul_q4_fused3(
10654 &self,
10655 w0: &crate::model::GpuTensor,
10656 w1: &crate::model::GpuTensor,
10657 w2: &crate::model::GpuTensor,
10658 aq: &CudaSlice<i8>,
10659 ad: &CudaSlice<f32>,
10660 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
10661 {
10662 use crate::model::GpuTensor;
10663 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
10664 match w {
10665 GpuTensor::Quant {
10666 qtype, row_bytes, ..
10667 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
10668 _ => None,
10669 }
10670 };
10671 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
10672 return Ok(None);
10673 };
10674 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
10675 return Ok(None);
10676 }
10677 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
10681 match w {
10682 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
10683 Some(m) => (m, true),
10684 None => (bytes, *rp),
10685 },
10686 _ => unreachable!(),
10687 }
10688 }
10689 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
10690 if rp0 != rp1 || rp1 != rp2 {
10691 return Ok(None);
10692 }
10693 let rp = rp0;
10694 let rpb: u32 = 4;
10695 let mr1 = rp && Self::q40_mr1_on();
10699 let nb = |o: usize| {
10700 if mr1 {
10701 (o as u32).div_ceil(rpb)
10702 } else {
10703 (o as u32).div_ceil(2).div_ceil(rpb)
10704 }
10705 };
10706 let grid = nb(o0) + nb(o1) + nb(o2);
10707 let mut y0 = self.alloc_uninit::<f32>(o0)?;
10708 let mut y1 = self.alloc_uninit::<f32>(o1)?;
10709 let mut y2 = self.alloc_uninit::<f32>(o2)?;
10710 let f = self.func(if mr1 {
10711 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
10712 } else if rp {
10713 "qmatvec_q4_0_mmvq_fused3_rp"
10714 } else {
10715 "qmatvec_q4_0_mmvq_fused3"
10716 });
10717 let cfg = LaunchConfig {
10718 grid_dim: (grid, 1, 1),
10719 block_dim: (32, rpb, 1),
10720 shared_mem_bytes: 0,
10721 };
10722 let inf = w0.in_features() as i32;
10723 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
10724 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
10725 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
10728 {
10729 use cudarc::driver::{DevicePtr, DevicePtrMut};
10730 let s = &self.gpu.stream();
10731 let (p0, _g0) = b0.device_ptr(s);
10732 let (p1, _g1) = b1.device_ptr(s);
10733 let (p2, _g2) = b2.device_ptr(s);
10734 let (paq, _g3) = aq.device_ptr(s);
10735 let (pad, _g4) = ad.device_ptr(s);
10736 let (py0, _g5) = y0.device_ptr_mut(s);
10737 let (py1, _g6) = y1.device_ptr_mut(s);
10738 let (py2, _g7) = y2.device_ptr_mut(s);
10739 let mut ps = [
10740 &p0 as *const _ as *mut std::ffi::c_void,
10741 &p1 as *const _ as *mut _,
10742 &p2 as *const _ as *mut _,
10743 &paq as *const _ as *mut _,
10744 &pad as *const _ as *mut _,
10745 &py0 as *const _ as *mut _,
10746 &py1 as *const _ as *mut _,
10747 &py2 as *const _ as *mut _,
10748 &inf as *const _ as *mut _,
10749 &oo0 as *const _ as *mut _,
10750 &oo1 as *const _ as *mut _,
10751 &oo2 as *const _ as *mut _,
10752 &r0 as *const _ as *mut _,
10753 &r1 as *const _ as *mut _,
10754 &r2 as *const _ as *mut _,
10755 ];
10756 unsafe {
10757 self.launch_pdl(
10758 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
10759 (grid, 1, 1),
10760 (32, rpb, 1),
10761 &mut ps,
10762 )?;
10763 }
10764 }
10765 return Ok(Some((y0, y1, y2)));
10766 }
10767 let __s_b = self.gpu.stream();
10768 let mut b = __s_b.launch_builder(&f);
10769 b.arg(b0)
10770 .arg(b1)
10771 .arg(b2)
10772 .arg(aq)
10773 .arg(ad)
10774 .arg(&mut y0)
10775 .arg(&mut y1)
10776 .arg(&mut y2)
10777 .arg(&inf)
10778 .arg(&oo0)
10779 .arg(&oo1)
10780 .arg(&oo2)
10781 .arg(&r0)
10782 .arg(&r1)
10783 .arg(&r2);
10784 unsafe {
10785 b.launch(cfg)?;
10786 }
10787 Ok(Some((y0, y1, y2)))
10788 }
10789
10790 #[allow(clippy::too_many_arguments)]
10793 pub fn matmul_q4_fused3_into(
10794 &self,
10795 w0: &crate::model::GpuTensor,
10796 w1: &crate::model::GpuTensor,
10797 w2: &crate::model::GpuTensor,
10798 aq: &CudaSlice<i8>,
10799 ad: &CudaSlice<f32>,
10800 y0: &mut CudaSlice<f32>,
10801 y1: &mut CudaSlice<f32>,
10802 y2: &mut CudaSlice<f32>,
10803 ) -> Result<bool, Box<dyn std::error::Error>> {
10804 use crate::model::GpuTensor;
10805 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
10806 match w {
10807 GpuTensor::Quant {
10808 qtype, row_bytes, ..
10809 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
10810 _ => None,
10811 }
10812 };
10813 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
10814 return Ok(false);
10815 };
10816 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
10817 return Ok(false);
10818 }
10819 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
10820 match w {
10821 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
10822 Some(m) => (m, true),
10823 None => (bytes, *rp),
10824 },
10825 _ => unreachable!(),
10826 }
10827 }
10828 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
10829 if rp0 != rp1 || rp1 != rp2 {
10830 return Ok(false);
10831 }
10832 let rp = rp0;
10833 let rpb: u32 = 4;
10834 let mr1 = rp && Self::q40_mr1_on();
10835 let nb = |o: usize| {
10836 if mr1 {
10837 (o as u32).div_ceil(rpb)
10838 } else {
10839 (o as u32).div_ceil(2).div_ceil(rpb)
10840 }
10841 };
10842 let grid = nb(o0) + nb(o1) + nb(o2);
10843 debug_assert!(y0.len() >= o0 && y1.len() >= o1 && y2.len() >= o2);
10844 let f = self.func(if mr1 {
10845 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
10846 } else if rp {
10847 "qmatvec_q4_0_mmvq_fused3_rp"
10848 } else {
10849 "qmatvec_q4_0_mmvq_fused3"
10850 });
10851 let cfg = LaunchConfig {
10852 grid_dim: (grid, 1, 1),
10853 block_dim: (32, rpb, 1),
10854 shared_mem_bytes: 0,
10855 };
10856 let inf = w0.in_features() as i32;
10857 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
10858 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
10859 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
10861 use cudarc::driver::{DevicePtr, DevicePtrMut};
10862 let s = &self.gpu.stream();
10863 let (p0, _g0) = b0.device_ptr(s);
10864 let (p1, _g1) = b1.device_ptr(s);
10865 let (p2, _g2) = b2.device_ptr(s);
10866 let (paq, _g3) = aq.device_ptr(s);
10867 let (pad, _g4) = ad.device_ptr(s);
10868 let (py0, _g5) = y0.device_ptr_mut(s);
10869 let (py1, _g6) = y1.device_ptr_mut(s);
10870 let (py2, _g7) = y2.device_ptr_mut(s);
10871 let mut ps = [
10872 &p0 as *const _ as *mut std::ffi::c_void,
10873 &p1 as *const _ as *mut _,
10874 &p2 as *const _ as *mut _,
10875 &paq as *const _ as *mut _,
10876 &pad as *const _ as *mut _,
10877 &py0 as *const _ as *mut _,
10878 &py1 as *const _ as *mut _,
10879 &py2 as *const _ as *mut _,
10880 &inf as *const _ as *mut _,
10881 &oo0 as *const _ as *mut _,
10882 &oo1 as *const _ as *mut _,
10883 &oo2 as *const _ as *mut _,
10884 &r0 as *const _ as *mut _,
10885 &r1 as *const _ as *mut _,
10886 &r2 as *const _ as *mut _,
10887 ];
10888 unsafe {
10889 self.launch_pdl(
10890 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
10891 (grid, 1, 1),
10892 (32, rpb, 1),
10893 &mut ps,
10894 )?;
10895 }
10896 return Ok(true);
10897 }
10898 let __s_b = self.gpu.stream();
10899 let mut b = __s_b.launch_builder(&f);
10900 b.arg(b0)
10901 .arg(b1)
10902 .arg(b2)
10903 .arg(aq)
10904 .arg(ad)
10905 .arg(&mut *y0)
10906 .arg(&mut *y1)
10907 .arg(&mut *y2)
10908 .arg(&inf)
10909 .arg(&oo0)
10910 .arg(&oo1)
10911 .arg(&oo2)
10912 .arg(&r0)
10913 .arg(&r1)
10914 .arg(&r2);
10915 unsafe {
10916 b.launch(cfg)?;
10917 }
10918 Ok(true)
10919 }
10920
10921 pub fn matmul_q4_fused2(
10923 &self,
10924 w0: &crate::model::GpuTensor,
10925 w1: &crate::model::GpuTensor,
10926 aq: &CudaSlice<i8>,
10927 ad: &CudaSlice<f32>,
10928 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10929 use crate::model::GpuTensor;
10930 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
10931 match w {
10932 GpuTensor::Quant {
10933 qtype, row_bytes, ..
10934 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
10935 _ => None,
10936 }
10937 };
10938 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
10939 return Ok(None);
10940 };
10941 if w0.in_features() != w1.in_features() {
10942 return Ok(None);
10943 }
10944 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
10946 match w {
10947 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
10948 Some(m) => (m, true),
10949 None => (bytes, *rp),
10950 },
10951 _ => unreachable!(),
10952 }
10953 }
10954 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
10955 if rp0 != rp1 {
10956 return Ok(None);
10957 }
10958 let rp = rp0;
10959 let rpb: u32 = 4;
10960 let mr1 = rp && Self::q40_mr1_on();
10962 let nb = |o: usize| {
10963 if mr1 {
10964 (o as u32).div_ceil(rpb)
10965 } else {
10966 (o as u32).div_ceil(2).div_ceil(rpb)
10967 }
10968 };
10969 let grid = nb(o0) + nb(o1);
10970 let mut y0 = self.alloc_uninit::<f32>(o0)?;
10971 let mut y1 = self.alloc_uninit::<f32>(o1)?;
10972 let f = self.func(if mr1 {
10973 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
10974 } else if rp {
10975 "qmatvec_q4_0_mmvq_fused2_rp"
10976 } else {
10977 "qmatvec_q4_0_mmvq_fused2"
10978 });
10979 let cfg = LaunchConfig {
10980 grid_dim: (grid, 1, 1),
10981 block_dim: (32, rpb, 1),
10982 shared_mem_bytes: 0,
10983 };
10984 let inf = w0.in_features() as i32;
10985 let (oo0, oo1) = (o0 as i32, o1 as i32);
10986 let (r0, r1) = (rb0 as i64, rb1 as i64);
10987 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
10989 {
10990 use cudarc::driver::{DevicePtr, DevicePtrMut};
10991 let s = &self.gpu.stream();
10992 let (p0, _g0) = b0.device_ptr(s);
10993 let (p1, _g1) = b1.device_ptr(s);
10994 let (paq, _g2) = aq.device_ptr(s);
10995 let (pad, _g3) = ad.device_ptr(s);
10996 let (py0, _g4) = y0.device_ptr_mut(s);
10997 let (py1, _g5) = y1.device_ptr_mut(s);
10998 let mut ps = [
10999 &p0 as *const _ as *mut std::ffi::c_void,
11000 &p1 as *const _ as *mut _,
11001 &paq as *const _ as *mut _,
11002 &pad as *const _ as *mut _,
11003 &py0 as *const _ as *mut _,
11004 &py1 as *const _ as *mut _,
11005 &inf as *const _ as *mut _,
11006 &oo0 as *const _ as *mut _,
11007 &oo1 as *const _ as *mut _,
11008 &r0 as *const _ as *mut _,
11009 &r1 as *const _ as *mut _,
11010 ];
11011 unsafe {
11012 self.launch_pdl(
11013 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
11014 (grid, 1, 1),
11015 (32, rpb, 1),
11016 &mut ps,
11017 )?;
11018 }
11019 }
11020 return Ok(Some((y0, y1)));
11021 }
11022 let __s_b = self.gpu.stream();
11023 let mut b = __s_b.launch_builder(&f);
11024 b.arg(b0)
11025 .arg(b1)
11026 .arg(aq)
11027 .arg(ad)
11028 .arg(&mut y0)
11029 .arg(&mut y1)
11030 .arg(&inf)
11031 .arg(&oo0)
11032 .arg(&oo1)
11033 .arg(&r0)
11034 .arg(&r1);
11035 unsafe {
11036 b.launch(cfg)?;
11037 }
11038 Ok(Some((y0, y1)))
11039 }
11040
11041 pub fn matmul_q4_fused2_into(
11043 &self,
11044 w0: &crate::model::GpuTensor,
11045 w1: &crate::model::GpuTensor,
11046 aq: &CudaSlice<i8>,
11047 ad: &CudaSlice<f32>,
11048 y0: &mut CudaSlice<f32>,
11049 y1: &mut CudaSlice<f32>,
11050 ) -> Result<bool, Box<dyn std::error::Error>> {
11051 use crate::model::GpuTensor;
11052 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11053 match w {
11054 GpuTensor::Quant {
11055 qtype, row_bytes, ..
11056 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11057 _ => None,
11058 }
11059 };
11060 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
11061 return Ok(false);
11062 };
11063 if w0.in_features() != w1.in_features() {
11064 return Ok(false);
11065 }
11066 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11067 match w {
11068 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11069 Some(m) => (m, true),
11070 None => (bytes, *rp),
11071 },
11072 _ => unreachable!(),
11073 }
11074 }
11075 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11076 if rp0 != rp1 {
11077 return Ok(false);
11078 }
11079 let rp = rp0;
11080 let rpb: u32 = 4;
11081 let mr1 = rp && Self::q40_mr1_on();
11082 let nb = |o: usize| {
11083 if mr1 {
11084 (o as u32).div_ceil(rpb)
11085 } else {
11086 (o as u32).div_ceil(2).div_ceil(rpb)
11087 }
11088 };
11089 let grid = nb(o0) + nb(o1);
11090 debug_assert!(y0.len() >= o0 && y1.len() >= o1);
11091 let f = self.func(if mr1 {
11092 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
11093 } else if rp {
11094 "qmatvec_q4_0_mmvq_fused2_rp"
11095 } else {
11096 "qmatvec_q4_0_mmvq_fused2"
11097 });
11098 let cfg = LaunchConfig {
11099 grid_dim: (grid, 1, 1),
11100 block_dim: (32, rpb, 1),
11101 shared_mem_bytes: 0,
11102 };
11103 let inf = w0.in_features() as i32;
11104 let (oo0, oo1) = (o0 as i32, o1 as i32);
11105 let (r0, r1) = (rb0 as i64, rb1 as i64);
11106 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11108 use cudarc::driver::{DevicePtr, DevicePtrMut};
11109 let s = &self.gpu.stream();
11110 let (p0, _g0) = b0.device_ptr(s);
11111 let (p1, _g1) = b1.device_ptr(s);
11112 let (paq, _g2) = aq.device_ptr(s);
11113 let (pad, _g3) = ad.device_ptr(s);
11114 let (py0, _g4) = y0.device_ptr_mut(s);
11115 let (py1, _g5) = y1.device_ptr_mut(s);
11116 let mut ps = [
11117 &p0 as *const _ as *mut std::ffi::c_void,
11118 &p1 as *const _ as *mut _,
11119 &paq as *const _ as *mut _,
11120 &pad as *const _ as *mut _,
11121 &py0 as *const _ as *mut _,
11122 &py1 as *const _ as *mut _,
11123 &inf as *const _ as *mut _,
11124 &oo0 as *const _ as *mut _,
11125 &oo1 as *const _ as *mut _,
11126 &r0 as *const _ as *mut _,
11127 &r1 as *const _ as *mut _,
11128 ];
11129 unsafe {
11130 self.launch_pdl(
11131 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
11132 (grid, 1, 1),
11133 (32, rpb, 1),
11134 &mut ps,
11135 )?;
11136 }
11137 return Ok(true);
11138 }
11139 let __s_b = self.gpu.stream();
11140 let mut b = __s_b.launch_builder(&f);
11141 b.arg(b0)
11142 .arg(b1)
11143 .arg(aq)
11144 .arg(ad)
11145 .arg(&mut *y0)
11146 .arg(&mut *y1)
11147 .arg(&inf)
11148 .arg(&oo0)
11149 .arg(&oo1)
11150 .arg(&r0)
11151 .arg(&r1);
11152 unsafe {
11153 b.launch(cfg)?;
11154 }
11155 Ok(true)
11156 }
11157
11158 pub fn matmul_q4_fused2_batched(
11163 &self,
11164 w0: &crate::model::GpuTensor,
11165 w1: &crate::model::GpuTensor,
11166 aq: &CudaSlice<i8>,
11167 ad: &CudaSlice<f32>,
11168 m: usize,
11169 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11170 use crate::model::GpuTensor;
11171 if m < 2 || m > 8 {
11172 return Ok(None);
11173 }
11174 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11175 match w {
11176 GpuTensor::Quant {
11177 qtype, row_bytes, ..
11178 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11179 _ => None,
11180 }
11181 };
11182 let (Some((rb0, o0)), Some((_rb1, o1))) = (q4(w0), q4(w1)) else {
11183 return Ok(None);
11184 };
11185 if w0.in_features() != w1.in_features() {
11186 return Ok(None);
11187 }
11188 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11189 match w {
11190 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11191 Some(mr) => (mr, true),
11192 None => (bytes, *rp),
11193 },
11194 _ => unreachable!(),
11195 }
11196 }
11197 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11198 if !rp0 || !rp1 {
11199 return Ok(None);
11200 }
11201 let mcols = Self::batched_mcols(m);
11202 let rpb: u32 = 4;
11203 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
11204 let grid = nb(o0) + nb(o1);
11205 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11206 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11207 let f = self.func(match mcols {
11208 2 => "qmatvec_q4_0_mmvq_b2_f2_rp",
11209 4 => "qmatvec_q4_0_mmvq_b4_f2_rp",
11210 _ => "qmatvec_q4_0_mmvq_b8_f2_rp",
11211 });
11212 let cfg = LaunchConfig {
11213 grid_dim: (grid, 1, 1),
11214 block_dim: (32, rpb, 1),
11215 shared_mem_bytes: 0,
11216 };
11217 let inf = w0.in_features() as i32;
11218 let (oo0, oo1, mi) = (o0 as i32, o1 as i32, m as i32);
11219 let rb = rb0 as i64;
11220 let __s_b = self.gpu.stream();
11221 let mut b = __s_b.launch_builder(&f);
11222 b.arg(b0)
11223 .arg(b1)
11224 .arg(aq)
11225 .arg(ad)
11226 .arg(&mut y0)
11227 .arg(&mut y1)
11228 .arg(&inf)
11229 .arg(&oo0)
11230 .arg(&oo1)
11231 .arg(&mi)
11232 .arg(&rb);
11233 unsafe {
11234 b.launch(cfg)?;
11235 }
11236 Ok(Some((y0, y1)))
11237 }
11238
11239 #[allow(clippy::too_many_arguments)]
11242 pub fn matmul_q4_fused3_batched(
11243 &self,
11244 w0: &crate::model::GpuTensor,
11245 w1: &crate::model::GpuTensor,
11246 w2: &crate::model::GpuTensor,
11247 aq: &CudaSlice<i8>,
11248 ad: &CudaSlice<f32>,
11249 m: usize,
11250 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11251 {
11252 use crate::model::GpuTensor;
11253 if m < 2 || m > 8 {
11254 return Ok(None);
11255 }
11256 let q4 = |w: &GpuTensor| -> Option<usize> {
11257 match w {
11258 GpuTensor::Quant { qtype, .. } if *qtype == QT_Q4_0 => Some(w.out_features()),
11259 _ => None,
11260 }
11261 };
11262 let (Some(o0), Some(o1), Some(o2)) = (q4(w0), q4(w1), q4(w2)) else {
11263 return Ok(None);
11264 };
11265 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
11266 return Ok(None);
11267 }
11268 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11269 match w {
11270 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11271 Some(mr) => (mr, true),
11272 None => (bytes, *rp),
11273 },
11274 _ => unreachable!(),
11275 }
11276 }
11277 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
11278 if !rp0 || !rp1 || !rp2 {
11279 return Ok(None);
11280 }
11281 let mcols = Self::batched_mcols(m);
11282 let rpb: u32 = 4;
11283 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
11284 let grid = nb(o0) + nb(o1) + nb(o2);
11285 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11286 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11287 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
11288 let f = self.func(match mcols {
11289 2 => "qmatvec_q4_0_mmvq_b2_f3_rp",
11290 4 => "qmatvec_q4_0_mmvq_b4_f3_rp",
11291 _ => "qmatvec_q4_0_mmvq_b8_f3_rp",
11292 });
11293 let cfg = LaunchConfig {
11294 grid_dim: (grid, 1, 1),
11295 block_dim: (32, rpb, 1),
11296 shared_mem_bytes: 0,
11297 };
11298 let inf = w0.in_features() as i32;
11299 let (oo0, oo1, oo2, mi) = (o0 as i32, o1 as i32, o2 as i32, m as i32);
11300 let rb = 0i64;
11301 let __s_b = self.gpu.stream();
11302 let mut b = __s_b.launch_builder(&f);
11303 b.arg(b0)
11304 .arg(b1)
11305 .arg(b2)
11306 .arg(aq)
11307 .arg(ad)
11308 .arg(&mut y0)
11309 .arg(&mut y1)
11310 .arg(&mut y2)
11311 .arg(&inf)
11312 .arg(&oo0)
11313 .arg(&oo1)
11314 .arg(&oo2)
11315 .arg(&mi)
11316 .arg(&rb);
11317 unsafe {
11318 b.launch(cfg)?;
11319 }
11320 Ok(Some((y0, y1, y2)))
11321 }
11322
11323 pub fn matmul_q8_fused3(
11324 &self,
11325 w0: &crate::model::GpuTensor,
11326 w1: &crate::model::GpuTensor,
11327 w2: &crate::model::GpuTensor,
11328 aq: &CudaSlice<i8>,
11329 ad: &CudaSlice<f32>,
11330 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11331 {
11332 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
11335 return Ok(Some(self.e4m3_fused3_core(
11336 p0.0,
11337 p1.0,
11338 p2.0,
11339 aq,
11340 ad,
11341 w0.in_features(),
11342 p0.1,
11343 p1.1,
11344 p2.1,
11345 p0.2,
11346 p0.3,
11347 p1.3,
11348 p2.3,
11349 )?));
11350 }
11351 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
11352 return Ok(None);
11353 };
11354 Ok(Some(self.q8_fused3_core(
11355 p0.0,
11356 p1.0,
11357 p2.0,
11358 aq,
11359 ad,
11360 w0.in_features(),
11361 p0.1,
11362 p1.1,
11363 p2.1,
11364 p0.2,
11365 )?))
11366 }
11367
11368 #[allow(clippy::too_many_arguments)]
11369 fn q8_fused3_core(
11370 &self,
11371 b0: &CudaSlice<u8>,
11372 b1: &CudaSlice<u8>,
11373 b2: &CudaSlice<u8>,
11374 aq: &CudaSlice<i8>,
11375 ad: &CudaSlice<f32>,
11376 in_f: usize,
11377 out0: usize,
11378 out1: usize,
11379 out2: usize,
11380 row_bytes: usize,
11381 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11382 const ROWS_PER_BLOCK: u32 = 4;
11383 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11384 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11385 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
11386 let f = self.func("qmatvec_q8_0_mmvq_fused3");
11387 let mut y0 = self.alloc_uninit::<f32>(out0)?;
11388 let mut y1 = self.alloc_uninit::<f32>(out1)?;
11389 let mut y2 = self.alloc_uninit::<f32>(out2)?;
11390 let cfg = LaunchConfig {
11391 grid_dim: (nb0 + nb1 + nb2, 1, 1),
11392 block_dim: (32, ROWS_PER_BLOCK, 1),
11393 shared_mem_bytes: 0,
11394 };
11395 let (inf, o0, o1, o2, rbl) = (
11396 in_f as i32,
11397 out0 as i32,
11398 out1 as i32,
11399 out2 as i32,
11400 row_bytes as i64,
11401 );
11402 let __s_b = self.gpu.stream();
11403 let mut b = __s_b.launch_builder(&f);
11404 b.arg(b0)
11405 .arg(b1)
11406 .arg(b2)
11407 .arg(aq)
11408 .arg(ad)
11409 .arg(&mut y0)
11410 .arg(&mut y1)
11411 .arg(&mut y2)
11412 .arg(&inf)
11413 .arg(&o0)
11414 .arg(&o1)
11415 .arg(&o2)
11416 .arg(&rbl);
11417 unsafe {
11418 b.launch(cfg)?;
11419 }
11420 Ok((y0, y1, y2))
11421 }
11422
11423 #[allow(clippy::too_many_arguments)]
11425 pub fn qmatvec_q8_fused3_raw(
11426 &self,
11427 b0: &CudaSlice<u8>,
11428 b1: &CudaSlice<u8>,
11429 b2: &CudaSlice<u8>,
11430 x: &CudaSlice<f32>,
11431 in_f: usize,
11432 out0: usize,
11433 out1: usize,
11434 out2: usize,
11435 row_bytes: usize,
11436 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11437 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
11438 self.q8_fused3_core(b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes)
11439 }
11440
11441 pub fn matmul_q8_fused2_t(
11452 &self,
11453 w0: &crate::model::GpuTensor,
11454 w1: &crate::model::GpuTensor,
11455 aq: &CudaSlice<i8>,
11456 ad: &CudaSlice<f32>,
11457 m: usize,
11458 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11459 if !(2..=8).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
11463 return Ok(None);
11464 }
11465 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
11468 if m > 4 && !Self::b8_enabled() {
11469 return Ok(None);
11470 }
11471 return Ok(Some(self.e4m3_fused2_t_core(
11472 p0.0,
11473 p1.0,
11474 aq,
11475 ad,
11476 m,
11477 w0.in_features(),
11478 p0.1,
11479 p1.1,
11480 p0.2,
11481 p0.3,
11482 p1.3,
11483 )?));
11484 }
11485 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
11486 return Ok(None);
11487 };
11488 Ok(Some(self.q8_fused2_t_core(
11489 p0.0,
11490 p1.0,
11491 aq,
11492 ad,
11493 m,
11494 w0.in_features(),
11495 p0.1,
11496 p1.1,
11497 p0.2,
11498 )?))
11499 }
11500
11501 #[allow(clippy::too_many_arguments)]
11502 fn q8_fused2_t_core(
11503 &self,
11504 b0: &CudaSlice<u8>,
11505 b1: &CudaSlice<u8>,
11506 aq: &CudaSlice<i8>,
11507 ad: &CudaSlice<f32>,
11508 m: usize,
11509 in_f: usize,
11510 out0: usize,
11511 out1: usize,
11512 row_bytes: usize,
11513 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11514 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11516 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11517 let f = self.func(match Self::batched_mcols(m) {
11518 2 => "qmatvec_q8_0_mmvq_fused2_b2",
11519 4 => "qmatvec_q8_0_mmvq_fused2_b4",
11520 _ => "qmatvec_q8_0_mmvq_fused2_b8",
11522 });
11523 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
11524 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
11525 let cfg = LaunchConfig {
11526 grid_dim: (nb0 + nb1, 1, 1),
11527 block_dim: (32, ROWS_PER_BLOCK, 1),
11528 shared_mem_bytes: 0,
11529 };
11530 let (inf, o0, o1, mi, rbl) = (
11531 in_f as i32,
11532 out0 as i32,
11533 out1 as i32,
11534 m as i32,
11535 row_bytes as i64,
11536 );
11537 let __s_b = self.gpu.stream();
11538 let mut b = __s_b.launch_builder(&f);
11539 b.arg(b0)
11540 .arg(b1)
11541 .arg(aq)
11542 .arg(ad)
11543 .arg(&mut y0)
11544 .arg(&mut y1)
11545 .arg(&inf)
11546 .arg(&o0)
11547 .arg(&o1)
11548 .arg(&mi)
11549 .arg(&rbl);
11550 unsafe {
11551 b.launch(cfg)?;
11552 }
11553 Ok((y0, y1))
11554 }
11555
11556 #[allow(clippy::too_many_arguments)]
11559 pub fn qmatvec_q8_fused2_t_raw(
11560 &self,
11561 b0: &CudaSlice<u8>,
11562 b1: &CudaSlice<u8>,
11563 x: &CudaSlice<f32>,
11564 m: usize,
11565 in_f: usize,
11566 out0: usize,
11567 out1: usize,
11568 row_bytes: usize,
11569 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11570 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
11571 self.q8_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes)
11572 }
11573
11574 #[allow(clippy::too_many_arguments)]
11577 pub fn matmul_q8_fused3_t(
11578 &self,
11579 w0: &crate::model::GpuTensor,
11580 w1: &crate::model::GpuTensor,
11581 w2: &crate::model::GpuTensor,
11582 aq: &CudaSlice<i8>,
11583 ad: &CudaSlice<f32>,
11584 m: usize,
11585 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11586 {
11587 if !(2..=4).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
11588 return Ok(None);
11589 }
11590 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
11591 return Ok(Some(self.e4m3_fused3_t_core(
11592 p0.0,
11593 p1.0,
11594 p2.0,
11595 aq,
11596 ad,
11597 m,
11598 w0.in_features(),
11599 p0.1,
11600 p1.1,
11601 p2.1,
11602 p0.2,
11603 p0.3,
11604 p1.3,
11605 p2.3,
11606 )?));
11607 }
11608 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
11609 return Ok(None);
11610 };
11611 Ok(Some(self.q8_fused3_t_core(
11612 p0.0,
11613 p1.0,
11614 p2.0,
11615 aq,
11616 ad,
11617 m,
11618 w0.in_features(),
11619 p0.1,
11620 p1.1,
11621 p2.1,
11622 p0.2,
11623 )?))
11624 }
11625
11626 #[allow(clippy::too_many_arguments)]
11627 fn q8_fused3_t_core(
11628 &self,
11629 b0: &CudaSlice<u8>,
11630 b1: &CudaSlice<u8>,
11631 b2: &CudaSlice<u8>,
11632 aq: &CudaSlice<i8>,
11633 ad: &CudaSlice<f32>,
11634 m: usize,
11635 in_f: usize,
11636 out0: usize,
11637 out1: usize,
11638 out2: usize,
11639 row_bytes: usize,
11640 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11641 const ROWS_PER_BLOCK: u32 = 4;
11642 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11643 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11644 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
11645 let f = self.func(if Self::batched_mcols(m) == 2 {
11646 "qmatvec_q8_0_mmvq_fused3_b2"
11647 } else {
11648 "qmatvec_q8_0_mmvq_fused3_b4"
11649 });
11650 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
11651 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
11652 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
11653 let cfg = LaunchConfig {
11654 grid_dim: (nb0 + nb1 + nb2, 1, 1),
11655 block_dim: (32, ROWS_PER_BLOCK, 1),
11656 shared_mem_bytes: 0,
11657 };
11658 let (inf, o0, o1, o2, mi, rbl) = (
11659 in_f as i32,
11660 out0 as i32,
11661 out1 as i32,
11662 out2 as i32,
11663 m as i32,
11664 row_bytes as i64,
11665 );
11666 let __s_b = self.gpu.stream();
11667 let mut b = __s_b.launch_builder(&f);
11668 b.arg(b0)
11669 .arg(b1)
11670 .arg(b2)
11671 .arg(aq)
11672 .arg(ad)
11673 .arg(&mut y0)
11674 .arg(&mut y1)
11675 .arg(&mut y2)
11676 .arg(&inf)
11677 .arg(&o0)
11678 .arg(&o1)
11679 .arg(&o2)
11680 .arg(&mi)
11681 .arg(&rbl);
11682 unsafe {
11683 b.launch(cfg)?;
11684 }
11685 Ok((y0, y1, y2))
11686 }
11687
11688 #[allow(clippy::too_many_arguments)]
11690 pub fn qmatvec_q8_fused3_t_raw(
11691 &self,
11692 b0: &CudaSlice<u8>,
11693 b1: &CudaSlice<u8>,
11694 b2: &CudaSlice<u8>,
11695 x: &CudaSlice<f32>,
11696 m: usize,
11697 in_f: usize,
11698 out0: usize,
11699 out1: usize,
11700 out2: usize,
11701 row_bytes: usize,
11702 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11703 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
11704 self.q8_fused3_t_core(b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes)
11705 }
11706
11707 pub fn q8_ffn_fuse2_on(&self) -> bool {
11711 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11712 *ON.get_or_init(|| std::env::var("MEMRA_Q8_FFN_FUSE2").as_deref() != Ok("0"))
11713 }
11714
11715 #[allow(clippy::type_complexity)]
11721 fn q8_fused_params<'w, const N: usize>(
11722 &self,
11723 ws: &[&'w crate::model::GpuTensor; N],
11724 ) -> Option<[(&'w CudaSlice<u8>, usize, usize); N]> {
11725 use crate::model::GpuTensor;
11726 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
11727 return None;
11728 }
11729 if std::env::var("MEMRA_Q8_DUAL").is_ok_and(|v| v == "0") {
11730 return None;
11731 }
11732 let in_f = ws[0].in_features();
11733 let mut out: [Option<(&CudaSlice<u8>, usize, usize)>; N] = [None; N];
11734 for (i, w) in ws.iter().enumerate() {
11735 match w {
11736 GpuTensor::Quant {
11737 bytes,
11738 qtype,
11739 row_bytes,
11740 scale,
11741 ..
11742 } if *qtype == QT_Q8_0 && *scale == 1.0 && w.in_features() == in_f => {
11743 out[i] = Some((bytes, w.out_features(), *row_bytes))
11744 }
11745 _ => return None,
11746 }
11747 }
11748 Some(out.map(|o| o.unwrap()))
11749 }
11750
11751 pub fn e4m3_dual_on(&self) -> bool {
11754 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11755 *ON.get_or_init(|| std::env::var("MEMRA_E4M3_DUAL").as_deref() != Ok("0"))
11756 }
11757
11758 #[allow(clippy::type_complexity)]
11770 fn e4m3_fused_params<'w, const N: usize>(
11771 &self,
11772 ws: &[&'w crate::model::GpuTensor; N],
11773 ) -> Option<[(&'w CudaSlice<u8>, usize, usize, f32); N]> {
11774 use crate::model::GpuTensor;
11775 if !self.e4m3_dual_on() {
11776 return None;
11777 }
11778 let in_f = ws[0].in_features();
11779 let mut out: [Option<(&CudaSlice<u8>, usize, usize, f32)>; N] = [None; N];
11780 for (i, w) in ws.iter().enumerate() {
11781 match w {
11782 GpuTensor::Quant {
11783 bytes,
11784 qtype,
11785 row_bytes,
11786 scale,
11787 rp,
11788 rp4,
11789 ..
11790 } if *qtype == QT_F8_E4M3
11791 && w.in_features() == in_f
11792 && *row_bytes == in_f
11793 && !*rp
11794 && rp4.is_none() =>
11795 {
11796 out[i] = Some((bytes, w.out_features(), *row_bytes, *scale))
11797 }
11798 _ => return None,
11799 }
11800 }
11801 Some(out.map(|o| o.unwrap()))
11802 }
11803
11804 #[allow(clippy::too_many_arguments)]
11808 fn e4m3_fused2_core(
11809 &self,
11810 b0: &CudaSlice<u8>,
11811 b1: &CudaSlice<u8>,
11812 aq: &CudaSlice<i8>,
11813 ad: &CudaSlice<f32>,
11814 in_f: usize,
11815 out0: usize,
11816 out1: usize,
11817 row_bytes: usize,
11818 ws0: f32,
11819 ws1: f32,
11820 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11821 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11823 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11824 let f = self.func("qmatvec_e4m3_mmvq_fused2");
11825 let mut y0 = self.alloc_uninit::<f32>(out0)?;
11826 let mut y1 = self.alloc_uninit::<f32>(out1)?;
11827 let cfg = LaunchConfig {
11828 grid_dim: (nb0 + nb1, 1, 1),
11829 block_dim: (32, ROWS_PER_BLOCK, 1),
11830 shared_mem_bytes: 0,
11831 };
11832 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
11833 let __s_b = self.gpu.stream();
11834 let mut b = __s_b.launch_builder(&f);
11835 b.arg(b0)
11836 .arg(b1)
11837 .arg(aq)
11838 .arg(ad)
11839 .arg(&mut y0)
11840 .arg(&mut y1)
11841 .arg(&inf)
11842 .arg(&o0)
11843 .arg(&o1)
11844 .arg(&rbl)
11845 .arg(&ws0)
11846 .arg(&ws1);
11847 unsafe {
11848 b.launch(cfg)?;
11849 }
11850 Ok((y0, y1))
11851 }
11852
11853 #[allow(clippy::too_many_arguments)]
11855 fn e4m3_fused3_core(
11856 &self,
11857 b0: &CudaSlice<u8>,
11858 b1: &CudaSlice<u8>,
11859 b2: &CudaSlice<u8>,
11860 aq: &CudaSlice<i8>,
11861 ad: &CudaSlice<f32>,
11862 in_f: usize,
11863 out0: usize,
11864 out1: usize,
11865 out2: usize,
11866 row_bytes: usize,
11867 ws0: f32,
11868 ws1: f32,
11869 ws2: f32,
11870 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11871 const ROWS_PER_BLOCK: u32 = 4;
11872 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11873 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11874 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
11875 let f = self.func("qmatvec_e4m3_mmvq_fused3");
11876 let mut y0 = self.alloc_uninit::<f32>(out0)?;
11877 let mut y1 = self.alloc_uninit::<f32>(out1)?;
11878 let mut y2 = self.alloc_uninit::<f32>(out2)?;
11879 let cfg = LaunchConfig {
11880 grid_dim: (nb0 + nb1 + nb2, 1, 1),
11881 block_dim: (32, ROWS_PER_BLOCK, 1),
11882 shared_mem_bytes: 0,
11883 };
11884 let (inf, o0, o1, o2, rbl) = (
11885 in_f as i32,
11886 out0 as i32,
11887 out1 as i32,
11888 out2 as i32,
11889 row_bytes as i64,
11890 );
11891 let __s_b = self.gpu.stream();
11892 let mut b = __s_b.launch_builder(&f);
11893 b.arg(b0)
11894 .arg(b1)
11895 .arg(b2)
11896 .arg(aq)
11897 .arg(ad)
11898 .arg(&mut y0)
11899 .arg(&mut y1)
11900 .arg(&mut y2)
11901 .arg(&inf)
11902 .arg(&o0)
11903 .arg(&o1)
11904 .arg(&o2)
11905 .arg(&rbl)
11906 .arg(&ws0)
11907 .arg(&ws1)
11908 .arg(&ws2);
11909 unsafe {
11910 b.launch(cfg)?;
11911 }
11912 Ok((y0, y1, y2))
11913 }
11914
11915 #[allow(clippy::too_many_arguments)]
11919 fn e4m3_fused2_t_core(
11920 &self,
11921 b0: &CudaSlice<u8>,
11922 b1: &CudaSlice<u8>,
11923 aq: &CudaSlice<i8>,
11924 ad: &CudaSlice<f32>,
11925 m: usize,
11926 in_f: usize,
11927 out0: usize,
11928 out1: usize,
11929 row_bytes: usize,
11930 ws0: f32,
11931 ws1: f32,
11932 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11933 const ROWS_PER_BLOCK: u32 = 4;
11934 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11935 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11936 let f = self.func(match Self::batched_mcols(m) {
11937 2 => "qmatvec_e4m3_mmvq_fused2_b2",
11938 4 => "qmatvec_e4m3_mmvq_fused2_b4",
11939 _ => "qmatvec_e4m3_mmvq_fused2_b8",
11940 });
11941 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
11942 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
11943 let cfg = LaunchConfig {
11944 grid_dim: (nb0 + nb1, 1, 1),
11945 block_dim: (32, ROWS_PER_BLOCK, 1),
11946 shared_mem_bytes: 0,
11947 };
11948 let (inf, o0, o1, mi, rbl) = (
11949 in_f as i32,
11950 out0 as i32,
11951 out1 as i32,
11952 m as i32,
11953 row_bytes as i64,
11954 );
11955 let __s_b = self.gpu.stream();
11956 let mut b = __s_b.launch_builder(&f);
11957 b.arg(b0)
11958 .arg(b1)
11959 .arg(aq)
11960 .arg(ad)
11961 .arg(&mut y0)
11962 .arg(&mut y1)
11963 .arg(&inf)
11964 .arg(&o0)
11965 .arg(&o1)
11966 .arg(&mi)
11967 .arg(&rbl);
11968 unsafe {
11969 b.launch(cfg)?;
11970 }
11971 if ws0 != 1.0 {
11972 self.scale_inplace(&mut y0, ws0, m * out0)?;
11973 }
11974 if ws1 != 1.0 {
11975 self.scale_inplace(&mut y1, ws1, m * out1)?;
11976 }
11977 Ok((y0, y1))
11978 }
11979
11980 #[allow(clippy::too_many_arguments)]
11982 fn e4m3_fused3_t_core(
11983 &self,
11984 b0: &CudaSlice<u8>,
11985 b1: &CudaSlice<u8>,
11986 b2: &CudaSlice<u8>,
11987 aq: &CudaSlice<i8>,
11988 ad: &CudaSlice<f32>,
11989 m: usize,
11990 in_f: usize,
11991 out0: usize,
11992 out1: usize,
11993 out2: usize,
11994 row_bytes: usize,
11995 ws0: f32,
11996 ws1: f32,
11997 ws2: f32,
11998 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11999 const ROWS_PER_BLOCK: u32 = 4;
12000 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12001 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12002 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12003 let f = self.func(if Self::batched_mcols(m) == 2 {
12004 "qmatvec_e4m3_mmvq_fused3_b2"
12005 } else {
12006 "qmatvec_e4m3_mmvq_fused3_b4"
12007 });
12008 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12009 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12010 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
12011 let cfg = LaunchConfig {
12012 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12013 block_dim: (32, ROWS_PER_BLOCK, 1),
12014 shared_mem_bytes: 0,
12015 };
12016 let (inf, o0, o1, o2, mi, rbl) = (
12017 in_f as i32,
12018 out0 as i32,
12019 out1 as i32,
12020 out2 as i32,
12021 m as i32,
12022 row_bytes as i64,
12023 );
12024 let __s_b = self.gpu.stream();
12025 let mut b = __s_b.launch_builder(&f);
12026 b.arg(b0)
12027 .arg(b1)
12028 .arg(b2)
12029 .arg(aq)
12030 .arg(ad)
12031 .arg(&mut y0)
12032 .arg(&mut y1)
12033 .arg(&mut y2)
12034 .arg(&inf)
12035 .arg(&o0)
12036 .arg(&o1)
12037 .arg(&o2)
12038 .arg(&mi)
12039 .arg(&rbl);
12040 unsafe {
12041 b.launch(cfg)?;
12042 }
12043 if ws0 != 1.0 {
12044 self.scale_inplace(&mut y0, ws0, m * out0)?;
12045 }
12046 if ws1 != 1.0 {
12047 self.scale_inplace(&mut y1, ws1, m * out1)?;
12048 }
12049 if ws2 != 1.0 {
12050 self.scale_inplace(&mut y2, ws2, m * out2)?;
12051 }
12052 Ok((y0, y1, y2))
12053 }
12054
12055 pub fn qmatvec_e4m3_blk_mmvq(
12065 &self,
12066 bytes: &CudaSlice<u8>,
12067 aq: &CudaSlice<i8>,
12068 ad: &CudaSlice<f32>,
12069 scales: &CudaSlice<f32>,
12070 m: usize,
12071 in_f: usize,
12072 out_f: usize,
12073 row_bytes: usize,
12074 scale_cols: usize,
12075 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12076 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_e4m3_blk_mmvq_into(
12078 bytes, aq, ad, scales, m, in_f, out_f, row_bytes, scale_cols, &mut y,
12079 )?;
12080 Ok(y)
12081 }
12082
12083 #[allow(clippy::too_many_arguments)]
12085 pub fn qmatvec_e4m3_blk_mmvq_into(
12086 &self,
12087 bytes: &CudaSlice<u8>,
12088 aq: &CudaSlice<i8>,
12089 ad: &CudaSlice<f32>,
12090 scales: &CudaSlice<f32>,
12091 m: usize,
12092 in_f: usize,
12093 out_f: usize,
12094 row_bytes: usize,
12095 scale_cols: usize,
12096 y: &mut CudaSlice<f32>,
12097 ) -> Result<(), Box<dyn std::error::Error>> {
12098 const ROWS_PER_BLOCK: u32 = 4; let f = self.func("qmatvec_e4m3_blk_mmvq");
12100 let cfg = LaunchConfig {
12101 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), m as u32, 1),
12102 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
12105 let (inf, outf, mi, rb, sc) = (
12106 in_f as i32,
12107 out_f as i32,
12108 m as i32,
12109 row_bytes as i64,
12110 scale_cols as i32,
12111 );
12112 let __s_b = self.gpu.stream();
12113 let mut b = __s_b.launch_builder(&f);
12114 b.arg(bytes)
12115 .arg(aq)
12116 .arg(ad)
12117 .arg(scales)
12118 .arg(&mut *y)
12119 .arg(&inf)
12120 .arg(&outf)
12121 .arg(&mi)
12122 .arg(&rb)
12123 .arg(&sc);
12124 unsafe {
12125 b.launch(cfg)?;
12126 }
12127 Ok(())
12128 }
12129
12130 #[allow(clippy::too_many_arguments)]
12136 pub fn qmatvec_e4m3_blk_mmvq_batched(
12137 &self,
12138 bytes: &CudaSlice<u8>,
12139 aq: &CudaSlice<i8>,
12140 ad: &CudaSlice<f32>,
12141 scales: &CudaSlice<f32>,
12142 m: usize,
12143 in_f: usize,
12144 out_f: usize,
12145 row_bytes: usize,
12146 scale_cols: usize,
12147 mcols: usize,
12148 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12149 const ROWS_PER_BLOCK: u32 = 4; debug_assert!(mcols >= m, "blk batched: mcols {mcols} < m {m}");
12151 let name = match mcols {
12152 2 => "qmatvec_e4m3_blk_mmvq_b2",
12153 4 => "qmatvec_e4m3_blk_mmvq_b4",
12154 8 => "qmatvec_e4m3_blk_mmvq_b8",
12155 16 => "qmatvec_e4m3_blk_mmvq_b16",
12156 _ => {
12157 return Err(
12158 format!("qmatvec_e4m3_blk_mmvq_batched: no kernel for mcols {mcols}").into(),
12159 );
12160 }
12161 };
12162 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
12163 let f = self.func(name);
12164 let cfg = LaunchConfig {
12165 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), 1, 1),
12166 block_dim: (32, ROWS_PER_BLOCK, 1),
12167 shared_mem_bytes: 0,
12168 };
12169 let (inf, outf, mi, rb, sc) = (
12170 in_f as i32,
12171 out_f as i32,
12172 m as i32,
12173 row_bytes as i64,
12174 scale_cols as i32,
12175 );
12176 let __s_b = self.gpu.stream();
12177 let mut b = __s_b.launch_builder(&f);
12178 b.arg(bytes)
12179 .arg(aq)
12180 .arg(ad)
12181 .arg(scales)
12182 .arg(&mut y)
12183 .arg(&inf)
12184 .arg(&outf)
12185 .arg(&mi)
12186 .arg(&rb)
12187 .arg(&sc);
12188 unsafe {
12189 b.launch(cfg)?;
12190 }
12191 Ok(y)
12192 }
12193
12194 #[allow(clippy::too_many_arguments)]
12197 pub fn qmatvec_e4m3_blk_batched_raw(
12198 &self,
12199 bytes: &CudaSlice<u8>,
12200 x: &CudaSlice<f32>,
12201 scales: &CudaSlice<f32>,
12202 m: usize,
12203 in_f: usize,
12204 out_f: usize,
12205 row_bytes: usize,
12206 scale_cols: usize,
12207 mcols: usize,
12208 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12209 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12210 self.qmatvec_e4m3_blk_mmvq_batched(
12211 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols, mcols,
12212 )
12213 }
12214
12215 #[allow(clippy::too_many_arguments)]
12218 pub fn qmatvec_e4m3_blk_mmvq_raw(
12219 &self,
12220 bytes: &CudaSlice<u8>,
12221 x: &CudaSlice<f32>,
12222 scales: &CudaSlice<f32>,
12223 m: usize,
12224 in_f: usize,
12225 out_f: usize,
12226 row_bytes: usize,
12227 scale_cols: usize,
12228 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12229 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12230 self.qmatvec_e4m3_blk_mmvq(
12231 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols,
12232 )
12233 }
12234
12235 #[allow(clippy::too_many_arguments)]
12238 pub fn qmatvec_e4m3_fused2_raw(
12239 &self,
12240 b0: &CudaSlice<u8>,
12241 b1: &CudaSlice<u8>,
12242 x: &CudaSlice<f32>,
12243 in_f: usize,
12244 out0: usize,
12245 out1: usize,
12246 row_bytes: usize,
12247 ws0: f32,
12248 ws1: f32,
12249 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12250 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
12251 self.e4m3_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes, ws0, ws1)
12252 }
12253
12254 #[allow(clippy::too_many_arguments)]
12255 pub fn qmatvec_e4m3_fused3_raw(
12256 &self,
12257 b0: &CudaSlice<u8>,
12258 b1: &CudaSlice<u8>,
12259 b2: &CudaSlice<u8>,
12260 x: &CudaSlice<f32>,
12261 in_f: usize,
12262 out0: usize,
12263 out1: usize,
12264 out2: usize,
12265 row_bytes: usize,
12266 ws0: f32,
12267 ws1: f32,
12268 ws2: f32,
12269 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12270 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
12271 self.e4m3_fused3_core(
12272 b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
12273 )
12274 }
12275
12276 #[allow(clippy::too_many_arguments)]
12277 pub fn qmatvec_e4m3_fused2_t_raw(
12278 &self,
12279 b0: &CudaSlice<u8>,
12280 b1: &CudaSlice<u8>,
12281 x: &CudaSlice<f32>,
12282 m: usize,
12283 in_f: usize,
12284 out0: usize,
12285 out1: usize,
12286 row_bytes: usize,
12287 ws0: f32,
12288 ws1: f32,
12289 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12290 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12291 self.e4m3_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes, ws0, ws1)
12292 }
12293
12294 #[allow(clippy::too_many_arguments)]
12295 pub fn qmatvec_e4m3_fused3_t_raw(
12296 &self,
12297 b0: &CudaSlice<u8>,
12298 b1: &CudaSlice<u8>,
12299 b2: &CudaSlice<u8>,
12300 x: &CudaSlice<f32>,
12301 m: usize,
12302 in_f: usize,
12303 out0: usize,
12304 out1: usize,
12305 out2: usize,
12306 row_bytes: usize,
12307 ws0: f32,
12308 ws1: f32,
12309 ws2: f32,
12310 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12311 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12312 self.e4m3_fused3_t_core(
12313 b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
12314 )
12315 }
12316
12317 fn try_e4m3_blk_pre(
12328 &self,
12329 w: &crate::model::GpuTensor,
12330 aq: &CudaSlice<i8>,
12331 ad: &CudaSlice<f32>,
12332 m: usize,
12333 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
12334 use crate::model::GpuTensor;
12335 if let GpuTensor::Quant {
12336 bytes,
12337 qtype,
12338 row_bytes,
12339 blk: Some(g),
12340 ..
12341 } = w
12342 {
12343 if *qtype == QT_F8_E4M3_BLK {
12344 if (2..=16).contains(&m)
12350 && std::env::var("MEMRA_NO_BATCHED").is_err()
12351 && (m <= 4 || Self::b8_enabled())
12352 {
12353 let mcols = Self::batched_mcols(m);
12354 return Ok(Some(self.qmatvec_e4m3_blk_mmvq_batched(
12355 bytes,
12356 aq,
12357 ad,
12358 &g.scales,
12359 m,
12360 w.in_features(),
12361 w.out_features(),
12362 *row_bytes,
12363 g.cols,
12364 mcols,
12365 )?));
12366 }
12367 return Ok(Some(self.qmatvec_e4m3_blk_mmvq(
12368 bytes,
12369 aq,
12370 ad,
12371 &g.scales,
12372 m,
12373 w.in_features(),
12374 w.out_features(),
12375 *row_bytes,
12376 g.cols,
12377 )?));
12378 }
12379 }
12380 Ok(None)
12381 }
12382
12383 fn try_e4m3_blk_prefill(
12430 &self,
12431 w: &crate::model::GpuTensor,
12432 x: &CudaSlice<f32>,
12433 m: usize,
12434 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
12435 use crate::model::GpuTensor;
12436 let GpuTensor::Quant {
12437 bytes,
12438 qtype,
12439 blk: Some(g),
12440 ..
12441 } = w
12442 else {
12443 return Ok(None);
12444 };
12445 if *qtype != QT_F8_E4M3_BLK {
12446 return Ok(None);
12447 }
12448 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
12453 return Ok(Some(y));
12454 }
12455 let (in_f, out_f) = (w.in_features(), w.out_features());
12456 let slab = self.fp8_blk_dequant_q8_0_dev(bytes, &g.scales, out_f, in_f)?;
12457 let tmp = GpuTensor::Quant {
12458 bytes: slab,
12459 qtype: QT_Q8_0,
12460 row_bytes: in_f / 32 * 34,
12461 ne: vec![in_f as u64, out_f as u64],
12462 scale: 1.0,
12463 rp: false,
12464 #[cfg(memra_cutlass)]
12465 cutlass: None,
12466 fp8: None,
12467 blk: None,
12468 f16: None,
12469 rp4: None,
12470 };
12471 Ok(Some(self.matmul(&tmp, x, m)?))
12473 }
12474
12475 pub fn matmul_pre_noscale(
12476 &self,
12477 w: &crate::model::GpuTensor,
12478 aq: &CudaSlice<i8>,
12479 ad: &CudaSlice<f32>,
12480 m: usize,
12481 ) -> Result<Option<(CudaSlice<f32>, f32)>, Box<dyn std::error::Error>> {
12482 use crate::model::GpuTensor;
12483 if m == 1 {
12487 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
12488 return Ok(Some((y, 1.0)));
12489 }
12490 }
12491 if m != 1 || !self.uses_q8_1_fast(w) {
12493 return Ok(None);
12494 }
12495 let in_f = w.in_features();
12496 let out_f = w.out_features();
12497 let (bytes, qtype, row_bytes, scale, rp) = match w {
12498 GpuTensor::Quant {
12499 bytes,
12500 qtype,
12501 row_bytes,
12502 scale,
12503 rp,
12504 ..
12505 } => (bytes, *qtype, *row_bytes, *scale, *rp),
12506 _ => return Ok(None),
12507 };
12508 if self.mmvq_supports(qtype) {
12510 let (mbytes, mrp) = match w {
12512 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
12513 _ => (bytes, rp),
12514 };
12515 let y = self.qmatvec_mmvq(
12516 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, 1.0, mrp,
12517 )?;
12518 return Ok(Some((y, scale)));
12519 }
12520 let name = match qtype {
12522 QT_Q8_0 => "qmatvec_q8_0_dp4a",
12523 QT_Q4_K => "qmatvec_q4_K_dp4a",
12524 QT_Q6_K => "qmatvec_q6_K_dp4a",
12525 QT_Q5_K => "qmatvec_q5_K_dp4a",
12526 QT_Q3_K => "qmatvec_q3_K_dp4a",
12527 QT_NVFP4 => {
12528 if rp {
12529 "qmatvec_nvfp4_dp4a_rp"
12530 } else {
12531 "qmatvec_nvfp4_dp4a"
12532 }
12533 }
12534 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
12535 _ => return Ok(None),
12536 };
12537 let f = self.func(name);
12538 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
12539 let cfg = LaunchConfig {
12540 grid_dim: (out_f as u32, m as u32, 1),
12541 block_dim: (128, 1, 1),
12542 shared_mem_bytes: 0,
12543 };
12544 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
12545 let __s_b = self.gpu.stream();
12546 let mut b = __s_b.launch_builder(&f);
12547 b.arg(bytes)
12548 .arg(aq)
12549 .arg(ad)
12550 .arg(&mut y)
12551 .arg(&inf)
12552 .arg(&outf)
12553 .arg(&mi)
12554 .arg(&rb);
12555 unsafe {
12556 b.launch(cfg)?;
12557 }
12558 Ok(Some((y, scale)))
12559 }
12560
12561 pub fn mmvq_supports(&self, qtype: i32) -> bool {
12564 if qtype == QT_F8_E4M3 {
12569 return true;
12570 }
12571 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
12572 return false;
12573 }
12574 matches!(
12575 qtype,
12576 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_Q4_0
12577 )
12578 }
12579
12580 pub fn qmatvec_mmvq(
12585 &self,
12586 bytes: &CudaSlice<u8>,
12587 aq: &CudaSlice<i8>,
12588 ad: &CudaSlice<f32>,
12589 m: usize,
12590 in_f: usize,
12591 out_f: usize,
12592 qtype: i32,
12593 row_bytes: usize,
12594 scale: f32,
12595 rp: bool,
12596 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12597 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_mmvq_into(
12599 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp, &mut y,
12600 )?;
12601 Ok(y)
12602 }
12603
12604 #[allow(clippy::too_many_arguments)]
12606 pub fn qmatvec_mmvq_into(
12607 &self,
12608 bytes: &CudaSlice<u8>,
12609 aq: &CudaSlice<i8>,
12610 ad: &CudaSlice<f32>,
12611 m: usize,
12612 in_f: usize,
12613 out_f: usize,
12614 qtype: i32,
12615 row_bytes: usize,
12616 scale: f32,
12617 rp: bool,
12618 y: &mut CudaSlice<f32>,
12619 ) -> Result<(), Box<dyn std::error::Error>> {
12620 debug_assert!(y.len() >= m * out_f);
12621 const ROWS_PER_BLOCK: u32 = 4; if qtype == QT_Q8_0
12627 && rp
12628 && m == 1
12629 && out_f >= 64
12630 && (out_f as u32).div_ceil(ROWS_PER_BLOCK) < 4 * self.sm_count() as u32
12631 && {
12632 static G2: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12633 *G2.get_or_init(|| std::env::var("MEMRA_Q80_G2").as_deref() != Ok("0"))
12634 }
12635 {
12636 let f = self.func("qmatvec_q8_0_mmvq_rp_g2");
12637 let cfg = LaunchConfig {
12638 grid_dim: ((out_f as u32).div_ceil(2), 1, 1),
12639 block_dim: (32, 2, 1),
12640 shared_mem_bytes: 0,
12641 };
12642 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, row_bytes as i64);
12643 let __s_b = self.gpu.stream();
12644 let mut b = __s_b.launch_builder(&f);
12645 b.arg(bytes)
12646 .arg(aq)
12647 .arg(ad)
12648 .arg(&mut *y)
12649 .arg(&inf)
12650 .arg(&outf)
12651 .arg(&mi)
12652 .arg(&rb);
12653 unsafe {
12654 b.launch(cfg)?;
12655 }
12656 if scale != 1.0 {
12657 self.scale_inplace(y, scale, out_f)?;
12658 }
12659 return Ok(());
12660 }
12661 let mut mr: u32 = if m == 1 && (qtype == QT_NVFP4 || qtype == QT_Q5_K) {
12670 2
12671 } else {
12672 1
12673 };
12674 if m == 1 && qtype == QT_Q4_0 {
12679 static Q40MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
12680 mr = *Q40MR.get_or_init(|| {
12683 std::env::var("MEMRA_Q40_MR")
12684 .ok()
12685 .and_then(|v| v.parse().ok())
12686 .unwrap_or(1)
12687 });
12688 }
12689 let q5_mode = std::env::var("MEMRA_Q5K_ISSUE").ok();
12700 let q5_force = q5_mode.as_deref() == Some("2");
12701 let q5_il = qtype == QT_Q5_K
12704 && m == 1
12705 && (q5_force || q5_mode.as_deref().map(|v| v != "0").unwrap_or(true));
12706 if q5_il && !q5_force && out_f > 65536 {
12707 mr = 1;
12708 }
12709 if qtype == QT_Q4_0 && rp && mr != 1 {
12712 mr = 2;
12713 }
12714 if qtype == QT_Q8_0 && rp {
12718 static Q80MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
12719 mr = *Q80MR.get_or_init(|| {
12720 std::env::var("MEMRA_Q80_MR")
12721 .ok()
12722 .and_then(|v| v.parse().ok())
12723 .unwrap_or(1)
12724 });
12725 }
12726 let name = match (qtype, mr, rp) {
12727 (QT_NVFP4, 2, false) => "qmatvec_nvfp4_mmvq_mr2",
12728 (QT_NVFP4, 2, true) => "qmatvec_nvfp4_mmvq_mr2_rp",
12729 (QT_NVFP4, _, true) => "qmatvec_nvfp4_mmvq_rp",
12730 (QT_Q4_0, 1, true) => "qmatvec_q4_0_mmvq_rp",
12731 (QT_Q4_0, _, true) => "qmatvec_q4_0_mmvq_mr2_rp",
12732 (QT_Q5_K, 2, _) => {
12733 if q5_il {
12734 "qmatvec_q5_K_mmvq_mr2_il"
12735 } else {
12736 "qmatvec_q5_K_mmvq_mr2"
12737 }
12738 }
12739 (QT_Q8_0, 2, true) => "qmatvec_q8_0_mmvq_mr2_rp",
12740 (QT_Q8_0, _, true)
12745 if in_f % 1024 == 0 && {
12746 static CA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12747 *CA.get_or_init(|| std::env::var("MEMRA_Q80_CA").as_deref() == Ok("1"))
12748 } =>
12749 {
12750 "qmatvec_q8_0_mmvq_rpca"
12751 }
12752 (QT_Q8_0, _, true) => "qmatvec_q8_0_mmvq_rp",
12753 (QT_Q8_0, _, _) => "qmatvec_q8_0_mmvq",
12754 (QT_Q4_K, _, true) => "qmatvec_q4_K_mmvq_rp",
12758 (QT_Q6_K, _, true) => "qmatvec_q6_K_mmvq_rp",
12759 (QT_Q4_K, _, _) => "qmatvec_q4_K_mmvq",
12760 (QT_Q4_0, 2, false) => "qmatvec_q4_0_mmvq_mr2",
12761 (QT_Q4_0, _, false) => "qmatvec_q4_0_mmvq",
12762 (QT_Q5_K, _, _) => {
12763 if q5_il {
12764 "qmatvec_q5_K_mmvq_il"
12765 } else {
12766 "qmatvec_q5_K_mmvq"
12767 }
12768 }
12769 (QT_Q6_K, _, _) => "qmatvec_q6_K_mmvq",
12770 (QT_NVFP4, _, false) => "qmatvec_nvfp4_mmvq",
12771 (QT_F8_E4M3, _, _) => "qmatvec_e4m3_mmvq",
12772 _ => panic!("qmatvec_mmvq: qtype {qtype} has no MMVQ kernel"),
12773 };
12774 let f = self.func(name);
12775 let rows_per_block = ROWS_PER_BLOCK * mr;
12777 let cfg = LaunchConfig {
12778 grid_dim: (
12779 (out_f as u32 + rows_per_block - 1) / rows_per_block,
12780 m as u32,
12781 1,
12782 ),
12783 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
12786 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
12787 let __s_b = self.gpu.stream();
12788 let mut b = __s_b.launch_builder(&f);
12789 if qtype == QT_NVFP4 || qtype == QT_F8_E4M3 {
12794 b.arg(bytes)
12795 .arg(aq)
12796 .arg(ad)
12797 .arg(&mut *y)
12798 .arg(&inf)
12799 .arg(&outf)
12800 .arg(&mi)
12801 .arg(&rb)
12802 .arg(&scale);
12803 unsafe {
12804 b.launch(cfg)?;
12805 }
12806 } else if Self::pdl_on()
12807 && Self::pdl_mmvq_on()
12808 && matches!(
12809 name,
12810 "qmatvec_q4_0_mmvq_rp" | "qmatvec_q6_K_mmvq" | "qmatvec_q6_K_mmvq_rp"
12811 )
12812 {
12813 {
12817 use cudarc::driver::{DevicePtr, DevicePtrMut};
12818 let s = &self.gpu.stream();
12819 let (pw, _g0) = bytes.device_ptr(s);
12820 let (paq, _g1) = aq.device_ptr(s);
12821 let (pad, _g2) = ad.device_ptr(s);
12822 let (py, _g3) = y.device_ptr_mut(s);
12823 let mut ps = [
12824 &pw as *const _ as *mut std::ffi::c_void,
12825 &paq as *const _ as *mut _,
12826 &pad as *const _ as *mut _,
12827 &py as *const _ as *mut _,
12828 &inf as *const _ as *mut _,
12829 &outf as *const _ as *mut _,
12830 &mi as *const _ as *mut _,
12831 &rb as *const _ as *mut _,
12832 ];
12833 unsafe {
12834 self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
12835 }
12836 }
12837 if scale != 1.0 {
12838 self.scale_inplace(y, scale, m * out_f)?;
12839 }
12840 } else {
12841 b.arg(bytes)
12842 .arg(aq)
12843 .arg(ad)
12844 .arg(&mut *y)
12845 .arg(&inf)
12846 .arg(&outf)
12847 .arg(&mi)
12848 .arg(&rb);
12849 unsafe {
12850 b.launch(cfg)?;
12851 }
12852 if scale != 1.0 {
12853 self.scale_inplace(y, scale, m * out_f)?;
12854 }
12855 }
12856 Ok(())
12857 }
12858
12859 pub fn qmatvec_mmvq_raw(
12863 &self,
12864 bytes: &CudaSlice<u8>,
12865 x: &CudaSlice<f32>,
12866 m: usize,
12867 in_f: usize,
12868 out_f: usize,
12869 qtype: i32,
12870 row_bytes: usize,
12871 rp: bool,
12872 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12873 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12874 self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, 1.0, rp)
12875 }
12876
12877 pub fn batched_supports(&self, qtype: i32) -> bool {
12881 matches!(
12882 qtype,
12883 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_F8_E4M3 | QT_Q4_0
12884 )
12885 }
12886
12887 pub fn iq_fast_enabled() -> bool {
12895 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12896 *ON.get_or_init(|| {
12897 std::env::var("MEMRA_IQ_FAST")
12898 .map(|v| v != "0")
12899 .unwrap_or(true)
12900 })
12901 }
12902
12903 pub fn b8_enabled() -> bool {
12906 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12907 *ON.get_or_init(|| std::env::var("MEMRA_B8").map(|v| v != "0").unwrap_or(true))
12908 }
12909
12910 pub fn batched_mcols(m: usize) -> usize {
12912 if m == 2 {
12913 2
12914 } else if m <= 4 {
12915 4
12916 } else if m <= 8 {
12917 8
12918 } else {
12919 16
12920 }
12921 }
12922
12923 fn batched_kernel_name(qtype: i32, mcols: usize) -> Option<&'static str> {
12928 Some(match (qtype, mcols) {
12929 (QT_Q8_0, 2) => "qmatvec_q8_0_mmvq_b2",
12930 (QT_Q8_0, 4) => "qmatvec_q8_0_mmvq_b4",
12931 (QT_Q8_0, 8) => "qmatvec_q8_0_mmvq_b8",
12932 (QT_Q8_0, 16) => "qmatvec_q8_0_mmvq_b16",
12938 (QT_Q4_K, 2) => "qmatvec_q4_K_mmvq_b2",
12939 (QT_Q4_K, 4) => "qmatvec_q4_K_mmvq_b4",
12940 (QT_Q4_K, 8) => "qmatvec_q4_K_mmvq_b8",
12941 (QT_Q4_K, 16) => "qmatvec_q4_K_mmvq_b16",
12944 (QT_Q5_K, 2) => "qmatvec_q5_K_mmvq_b2",
12945 (QT_Q5_K, 4) => "qmatvec_q5_K_mmvq_b4",
12946 (QT_Q5_K, 8) => "qmatvec_q5_K_mmvq_b8",
12947 (QT_Q5_K, 16) => "qmatvec_q5_K_mmvq_b16",
12950 (QT_Q6_K, 2) => "qmatvec_q6_K_mmvq_b2",
12951 (QT_Q6_K, 4) => "qmatvec_q6_K_mmvq_b4",
12952 (QT_Q6_K, 8) => "qmatvec_q6_K_mmvq_b8",
12953 (QT_Q6_K, 16) => "qmatvec_q6_K_mmvq_b16",
12954 (QT_NVFP4, 2) => "qmatvec_nvfp4_mmvq_b2",
12955 (QT_NVFP4, 4) => "qmatvec_nvfp4_mmvq_b4",
12956 (QT_NVFP4, 8) => "qmatvec_nvfp4_mmvq_b8",
12957 (QT_NVFP4, 16) => "qmatvec_nvfp4_mmvq_b16",
12961 (QT_F8_E4M3, 2) => "qmatvec_e4m3_mmvq_b2",
12962 (QT_F8_E4M3, 4) => "qmatvec_e4m3_mmvq_b4",
12963 (QT_F8_E4M3, 8) => "qmatvec_e4m3_mmvq_b8",
12964 (QT_F8_E4M3, 16) => "qmatvec_e4m3_mmvq_b16",
12968 (QT_Q4_0, 2) => "qmatvec_q4_0_mmvq_b2",
12969 (QT_Q4_0, 4) => "qmatvec_q4_0_mmvq_b4",
12970 (QT_Q4_0, 8) => "qmatvec_q4_0_mmvq_b8",
12971 (QT_Q4_0, 16) => "qmatvec_q4_0_mmvq_b16",
12972 _ => return None,
12973 })
12974 }
12975
12976 pub fn sm_count(&self) -> i32 {
13011 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
13012 *SMS.get_or_init(|| {
13013 use cudarc::driver::sys::CUdevice_attribute_enum as A;
13014 self.gpu
13015 .ctx
13016 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
13017 .unwrap_or(82)
13018 })
13019 }
13020
13021 pub fn batched_variant(
13022 &self,
13023 _m: usize,
13024 in_f: usize,
13025 out_f: usize,
13026 qtype: i32,
13027 row_bytes: usize,
13028 mcols: usize,
13029 rp: bool,
13030 ) -> &'static str {
13031 if qtype == QT_Q8_0 {
13036 return if rp { "rp" } else { "base" };
13037 }
13038 static BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13039 let bv = *BV.get_or_init(|| match std::env::var("MEMRA_MMVQ_BV").as_deref() {
13040 Ok("base") => "base",
13041 Ok("pf") => "pf",
13042 Ok("r2") => "r2",
13043 Ok("r2w8") => "r2w8",
13044 Ok("pfr2") => "pfr2",
13045 Ok("ca") => "ca",
13046 Ok("car2") => "car2",
13047 Ok("rp") => "rp",
13050 Ok("rpr2") => "rpr2",
13051 Ok("rpr2w8") => "rpr2w8",
13052 Ok("rpca") => "rpca",
13055 Ok("rpcar2") => "rpcar2",
13056 Ok("rpsc") => "rpsc",
13063 Ok("rpms") => "rpms",
13064 Ok("rpmsc") => "rpmsc",
13065 Ok("rpks") => "rpks",
13066 Ok("rpksc") => "rpksc",
13067 _ => "auto",
13068 });
13069 let ca_ok = qtype == QT_NVFP4 && (row_bytes % 16 == 0) && (in_f % 1024 == 0);
13073 static KS_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13078 let ks_on = *KS_ON.get_or_init(|| std::env::var("MEMRA_KS").as_deref() != Ok("0"));
13079 let sc_ok = ks_on && qtype == QT_NVFP4 && (in_f % 256 == 0) && (in_f / 64 <= 272);
13080 let ks_ok = ks_on && qtype == QT_NVFP4 && (in_f % 512 == 0) && (in_f / 64 <= 272);
13081 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
13082 let sms = *SMS.get_or_init(|| {
13083 use cudarc::driver::sys::CUdevice_attribute_enum as A;
13084 self.gpu
13085 .ctx
13086 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
13087 .unwrap_or(82)
13088 });
13089 let kq_r2 = matches!(qtype, QT_Q4_K | QT_Q5_K | QT_Q6_K);
13109 static KQBV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13112 let kq_bv = *KQBV.get_or_init(|| match std::env::var("MEMRA_KQ_BV").as_deref() {
13113 Ok("base") => "base",
13114 Ok("r2") => "r2",
13115 Ok("r2w8") => "r2w8",
13116 _ => "auto",
13117 });
13118 let variant: &'static str = if qtype == QT_Q4_0 {
13119 static Q40BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13123 let q40 = *Q40BV.get_or_init(|| match std::env::var("MEMRA_Q40_BV").as_deref() {
13124 Ok("base") => "base",
13130 Ok("r2") => "r2",
13131 Ok("ms") => "ms",
13132 Ok("sm") => "sm",
13133 Ok("la") => "la",
13134 _ => "auto",
13135 });
13136 let v = if q40 != "auto" {
13137 q40
13138 } else if (out_f as u32).div_ceil(8) >= 4 * sms as u32 {
13139 "r2"
13140 } else {
13141 "base"
13142 };
13143 if rp {
13148 match v {
13149 "ms" => "r2ms_rp",
13150 "sm" => "r2sm_rp",
13151 "la" => "r2la_rp",
13152 "r2" => "r2_rp",
13153 _ => "rp",
13154 }
13155 } else if matches!(v, "ms" | "sm" | "la") {
13156 "r2"
13157 } else {
13158 v
13159 }
13160 } else if qtype != QT_NVFP4 && !kq_r2 {
13161 "base"
13162 } else if kq_r2 && rp {
13163 "rp"
13167 } else if kq_r2 {
13168 if kq_bv != "auto" {
13171 if kq_bv == "r2w8" && mcols != 4 {
13172 "r2"
13173 } else {
13174 kq_bv
13175 }
13176 } else if bv != "auto" {
13177 match bv {
13178 "r2" | "pfr2" | "rpr2" | "car2" => "r2",
13179 "r2w8" | "rpr2w8" => {
13180 if mcols != 4 {
13181 "r2"
13182 } else {
13183 "r2w8"
13184 }
13185 }
13186 _ => "base", }
13188 } else {
13189 let blocks = (out_f + 7) / 8;
13190 let waves = blocks as f64 / (7 * sms as usize) as f64;
13191 let filled = blocks >= 4 * sms as usize;
13192 let use_r2 = if qtype == QT_Q4_K {
13193 filled
13194 } else {
13195 waves >= 2.0
13196 };
13197 if use_r2 { "r2" } else { "base" }
13198 }
13199 } else if bv != "auto" {
13200 let v = if bv == "r2w8" && mcols == 2 {
13205 "r2"
13206 } else if bv == "ca" && (!ca_ok || mcols == 8) {
13207 "pf"
13208 } else if bv == "car2" && (!ca_ok || mcols == 8) {
13209 "r2"
13210 } else if bv == "pfr2" && mcols == 8 {
13211 "r2"
13212 } else if (bv == "rpr2w8" || bv == "rpr2") && mcols == 2 {
13213 "rpr2"
13214 }
13215 else if (bv == "rpca" || bv == "rpcar2") && (!ca_ok || mcols == 8) {
13217 if mcols == 8 { "rpr2w8" } else { "rpr2" }
13218 } else if bv == "rpcar2" && mcols == 2 {
13219 "rpca"
13220 }
13221 else if (bv == "rpsc" || bv == "rpmsc") && !sc_ok {
13224 "rpr2"
13225 } else if (bv == "rpks" || bv == "rpksc") && !ks_ok {
13226 "rpr2"
13227 } else {
13228 bv
13229 };
13230 if rp {
13231 match v {
13232 "base" | "pf" | "ca" | "rp" => "rp",
13233 "r2" | "pfr2" | "car2" | "rpr2" => "rpr2",
13234 "r2w8" | "rpr2w8" => {
13235 if mcols == 2 {
13236 "rpr2"
13237 } else {
13238 "rpr2w8"
13239 }
13240 }
13241 other => other, }
13243 } else {
13244 v
13245 }
13246 } else if mcols == 8 {
13247 if rp {
13258 if sc_ok { "rpsc" } else { "rpr2w8" }
13259 } else {
13260 "r2w8"
13261 }
13262 } else if mcols >= 4 {
13263 let blocks = (out_f + 7) / 8;
13267 let r7 = 7 * sms as usize;
13268 let r8 = 8 * sms as usize;
13269 let waves = blocks as f64 / r7 as f64;
13270 let filled = blocks >= 4 * sms as usize;
13271 if filled && blocks.div_ceil(r8) < blocks.div_ceil(r7) {
13275 if rp { "rpr2w8" } else { "r2w8" }
13279 } else if waves >= 2.0 || (waves <= 1.0 && filled) {
13280 if rp { "rpr2" } else { "r2" }
13283 } else {
13284 if rp { "rp" } else { "pf" }
13288 }
13289 } else if in_f >= 6144 {
13290 if rp { "rpr2" } else { "r2" }
13294 } else if rp {
13295 let waves = ((out_f + 7) / 8) as f64 / (7 * sms as usize) as f64;
13300 if sc_ok && waves >= 0.9 && waves <= 1.1 {
13301 "rpsc"
13302 } else {
13303 "rp"
13304 }
13305 } else {
13306 "base"
13307 };
13308 variant
13309 }
13310
13311 pub fn qmatvec_mmvq_batched(
13312 &self,
13313 bytes: &CudaSlice<u8>,
13314 aq: &CudaSlice<i8>,
13315 ad: &CudaSlice<f32>,
13316 m: usize,
13317 in_f: usize,
13318 out_f: usize,
13319 qtype: i32,
13320 row_bytes: usize,
13321 mcols: usize,
13322 scale: f32,
13323 rp: bool,
13324 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13325 const ROWS_PER_BLOCK: u32 = 4;
13326 let forced: Option<&'static str> = {
13331 static V: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
13332 V.get_or_init(|| std::env::var("MEMRA_BVAR").ok())
13333 .as_deref()
13334 .map(|s| Box::leak(s.to_string().into_boxed_str()) as &'static str)
13335 };
13336 let variant = match forced {
13337 Some(v) if !rp || v.contains("rp") => v,
13338 _ => self.batched_variant(m, in_f, out_f, qtype, row_bytes, mcols, rp),
13339 };
13340 let base_name = Self::batched_kernel_name(qtype, mcols).ok_or_else(|| {
13341 format!("qmatvec_mmvq_batched: no kernel for qtype {qtype} mcols {mcols}")
13342 })?;
13343 let variant = if mcols == 16 {
13347 if rp { "rp" } else { "base" }
13348 } else {
13349 variant
13350 };
13351 static B567: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13358 let b567 = *B567.get_or_init(|| std::env::var("MEMRA_B567").as_deref() != Ok("0"));
13359 if b567
13360 && qtype == QT_NVFP4
13361 && rp
13362 && mcols == 8
13363 && (5..=7).contains(&m)
13364 && matches!(variant, "rpsc" | "rpr2w8")
13365 {
13366 let f = self.func(&format!("qmatvec_nvfp4_mmvq_b{m}_{variant}"));
13367 let rows_per_block = ROWS_PER_BLOCK * 2; let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13369 let cfg = LaunchConfig {
13370 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
13371 block_dim: (32, ROWS_PER_BLOCK, 1),
13372 shared_mem_bytes: 0,
13373 };
13374 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13375 let __s_b = self.gpu.stream();
13376 let mut b = __s_b.launch_builder(&f);
13377 b.arg(bytes)
13378 .arg(aq)
13379 .arg(ad)
13380 .arg(&mut y)
13381 .arg(&inf)
13382 .arg(&outf)
13383 .arg(&mi)
13384 .arg(&rb);
13385 unsafe {
13386 b.launch(cfg)?;
13387 }
13388 if scale != 1.0 {
13389 self.scale_inplace(&mut y, scale, m * out_f)?;
13390 }
13391 return Ok(y);
13392 }
13393 let (name, rows_per_block): (std::borrow::Cow<'static, str>, u32) = match variant {
13394 "base" => (base_name.into(), ROWS_PER_BLOCK),
13395 "pf" => (format!("{base_name}_pf").into(), ROWS_PER_BLOCK),
13396 "ca" => (format!("{base_name}_ca").into(), ROWS_PER_BLOCK),
13397 "rp" => (format!("{base_name}_rp").into(), ROWS_PER_BLOCK),
13398 "rpca" => (format!("{base_name}_rpca").into(), ROWS_PER_BLOCK), "rpks" => (format!("{base_name}_rpks").into(), ROWS_PER_BLOCK),
13402 "rpksc" => (format!("{base_name}_rpksc").into(), ROWS_PER_BLOCK),
13403 "rpms" => (format!("{base_name}_rpms").into(), ROWS_PER_BLOCK),
13404 "rpmsc" => (format!("{base_name}_rpmsc").into(), ROWS_PER_BLOCK),
13405 "r2ms_rp" => (format!("{base_name}_r2ms_rp").into(), ROWS_PER_BLOCK),
13406 "r2sm_rp" => (format!("{base_name}_r2sm_rp").into(), ROWS_PER_BLOCK * 2),
13407 "r2la_rp" => (format!("{base_name}_r2la_rp").into(), ROWS_PER_BLOCK * 2),
13408 v => (format!("{base_name}_{v}").into(), ROWS_PER_BLOCK * 2), };
13410 debug_assert!(
13411 !rp || name.contains("_rp"),
13412 "rp weight dispatched to a GGUF-layout kernel"
13413 );
13414 let f = self.func(&name);
13415 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13416 let smem = if name.contains("_r2sm_rp") {
13418 (mcols * 32 * 9 * 4 + mcols * 32 * 4) as u32
13419 } else {
13420 0
13421 };
13422 let cfg = LaunchConfig {
13423 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
13424 block_dim: (32, ROWS_PER_BLOCK, 1),
13425 shared_mem_bytes: smem,
13426 };
13427 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13428 let __s_b = self.gpu.stream();
13429 let mut b = __s_b.launch_builder(&f);
13430 b.arg(bytes)
13431 .arg(aq)
13432 .arg(ad)
13433 .arg(&mut y)
13434 .arg(&inf)
13435 .arg(&outf)
13436 .arg(&mi)
13437 .arg(&rb);
13438 unsafe {
13439 b.launch(cfg)?;
13440 }
13441 if scale != 1.0 {
13442 self.scale_inplace(&mut y, scale, m * out_f)?;
13443 }
13444 Ok(y)
13445 }
13446
13447 pub fn qmatvec_batched_raw(
13451 &self,
13452 bytes: &CudaSlice<u8>,
13453 x: &CudaSlice<f32>,
13454 m: usize,
13455 in_f: usize,
13456 out_f: usize,
13457 qtype: i32,
13458 row_bytes: usize,
13459 mcols: usize,
13460 rp: bool,
13461 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13462 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13463 self.qmatvec_mmvq_batched(
13464 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, 1.0, rp,
13465 )
13466 }
13467
13468 pub fn qmatvec_nvfp4_batched_raw(
13470 &self,
13471 bytes: &CudaSlice<u8>,
13472 x: &CudaSlice<f32>,
13473 m: usize,
13474 in_f: usize,
13475 out_f: usize,
13476 row_bytes: usize,
13477 mcols: usize,
13478 rp: bool,
13479 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13480 self.qmatvec_batched_raw(bytes, x, m, in_f, out_f, QT_NVFP4, row_bytes, mcols, rp)
13481 }
13482
13483 fn try_fp4_gemm(
13487 &self,
13488 w: &crate::model::GpuTensor,
13489 x: &CudaSlice<f32>,
13490 m: usize,
13491 in_f: usize,
13492 out_f: usize,
13493 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13494 use crate::model::GpuTensor;
13495 if cfg!(memra_portable_cuda) {
13496 return Ok(None);
13497 }
13498 if std::env::var("MEMRA_FP4").is_err() {
13499 return Ok(None);
13500 }
13501 #[cfg(memra_cutlass)]
13510 if m >= 128 && std::env::var("MEMRA_FP4_CUTLASS").is_ok() {
13511 if let GpuTensor::Quant {
13512 bytes,
13513 qtype,
13514 scale,
13515 row_bytes,
13516 cutlass,
13517 ..
13518 } = w
13519 {
13520 if *qtype == QT_NVFP4 && in_f % 64 == 0 {
13521 if let Some(cw) = cutlass {
13522 let y = self.cutlass_fp4_gemm(
13524 &cw.b_packed,
13525 &cw.sfb_swizzled,
13526 x,
13527 *scale,
13528 m,
13529 out_f,
13530 in_f,
13531 )?;
13532 return Ok(Some(y));
13533 } else if std::env::var("MEMRA_FP4_CUTLASS_OTF").is_ok() {
13534 let (b_packed, sfb_sw) =
13539 self.build_cutlass_weight(bytes, out_f, in_f, *row_bytes)?;
13540 let y =
13541 self.cutlass_fp4_gemm(&b_packed, &sfb_sw, x, *scale, m, out_f, in_f)?;
13542 return Ok(Some(y));
13543 }
13544 }
13545 }
13546 }
13547 if let GpuTensor::Quant {
13548 bytes,
13549 qtype,
13550 row_bytes,
13551 scale,
13552 rp,
13553 ..
13554 } = w
13555 {
13556 if *qtype == QT_NVFP4 && in_f % 64 == 0 && !*rp {
13559 let y =
13560 self.qmatvec_gemm_nvfp4_fp4(bytes, x, m, in_f, out_f, *row_bytes, *scale)?;
13561 return Ok(Some(y));
13562 }
13563 }
13564 Ok(None)
13565 }
13566
13567 pub fn rms_norm_f16out(
13571 &self,
13572 x: &CudaSlice<f32>,
13573 w: &CudaSlice<f32>,
13574 dst: &mut CudaSlice<f32>,
13575 dst16: &mut CudaSlice<u8>,
13576 ncols: usize,
13577 nrows: usize,
13578 eps: f32,
13579 ) -> Result<(), Box<dyn std::error::Error>> {
13580 let f = self.func("rms_norm_f16out_f32");
13581 let cfg = LaunchConfig {
13582 grid_dim: (nrows as u32, 1, 1),
13583 block_dim: (rms_block(), 1, 1),
13584 shared_mem_bytes: 0,
13585 };
13586 let (nc, e) = (ncols as i32, eps);
13587 let __s_b = self.gpu.stream();
13588 let mut b = __s_b.launch_builder(&f);
13589 b.arg(x).arg(w).arg(dst).arg(dst16).arg(&nc).arg(&e);
13590 unsafe {
13591 b.launch(cfg)?;
13592 }
13593 Ok(())
13594 }
13595
13596 #[allow(clippy::too_many_arguments)]
13599 pub fn add_rms_norm_f16out(
13600 &self,
13601 a: &CudaSlice<f32>,
13602 b: &CudaSlice<f32>,
13603 w: &CudaSlice<f32>,
13604 res: &mut CudaSlice<f32>,
13605 dst: &mut CudaSlice<f32>,
13606 dst16: &mut CudaSlice<u8>,
13607 ncols: usize,
13608 nrows: usize,
13609 eps: f32,
13610 ) -> Result<(), Box<dyn std::error::Error>> {
13611 let f = self.func("add_rms_norm_f16out_f32");
13612 let cfg = LaunchConfig {
13613 grid_dim: (nrows as u32, 1, 1),
13614 block_dim: (rms_block(), 1, 1),
13615 shared_mem_bytes: 0,
13616 };
13617 let (nc, e) = (ncols as i32, eps);
13618 let __s_lb = self.gpu.stream();
13619 let mut lb = __s_lb.launch_builder(&f);
13620 lb.arg(a)
13621 .arg(b)
13622 .arg(w)
13623 .arg(res)
13624 .arg(dst)
13625 .arg(dst16)
13626 .arg(&nc)
13627 .arg(&e);
13628 unsafe {
13629 lb.launch(cfg)?;
13630 }
13631 Ok(())
13632 }
13633
13634 pub fn matmul_group_xh(
13637 &self,
13638 ws: &[&crate::model::GpuTensor],
13639 x: &CudaSlice<f32>,
13640 xh: &CudaSlice<u8>,
13641 m: usize,
13642 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13643 let mut out = Vec::with_capacity(ws.len());
13644 let in_f = ws[0].in_features();
13645 for w in ws {
13646 if w.in_features() == in_f && m >= 16 && !self.verify_exact_on() {
13647 if let Some(y) = self.try_f16_gemm_pre(w, xh, m)? {
13648 out.push(y);
13649 continue;
13650 }
13651 }
13652 out.push(self.matmul(w, x, m)?);
13653 }
13654 Ok(out)
13655 }
13656
13657 pub fn gdn_pad_mask(
13660 &self,
13661 beta: &mut CudaSlice<f32>,
13662 g_log: &mut CudaSlice<f32>,
13663 len_d: &CudaSlice<i32>,
13664 h: usize,
13665 t: usize,
13666 ) -> Result<(), Box<dyn std::error::Error>> {
13667 let f = self.func("gdn_pad_mask_f32");
13668 let cfg = LaunchConfig::for_num_elems((t * h) as u32);
13669 let (hi, ti) = (h as i32, t as i32);
13670 let __s_b = self.gpu.stream();
13671 let mut b = __s_b.launch_builder(&f);
13672 b.arg(beta).arg(g_log).arg(len_d).arg(&hi).arg(&ti);
13673 unsafe {
13674 b.launch(cfg)?;
13675 }
13676 Ok(())
13677 }
13678
13679 pub fn row_gather_dev(
13682 &self,
13683 src: &CudaSlice<f32>,
13684 dst: &mut CudaSlice<f32>,
13685 len_d: &CudaSlice<i32>,
13686 ncols: usize,
13687 ) -> Result<(), Box<dyn std::error::Error>> {
13688 let f = self.func("row_gather_dev_f32");
13689 let cfg = LaunchConfig::for_num_elems(ncols as u32);
13690 let nc = ncols as i32;
13691 let __s_b = self.gpu.stream();
13692 let mut b = __s_b.launch_builder(&f);
13693 b.arg(src).arg(dst).arg(len_d).arg(&nc);
13694 unsafe {
13695 b.launch(cfg)?;
13696 }
13697 Ok(())
13698 }
13699
13700 pub fn matmul_group(
13707 &self,
13708 ws: &[&crate::model::GpuTensor],
13709 x: &CudaSlice<f32>,
13710 m: usize,
13711 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13712 use crate::model::GpuTensor;
13713 let mut out = Vec::with_capacity(ws.len());
13714 let any_mirror = ws
13715 .iter()
13716 .any(|w| matches!(w, GpuTensor::Quant { f16: Some(_), .. }));
13717 if m >= 16 && any_mirror && !self.verify_exact_on() {
13718 let in_f = ws[0].in_features();
13719 let xh = self.f16_act(x, m * in_f, in_f)?;
13720 for w in ws {
13721 if w.in_features() == in_f {
13722 if let Some(y) = self.try_f16_gemm_pre(w, &xh, m)? {
13723 out.push(y);
13724 continue;
13725 }
13726 }
13727 out.push(self.matmul(w, x, m)?);
13728 }
13729 return Ok(out);
13730 }
13731 for w in ws {
13732 out.push(self.matmul(w, x, m)?);
13733 }
13734 Ok(out)
13735 }
13736
13737 pub fn matmul_group_multi(
13744 &self,
13745 ws: &[&crate::model::GpuTensor],
13746 xs: &[&CudaSlice<f32>],
13747 ms: &[usize],
13748 ) -> Result<Vec<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
13749 assert_eq!(xs.len(), ms.len());
13750 let in_f = ws[0].in_features();
13751 let total: usize = ms.iter().sum();
13752 let mut xcat = self.uninit(total * in_f)?;
13753 let mut off = 0usize;
13754 for (x, &m) in xs.iter().zip(ms) {
13755 self.copy_into(&mut xcat, off * in_f, x, m * in_f)?;
13756 off += m;
13757 }
13758 let ys = self.matmul_group(ws, &xcat, total)?;
13759 let mut out: Vec<Vec<CudaSlice<f32>>> = (0..xs.len()).map(|_| Vec::new()).collect();
13760 for (w, y) in ws.iter().zip(ys) {
13761 let out_f = w.out_features();
13762 let mut off = 0usize;
13763 for (s, &m) in ms.iter().enumerate() {
13764 let mut ys_s = self.uninit(m * out_f)?;
13765 let src = y.slice(off * out_f..(off + m) * out_f);
13766 self.gpu.stream().memcpy_dtod(&src, &mut ys_s)?;
13767 out[s].push(ys_s);
13768 off += m;
13769 }
13770 }
13771 Ok(out)
13772 }
13773
13774 pub fn gemm_supports(&self, w: &crate::model::GpuTensor) -> bool {
13784 use crate::model::GpuTensor;
13785 if !legacy_quant_gemm_allowed(
13786 cfg!(memra_portable_cuda),
13787 cfg!(memra_hopper_mma),
13788 std::env::var_os("MEMRA_NO_GEMM").is_some(),
13789 ) {
13790 return false;
13791 }
13792 match w {
13793 GpuTensor::Quant { qtype, .. } => {
13794 matches!(*qtype, QT_Q8_0 | QT_Q4_K | QT_Q6_K | QT_Q5_K | QT_Q4_0)
13795 || (*qtype == QT_NVFP4 && w.in_features() % 64 == 0)
13796 }
13797 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
13798 }
13799 }
13800
13801 pub fn qmatvec_gemm(
13808 &self,
13809 w: &crate::model::GpuTensor,
13810 aq: &CudaSlice<i8>,
13811 ad: &CudaSlice<f32>,
13812 m: usize,
13813 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13814 use crate::model::GpuTensor;
13815 let in_f = w.in_features();
13816 let out_f = w.out_features();
13817 let (bytes, qtype, row_bytes, scale, rp) = match w {
13818 GpuTensor::Quant {
13819 bytes,
13820 qtype,
13821 row_bytes,
13822 scale,
13823 rp,
13824 ..
13825 } => (bytes, *qtype, *row_bytes, *scale, *rp),
13826 _ => unreachable!("gemm_supports guaranteed Quant"),
13827 };
13828 if cfg!(memra_hopper_mma) && qtype == QT_Q8_0 && out_f % 64 == 0 && wgmma_gemm_enabled() {
13834 if let GpuTensor::Quant { rp4: Some(m4), .. } = w {
13835 let mut y = self.qmatvec_gemm_q8_0_wgmma_raw(m4, aq, ad, m, in_f, out_f)?;
13836 if scale != 1.0 {
13837 self.scale_inplace(&mut y, scale, m * out_f)?;
13838 }
13839 return Ok(y);
13840 }
13841 }
13842 let name = match qtype {
13843 QT_Q8_0 => "qmatvec_gemm_q8_0",
13844 QT_Q4_K => "qmatvec_gemm_q4_K",
13845 QT_Q4_0 => {
13846 if rp {
13847 "qmatvec_gemm_q4_0_rp"
13848 } else {
13849 "qmatvec_gemm_q4_0"
13850 }
13851 }
13852 QT_Q5_K => "qmatvec_gemm_q5_K",
13853 QT_Q6_K => "qmatvec_gemm_q6_K",
13854 QT_NVFP4 => {
13855 if rp {
13856 "qmatvec_gemm_nvfp4_rp"
13857 } else {
13858 "qmatvec_gemm_nvfp4"
13859 }
13860 }
13861 _ => unreachable!(),
13862 };
13863 let f = self.func(name);
13864 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);
13869 let k1_tile = if is_k1 {
13871 k1_launch_override().unwrap_or((128, 128, 8))
13872 } else {
13873 (128, 128, 8)
13874 };
13875 let (bm, bn): (u32, u32) = if is_k1 {
13876 (k1_tile.0, k1_tile.1)
13877 } else {
13878 (64, 256)
13879 };
13880 let warps: u32 = if is_k1 {
13881 k1_tile.2
13882 } else {
13883 match qtype {
13884 QT_NVFP4 => 8,
13885 _ => 4,
13886 }
13887 };
13888 let cfg = LaunchConfig {
13889 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
13890 block_dim: (32, warps, 1),
13891 shared_mem_bytes: 0,
13892 };
13893 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13894 let __s_b = self.gpu.stream();
13895 let mut b = __s_b.launch_builder(&f);
13896 b.arg(bytes)
13897 .arg(aq)
13898 .arg(ad)
13899 .arg(&mut y)
13900 .arg(&inf)
13901 .arg(&outf)
13902 .arg(&mi)
13903 .arg(&rb);
13904 unsafe {
13905 b.launch(cfg)?;
13906 }
13907 if scale != 1.0 {
13908 self.scale_inplace(&mut y, scale, m * out_f)?;
13909 }
13910 Ok(y)
13911 }
13912
13913 pub fn qmatvec_gemm_raw(
13918 &self,
13919 bytes: &CudaSlice<u8>,
13920 x: &CudaSlice<f32>,
13921 m: usize,
13922 in_f: usize,
13923 out_f: usize,
13924 qtype: i32,
13925 row_bytes: usize,
13926 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13927 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13928 let name = match qtype {
13929 QT_Q8_0 => "qmatvec_gemm_q8_0",
13930 QT_Q4_K => "qmatvec_gemm_q4_K",
13931 QT_Q4_0 => "qmatvec_gemm_q4_0",
13932 QT_Q5_K => "qmatvec_gemm_q5_K",
13933 QT_Q6_K => "qmatvec_gemm_q6_K",
13934 QT_NVFP4 => "qmatvec_gemm_nvfp4",
13935 QT_NVFP4_RP => "qmatvec_gemm_nvfp4_rp",
13936 _ => panic!("qmatvec_gemm_raw: qtype {qtype} has no GEMM kernel"),
13937 };
13938 let f = self.func(name);
13939 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);
13943 let k1_tile = if is_k1 {
13945 k1_launch_override().unwrap_or((128, 128, 8))
13946 } else {
13947 (128, 128, 8)
13948 };
13949 let (bm, bn): (u32, u32) = if is_k1 {
13950 (k1_tile.0, k1_tile.1)
13951 } else {
13952 (64, 256)
13953 };
13954 let warps: u32 = if is_k1 {
13955 k1_tile.2
13956 } else {
13957 match qtype {
13958 QT_NVFP4 | QT_NVFP4_RP => 8,
13959 _ => 4,
13960 }
13961 };
13962 let cfg = LaunchConfig {
13963 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
13964 block_dim: (32, warps, 1),
13965 shared_mem_bytes: 0,
13966 };
13967 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13968 let __s_b = self.gpu.stream();
13969 let mut b = __s_b.launch_builder(&f);
13970 b.arg(bytes)
13971 .arg(&aq)
13972 .arg(&ad)
13973 .arg(&mut y)
13974 .arg(&inf)
13975 .arg(&outf)
13976 .arg(&mi)
13977 .arg(&rb);
13978 unsafe {
13979 b.launch(cfg)?;
13980 }
13981 Ok(y)
13982 }
13983
13984 pub fn qmatvec_gemm_q8_0_wgmma_raw(
13991 &self,
13992 rp4: &CudaSlice<u8>,
13993 aq: &CudaSlice<i8>,
13994 ad: &CudaSlice<f32>,
13995 m: usize,
13996 in_f: usize,
13997 out_f: usize,
13998 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13999 assert!(
14000 out_f % 64 == 0 && in_f % 32 == 0,
14001 "wgmma GEMM needs out_f%64==0, in_f%32==0"
14002 );
14003 let f = self.func("qmatvec_gemm_q8_0_wgmma");
14004 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
14006 grid_dim: ((out_f / 64) as u32, (m as u32).div_ceil(64), 1),
14007 block_dim: (128, 1, 1),
14008 shared_mem_bytes: 0,
14009 };
14010 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
14011 let __s_b = self.gpu.stream();
14012 let mut b = __s_b.launch_builder(&f);
14013 b.arg(rp4)
14014 .arg(aq)
14015 .arg(ad)
14016 .arg(&mut y)
14017 .arg(&inf)
14018 .arg(&outf)
14019 .arg(&mi);
14020 unsafe {
14021 b.launch(cfg)?;
14022 }
14023 Ok(y)
14024 }
14025
14026 pub fn scale_inplace(
14028 &self,
14029 y: &mut CudaSlice<f32>,
14030 s: f32,
14031 n: usize,
14032 ) -> Result<(), Box<dyn std::error::Error>> {
14033 let f = self.func("scale_f32");
14034 let cfg = LaunchConfig::for_num_elems(n as u32);
14035 let (sf, ni) = (s, n as i32);
14036 let __s_b = self.gpu.stream();
14037 let mut b = __s_b.launch_builder(&f);
14038 b.arg(y).arg(&sf).arg(&ni);
14039 unsafe {
14040 b.launch(cfg)?;
14041 }
14042 Ok(())
14043 }
14044
14045 pub fn bf16_to_f32(
14050 &self,
14051 data: &cudarc::driver::CudaView<'_, u8>,
14052 n: usize,
14053 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14054 let mut out = self.alloc_uninit::<f32>(n)?;
14055 let f = self.func("bf16_to_f32");
14056 let cfg = LaunchConfig::for_num_elems(n as u32);
14057 let ni = n as i32;
14058 let __s_b = self.gpu.stream();
14059 let mut b = __s_b.launch_builder(&f);
14060 b.arg(data).arg(&mut out).arg(&ni);
14061 unsafe {
14062 b.launch(cfg)?;
14063 }
14064 Ok(out)
14065 }
14066
14067 fn linear_bf16_chunked(
14074 &self,
14075 x: &CudaSlice<f32>,
14076 data: &CudaSlice<u8>,
14077 m: usize,
14078 in_f: usize,
14079 out_f: usize,
14080 exact: bool,
14081 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14082 const CHUNK_BYTES: usize = 256 << 20;
14083 let chunk_rows = (CHUNK_BYTES / (in_f * 4)).max(1).min(out_f);
14084 if chunk_rows >= out_f {
14085 let wf32 = self.bf16_to_f32(&data.slice(0..in_f * out_f * 2), in_f * out_f)?;
14086 return if exact {
14087 self.linear_decode_exact(x, &wf32, m, in_f, out_f)
14088 } else {
14089 self.linear(x, &wf32, m, in_f, out_f)
14090 };
14091 }
14092 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
14093 let mut r0 = 0usize;
14094 while r0 < out_f {
14095 let rows = chunk_rows.min(out_f - r0);
14096 let wslice = data.slice(r0 * in_f * 2..(r0 + rows) * in_f * 2);
14097 let wf32 = self.bf16_to_f32(&wslice, in_f * rows)?;
14098 let yc = if exact {
14099 self.linear_decode_exact(x, &wf32, m, in_f, rows)?
14100 } else {
14101 self.linear(x, &wf32, m, in_f, rows)?
14102 };
14103 for mi in 0..m {
14105 let src = yc.slice(mi * rows..(mi + 1) * rows);
14106 let mut dst = y.slice_mut(mi * out_f + r0..mi * out_f + r0 + rows);
14107 self.gpu.stream().memcpy_dtod(&src, &mut dst)?;
14108 }
14109 r0 += rows;
14110 }
14111 Ok(y)
14112 }
14113
14114 pub fn linear_decode_exact(
14121 &self,
14122 x: &CudaSlice<f32>,
14123 w: &CudaSlice<f32>,
14124 m_tokens: usize,
14125 in_f: usize,
14126 out_f: usize,
14127 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14128 if m_tokens == 1 {
14129 return self.linear(x, w, 1, in_f, out_f);
14130 }
14131 let xv = self.view(x, m_tokens * in_f);
14132 let mut y = self.alloc_uninit::<f32>(m_tokens * out_f)?;
14133 for t in 0..m_tokens {
14134 let row = xv.slice(t * in_f..(t + 1) * in_f);
14135 let mut xr = self.alloc_uninit::<f32>(in_f)?;
14136 self.copy_view_into(&mut xr, 0, &row, in_f)?;
14137 let yr = self.linear(&xr, w, 1, in_f, out_f)?;
14138 self.copy_into(&mut y, t * out_f, &yr, out_f)?;
14139 }
14140 Ok(y)
14141 }
14142
14143 pub fn linear(
14144 &self,
14145 x: &CudaSlice<f32>,
14146 w: &CudaSlice<f32>,
14147 m_tokens: usize,
14148 in_f: usize,
14149 out_f: usize,
14150 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14151 use cudarc::cublaslt::{Matmul, MatmulConfig};
14152 let mut c = self.alloc_uninit::<f32>(m_tokens * out_f)?; let cfg = MatmulConfig {
14154 transa: true,
14155 transb: false,
14156 transc: false,
14157 m: out_f as u64,
14158 n: m_tokens as u64,
14159 k: in_f as u64,
14160 alpha: 1.0,
14161 lda: in_f as i64,
14162 ldb: in_f as i64,
14163 beta: 0.0,
14164 ldc: out_f as i64,
14165 stride_a: None,
14166 stride_b: None,
14167 stride_c: None,
14168 stride_bias: None,
14169 batch_size: None,
14170 };
14171 unsafe {
14172 self.gpu.blas.matmul(cfg, w, x, &mut c, None, None)?;
14173 }
14174 Ok(c)
14175 }
14176
14177 pub fn sdpa_naive(
14179 &self,
14180 q: &CudaSlice<f32>,
14181 k: &CudaSlice<f32>,
14182 v: &CudaSlice<f32>,
14183 o: &mut CudaSlice<f32>,
14184 head_dim: usize,
14185 n_head: usize,
14186 n_head_kv: usize,
14187 t: usize,
14188 t_kv: usize,
14189 scale: f32,
14190 causal: bool,
14191 ) -> Result<(), Box<dyn std::error::Error>> {
14192 let f = self.func("sdpa_naive_f32");
14193 let cfg = LaunchConfig {
14194 grid_dim: (n_head as u32, t as u32, 1),
14195 block_dim: (128, 1, 1),
14196 shared_mem_bytes: (t_kv * 4) as u32,
14197 };
14198 let (hd, nh, nhkv, ti, tkvi, cz) = (
14199 head_dim as i32,
14200 n_head as i32,
14201 n_head_kv as i32,
14202 t as i32,
14203 t_kv as i32,
14204 causal as i32,
14205 );
14206 let __s_b = self.gpu.stream();
14207 let mut b = __s_b.launch_builder(&f);
14208 b.arg(q)
14209 .arg(k)
14210 .arg(v)
14211 .arg(o)
14212 .arg(&hd)
14213 .arg(&nh)
14214 .arg(&nhkv)
14215 .arg(&ti)
14216 .arg(&tkvi)
14217 .arg(&scale)
14218 .arg(&cz);
14219 unsafe {
14220 b.launch(cfg)?;
14221 }
14222 Ok(())
14223 }
14224
14225 #[allow(clippy::too_many_arguments)]
14227 pub fn sdpa_naive_w(
14228 &self,
14229 q: &CudaSlice<f32>,
14230 k: &CudaSlice<f32>,
14231 v: &CudaSlice<f32>,
14232 o: &mut CudaSlice<f32>,
14233 head_dim: usize,
14234 n_head: usize,
14235 n_head_kv: usize,
14236 t: usize,
14237 t_kv: usize,
14238 scale: f32,
14239 causal: bool,
14240 window: usize,
14241 ) -> Result<(), Box<dyn std::error::Error>> {
14242 let f = self.func("sdpa_naive_w_f32");
14243 let cfg = LaunchConfig {
14244 grid_dim: (n_head as u32, t as u32, 1),
14245 block_dim: (128, 1, 1),
14246 shared_mem_bytes: (t_kv * 4) as u32,
14247 };
14248 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
14249 head_dim as i32,
14250 n_head as i32,
14251 n_head_kv as i32,
14252 t as i32,
14253 t_kv as i32,
14254 causal as i32,
14255 window as i32,
14256 );
14257 let __s_b = self.gpu.stream();
14258 let mut b = __s_b.launch_builder(&f);
14259 b.arg(q)
14260 .arg(k)
14261 .arg(v)
14262 .arg(o)
14263 .arg(&hd)
14264 .arg(&nh)
14265 .arg(&nhkv)
14266 .arg(&ti)
14267 .arg(&tkvi)
14268 .arg(&scale)
14269 .arg(&cz)
14270 .arg(&wi);
14271 unsafe {
14272 b.launch(cfg)?;
14273 }
14274 Ok(())
14275 }
14276
14277 pub fn sdpa_naive_view(
14279 &self,
14280 q: &CudaSlice<f32>,
14281 k: &cudarc::driver::CudaView<f32>,
14282 v: &cudarc::driver::CudaView<f32>,
14283 o: &mut CudaSlice<f32>,
14284 head_dim: usize,
14285 n_head: usize,
14286 n_head_kv: usize,
14287 t: usize,
14288 t_kv: usize,
14289 scale: f32,
14290 causal: bool,
14291 ) -> Result<(), Box<dyn std::error::Error>> {
14292 let f = self.func("sdpa_naive_f32");
14293 let cfg = LaunchConfig {
14294 grid_dim: (n_head as u32, t as u32, 1),
14295 block_dim: (128, 1, 1),
14296 shared_mem_bytes: (t_kv * 4) as u32,
14297 };
14298 let (hd, nh, nhkv, ti, tkvi, cz) = (
14299 head_dim as i32,
14300 n_head as i32,
14301 n_head_kv as i32,
14302 t as i32,
14303 t_kv as i32,
14304 causal as i32,
14305 );
14306 let __s_b = self.gpu.stream();
14307 let mut b = __s_b.launch_builder(&f);
14308 b.arg(q)
14309 .arg(k)
14310 .arg(v)
14311 .arg(o)
14312 .arg(&hd)
14313 .arg(&nh)
14314 .arg(&nhkv)
14315 .arg(&ti)
14316 .arg(&tkvi)
14317 .arg(&scale)
14318 .arg(&cz);
14319 unsafe {
14320 b.launch(cfg)?;
14321 }
14322 Ok(())
14323 }
14324
14325 #[allow(clippy::too_many_arguments)]
14333 pub fn fa_dequant_kv_view_f32(
14334 &self,
14335 k: &cudarc::driver::CudaView<u8>,
14336 v: &cudarc::driver::CudaView<u8>,
14337 kf: &mut CudaSlice<f32>,
14338 vf: &mut CudaSlice<f32>,
14339 kv_dim_k: usize,
14340 kv_dim_v: usize,
14341 t_kv: usize,
14342 k_tok_bytes: usize,
14343 v_tok_bytes: usize,
14344 g: bool,
14345 ) -> Result<(), Box<dyn std::error::Error>> {
14346 let f = if g {
14347 self.func_g("fa_dequant_kv_ws_f32")
14348 } else {
14349 self.func("fa_dequant_kv_ws_f32")
14350 };
14351 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
14352 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
14353 let cfg = LaunchConfig {
14354 grid_dim: (nblk.max(1), 1, 1),
14355 block_dim: (256, 1, 1),
14356 shared_mem_bytes: 0,
14357 };
14358 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
14359 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
14360 let __s_b = self.gpu.stream();
14361 let mut b = __s_b.launch_builder(&f);
14362 b.arg(k)
14363 .arg(v)
14364 .arg(&mut *kf)
14365 .arg(&mut *vf)
14366 .arg(&kdk)
14367 .arg(&kdv)
14368 .arg(&tkvi)
14369 .arg(&ktb)
14370 .arg(&vtb);
14371 unsafe {
14372 b.launch(cfg)?;
14373 }
14374 Ok(())
14375 }
14376
14377 #[allow(clippy::too_many_arguments)]
14378 pub fn sdpa_naive_quantized_view(
14379 &self,
14380 q: &CudaSlice<f32>,
14381 k: &cudarc::driver::CudaView<u8>,
14382 v: &cudarc::driver::CudaView<u8>,
14383 o: &mut CudaSlice<f32>,
14384 head_dim: usize,
14385 n_head: usize,
14386 n_head_kv: usize,
14387 t: usize,
14388 t_kv: usize,
14389 scale: f32,
14390 causal: bool,
14391 k_tok_bytes: usize,
14392 v_tok_bytes: usize,
14393 ) -> Result<(), Box<dyn std::error::Error>> {
14394 let kv_dim = n_head_kv * head_dim;
14395 let mut kf = self.uninit(t_kv * kv_dim)?;
14396 let mut vf = self.uninit(t_kv * kv_dim)?;
14397 let f = self.func("fa_dequant_kv_ws_f32");
14398 let total = (2 * t_kv * kv_dim) as u64;
14399 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
14400 let cfg = LaunchConfig {
14401 grid_dim: (nblk.max(1), 1, 1),
14402 block_dim: (256, 1, 1),
14403 shared_mem_bytes: 0,
14404 };
14405 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
14406 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
14407 let __s_b = self.gpu.stream();
14408 let mut b = __s_b.launch_builder(&f);
14409 b.arg(k)
14410 .arg(v)
14411 .arg(&mut kf)
14412 .arg(&mut vf)
14413 .arg(&kv_dim_i)
14414 .arg(&kv_dim_i)
14415 .arg(&t_kv_i)
14416 .arg(&k_tok_bytes_i)
14417 .arg(&v_tok_bytes_i);
14418 unsafe { b.launch(cfg)? };
14419 self.sdpa_naive(
14420 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
14421 )
14422 }
14423
14424 #[allow(clippy::too_many_arguments)]
14436 pub fn sdpa_naive_w_quantized_view(
14437 &self,
14438 q: &CudaSlice<f32>,
14439 k: &cudarc::driver::CudaView<u8>,
14440 v: &cudarc::driver::CudaView<u8>,
14441 o: &mut CudaSlice<f32>,
14442 head_dim: usize,
14443 n_head: usize,
14444 n_head_kv: usize,
14445 t: usize,
14446 t_kv: usize,
14447 scale: f32,
14448 causal: bool,
14449 window: usize,
14450 k_tok_bytes: usize,
14451 v_tok_bytes: usize,
14452 ) -> Result<(), Box<dyn std::error::Error>> {
14453 let kv_dim = n_head_kv * head_dim;
14454 let mut kf = self.uninit(t_kv * kv_dim)?;
14455 let mut vf = self.uninit(t_kv * kv_dim)?;
14456 let f = self.func("fa_dequant_kv_ws_f32");
14457 let total = (2 * t_kv * kv_dim) as u64;
14458 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
14459 let cfg = LaunchConfig {
14460 grid_dim: (nblk.max(1), 1, 1),
14461 block_dim: (256, 1, 1),
14462 shared_mem_bytes: 0,
14463 };
14464 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
14465 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
14466 let __s_b = self.gpu.stream();
14467 let mut b = __s_b.launch_builder(&f);
14468 b.arg(k)
14469 .arg(v)
14470 .arg(&mut kf)
14471 .arg(&mut vf)
14472 .arg(&kv_dim_i)
14473 .arg(&kv_dim_i)
14474 .arg(&t_kv_i)
14475 .arg(&k_tok_bytes_i)
14476 .arg(&v_tok_bytes_i);
14477 unsafe { b.launch(cfg)? };
14478 self.sdpa_naive_w(
14479 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
14480 )
14481 }
14482
14483 pub fn fa_prefill(
14487 &self,
14488 q: &CudaSlice<f32>,
14489 k: &CudaSlice<f32>,
14490 v: &CudaSlice<f32>,
14491 o: &mut CudaSlice<f32>,
14492 head_dim: usize,
14493 n_head: usize,
14494 n_head_kv: usize,
14495 t: usize,
14496 t_kv: usize,
14497 scale: f32,
14498 causal: bool,
14499 ) -> Result<(), Box<dyn std::error::Error>> {
14500 if portable_mma_gated() {
14501 return self.sdpa_naive(
14502 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
14503 );
14504 }
14505 let fa3_on = head_dim == 256
14513 && causal
14514 && t == t_kv
14515 && match std::env::var("MEMRA_FA3").as_deref() {
14516 Ok("0") => false,
14517 Ok("1") => true,
14518 _ => cfg!(memra_hopper_mma),
14519 };
14520 if fa3_on {
14521 let n = t * n_head * head_dim;
14522 let nkv = t * n_head_kv * head_dim;
14523 let mut q16 = self.alloc_u8_uninit(n * 2)?;
14524 let mut k16 = self.alloc_u8_uninit(nkv * 2)?;
14525 let mut v16 = self.alloc_u8_uninit(nkv * 2)?;
14526 self.f32_to_bf16_into(q, &mut q16, n)?;
14527 self.f32_to_bf16_into(k, &mut k16, nkv)?;
14528 self.f32_to_bf16_into(v, &mut v16, nkv)?;
14529 let rc = {
14530 use cudarc::driver::{DevicePtr, DevicePtrMut};
14531 let stream = self.gpu.stream();
14532 let (qp, _g1) = q16.device_ptr(&stream);
14533 let (kp, _g2) = k16.device_ptr(&stream);
14534 let (vp, _g3) = v16.device_ptr(&stream);
14535 let (op, _g4) = o.device_ptr_mut(&stream);
14536 unsafe {
14537 memra_fa3_prefill(
14538 qp as *const core::ffi::c_void,
14539 kp as *const core::ffi::c_void,
14540 vp as *const core::ffi::c_void,
14541 op as *mut f32,
14542 t as i32,
14543 n_head as i32,
14544 n_head_kv as i32,
14545 head_dim as i32,
14546 scale,
14547 stream.cu_stream() as *mut core::ffi::c_void,
14548 )
14549 }
14550 };
14551 if rc != 0 {
14552 return Err(format!("memra_fa3_prefill rc={rc}").into());
14553 }
14554 return Ok(());
14555 }
14556 static FA_P1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14561 let fa_p1 = *FA_P1.get_or_init(|| std::env::var("MEMRA_FA_P1").as_deref() == Ok("1"));
14562 if fa_p1 && head_dim == 256 && !std::env::var("MEMRA_FA_FLOOR").is_ok() {
14563 const BLOCK_Q: usize = 64;
14564 const BKX: usize = 32;
14565 let f = self.func("fa_prefill_bf16_p1");
14566 let shmem = (2 * (2 * BKX * head_dim + BLOCK_Q * BKX)
14567 + 4 * (BLOCK_Q * BKX + 2 * BLOCK_Q)) as u32;
14568 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14569 f.set_attribute(
14570 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14571 shmem as i32,
14572 )?;
14573 let cfg = LaunchConfig {
14574 grid_dim: (
14575 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
14576 n_head as u32,
14577 1,
14578 ),
14579 block_dim: (32, 4, 1),
14580 shared_mem_bytes: shmem,
14581 };
14582 let (hd, nh, nhkv, ti, tkvi, cz) = (
14583 head_dim as i32,
14584 n_head as i32,
14585 n_head_kv as i32,
14586 t as i32,
14587 t_kv as i32,
14588 causal as i32,
14589 );
14590 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
14591 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
14592 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
14593 let __s_b = self.gpu.stream();
14594 let mut b = __s_b.launch_builder(&f);
14595 b.arg(&qb)
14596 .arg(&kb)
14597 .arg(&vb)
14598 .arg(o)
14599 .arg(&hd)
14600 .arg(&nh)
14601 .arg(&nhkv)
14602 .arg(&ti)
14603 .arg(&tkvi)
14604 .arg(&scale)
14605 .arg(&cz);
14606 unsafe {
14607 b.launch(cfg)?;
14608 }
14609 return Ok(());
14610 }
14611 const BK: usize = 32;
14617 let w2 = std::env::var("MEMRA_FA_PP_W2").as_deref() == Ok("1");
14620 let (block_q, warps, w2_sfx): (usize, u32, &str) =
14621 if w2 { (32, 2, "_w2") } else { (64, 4, "") };
14622 let hd_sfx = fa_hd_suffix(head_dim)?;
14626 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
14627 let bf16kv = !floor && !w2 && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0");
14632 let (kb16, vb16) = if bf16kv {
14633 let n = t_kv * n_head_kv * head_dim;
14634 let mut kb = self.alloc_u8_uninit(n * 2)?;
14635 let mut vb = self.alloc_u8_uninit(n * 2)?;
14636 let fcv = self.func("f32_to_bf16_bulk");
14637 let ni = n as i64;
14638 let cfgc = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
14639 let __s_b = self.gpu.stream();
14640 let mut b = __s_b.launch_builder(&fcv);
14641 b.arg(k).arg(&mut kb).arg(&ni);
14642 unsafe {
14643 b.launch(cfgc)?;
14644 }
14645 let __s_b = self.gpu.stream();
14646 let mut b = __s_b.launch_builder(&fcv);
14647 b.arg(v).arg(&mut vb).arg(&ni);
14648 unsafe {
14649 b.launch(cfgc)?;
14650 }
14651 (Some(kb), Some(vb))
14652 } else {
14653 (None, None)
14654 };
14655 let f = self.func(&if bf16kv {
14656 format!("fa_prefill_bf16kv_pp{hd_sfx}")
14657 } else {
14658 format!(
14659 "fa_prefill_f32{}{}{hd_sfx}",
14660 if floor { "" } else { "_pp" },
14661 if floor { "" } else { w2_sfx }
14662 )
14663 });
14664 let kv_stages = if bf16kv { 2 } else { 1 };
14667 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
14668 + 4 * (block_q * BK + 2 * block_q)) as u32;
14669 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14670 f.set_attribute(
14671 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14672 shmem as i32,
14673 )?;
14674 let cfg = LaunchConfig {
14675 grid_dim: (
14676 (t as u32 + block_q as u32 - 1) / block_q as u32,
14677 n_head as u32,
14678 1,
14679 ),
14680 block_dim: (32, warps, 1),
14681 shared_mem_bytes: shmem,
14682 };
14683 let (hd, nh, nhkv, ti, tkvi, cz) = (
14684 head_dim as i32,
14685 n_head as i32,
14686 n_head_kv as i32,
14687 t as i32,
14688 t_kv as i32,
14689 causal as i32,
14690 );
14691 let __s_b = self.gpu.stream();
14692 let mut b = __s_b.launch_builder(&f);
14693 b.arg(q);
14694 match (&kb16, &vb16) {
14695 (Some(kb), Some(vb)) => {
14696 b.arg(kb).arg(vb);
14697 }
14698 _ => {
14699 b.arg(k).arg(v);
14700 }
14701 }
14702 b.arg(o)
14703 .arg(&hd)
14704 .arg(&nh)
14705 .arg(&nhkv)
14706 .arg(&ti)
14707 .arg(&tkvi)
14708 .arg(&scale)
14709 .arg(&cz);
14710 unsafe {
14711 b.launch(cfg)?;
14712 }
14713 Ok(())
14714 }
14715
14716 #[allow(clippy::too_many_arguments)]
14720 pub fn fa_prefill_w(
14721 &self,
14722 q: &CudaSlice<f32>,
14723 k: &CudaSlice<f32>,
14724 v: &CudaSlice<f32>,
14725 o: &mut CudaSlice<f32>,
14726 head_dim: usize,
14727 n_head: usize,
14728 n_head_kv: usize,
14729 t: usize,
14730 t_kv: usize,
14731 scale: f32,
14732 causal: bool,
14733 window: usize,
14734 ) -> Result<(), Box<dyn std::error::Error>> {
14735 if portable_mma_gated() {
14738 return self.sdpa_naive_w(
14739 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
14740 );
14741 }
14742 static FAW_F32: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14746 let faw_f32 =
14747 *FAW_F32.get_or_init(|| std::env::var("MEMRA_FAW_STAGE").as_deref() == Ok("f32"));
14748 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
14749 self.fa_prefill_w_arm(
14750 q,
14751 k,
14752 v,
14753 o,
14754 head_dim,
14755 n_head,
14756 n_head_kv,
14757 t,
14758 t_kv,
14759 scale,
14760 causal,
14761 window,
14762 floor || faw_f32,
14763 floor,
14764 )
14765 }
14766
14767 #[allow(clippy::too_many_arguments)]
14770 pub fn fa_prefill_w_pre(
14771 &self,
14772 qb: &CudaSlice<u8>,
14773 kb: &CudaSlice<u8>,
14774 vb: &CudaSlice<u8>,
14775 o: &mut CudaSlice<f32>,
14776 head_dim: usize,
14777 n_head: usize,
14778 n_head_kv: usize,
14779 t: usize,
14780 t_kv: usize,
14781 scale: f32,
14782 causal: bool,
14783 window: usize,
14784 v_f16: bool,
14785 ) -> Result<(), Box<dyn std::error::Error>> {
14786 const BLOCK_Q: usize = 64;
14787 const BK: usize = 32;
14788 debug_assert_eq!(head_dim, 256);
14789 let hp = fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
14790 debug_assert!(!v_f16 || hp, "f16 V emitted but the SWA hp arm is off");
14791 if hp {
14792 const BLOCK_QH: usize = 32;
14793 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
14796 let vh: &CudaSlice<u8> = if v_f16 {
14797 vb
14798 } else {
14799 let n = t_kv * n_head_kv * head_dim;
14800 if vguard.as_ref().map(|b| b.len() < n * 2).unwrap_or(true) {
14801 *vguard = Some(self.alloc_uninit::<u8>(n * 2)?);
14802 }
14803 self.bf16_to_f16_into(vb, n, vguard.as_mut().unwrap())?;
14804 vguard.as_ref().unwrap()
14805 };
14806 let f = self.func("fa_prefill_w_bf16_p1h2");
14807 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
14808 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14809 f.set_attribute(
14810 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14811 shmem as i32,
14812 )?;
14813 let cfg = LaunchConfig {
14814 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
14815 block_dim: (32, 4, 1),
14816 shared_mem_bytes: shmem,
14817 };
14818 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
14819 head_dim as i32,
14820 n_head as i32,
14821 n_head_kv as i32,
14822 t as i32,
14823 t_kv as i32,
14824 causal as i32,
14825 window as i32,
14826 );
14827 let __s_b = self.gpu.stream();
14828 let mut b = __s_b.launch_builder(&f);
14829 b.arg(qb)
14830 .arg(kb)
14831 .arg(vh)
14832 .arg(o)
14833 .arg(&hd)
14834 .arg(&nh)
14835 .arg(&nhkv)
14836 .arg(&ti)
14837 .arg(&tkvi)
14838 .arg(&scale)
14839 .arg(&cz)
14840 .arg(&wi);
14841 unsafe {
14842 b.launch(cfg)?;
14843 }
14844 return Ok(());
14845 }
14846 let f = self.func("fa_prefill_w_bf16_p1");
14847 let shmem =
14848 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
14849 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14850 f.set_attribute(
14851 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14852 shmem as i32,
14853 )?;
14854 let cfg = LaunchConfig {
14855 grid_dim: (
14856 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
14857 n_head as u32,
14858 1,
14859 ),
14860 block_dim: (32, 4, 1),
14861 shared_mem_bytes: shmem,
14862 };
14863 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
14864 head_dim as i32,
14865 n_head as i32,
14866 n_head_kv as i32,
14867 t as i32,
14868 t_kv as i32,
14869 causal as i32,
14870 window as i32,
14871 );
14872 let __s_b = self.gpu.stream();
14873 let mut b = __s_b.launch_builder(&f);
14874 b.arg(qb)
14875 .arg(kb)
14876 .arg(vb)
14877 .arg(o)
14878 .arg(&hd)
14879 .arg(&nh)
14880 .arg(&nhkv)
14881 .arg(&ti)
14882 .arg(&tkvi)
14883 .arg(&scale)
14884 .arg(&cz)
14885 .arg(&wi);
14886 unsafe {
14887 b.launch(cfg)?;
14888 }
14889 Ok(())
14890 }
14891
14892 #[allow(clippy::too_many_arguments)]
14894 pub fn fa_prefill_w_arm(
14895 &self,
14896 q: &CudaSlice<f32>,
14897 k: &CudaSlice<f32>,
14898 v: &CudaSlice<f32>,
14899 o: &mut CudaSlice<f32>,
14900 head_dim: usize,
14901 n_head: usize,
14902 n_head_kv: usize,
14903 t: usize,
14904 t_kv: usize,
14905 scale: f32,
14906 causal: bool,
14907 window: usize,
14908 f32_stage: bool,
14909 floor: bool,
14910 ) -> Result<(), Box<dyn std::error::Error>> {
14911 const BLOCK_Q: usize = 64;
14912 const BK: usize = 32;
14913 debug_assert_eq!(head_dim, 256, "fa_prefill_w is stamped hd256 only");
14914 static P1_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14918 let p1 = !floor
14919 && !f32_stage
14920 && *P1_ON.get_or_init(|| {
14921 std::env::var("MEMRA_FAW_P1")
14922 .map(|v| v != "0")
14923 .unwrap_or(true)
14924 });
14925 let hp =
14926 p1 && fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
14927 if hp {
14928 const BLOCK_QH: usize = 32;
14929 let f = self.func("fa_prefill_w_bf16_p1h2");
14930 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
14931 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14932 f.set_attribute(
14933 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14934 shmem as i32,
14935 )?;
14936 let cfg = LaunchConfig {
14937 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
14938 block_dim: (32, 4, 1),
14939 shared_mem_bytes: shmem,
14940 };
14941 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
14942 head_dim as i32,
14943 n_head as i32,
14944 n_head_kv as i32,
14945 t as i32,
14946 t_kv as i32,
14947 causal as i32,
14948 window as i32,
14949 );
14950 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
14951 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
14952 let vh = self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?;
14953 let __s_b = self.gpu.stream();
14954 let mut b = __s_b.launch_builder(&f);
14955 b.arg(&qb)
14956 .arg(&kb)
14957 .arg(&vh)
14958 .arg(o)
14959 .arg(&hd)
14960 .arg(&nh)
14961 .arg(&nhkv)
14962 .arg(&ti)
14963 .arg(&tkvi)
14964 .arg(&scale)
14965 .arg(&cz)
14966 .arg(&wi);
14967 unsafe {
14968 b.launch(cfg)?;
14969 }
14970 return Ok(());
14971 }
14972 if p1 {
14973 let f = self.func("fa_prefill_w_bf16_p1");
14974 let shmem =
14975 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
14976 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14977 f.set_attribute(
14978 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14979 shmem as i32,
14980 )?;
14981 let cfg = LaunchConfig {
14982 grid_dim: (
14983 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
14984 n_head as u32,
14985 1,
14986 ),
14987 block_dim: (32, 4, 1),
14988 shared_mem_bytes: shmem,
14989 };
14990 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
14991 head_dim as i32,
14992 n_head as i32,
14993 n_head_kv as i32,
14994 t as i32,
14995 t_kv as i32,
14996 causal as i32,
14997 window as i32,
14998 );
14999 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15000 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15001 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15002 let __s_b = self.gpu.stream();
15003 let mut b = __s_b.launch_builder(&f);
15004 b.arg(&qb)
15005 .arg(&kb)
15006 .arg(&vb)
15007 .arg(o)
15008 .arg(&hd)
15009 .arg(&nh)
15010 .arg(&nhkv)
15011 .arg(&ti)
15012 .arg(&tkvi)
15013 .arg(&scale)
15014 .arg(&cz)
15015 .arg(&wi);
15016 unsafe {
15017 b.launch(cfg)?;
15018 }
15019 return Ok(());
15020 }
15021 static G4_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15024 let g4 = !floor
15025 && !f32_stage
15026 && n_head_kv == 1
15027 && n_head % 4 == 0
15028 && *G4_ON.get_or_init(|| {
15029 std::env::var("MEMRA_FAW_G4")
15030 .map(|v| v != "0")
15031 .unwrap_or(true)
15032 });
15033 if g4 {
15034 const SP_M: usize = 16;
15035 static O2_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15038 let o2 = *O2_ON.get_or_init(|| {
15039 std::env::var("MEMRA_FAW_O2")
15040 .map(|v| v != "0")
15041 .unwrap_or(true)
15042 });
15043 let f = self.func(if o2 {
15044 "fa_prefill_w_bf16_g4o2"
15045 } else {
15046 "fa_prefill_w_bf16_g4"
15047 });
15048 let shmem = if o2 {
15049 (2 * (4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M)) as u32
15050 } else {
15051 (2 * (2 * BK * head_dim + 4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M))
15052 as u32
15053 };
15054 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15055 f.set_attribute(
15056 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15057 shmem as i32,
15058 )?;
15059 let cfg = LaunchConfig {
15060 grid_dim: ((t as u32).div_ceil(SP_M as u32), (n_head / 4) as u32, 1),
15061 block_dim: (32, 4, 1),
15062 shared_mem_bytes: shmem,
15063 };
15064 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15065 head_dim as i32,
15066 n_head as i32,
15067 n_head_kv as i32,
15068 t as i32,
15069 t_kv as i32,
15070 causal as i32,
15071 window as i32,
15072 );
15073 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15074 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15075 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15076 let __s_b = self.gpu.stream();
15077 let mut b = __s_b.launch_builder(&f);
15078 b.arg(&qb)
15079 .arg(&kb)
15080 .arg(&vb)
15081 .arg(o)
15082 .arg(&hd)
15083 .arg(&nh)
15084 .arg(&nhkv)
15085 .arg(&ti)
15086 .arg(&tkvi)
15087 .arg(&scale)
15088 .arg(&cz)
15089 .arg(&wi);
15090 unsafe {
15091 b.launch(cfg)?;
15092 }
15093 return Ok(());
15094 }
15095 let f = self.func(if floor {
15096 "fa_prefill_w_f32"
15097 } else if f32_stage {
15098 "fa_prefill_w_f32_pp"
15099 } else {
15100 "fa_prefill_w_bf16_pp"
15101 });
15102 let shmem =
15103 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15104 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15105 f.set_attribute(
15106 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15107 shmem as i32,
15108 )?;
15109 let cfg = LaunchConfig {
15110 grid_dim: (
15111 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15112 n_head as u32,
15113 1,
15114 ),
15115 block_dim: (32, 4, 1),
15116 shared_mem_bytes: shmem,
15117 };
15118 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15119 head_dim as i32,
15120 n_head as i32,
15121 n_head_kv as i32,
15122 t as i32,
15123 t_kv as i32,
15124 causal as i32,
15125 window as i32,
15126 );
15127 if f32_stage {
15128 let __s_b = self.gpu.stream();
15129 let mut b = __s_b.launch_builder(&f);
15130 b.arg(q)
15131 .arg(k)
15132 .arg(v)
15133 .arg(o)
15134 .arg(&hd)
15135 .arg(&nh)
15136 .arg(&nhkv)
15137 .arg(&ti)
15138 .arg(&tkvi)
15139 .arg(&scale)
15140 .arg(&cz)
15141 .arg(&wi);
15142 unsafe {
15143 b.launch(cfg)?;
15144 }
15145 } else {
15146 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15147 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15148 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15149 let __s_b = self.gpu.stream();
15150 let mut b = __s_b.launch_builder(&f);
15151 b.arg(&qb)
15152 .arg(&kb)
15153 .arg(&vb)
15154 .arg(o)
15155 .arg(&hd)
15156 .arg(&nh)
15157 .arg(&nhkv)
15158 .arg(&ti)
15159 .arg(&tkvi)
15160 .arg(&scale)
15161 .arg(&cz)
15162 .arg(&wi);
15163 unsafe {
15164 b.launch(cfg)?;
15165 }
15166 }
15167 Ok(())
15168 }
15169
15170 #[allow(clippy::too_many_arguments)]
15174 pub fn fa_prefill_hd512(
15175 &self,
15176 q: &CudaSlice<f32>,
15177 k: &CudaSlice<f32>,
15178 v: &CudaSlice<f32>,
15179 o: &mut CudaSlice<f32>,
15180 head_dim: usize,
15181 n_head: usize,
15182 n_head_kv: usize,
15183 t: usize,
15184 t_kv: usize,
15185 scale: f32,
15186 causal: bool,
15187 ) -> Result<(), Box<dyn std::error::Error>> {
15188 if portable_mma_gated() {
15190 return self.sdpa_naive(
15191 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
15192 );
15193 }
15194 static F32_STAGE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15200 let f32_stage =
15201 *F32_STAGE.get_or_init(|| std::env::var("MEMRA_FA512_STAGE").as_deref() == Ok("f32"));
15202 static SP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15206 let sp = !f32_stage
15207 && *SP_ON.get_or_init(|| {
15208 std::env::var("MEMRA_FA512_SP")
15209 .map(|v| v != "0")
15210 .unwrap_or(true)
15211 });
15212 self.fa_prefill_hd512_arm(
15213 q,
15214 k,
15215 v,
15216 o,
15217 head_dim,
15218 n_head,
15219 n_head_kv,
15220 t,
15221 t_kv,
15222 scale,
15223 causal,
15224 f32_stage,
15225 sp,
15226 sp && fa_f16pv_on(),
15227 )
15228 }
15229
15230 #[allow(clippy::too_many_arguments)]
15232 pub fn fa_prefill_hd512_pre(
15233 &self,
15234 qb: &CudaSlice<u8>,
15235 kb: &CudaSlice<u8>,
15236 vb: &CudaSlice<u8>,
15237 o: &mut CudaSlice<f32>,
15238 head_dim: usize,
15239 n_head: usize,
15240 n_head_kv: usize,
15241 t: usize,
15242 t_kv: usize,
15243 scale: f32,
15244 causal: bool,
15245 v_f16: bool,
15246 ) -> Result<(), Box<dyn std::error::Error>> {
15247 debug_assert_eq!(head_dim, 512);
15248 const SP_M: usize = 16;
15249 const BKS: usize = 32;
15250 let f16pv = fa_f16pv_on();
15254 let nw = if f16pv { fa512_wide_warps() } else { 2 };
15255 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15256 debug_assert!(!v_f16 || f16pv, "f16 V emitted without the door on");
15257 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
15258 let vref: &CudaSlice<u8> = if f16pv && !v_f16 {
15259 let n = t_kv * n_head_kv * head_dim;
15261 let need = n * 2;
15262 if vguard.as_ref().map(|b| b.len() < need).unwrap_or(true) {
15263 *vguard = Some(self.alloc_uninit::<u8>(need)?);
15264 }
15265 let dst = vguard.as_mut().unwrap();
15266 self.bf16_to_f16_into(vb, n, dst)?;
15267 vguard.as_ref().unwrap()
15268 } else {
15269 vb
15270 };
15271 let f = self.func(if hp {
15272 "fa_prefill_bf16_hd512_sp16h2"
15273 } else {
15274 match (f16pv, nw) {
15275 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
15276 (true, _) => "fa_prefill_bf16_hd512_sp16",
15277 _ => "fa_prefill_bf16_hd512_sp",
15278 }
15279 });
15280 let (nwarp, npart) = if hp {
15281 (4usize, 4usize)
15282 } else if nw > 2 {
15283 (nw, nw)
15284 } else {
15285 (2, 1)
15286 };
15287 let shmem = if hp {
15289 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS) + 4 * (2 * npart * SP_M * BKS + 2 * SP_M))
15290 as u32
15291 } else {
15292 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
15293 + 4 * (npart * SP_M * BKS + SP_M)) as u32
15294 };
15295 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15296 f.set_attribute(
15297 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15298 shmem as i32,
15299 )?;
15300 let grid_y = if hp {
15301 (n_head / 2) as u32
15302 } else {
15303 n_head as u32
15304 };
15305 let cfg = LaunchConfig {
15306 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
15307 block_dim: (32, nwarp as u32, 1),
15308 shared_mem_bytes: shmem,
15309 };
15310 let (hd, nh, nhkv, ti, tkvi, cz) = (
15311 head_dim as i32,
15312 n_head as i32,
15313 n_head_kv as i32,
15314 t as i32,
15315 t_kv as i32,
15316 causal as i32,
15317 );
15318 let __s_b = self.gpu.stream();
15319 let mut b = __s_b.launch_builder(&f);
15320 b.arg(qb)
15321 .arg(kb)
15322 .arg(vref)
15323 .arg(o)
15324 .arg(&hd)
15325 .arg(&nh)
15326 .arg(&nhkv)
15327 .arg(&ti)
15328 .arg(&tkvi)
15329 .arg(&scale)
15330 .arg(&cz);
15331 unsafe {
15332 b.launch(cfg)?;
15333 }
15334 Ok(())
15335 }
15336
15337 #[allow(clippy::too_many_arguments)]
15340 pub fn fa_prefill_hd512_arm(
15341 &self,
15342 q: &CudaSlice<f32>,
15343 k: &CudaSlice<f32>,
15344 v: &CudaSlice<f32>,
15345 o: &mut CudaSlice<f32>,
15346 head_dim: usize,
15347 n_head: usize,
15348 n_head_kv: usize,
15349 t: usize,
15350 t_kv: usize,
15351 scale: f32,
15352 causal: bool,
15353 f32_stage: bool,
15354 sp: bool,
15355 f16pv: bool,
15356 ) -> Result<(), Box<dyn std::error::Error>> {
15357 debug_assert_eq!(head_dim, 512, "fa_prefill_hd512 is hd512 only");
15358 if sp && !f32_stage {
15359 const SP_M: usize = 16;
15363 const BKS: usize = 32;
15364 let nw = if f16pv { fa512_wide_warps() } else { 2 };
15365 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15366 let f = self.func(if hp {
15367 "fa_prefill_bf16_hd512_sp16h2"
15368 } else {
15369 match (f16pv, nw) {
15370 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
15371 (true, _) => "fa_prefill_bf16_hd512_sp16",
15372 _ => "fa_prefill_bf16_hd512_sp",
15373 }
15374 });
15375 let (nwarp, npart) = if hp {
15376 (4usize, 4usize)
15377 } else if nw > 2 {
15378 (nw, nw)
15379 } else {
15380 (2, 1)
15381 };
15382 let shmem = if hp {
15383 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS)
15384 + 4 * (2 * npart * SP_M * BKS + 2 * SP_M)) as u32
15385 } else {
15386 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
15387 + 4 * (npart * SP_M * BKS + SP_M)) as u32
15388 };
15389 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15390 f.set_attribute(
15391 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15392 shmem as i32,
15393 )?;
15394 let grid_y = if hp {
15395 (n_head / 2) as u32
15396 } else {
15397 n_head as u32
15398 };
15399 let cfg = LaunchConfig {
15400 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
15401 block_dim: (32, nwarp as u32, 1),
15402 shared_mem_bytes: shmem,
15403 };
15404 let (hd, nh, nhkv, ti, tkvi, cz) = (
15405 head_dim as i32,
15406 n_head as i32,
15407 n_head_kv as i32,
15408 t as i32,
15409 t_kv as i32,
15410 causal as i32,
15411 );
15412 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15413 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15414 let vb = if f16pv {
15415 self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?
15416 } else {
15417 self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?
15418 };
15419 let __s_b = self.gpu.stream();
15420 let mut b = __s_b.launch_builder(&f);
15421 b.arg(&qb)
15422 .arg(&kb)
15423 .arg(&vb)
15424 .arg(o)
15425 .arg(&hd)
15426 .arg(&nh)
15427 .arg(&nhkv)
15428 .arg(&ti)
15429 .arg(&tkvi)
15430 .arg(&scale)
15431 .arg(&cz);
15432 unsafe {
15433 b.launch(cfg)?;
15434 }
15435 return Ok(());
15436 }
15437 const BLOCK_Q: usize = 32;
15438 const BK: usize = 32;
15439 const HALF: usize = 256;
15440 let f = self.func(if f32_stage {
15441 "fa_prefill_f32_hd512"
15442 } else {
15443 "fa_prefill_bf16_hd512"
15444 });
15445 let shmem = (2 * (BLOCK_Q * head_dim + BK * head_dim + BK * HALF + BLOCK_Q * BK)
15447 + 4 * BLOCK_Q) as u32;
15448 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15449 f.set_attribute(
15450 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15451 shmem as i32,
15452 )?;
15453 let cfg = LaunchConfig {
15454 grid_dim: (
15455 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15456 n_head as u32,
15457 2,
15458 ),
15459 block_dim: (32, 2, 1),
15460 shared_mem_bytes: shmem,
15461 };
15462 let (hd, nh, nhkv, ti, tkvi, cz) = (
15463 head_dim as i32,
15464 n_head as i32,
15465 n_head_kv as i32,
15466 t as i32,
15467 t_kv as i32,
15468 causal as i32,
15469 );
15470 if f32_stage {
15471 let __s_b = self.gpu.stream();
15472 let mut b = __s_b.launch_builder(&f);
15473 b.arg(q)
15474 .arg(k)
15475 .arg(v)
15476 .arg(o)
15477 .arg(&hd)
15478 .arg(&nh)
15479 .arg(&nhkv)
15480 .arg(&ti)
15481 .arg(&tkvi)
15482 .arg(&scale)
15483 .arg(&cz);
15484 unsafe {
15485 b.launch(cfg)?;
15486 }
15487 } else {
15488 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15489 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15490 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15491 let __s_b = self.gpu.stream();
15492 let mut b = __s_b.launch_builder(&f);
15493 b.arg(&qb)
15494 .arg(&kb)
15495 .arg(&vb)
15496 .arg(o)
15497 .arg(&hd)
15498 .arg(&nh)
15499 .arg(&nhkv)
15500 .arg(&ti)
15501 .arg(&tkvi)
15502 .arg(&scale)
15503 .arg(&cz);
15504 unsafe {
15505 b.launch(cfg)?;
15506 }
15507 }
15508 Ok(())
15509 }
15510
15511 #[allow(clippy::too_many_arguments)]
15515 pub fn rope_neox2_bf16e(
15516 &self,
15517 q: &mut CudaSlice<f32>,
15518 k: &mut CudaSlice<f32>,
15519 qb: &mut CudaSlice<u8>,
15520 kb: &mut CudaSlice<u8>,
15521 pos: &CudaSlice<i32>,
15522 head_dim: usize,
15523 n_dims: usize,
15524 nh_q: usize,
15525 nh_k: usize,
15526 n_tokens: usize,
15527 base: f32,
15528 freq_scale: f32,
15529 ff: Option<&CudaSlice<f32>>,
15530 ) -> Result<(), Box<dyn std::error::Error>> {
15531 let f = self.func("rope_neox2_bf16e_f32");
15532 let rows = ((nh_q + nh_k) * n_tokens) as u32;
15533 let cfg = LaunchConfig {
15534 grid_dim: (rows, 1, 1),
15535 block_dim: ((head_dim / 2) as u32, 1, 1),
15536 shared_mem_bytes: 0,
15537 };
15538 let theta_scale = base.powf(-2.0 / n_dims as f32);
15539 let (hd, nd, nhq, nhk, nt) = (
15540 head_dim as i32,
15541 n_dims as i32,
15542 nh_q as i32,
15543 nh_k as i32,
15544 n_tokens as i32,
15545 );
15546 let __s_b = self.gpu.stream();
15547 let mut b = __s_b.launch_builder(&f);
15548 match ff {
15549 Some(t) => {
15550 b.arg(&mut *q)
15551 .arg(&mut *k)
15552 .arg(&mut *qb)
15553 .arg(&mut *kb)
15554 .arg(pos)
15555 .arg(&hd)
15556 .arg(&nd)
15557 .arg(&nhq)
15558 .arg(&nhk)
15559 .arg(&nt)
15560 .arg(&theta_scale)
15561 .arg(&freq_scale)
15562 .arg(t);
15563 unsafe {
15564 b.launch(cfg)?;
15565 }
15566 }
15567 None => {
15568 let null: u64 = 0;
15569 b.arg(&mut *q)
15570 .arg(&mut *k)
15571 .arg(&mut *qb)
15572 .arg(&mut *kb)
15573 .arg(pos)
15574 .arg(&hd)
15575 .arg(&nd)
15576 .arg(&nhq)
15577 .arg(&nhk)
15578 .arg(&nt)
15579 .arg(&theta_scale)
15580 .arg(&freq_scale)
15581 .arg(&null);
15582 unsafe {
15583 b.launch(cfg)?;
15584 }
15585 }
15586 }
15587 Ok(())
15588 }
15589
15590 pub fn f32_to_bf16(
15593 &self,
15594 x: &CudaSlice<f32>,
15595 n: usize,
15596 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
15597 assert!(n % 4 == 0, "f32_to_bf16 requires n % 4 == 0, got {n}");
15598 let mut y = self.alloc_uninit::<u8>(n * 2)?;
15599 let f = self.func("f32_to_bf16_flat");
15600 let n_i = n as i64;
15601 let cfg = LaunchConfig {
15602 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
15603 block_dim: (256, 1, 1),
15604 shared_mem_bytes: 0,
15605 };
15606 let __s_b = self.gpu.stream();
15607 let mut b = __s_b.launch_builder(&f);
15608 b.arg(x).arg(&mut y).arg(&n_i);
15609 unsafe {
15610 b.launch(cfg)?;
15611 }
15612 Ok(y)
15613 }
15614
15615 pub fn f32_to_f16(
15616 &self,
15617 x: &CudaSlice<f32>,
15618 n: usize,
15619 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
15620 assert!(n % 4 == 0, "f32_to_f16 requires n % 4 == 0, got {n}");
15621 let mut y = self.alloc_uninit::<u8>(n * 2)?;
15622 let f = self.func("f32_to_f16_flat");
15623 let n_i = n as i64;
15624 let cfg = LaunchConfig {
15625 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
15626 block_dim: (256, 1, 1),
15627 shared_mem_bytes: 0,
15628 };
15629 let __s_b = self.gpu.stream();
15630 let mut b = __s_b.launch_builder(&f);
15631 b.arg(x).arg(&mut y).arg(&n_i);
15632 unsafe {
15633 b.launch(cfg)?;
15634 }
15635 Ok(y)
15636 }
15637
15638 pub fn bf16_to_f16(
15640 &self,
15641 xb: &CudaSlice<u8>,
15642 n: usize,
15643 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
15644 let mut y = self.alloc_uninit::<u8>(n * 2)?;
15645 self.bf16_to_f16_into(xb, n, &mut y)?;
15646 Ok(y)
15647 }
15648
15649 pub fn bf16_to_f16_into(
15651 &self,
15652 xb: &CudaSlice<u8>,
15653 n: usize,
15654 y: &mut CudaSlice<u8>,
15655 ) -> Result<(), Box<dyn std::error::Error>> {
15656 assert!(n % 2 == 0, "bf16_to_f16 requires n % 2 == 0, got {n}");
15657 assert!(y.len() >= n * 2);
15658 let f = self.func("bf16_to_f16_flat");
15659 let n2 = (n / 2) as i64;
15660 let cfg = LaunchConfig {
15661 grid_dim: (((n / 2) as u32).div_ceil(256), 1, 1),
15662 block_dim: (256, 1, 1),
15663 shared_mem_bytes: 0,
15664 };
15665 let __s_b = self.gpu.stream();
15666 let mut b = __s_b.launch_builder(&f);
15667 b.arg(xb).arg(y).arg(&n2);
15668 unsafe {
15669 b.launch(cfg)?;
15670 }
15671 Ok(())
15672 }
15673
15674 #[allow(clippy::too_many_arguments)]
15679 pub fn fa_prefill_vl8(
15680 &self,
15681 seqs: &[FaSeqVl],
15682 head_dim: usize,
15683 n_head: usize,
15684 n_head_kv: usize,
15685 scale: f32,
15686 ) -> Result<(), Box<dyn std::error::Error>> {
15687 const BK: usize = 32;
15688 let b = seqs.len();
15689 assert!(b >= 1 && b <= 8);
15690 let mut packed = [FaSeqVl::default(); 8];
15691 packed[..b].copy_from_slice(seqs);
15692 let v = FaVl8(packed);
15693 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
15694 let ept = (n_head_kv * head_dim) as i32;
15695 {
15696 let f = self.func("fa_mirror_vl");
15697 let max_n = (max_t as i64) * ept as i64;
15698 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
15699 for which in 0..2i32 {
15700 let cfg = LaunchConfig {
15701 grid_dim: (blocks, 1, b as u32),
15702 block_dim: (256, 1, 1),
15703 shared_mem_bytes: 0,
15704 };
15705 let __s_lb = self.gpu.stream();
15706 let mut lb = __s_lb.launch_builder(&f);
15707 lb.arg(&v).arg(&ept).arg(&which);
15708 unsafe {
15709 lb.launch(cfg)?;
15710 }
15711 }
15712 }
15713 let hd_sfx = fa_hd_suffix(head_dim)?;
15714 let f = self.func(&format!("fa_prefill_bf16kv_vl{hd_sfx}"));
15715 let block_q = 64usize;
15716 let kv_stages = 2usize;
15717 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
15718 + 4 * (block_q * BK + 2 * block_q)) as u32;
15719 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15720 f.set_attribute(
15721 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15722 shmem as i32,
15723 )?;
15724 let cfg = LaunchConfig {
15725 grid_dim: (max_t.div_ceil(block_q as u32), n_head as u32, b as u32),
15726 block_dim: (32, 4, 1),
15727 shared_mem_bytes: shmem,
15728 };
15729 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
15730 let __s_lb = self.gpu.stream();
15731 let mut lb = __s_lb.launch_builder(&f);
15732 lb.arg(&v).arg(&hd).arg(&nh).arg(&nhkv).arg(&scale);
15733 unsafe {
15734 lb.launch(cfg)?;
15735 }
15736 Ok(())
15737 }
15738
15739 #[allow(clippy::too_many_arguments)]
15743 pub fn attn_pre_vl8(
15744 &self,
15745 seqs: &[AttnPreVl],
15746 wq: &CudaSlice<f32>,
15747 wk: &CudaSlice<f32>,
15748 head_dim: usize,
15749 rope_dims: usize,
15750 n_head: usize,
15751 n_head_kv: usize,
15752 eps: f32,
15753 freq_base: f32,
15754 freq_scale: f32,
15755 kv_dim_k: usize,
15756 kv_dim_v: usize,
15757 k_tok_bytes: usize,
15758 v_tok_bytes: usize,
15759 ) -> Result<(), Box<dyn std::error::Error>> {
15760 let b = seqs.len();
15761 assert!(b >= 1 && b <= 8);
15762 let mut packed = [AttnPreVl::default(); 8];
15763 packed[..b].copy_from_slice(seqs);
15764 let v = AttnPreVl8(packed);
15765 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
15766 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
15767 {
15768 let f = self.func("q_gate_split_vl");
15769 let n = max_t * (n_head * head_dim) as u32;
15770 let cfg = LaunchConfig {
15771 grid_dim: (n.div_ceil(256), 1, b as u32),
15772 block_dim: (256, 1, 1),
15773 shared_mem_bytes: 0,
15774 };
15775 let __s_lb = self.gpu.stream();
15776 let mut lb = __s_lb.launch_builder(&f);
15777 lb.arg(&v).arg(&hd).arg(&nh);
15778 unsafe {
15779 lb.launch(cfg)?;
15780 }
15781 }
15782 {
15783 let f = self.func("attn_rms_vl");
15784 let cfg = LaunchConfig {
15785 grid_dim: (max_t * n_head as u32, 2, b as u32),
15786 block_dim: (rms_block(), 1, 1),
15787 shared_mem_bytes: 0,
15788 };
15789 let __s_lb = self.gpu.stream();
15790 let mut lb = __s_lb.launch_builder(&f);
15791 lb.arg(&v)
15792 .arg(wq)
15793 .arg(wk)
15794 .arg(&hd)
15795 .arg(&nh)
15796 .arg(&nhkv)
15797 .arg(&eps);
15798 unsafe {
15799 lb.launch(cfg)?;
15800 }
15801 }
15802 {
15803 let f = self.func("attn_rope_vl");
15804 let theta_scale = freq_base.powf(-2.0 / rope_dims as f32);
15805 let nd = rope_dims as i32;
15806 let cfg = LaunchConfig {
15807 grid_dim: (max_t * n_head as u32, 2, b as u32),
15808 block_dim: ((head_dim / 2) as u32, 1, 1),
15809 shared_mem_bytes: 0,
15810 };
15811 let __s_lb = self.gpu.stream();
15812 let mut lb = __s_lb.launch_builder(&f);
15813 lb.arg(&v)
15814 .arg(&hd)
15815 .arg(&nd)
15816 .arg(&nh)
15817 .arg(&nhkv)
15818 .arg(&theta_scale)
15819 .arg(&freq_scale);
15820 unsafe {
15821 lb.launch(cfg)?;
15822 }
15823 }
15824 {
15825 let f = self.func("append_kv_vl");
15826 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
15827 let cfg = LaunchConfig {
15828 grid_dim: (nblk, max_t, b as u32),
15829 block_dim: (32, 1, 1),
15830 shared_mem_bytes: 0,
15831 };
15832 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
15833 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
15834 let __s_lb = self.gpu.stream();
15835 let mut lb = __s_lb.launch_builder(&f);
15836 lb.arg(&v).arg(&kdk).arg(&kdv).arg(&ktb).arg(&vtb);
15837 unsafe {
15838 lb.launch(cfg)?;
15839 }
15840 }
15841 Ok(())
15842 }
15843
15844 pub fn fa_prefill_view(
15849 &self,
15850 q: &CudaSlice<f32>,
15851 k: &cudarc::driver::CudaView<u8>,
15852 v: &cudarc::driver::CudaView<u8>,
15853 o: &mut CudaSlice<f32>,
15854 head_dim: usize,
15855 n_head: usize,
15856 n_head_kv: usize,
15857 t: usize,
15858 t_kv: usize,
15859 scale: f32,
15860 causal: bool,
15861 k_tok_bytes: usize,
15862 v_tok_bytes: usize,
15863 g: bool,
15864 ) -> Result<(), Box<dyn std::error::Error>> {
15865 if portable_mma_gated() {
15866 return self.sdpa_naive_quantized_view(
15867 q,
15868 k,
15869 v,
15870 o,
15871 head_dim,
15872 n_head,
15873 n_head_kv,
15874 t,
15875 t_kv,
15876 scale,
15877 causal,
15878 k_tok_bytes,
15879 v_tok_bytes,
15880 );
15881 }
15882 const BLOCK_Q: usize = 64;
15883 const BK: usize = 32;
15884 let name = format!("fa_prefill_q{}", fa_hd_suffix(head_dim)?);
15887 let f = if g {
15888 self.func_g(&name)
15889 } else {
15890 self.func(&name)
15891 };
15892 let shmem =
15893 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15894 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15895 f.set_attribute(
15896 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15897 shmem as i32,
15898 )?;
15899 let cfg = LaunchConfig {
15900 grid_dim: (
15901 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15902 n_head as u32,
15903 1,
15904 ),
15905 block_dim: (32, 4, 1),
15906 shared_mem_bytes: shmem,
15907 };
15908 let (hd, nh, nhkv, ti, tkvi, cz) = (
15909 head_dim as i32,
15910 n_head as i32,
15911 n_head_kv as i32,
15912 t as i32,
15913 t_kv as i32,
15914 causal as i32,
15915 );
15916 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
15917 let __s_b = self.gpu.stream();
15918 let mut b = __s_b.launch_builder(&f);
15919 b.arg(q)
15920 .arg(k)
15921 .arg(v)
15922 .arg(o)
15923 .arg(&hd)
15924 .arg(&nh)
15925 .arg(&nhkv)
15926 .arg(&ti)
15927 .arg(&tkvi)
15928 .arg(&scale)
15929 .arg(&cz)
15930 .arg(&ktb)
15931 .arg(&vtb);
15932 unsafe {
15933 b.launch(cfg)?;
15934 }
15935 Ok(())
15936 }
15937
15938 #[allow(clippy::too_many_arguments)]
15948 pub fn fa_prefill_view_ws(
15949 &self,
15950 q: &CudaSlice<f32>,
15951 k: &cudarc::driver::CudaView<u8>,
15952 v: &cudarc::driver::CudaView<u8>,
15953 o: &mut CudaSlice<f32>,
15954 head_dim: usize,
15955 n_head: usize,
15956 n_head_kv: usize,
15957 t: usize,
15958 t_kv: usize,
15959 scale: f32,
15960 causal: bool,
15961 k_tok_bytes: usize,
15962 v_tok_bytes: usize,
15963 g: bool,
15964 ) -> Result<(), Box<dyn std::error::Error>> {
15965 if portable_mma_gated() {
15966 return self.sdpa_naive_quantized_view(
15967 q,
15968 k,
15969 v,
15970 o,
15971 head_dim,
15972 n_head,
15973 n_head_kv,
15974 t,
15975 t_kv,
15976 scale,
15977 causal,
15978 k_tok_bytes,
15979 v_tok_bytes,
15980 );
15981 }
15982 const BLOCK_Q: usize = 64;
15983 const BK: usize = 32;
15984 let kv_dim_k = n_head_kv * head_dim;
15985 let kv_dim_v = n_head_kv * head_dim;
15986 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
15988 let mut guard = self.prime_deqw_ws.lock().unwrap();
15990 let need_grow = match guard.as_ref() {
15991 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
15992 None => true,
15993 };
15994 if need_grow {
15995 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
15996 let (ck, cv) = guard
15997 .as_ref()
15998 .map(|(a, b)| (a.len(), b.len()))
15999 .unwrap_or((0, 0));
16000 *guard = Some((
16001 self.alloc_u8(grow(ck, k_ws_bytes))?,
16002 self.alloc_u8(grow(cv, v_ws_bytes))?,
16003 ));
16004 }
16005 let (kw, vw) = guard.as_mut().unwrap();
16006 {
16008 let f = if g {
16010 self.func_g("fa_dequant_kv_ws_bf16")
16011 } else {
16012 self.func("fa_dequant_kv_ws_bf16")
16013 };
16014 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
16015 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
16016 let cfg = LaunchConfig {
16017 grid_dim: (nblk.max(1), 1, 1),
16018 block_dim: (256, 1, 1),
16019 shared_mem_bytes: 0,
16020 };
16021 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
16022 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16023 let __s_b = self.gpu.stream();
16024 let mut b = __s_b.launch_builder(&f);
16025 b.arg(k)
16026 .arg(v)
16027 .arg(&mut *kw)
16028 .arg(&mut *vw)
16029 .arg(&kdk)
16030 .arg(&kdv)
16031 .arg(&tkvi)
16032 .arg(&ktb)
16033 .arg(&vtb);
16034 unsafe {
16035 b.launch(cfg)?;
16036 }
16037 }
16038 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
16046 .map(|v| v != "0")
16047 .unwrap_or(true);
16048 {
16049 let hd_sfx = fa_hd_suffix(head_dim)?;
16050 let f = self.func(&format!(
16051 "fa_prefill_qw{}{hd_sfx}",
16052 if db { "_db" } else { "" }
16053 ));
16054 let shmem = if db {
16055 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
16057 } else {
16058 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
16059 };
16060 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16061 f.set_attribute(
16062 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16063 shmem as i32,
16064 )?;
16065 let cfg = LaunchConfig {
16066 grid_dim: (
16067 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16068 n_head as u32,
16069 1,
16070 ),
16071 block_dim: (32, 4, 1),
16072 shared_mem_bytes: shmem,
16073 };
16074 let (hd, nh, nhkv, ti, tkvi, cz) = (
16075 head_dim as i32,
16076 n_head as i32,
16077 n_head_kv as i32,
16078 t as i32,
16079 t_kv as i32,
16080 causal as i32,
16081 );
16082 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
16083 let __s_b = self.gpu.stream();
16084 let mut b = __s_b.launch_builder(&f);
16085 b.arg(q)
16086 .arg(&*kw)
16087 .arg(&*vw)
16088 .arg(o)
16089 .arg(&hd)
16090 .arg(&nh)
16091 .arg(&nhkv)
16092 .arg(&ti)
16093 .arg(&tkvi)
16094 .arg(&scale)
16095 .arg(&cz)
16096 .arg(&kdk)
16097 .arg(&kdv);
16098 unsafe {
16099 b.launch(cfg)?;
16100 }
16101 }
16102 Ok(())
16103 }
16104
16105 #[allow(clippy::too_many_arguments)]
16121 pub fn fa_prefill_view_ws_w_hd128(
16122 &self,
16123 q: &CudaSlice<f32>,
16124 k: &cudarc::driver::CudaView<u8>,
16125 v: &cudarc::driver::CudaView<u8>,
16126 o: &mut CudaSlice<f32>,
16127 head_dim: usize,
16128 n_head: usize,
16129 n_head_kv: usize,
16130 t: usize,
16131 t_kv: usize,
16132 scale: f32,
16133 causal: bool,
16134 window: usize,
16135 k_tok_bytes: usize,
16136 v_tok_bytes: usize,
16137 ) -> Result<(), Box<dyn std::error::Error>> {
16138 assert_eq!(
16139 head_dim, 128,
16140 "fa_prefill_view_ws_w_hd128: only the hd128 twin is stamped"
16141 );
16142 if portable_mma_gated() {
16143 return self.sdpa_naive_w_quantized_view(
16144 q,
16145 k,
16146 v,
16147 o,
16148 head_dim,
16149 n_head,
16150 n_head_kv,
16151 t,
16152 t_kv,
16153 scale,
16154 causal,
16155 window,
16156 k_tok_bytes,
16157 v_tok_bytes,
16158 );
16159 }
16160 const BLOCK_Q: usize = 64;
16161 const BK: usize = 32;
16162 let kv_dim_k = n_head_kv * head_dim;
16163 let kv_dim_v = n_head_kv * head_dim;
16164 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
16166 let mut guard = self.prime_deqw_ws.lock().unwrap();
16167 let need_grow = match guard.as_ref() {
16168 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
16169 None => true,
16170 };
16171 if need_grow {
16172 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
16173 let (ck, cv) = guard
16174 .as_ref()
16175 .map(|(a, b)| (a.len(), b.len()))
16176 .unwrap_or((0, 0));
16177 *guard = Some((
16178 self.alloc_u8(grow(ck, k_ws_bytes))?,
16179 self.alloc_u8(grow(cv, v_ws_bytes))?,
16180 ));
16181 }
16182 let (kw, vw) = guard.as_mut().unwrap();
16183 {
16186 let f = self.func("fa_dequant_kv_ws_bf16");
16187 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
16188 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
16189 let cfg = LaunchConfig {
16190 grid_dim: (nblk.max(1), 1, 1),
16191 block_dim: (256, 1, 1),
16192 shared_mem_bytes: 0,
16193 };
16194 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
16195 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16196 let __s_b = self.gpu.stream();
16197 let mut b = __s_b.launch_builder(&f);
16198 b.arg(k)
16199 .arg(v)
16200 .arg(&mut *kw)
16201 .arg(&mut *vw)
16202 .arg(&kdk)
16203 .arg(&kdv)
16204 .arg(&tkvi)
16205 .arg(&ktb)
16206 .arg(&vtb);
16207 unsafe {
16208 b.launch(cfg)?;
16209 }
16210 }
16211 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
16213 .map(|v| v != "0")
16214 .unwrap_or(true);
16215 {
16216 let f = self.func(if db {
16217 "fa_prefill_qw_db_w_hd128"
16218 } else {
16219 "fa_prefill_qw_w_hd128"
16220 });
16221 let shmem = if db {
16222 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
16223 } else {
16224 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
16225 };
16226 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16227 f.set_attribute(
16228 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16229 shmem as i32,
16230 )?;
16231 let cfg = LaunchConfig {
16232 grid_dim: (
16233 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16234 n_head as u32,
16235 1,
16236 ),
16237 block_dim: (32, 4, 1),
16238 shared_mem_bytes: shmem,
16239 };
16240 let (hd, nh, nhkv, ti, tkvi, cz) = (
16241 head_dim as i32,
16242 n_head as i32,
16243 n_head_kv as i32,
16244 t as i32,
16245 t_kv as i32,
16246 causal as i32,
16247 );
16248 let (kdk, kdv, wnd) = (kv_dim_k as i32, kv_dim_v as i32, window as i32);
16249 let __s_b = self.gpu.stream();
16250 let mut b = __s_b.launch_builder(&f);
16251 b.arg(q)
16252 .arg(&*kw)
16253 .arg(&*vw)
16254 .arg(o)
16255 .arg(&hd)
16256 .arg(&nh)
16257 .arg(&nhkv)
16258 .arg(&ti)
16259 .arg(&tkvi)
16260 .arg(&scale)
16261 .arg(&cz)
16262 .arg(&kdk)
16263 .arg(&kdv)
16264 .arg(&wnd);
16265 unsafe {
16266 b.launch(cfg)?;
16267 }
16268 }
16269 Ok(())
16270 }
16271
16272 pub fn fa_decode(
16276 &self,
16277 q: &CudaSlice<f32>,
16278 k: &cudarc::driver::CudaView<u8>,
16279 v: &cudarc::driver::CudaView<u8>,
16280 o: &mut CudaSlice<f32>,
16281 head_dim: usize,
16282 n_head: usize,
16283 n_head_kv: usize,
16284 t_kv: usize,
16285 scale: f32,
16286 k_tok_bytes: usize,
16287 v_tok_bytes: usize,
16288 ) -> Result<(), Box<dyn std::error::Error>> {
16289 self.fa_decode_kvmod(
16290 q,
16291 k,
16292 v,
16293 o,
16294 head_dim,
16295 n_head,
16296 n_head_kv,
16297 t_kv,
16298 scale,
16299 k_tok_bytes,
16300 v_tok_bytes,
16301 false,
16302 )
16303 }
16304
16305 #[allow(clippy::too_many_arguments)]
16309 #[allow(clippy::too_many_arguments)]
16313 #[allow(clippy::too_many_arguments)]
16314 fn fa_decode_scalar_unified(
16315 &self,
16316 q: &cudarc::driver::CudaView<f32>,
16317 k: &cudarc::driver::CudaView<u8>,
16318 v: &cudarc::driver::CudaView<u8>,
16319 o: &mut cudarc::driver::CudaViewMut<f32>,
16320 head_dim: usize,
16321 n_head: usize,
16322 n_head_kv: usize,
16323 t_kv_host: usize,
16324 t_kv_dev: Option<&CudaSlice<i32>>,
16325 scale: f32,
16326 n_splits: usize,
16327 split_keys: usize,
16328 k_tok_bytes: usize,
16329 v_tok_bytes: usize,
16330 g: bool,
16331 part_o: &mut CudaSlice<f32>,
16332 part_m: &mut CudaSlice<f32>,
16333 part_l: &mut CudaSlice<f32>,
16334 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
16335 ) -> Result<(), Box<dyn std::error::Error>> {
16336 let f = if g {
16337 self.func_g("fa_decode_f32")
16338 } else {
16339 self.fa_func("fa_decode_f32", head_dim)
16340 };
16341 let cfg = LaunchConfig {
16342 grid_dim: (n_head as u32, n_splits as u32, 1),
16343 block_dim: (head_dim as u32, 1, 1),
16344 shared_mem_bytes: (4 * (head_dim + 32)) as u32,
16345 };
16346 let (hd, nh, nhkv, nsp) = (
16347 head_dim as i32,
16348 n_head as i32,
16349 n_head_kv as i32,
16350 n_splits as i32,
16351 );
16352 let (ktb, vtb, tkvi, ski) = (
16353 k_tok_bytes as i64,
16354 v_tok_bytes as i64,
16355 t_kv_host as i32,
16356 split_keys as i32,
16357 );
16358 let __s_b = self.gpu.stream();
16359 let mut b = __s_b.launch_builder(&f);
16360 match t_kv_dev {
16361 Some(d) => {
16362 b.arg(q)
16363 .arg(k)
16364 .arg(v)
16365 .arg(&mut *part_o)
16366 .arg(&mut *part_m)
16367 .arg(&mut *part_l)
16368 .arg(&hd)
16369 .arg(&nh)
16370 .arg(&nhkv)
16371 .arg(&tkvi)
16372 .arg(d)
16373 .arg(&scale)
16374 .arg(&nsp)
16375 .arg(&ski)
16376 .arg(&ktb)
16377 .arg(&vtb);
16378 unsafe {
16379 b.launch(cfg)?;
16380 }
16381 }
16382 None => {
16383 let null: u64 = 0;
16384 b.arg(q)
16385 .arg(k)
16386 .arg(v)
16387 .arg(&mut *part_o)
16388 .arg(&mut *part_m)
16389 .arg(&mut *part_l)
16390 .arg(&hd)
16391 .arg(&nh)
16392 .arg(&nhkv)
16393 .arg(&tkvi)
16394 .arg(&null)
16395 .arg(&scale)
16396 .arg(&nsp)
16397 .arg(&ski)
16398 .arg(&ktb)
16399 .arg(&vtb);
16400 unsafe {
16401 b.launch(cfg)?;
16402 }
16403 }
16404 }
16405 let cfg2 = LaunchConfig {
16406 grid_dim: (n_head as u32, 1, 1),
16407 block_dim: (head_dim as u32, 1, 1),
16408 shared_mem_bytes: 0,
16409 };
16410 if let Some((oq, od)) = q8_out {
16411 let fc = if g {
16413 self.func_g("fa_decode_combine_q8_1")
16414 } else {
16415 self.fa_func("fa_decode_combine_q8_1", head_dim)
16416 };
16417 let __s_b2 = self.gpu.stream();
16418 let mut b2 = __s_b2.launch_builder(&fc);
16419 b2.arg(&*part_o)
16420 .arg(&*part_m)
16421 .arg(&*part_l)
16422 .arg(oq)
16423 .arg(od)
16424 .arg(&hd)
16425 .arg(&nh)
16426 .arg(&nsp);
16427 unsafe {
16428 b2.launch(cfg2)?;
16429 }
16430 return Ok(());
16431 }
16432 let fc = if g {
16433 self.func_g("fa_decode_combine_f32")
16434 } else {
16435 self.fa_func("fa_decode_combine_f32", head_dim)
16436 };
16437 let __s_b2 = self.gpu.stream();
16438 let mut b2 = __s_b2.launch_builder(&fc);
16439 b2.arg(&*part_o)
16440 .arg(&*part_m)
16441 .arg(&*part_l)
16442 .arg(o)
16443 .arg(&hd)
16444 .arg(&nh)
16445 .arg(&nsp);
16446 unsafe {
16447 b2.launch(cfg2)?;
16448 }
16449 Ok(())
16450 }
16451
16452 pub fn fa_decode_kvmod(
16453 &self,
16454 q: &CudaSlice<f32>,
16455 k: &cudarc::driver::CudaView<u8>,
16456 v: &cudarc::driver::CudaView<u8>,
16457 o: &mut CudaSlice<f32>,
16458 head_dim: usize,
16459 n_head: usize,
16460 n_head_kv: usize,
16461 t_kv: usize,
16462 scale: f32,
16463 k_tok_bytes: usize,
16464 v_tok_bytes: usize,
16465 g: bool,
16466 ) -> Result<(), Box<dyn std::error::Error>> {
16467 let q_view = q.as_view();
16468 let mut o_view = o.as_view_mut();
16469 self.fa_decode_kvmod_view(
16470 &q_view,
16471 k,
16472 v,
16473 &mut o_view,
16474 head_dim,
16475 n_head,
16476 n_head_kv,
16477 t_kv,
16478 scale,
16479 k_tok_bytes,
16480 v_tok_bytes,
16481 g,
16482 )
16483 }
16484
16485 #[allow(clippy::too_many_arguments)]
16490 pub fn fa_decode_kvmod_view(
16491 &self,
16492 q: &cudarc::driver::CudaView<f32>,
16493 k: &cudarc::driver::CudaView<u8>,
16494 v: &cudarc::driver::CudaView<u8>,
16495 o: &mut cudarc::driver::CudaViewMut<f32>,
16496 head_dim: usize,
16497 n_head: usize,
16498 n_head_kv: usize,
16499 t_kv: usize,
16500 scale: f32,
16501 k_tok_bytes: usize,
16502 v_tok_bytes: usize,
16503 g: bool,
16504 ) -> Result<(), Box<dyn std::error::Error>> {
16505 let mut fa_vec = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
16526 if g && head_dim == 256 && !fa_v4_at(t_kv) {
16530 fa_vec = false;
16531 }
16532 let sp = fa_split_keys(t_kv, n_head_kv);
16533 let n_splits = if fa_vec {
16534 ((t_kv + sp - 1) / sp).max(1)
16535 } else {
16536 ((t_kv + 255) / 256).max(1)
16537 };
16538 let o_len = n_head * n_splits * head_dim;
16539 let ml_len = n_head * n_splits;
16540 let mut part_guard = self.fa_part_pool.lock().unwrap();
16541 if part_guard
16542 .as_ref()
16543 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
16544 .unwrap_or(true)
16545 {
16546 let old = part_guard.take();
16557 let (co, cm) = old
16558 .as_ref()
16559 .map(|pp| (pp.0.len(), pp.1.len()))
16560 .unwrap_or((0, 0));
16561 if let Some(old) = old {
16562 self.fa_part_retired.lock().unwrap().push(old);
16563 }
16564 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
16565 eprintln!(
16566 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
16567 co, o_len, cm, ml_len
16568 );
16569 }
16570 *part_guard = Some((
16571 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
16572 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
16573 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
16574 ));
16575 }
16576 let pg = part_guard.as_mut().unwrap();
16577 self.gpu
16578 .stream()
16579 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
16580 self.gpu
16581 .stream()
16582 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
16583 self.gpu
16584 .stream()
16585 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
16586 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
16587 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
16588 let (hd, nh, nhkv, tkvi, nsp) = (
16589 head_dim as i32,
16590 n_head as i32,
16591 n_head_kv as i32,
16592 t_kv as i32,
16593 n_splits as i32,
16594 );
16595 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16596 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
16600 let fa512_min = fa512_min_tkv();
16605 let deep = fa_vec
16608 && head_dim == 256
16609 && fa_v4_at(t_kv)
16610 && !g
16611 && fa_deep_at(t_kv)
16612 && !matches!(fa_v4_mode(), "noB3" | "stage");
16613 let (f, cfg) = if fa_vec && head_dim == 512 && t_kv >= fa512_min {
16614 let gqa = (n_head / n_head_kv).max(1) as u32;
16617 let fv = self.fa_func("fa_decode_vec_q_dpl16", head_dim);
16618 (
16619 fv,
16620 LaunchConfig {
16621 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16622 block_dim: (32, gqa, 1),
16623 shared_mem_bytes: 0,
16624 },
16625 )
16626 } else if fa_vec && head_dim <= 256 {
16627 let gqa = (n_head / n_head_kv).max(1) as u32;
16628 static SMEM_TKV: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
16639 let smem_tkv = *SMEM_TKV.get_or_init(|| {
16640 std::env::var("MEMRA_FA_SMEM_TKV")
16641 .ok()
16642 .and_then(|v| v.parse().ok())
16643 .unwrap_or_else(|| {
16644 FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
16645 })
16646 });
16647 if fa_v4_at(t_kv) && head_dim == 256 {
16648 let v4name = match fa_v4_mode() {
16652 "noB3" => "fa_decode_vec_q_v4_noB3", "stage" => "fa_decode_vec_q_v4_stage", _ if deep => "fa_decode_vec_q_v4_deep",
16655 _ => "fa_decode_vec_q_v4",
16656 };
16657 let fv = if g {
16658 self.func_g(v4name)
16659 } else {
16660 self.func(v4name)
16661 };
16662 let shmem = (if deep { 12160 } else { 11520 }
16665 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
16666 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16667 fv.set_attribute(
16668 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16669 shmem as i32,
16670 )?;
16671 (
16672 fv,
16673 LaunchConfig {
16674 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16675 block_dim: (32, gqa, 1),
16676 shared_mem_bytes: shmem,
16677 },
16678 )
16679 } else if fa_v3_active(head_dim) {
16680 let fv = if g {
16683 self.func_g("fa_decode_vec_q_v3")
16684 } else {
16685 self.func("fa_decode_vec_q_v3")
16686 };
16687 let shmem = (32 * head_dim * 2) as u32; (
16689 fv,
16690 LaunchConfig {
16691 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16692 block_dim: (32, gqa, 1),
16693 shared_mem_bytes: shmem,
16694 },
16695 )
16696 } else if fa_v2_on() {
16697 let fv = if g {
16701 self.func_g("fa_decode_vec_q_v2")
16702 } else {
16703 self.func("fa_decode_vec_q_v2")
16704 };
16705 let shmem = (2 * 32 * head_dim * 2) as u32; (
16707 fv,
16708 LaunchConfig {
16709 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16710 block_dim: (32, gqa, 1),
16711 shared_mem_bytes: shmem,
16712 },
16713 )
16714 } else if smem_tkv > 0 && t_kv >= smem_tkv && !g && !(head_dim == 512 && Self::gkv_on())
16715 {
16716 let fv = if g {
16720 self.func_g("fa_decode_vec_q_smem")
16721 } else {
16722 self.func("fa_decode_vec_q_smem")
16723 };
16724 let shmem = (2 * 32 * head_dim * 2) as u32; use cudarc::driver::sys::CUfunction_attribute_enum as A;
16726 fv.set_attribute(
16727 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16728 shmem as i32,
16729 )?;
16730 (
16731 fv,
16732 LaunchConfig {
16733 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16734 block_dim: (32, gqa, 1),
16735 shared_mem_bytes: shmem,
16736 },
16737 )
16738 } else {
16739 let fv = if g {
16742 self.func_g("fa_decode_vec_q")
16743 } else {
16744 self.func("fa_decode_vec_q")
16745 };
16746 (
16747 fv,
16748 LaunchConfig {
16749 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16750 block_dim: (32, gqa, 1),
16751 shared_mem_bytes: 0,
16752 },
16753 )
16754 }
16755 } else {
16756 return self.fa_decode_scalar_unified(
16759 q,
16760 k,
16761 v,
16762 o,
16763 head_dim,
16764 n_head,
16765 n_head_kv,
16766 t_kv,
16767 None,
16768 scale,
16769 n_splits,
16770 if fa_vec { sp } else { 256 },
16771 k_tok_bytes,
16772 v_tok_bytes,
16773 g,
16774 part_o,
16775 part_m,
16776 part_l,
16777 None,
16778 );
16779 };
16780 let __s_b = self.gpu.stream();
16781 let mut b = __s_b.launch_builder(&f);
16782 b.arg(q)
16783 .arg(k)
16784 .arg(v)
16785 .arg(&mut *part_o)
16786 .arg(&mut *part_m)
16787 .arg(&mut *part_l)
16788 .arg(&hd)
16789 .arg(&nh)
16790 .arg(&nhkv)
16791 .arg(&tkvi)
16792 .arg(&scale)
16793 .arg(&nsp)
16794 .arg(&ktb)
16795 .arg(&vtb);
16796 unsafe {
16797 b.launch(cfg)?;
16798 }
16799 let (fc, cfg2) = (
16802 if g {
16803 self.func_g("fa_decode_combine_f32")
16804 } else {
16805 self.fa_func("fa_decode_combine_f32", head_dim)
16806 },
16807 LaunchConfig {
16808 grid_dim: (n_head as u32, 1, 1),
16809 block_dim: (head_dim as u32, 1, 1),
16810 shared_mem_bytes: 0,
16811 },
16812 );
16813 let __s_b2 = self.gpu.stream();
16814 let mut b2 = __s_b2.launch_builder(&fc);
16815 b2.arg(&*part_o)
16816 .arg(&*part_m)
16817 .arg(&*part_l)
16818 .arg(o)
16819 .arg(&hd)
16820 .arg(&nh)
16821 .arg(&nsp);
16822 unsafe {
16823 b2.launch(cfg2)?;
16824 }
16825 Ok(())
16826 }
16827
16828 #[allow(clippy::too_many_arguments)]
16839 pub fn fa_decode_batch_seqs_v4(
16840 &self,
16841 q: &CudaSlice<f32>,
16842 kv_ptrs: &cudarc::driver::CudaView<u64>,
16843 pos_seq: &CudaSlice<i32>,
16844 o: &mut CudaSlice<f32>,
16845 head_dim: usize,
16846 n_head: usize,
16847 n_head_kv: usize,
16848 b_n: usize,
16849 t_kv_max: usize,
16850 scale: f32,
16851 split_keys: usize,
16852 k_tok_bytes: usize,
16853 v_tok_bytes: usize,
16854 ) -> Result<(), Box<dyn std::error::Error>> {
16855 debug_assert!(head_dim == 256, "seqs twin is v4-stamped (hd256 only)");
16856 let n_splits_max = (t_kv_max + split_keys - 1) / split_keys;
16857 let o_len = b_n * n_head * n_splits_max * head_dim;
16858 let ml_len = b_n * n_head * n_splits_max;
16859 let mut part_guard = self.fa_part_pool.lock().unwrap();
16860 if part_guard
16861 .as_ref()
16862 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
16863 .unwrap_or(true)
16864 {
16865 let old = part_guard.take();
16876 let (co, cm) = old
16877 .as_ref()
16878 .map(|pp| (pp.0.len(), pp.1.len()))
16879 .unwrap_or((0, 0));
16880 if let Some(old) = old {
16881 self.fa_part_retired.lock().unwrap().push(old);
16882 }
16883 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
16884 eprintln!(
16885 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
16886 co, o_len, cm, ml_len
16887 );
16888 }
16889 *part_guard = Some((
16890 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
16891 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
16892 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
16893 ));
16894 }
16895 let pg = part_guard.as_mut().unwrap();
16896 self.gpu
16897 .stream()
16898 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
16899 self.gpu
16900 .stream()
16901 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
16902 self.gpu
16903 .stream()
16904 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
16905 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
16906 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
16907 let (nspm, spk) = (n_splits_max as i32, split_keys as i32);
16908 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16909 let gqa = (n_head / n_head_kv).max(1) as u32;
16910 let f = self.func("fa_decode_vec_q_seqs_v4");
16911 let shmem = (11520 + 32 * head_dim * 2) as u32;
16913 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16914 f.set_attribute(
16915 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16916 shmem as i32,
16917 )?;
16918 let cfg = LaunchConfig {
16919 grid_dim: (n_head_kv as u32, n_splits_max as u32, b_n as u32),
16920 block_dim: (32, gqa, 1),
16921 shared_mem_bytes: shmem,
16922 };
16923 {
16924 let __s_b = self.gpu.stream();
16925 let mut b = __s_b.launch_builder(&f);
16926 b.arg(q)
16927 .arg(kv_ptrs)
16928 .arg(pos_seq)
16929 .arg(&mut *part_o)
16930 .arg(&mut *part_m)
16931 .arg(&mut *part_l)
16932 .arg(&hd)
16933 .arg(&nh)
16934 .arg(&nhkv)
16935 .arg(&scale)
16936 .arg(&nspm)
16937 .arg(&spk)
16938 .arg(&ktb)
16939 .arg(&vtb);
16940 unsafe {
16941 b.launch(cfg)?;
16942 }
16943 }
16944 let fc = self.func("fa_decode_combine_seqs");
16945 let cfg2 = LaunchConfig {
16946 grid_dim: (n_head as u32, b_n as u32, 1),
16947 block_dim: (head_dim as u32, 1, 1),
16948 shared_mem_bytes: 0,
16949 };
16950 let __s_b2 = self.gpu.stream();
16951 let mut b2 = __s_b2.launch_builder(&fc);
16952 b2.arg(&*part_o)
16953 .arg(&*part_m)
16954 .arg(&*part_l)
16955 .arg(o)
16956 .arg(&hd)
16957 .arg(&nh)
16958 .arg(pos_seq)
16959 .arg(&nspm)
16960 .arg(&spk);
16961 unsafe {
16962 b2.launch(cfg2)?;
16963 }
16964 Ok(())
16965 }
16966
16967 #[allow(clippy::too_many_arguments)]
16974 pub fn append_kv_quantized_seqs(
16975 &self,
16976 k_rows: &CudaSlice<f32>,
16977 v_rows: &CudaSlice<f32>,
16978 kv_ptrs: &cudarc::driver::CudaView<u64>,
16979 pos_seq: &CudaSlice<i32>,
16980 b_n: usize,
16981 kv_dim_k: usize,
16982 kv_dim_v: usize,
16983 k_tok_bytes: usize,
16984 v_tok_bytes: usize,
16985 ) -> Result<(), Box<dyn std::error::Error>> {
16986 let f = self.func("append_quantize_kv_q8_0_q5_1_seqs");
16987 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
16988 let cfg = LaunchConfig {
16989 grid_dim: (nblk, b_n as u32, 1),
16990 block_dim: (32, 1, 1),
16991 shared_mem_bytes: 0,
16992 };
16993 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
16994 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16995 let __s_b = self.gpu.stream();
16996 let mut b = __s_b.launch_builder(&f);
16997 b.arg(k_rows)
16998 .arg(v_rows)
16999 .arg(kv_ptrs)
17000 .arg(pos_seq)
17001 .arg(&kdk)
17002 .arg(&kdv)
17003 .arg(&ktb)
17004 .arg(&vtb);
17005 unsafe {
17006 b.launch(cfg)?;
17007 }
17008 Ok(())
17009 }
17010
17011 pub fn fa_rows_eligible(&self, base_len: usize, head_dim: usize) -> bool {
17017 std::env::var("MEMRA_NO_FA_VEC").is_err()
17018 && std::env::var("MEMRA_FA_ROWS_OFF").is_err()
17019 && base_len + 1 >= fa_vec_min_tkv()
17020 && head_dim <= 256
17021 && head_dim % 32 == 0
17022 }
17023
17024 #[allow(clippy::too_many_arguments)]
17033 pub fn fa_decode_rows(
17034 &self,
17035 q: &CudaSlice<f32>,
17036 k: &cudarc::driver::CudaView<u8>,
17037 v: &cudarc::driver::CudaView<u8>,
17038 o: &mut CudaSlice<f32>,
17039 head_dim: usize,
17040 n_head: usize,
17041 n_head_kv: usize,
17042 base_len: usize,
17043 t: usize,
17044 scale: f32,
17045 k_tok_bytes: usize,
17046 v_tok_bytes: usize,
17047 base_dev: Option<(&CudaSlice<i32>, i32)>,
17051 kv_shared: bool,
17054 g: bool,
17058 mut q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
17061 ) -> Result<(), Box<dyn std::error::Error>> {
17062 debug_assert!(base_len + 1 >= fa_vec_min_tkv() && head_dim <= 512 && head_dim % 32 == 0);
17063 let t_kv_max = base_len + t; let mut sp = fa_split_keys(t_kv_max, n_head_kv); if head_dim == 512 {
17070 static SP512: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17071 let v = *SP512.get_or_init(|| {
17074 std::env::var("MEMRA_FA_SP512")
17075 .ok()
17076 .and_then(|x| x.parse().ok())
17077 .unwrap_or(0)
17078 });
17079 sp = if v >= 8 {
17080 v
17081 } else {
17082 FA_SP512_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
17083 };
17084 }
17085 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17086 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17087 let gqa = (n_head / n_head_kv).max(1) as u32;
17088 let mut groups: Vec<(usize, usize, usize)> = Vec::new(); if head_dim == 512 || fa_split_keys(base_len + 1, n_head_kv) == sp {
17099 groups.push((0, t, sp));
17100 } else {
17101 let mut r0 = 0usize;
17102 while r0 < t {
17103 let sp_g = fa_split_keys(base_len + r0 + 1, n_head_kv);
17104 let mut r1 = r0 + 1;
17105 while r1 < t && fa_split_keys(base_len + r1 + 1, n_head_kv) == sp_g {
17106 r1 += 1;
17107 }
17108 groups.push((r0, r1 - r0, sp_g));
17109 r0 = r1;
17110 }
17111 }
17112 static SMEM_TKV_R: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17116 let smem_tkv = *SMEM_TKV_R.get_or_init(|| {
17117 std::env::var("MEMRA_FA_SMEM_TKV")
17118 .ok()
17119 .and_then(|v| v.parse().ok())
17120 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
17121 });
17122 let v4 = fa_v4_at(base_len + t) && head_dim == 256;
17123 let v3 = fa_v3_active(head_dim);
17124 let smem_rows =
17125 head_dim <= 256 && !v3 && !fa_v2_on() && smem_tkv > 0 && t_kv_max >= smem_tkv;
17126 let _ = kv_shared;
17131 let i2 = head_dim == 512 && std::env::var("MEMRA_FA_I2").as_deref() != Ok("0");
17134 static TB512: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
17148 let tb512 = head_dim == 512
17150 && sp <= 32
17151 && n_head / n_head_kv.max(1) <= 16
17152 && *TB512.get_or_init(|| std::env::var("MEMRA_FA_TB512").as_deref() != Ok("0"));
17153 let fname = if tb512 {
17154 "fa_decode_vec_q_rows_v4_512_tb"
17155 } else if i2 {
17156 "fa_decode_vec_q_rows_dpl16_i2"
17157 } else if head_dim == 512 {
17158 "fa_decode_vec_q_rows_dpl16"
17159 }
17160 else if v4 {
17162 "fa_decode_vec_q_rows_v4"
17163 } else if v3 {
17164 "fa_decode_vec_q_rows_v3"
17165 } else if fa_v2_on() {
17166 "fa_decode_vec_q_rows_v2"
17167 } else if smem_rows {
17168 "fa_decode_vec_q_rows_smem"
17169 } else {
17170 "fa_decode_vec_q_rows"
17171 };
17172 let f = if head_dim == 512 {
17173 self.fa_func(fname, head_dim)
17174 } else if g {
17175 self.func_g(if smem_rows {
17183 "fa_decode_vec_q_rows"
17184 } else {
17185 fname
17186 })
17187 } else {
17188 self.func(fname)
17189 };
17190 let shmem = if tb512 {
17191 let gk = Self::gkv_on();
17193 let sh =
17194 (8192 + 1024 + 32 * 512 + 32 * 64 + 32 * head_dim * if gk { 1 } else { 2 }) as u32;
17195 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17196 f.set_attribute(
17197 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17198 sh as i32,
17199 )?;
17200 sh
17201 } else if v4 || v3 || smem_rows || fa_v2_on() {
17202 let sh = (if v4 {
17204 11520 + 32 * head_dim * if g { 1 } else { 2 }
17205 } else if v3 {
17206 32 * head_dim * 2
17207 } else {
17208 2 * 32 * head_dim * 2
17209 }) as u32;
17210 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17211 f.set_attribute(
17212 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17213 sh as i32,
17214 )?;
17215 sh
17216 } else {
17217 0
17218 };
17219 for &(r0, t_g, sp_g) in &groups {
17223 let n_splits_g = (base_len + r0 + t_g).div_ceil(sp_g);
17224 let (nspm, spk) = (n_splits_g as i32, sp_g as i32);
17225 let base_i = (base_len + r0) as i32;
17226 let o_len = t_g * n_head * n_splits_g * head_dim;
17227 let ml_len = t_g * n_head * n_splits_g;
17228 let mut part_guard = self.fa_part_pool.lock().unwrap();
17229 if part_guard
17230 .as_ref()
17231 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17232 .unwrap_or(true)
17233 {
17234 let old = part_guard.take();
17245 let (co, cm) = old
17246 .as_ref()
17247 .map(|pp| (pp.0.len(), pp.1.len()))
17248 .unwrap_or((0, 0));
17249 if let Some(old) = old {
17250 self.fa_part_retired.lock().unwrap().push(old);
17251 }
17252 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17253 eprintln!(
17254 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17255 co, o_len, cm, ml_len
17256 );
17257 }
17258 *part_guard = Some((
17259 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17260 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17261 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17262 ));
17263 }
17264 let pg = part_guard.as_mut().unwrap();
17265 self.gpu
17266 .stream()
17267 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17268 self.gpu
17269 .stream()
17270 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17271 self.gpu
17272 .stream()
17273 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17274 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17275 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
17276 let qv = self.view(q, t * n_head * head_dim);
17277 let q_g = qv.slice(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
17278 let cfg = LaunchConfig {
17279 grid_dim: (n_head_kv as u32, n_splits_g as u32, t_g as u32),
17280 block_dim: (32, gqa, 1),
17281 shared_mem_bytes: shmem,
17282 };
17283 {
17284 let __s_b = self.gpu.stream();
17285 let mut b = __s_b.launch_builder(&f);
17286 if tb512 {
17287 let (bd, plus) =
17289 base_dev.expect("hd512 rows twin requires a device base counter");
17290 let plus_g = plus + r0 as i32;
17291 let nr = t_g as i32;
17292 if Self::pdl_on() && Self::pdl_wb_on() {
17293 use cudarc::driver::{DevicePtr, DevicePtrMut};
17295 let s = &self.gpu.stream();
17296 let (pq, _b0) = q_g.device_ptr(s);
17297 let (pk, _b1) = k.device_ptr(s);
17298 let (pv, _b2) = v.device_ptr(s);
17299 let (po, _b3) = part_o.device_ptr_mut(s);
17300 let (pm, _b4) = part_m.device_ptr_mut(s);
17301 let (pl, _b5) = part_l.device_ptr_mut(s);
17302 let (pb, _b6) = bd.device_ptr(s);
17303 let mut ps = [
17304 &pq as *const _ as *mut std::ffi::c_void,
17305 &pk as *const _ as *mut _,
17306 &pv as *const _ as *mut _,
17307 &po as *const _ as *mut _,
17308 &pm as *const _ as *mut _,
17309 &pl as *const _ as *mut _,
17310 &hd as *const _ as *mut _,
17311 &nh as *const _ as *mut _,
17312 &nhkv as *const _ as *mut _,
17313 &pb as *const _ as *mut _,
17314 &plus_g as *const _ as *mut _,
17315 &scale as *const _ as *mut _,
17316 &nspm as *const _ as *mut _,
17317 &spk as *const _ as *mut _,
17318 &ktb as *const _ as *mut _,
17319 &vtb as *const _ as *mut _,
17320 &nr as *const _ as *mut _,
17321 ];
17322 unsafe {
17323 self.launch_pdl_flash(
17324 Self::gkv_on(),
17325 "fa_decode_vec_q_rows_v4_512_tb",
17326 (n_head_kv as u32, n_splits_g as u32, 1),
17327 (32, gqa, 1),
17328 shmem,
17329 &mut ps,
17330 )?;
17331 }
17332 } else {
17333 let cfg_tb = LaunchConfig {
17334 grid_dim: (n_head_kv as u32, n_splits_g as u32, 1),
17335 block_dim: (32, gqa, 1),
17336 shared_mem_bytes: shmem,
17337 };
17338 b.arg(&q_g)
17339 .arg(k)
17340 .arg(v)
17341 .arg(&mut *part_o)
17342 .arg(&mut *part_m)
17343 .arg(&mut *part_l)
17344 .arg(&hd)
17345 .arg(&nh)
17346 .arg(&nhkv)
17347 .arg(bd)
17348 .arg(&plus_g)
17349 .arg(&scale)
17350 .arg(&nspm)
17351 .arg(&spk)
17352 .arg(&ktb)
17353 .arg(&vtb)
17354 .arg(&nr);
17355 unsafe {
17356 b.launch(cfg_tb)?;
17357 }
17358 }
17359 } else if head_dim == 512 {
17360 let (bd, plus) =
17361 base_dev.expect("hd512 rows twin requires a device base counter");
17362 let plus_g = plus + r0 as i32;
17363 b.arg(&q_g)
17364 .arg(k)
17365 .arg(v)
17366 .arg(&mut *part_o)
17367 .arg(&mut *part_m)
17368 .arg(&mut *part_l)
17369 .arg(&hd)
17370 .arg(&nh)
17371 .arg(&nhkv)
17372 .arg(bd)
17373 .arg(&plus_g)
17374 .arg(&scale)
17375 .arg(&nspm)
17376 .arg(&spk)
17377 .arg(&ktb)
17378 .arg(&vtb);
17379 unsafe {
17380 b.launch(cfg)?;
17381 }
17382 } else {
17383 b.arg(&q_g)
17384 .arg(k)
17385 .arg(v)
17386 .arg(&mut *part_o)
17387 .arg(&mut *part_m)
17388 .arg(&mut *part_l)
17389 .arg(&hd)
17390 .arg(&nh)
17391 .arg(&nhkv)
17392 .arg(&base_i)
17393 .arg(&scale)
17394 .arg(&nspm)
17395 .arg(&spk)
17396 .arg(&ktb)
17397 .arg(&vtb);
17398 unsafe {
17399 b.launch(cfg)?;
17400 }
17401 }
17402 }
17403 let cfg2 = LaunchConfig {
17404 grid_dim: (n_head as u32, t_g as u32, 1),
17405 block_dim: (head_dim as u32, 1, 1),
17406 shared_mem_bytes: 0,
17407 };
17408 let mut o_g = o.slice_mut(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
17409 if head_dim == 512 {
17410 let (bd, plus) = base_dev.unwrap();
17413 let plus_g = plus + r0 as i32;
17414 if let Some((oq, od)) = q8_out.as_mut() {
17415 debug_assert!(t == 1, "rows q8 emit is a t=1 decode arm");
17417 if Self::pdl_on() && Self::pdl_wb_on() {
17418 use cudarc::driver::{DevicePtr, DevicePtrMut};
17420 let s = &self.gpu.stream();
17421 let (po, _g0) = part_o.device_ptr(s);
17422 let (pm, _g1) = part_m.device_ptr(s);
17423 let (pl, _g2) = part_l.device_ptr(s);
17424 let (pq, _g3) = oq.device_ptr_mut(s);
17425 let (pd, _g4) = od.device_ptr_mut(s);
17426 let (pb, _g5) = bd.device_ptr(s);
17427 let mut ps = [
17428 &po as *const _ as *mut std::ffi::c_void,
17429 &pm as *const _ as *mut _,
17430 &pl as *const _ as *mut _,
17431 &pq as *const _ as *mut _,
17432 &pd as *const _ as *mut _,
17433 &hd as *const _ as *mut _,
17434 &nh as *const _ as *mut _,
17435 &pb as *const _ as *mut _,
17436 &plus_g as *const _ as *mut _,
17437 &nspm as *const _ as *mut _,
17438 &spk as *const _ as *mut _,
17439 ];
17440 unsafe {
17441 self.launch_pdl_flash(
17442 Self::gkv_on(),
17443 "fa_decode_combine_rows_dc_q8_1",
17444 cfg2.grid_dim,
17445 cfg2.block_dim,
17446 0,
17447 &mut ps,
17448 )?;
17449 }
17450 continue;
17451 }
17452 let fc = self.fa_func("fa_decode_combine_rows_dc_q8_1", head_dim);
17453 let __s_b2 = self.gpu.stream();
17454 let mut b2 = __s_b2.launch_builder(&fc);
17455 b2.arg(&*part_o)
17456 .arg(&*part_m)
17457 .arg(&*part_l)
17458 .arg(&mut **oq)
17459 .arg(&mut **od)
17460 .arg(&hd)
17461 .arg(&nh)
17462 .arg(bd)
17463 .arg(&plus_g)
17464 .arg(&nspm)
17465 .arg(&spk);
17466 unsafe {
17467 b2.launch(cfg2)?;
17468 }
17469 continue;
17470 }
17471 let fc = self.fa_func("fa_decode_combine_rows_dc", head_dim);
17472 let __s_b2 = self.gpu.stream();
17473 let mut b2 = __s_b2.launch_builder(&fc);
17474 b2.arg(&*part_o)
17475 .arg(&*part_m)
17476 .arg(&*part_l)
17477 .arg(&mut o_g)
17478 .arg(&hd)
17479 .arg(&nh)
17480 .arg(bd)
17481 .arg(&plus_g)
17482 .arg(&nspm)
17483 .arg(&spk);
17484 unsafe {
17485 b2.launch(cfg2)?;
17486 }
17487 } else {
17488 assert!(
17491 q8_out.is_none(),
17492 "rows q8 emit requires the hd512 dc combine"
17493 );
17494 let fc = self.func("fa_decode_combine_rows");
17495 let __s_b2 = self.gpu.stream();
17496 let mut b2 = __s_b2.launch_builder(&fc);
17497 b2.arg(&*part_o)
17498 .arg(&*part_m)
17499 .arg(&*part_l)
17500 .arg(&mut o_g)
17501 .arg(&hd)
17502 .arg(&nh)
17503 .arg(&base_i)
17504 .arg(&nspm)
17505 .arg(&spk);
17506 unsafe {
17507 b2.launch(cfg2)?;
17508 }
17509 }
17510 }
17511 Ok(())
17512 }
17513
17514 #[allow(clippy::too_many_arguments)]
17518 pub fn fa_decode_rows_w(
17519 &self,
17520 q: &CudaSlice<f32>,
17521 k: &cudarc::driver::CudaView<u8>,
17522 v: &cudarc::driver::CudaView<u8>,
17523 o: &mut CudaSlice<f32>,
17524 head_dim: usize,
17525 n_head: usize,
17526 n_head_kv: usize,
17527 base_dev: &CudaSlice<i32>,
17528 base_plus: i32,
17529 t: usize,
17530 scale: f32,
17531 window: usize,
17532 k_tok_bytes: usize,
17533 v_tok_bytes: usize,
17534 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
17535 ) -> Result<(), Box<dyn std::error::Error>> {
17536 debug_assert!(head_dim == 256);
17541 let sp = {
17549 static SPW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17550 let v = *SPW.get_or_init(|| {
17551 std::env::var("MEMRA_FA_SPW")
17552 .ok()
17553 .and_then(|x| x.parse().ok())
17554 .unwrap_or(0)
17555 });
17556 if v >= 8 {
17557 v
17558 } else {
17559 FA_SPW_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
17560 }
17561 };
17562 let n_splits_max = (window + sp - 1) / sp;
17563 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17564 let (nspm, spk, wini) = (n_splits_max as i32, sp as i32, window as i32);
17565 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17566 let gqa = (n_head / n_head_kv).max(1) as u32;
17567 let o_len = t * n_head * n_splits_max * head_dim;
17568 let ml_len = t * n_head * n_splits_max;
17569 let mut part_guard = self.fa_part_pool.lock().unwrap();
17570 if part_guard
17571 .as_ref()
17572 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17573 .unwrap_or(true)
17574 {
17575 let old = part_guard.take();
17586 let (co, cm) = old
17587 .as_ref()
17588 .map(|pp| (pp.0.len(), pp.1.len()))
17589 .unwrap_or((0, 0));
17590 if let Some(old) = old {
17591 self.fa_part_retired.lock().unwrap().push(old);
17592 }
17593 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17594 eprintln!(
17595 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17596 co, o_len, cm, ml_len
17597 );
17598 }
17599 *part_guard = Some((
17600 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17601 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17602 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17603 ));
17604 }
17605 let pg = part_guard.as_mut().unwrap();
17606 self.gpu
17607 .stream()
17608 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17609 self.gpu
17610 .stream()
17611 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17612 self.gpu
17613 .stream()
17614 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17615 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17616 static SMEM_TKV_W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17622 let smem_tkv = *SMEM_TKV_W.get_or_init(|| {
17623 std::env::var("MEMRA_FA_SMEM_TKV")
17624 .ok()
17625 .and_then(|v| v.parse().ok())
17626 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
17627 });
17628 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17634 let wg = Self::wkv_on();
17639 let sp2 =
17642 gqa <= 4 && fa_v4_at(window) && std::env::var("MEMRA_FA_SPW2").as_deref() != Ok("0");
17643 if sp2 {
17644 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
17645 if Self::pdl_on() && Self::pdl_wb_on() {
17646 use cudarc::driver::{DevicePtr, DevicePtrMut};
17648 let s = &self.gpu.stream();
17649 let (pq, _b0) = q.device_ptr(s);
17650 let (pk, _b1) = k.device_ptr(s);
17651 let (pv, _b2) = v.device_ptr(s);
17652 let (po, _b3) = part_o.device_ptr_mut(s);
17653 let (pm, _b4) = part_m.device_ptr_mut(s);
17654 let (pl, _b5) = part_l.device_ptr_mut(s);
17655 let (pb, _b6) = base_dev.device_ptr(s);
17656 let mut ps = [
17657 &pq as *const _ as *mut std::ffi::c_void,
17658 &pk as *const _ as *mut _,
17659 &pv as *const _ as *mut _,
17660 &po as *const _ as *mut _,
17661 &pm as *const _ as *mut _,
17662 &pl as *const _ as *mut _,
17663 &hd as *const _ as *mut _,
17664 &nh as *const _ as *mut _,
17665 &nhkv as *const _ as *mut _,
17666 &pb as *const _ as *mut _,
17667 &base_plus as *const _ as *mut _,
17668 &scale as *const _ as *mut _,
17669 &nspm as *const _ as *mut _,
17670 &spk as *const _ as *mut _,
17671 &ktb as *const _ as *mut _,
17672 &vtb as *const _ as *mut _,
17673 &wini as *const _ as *mut _,
17674 ];
17675 unsafe {
17676 self.launch_pdl_flash(
17677 wg,
17678 "fa_decode_vec_q_rows_v4_w_sp",
17679 (n_head_kv as u32, n_splits_max as u32, t as u32),
17680 (32, gqa + 1, 1),
17681 sh,
17682 &mut ps,
17683 )?;
17684 }
17685 } else {
17686 let f = if wg {
17687 self.func_g("fa_decode_vec_q_rows_v4_w_sp")
17688 } else {
17689 self.func("fa_decode_vec_q_rows_v4_w_sp")
17690 };
17691 f.set_attribute(
17692 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17693 sh as i32,
17694 )?;
17695 let cfg = LaunchConfig {
17696 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
17697 block_dim: (32, gqa + 1, 1),
17698 shared_mem_bytes: sh,
17699 };
17700 let __s_b = self.gpu.stream();
17701 let mut b = __s_b.launch_builder(&f);
17702 b.arg(q)
17703 .arg(k)
17704 .arg(v)
17705 .arg(&mut *part_o)
17706 .arg(&mut *part_m)
17707 .arg(&mut *part_l)
17708 .arg(&hd)
17709 .arg(&nh)
17710 .arg(&nhkv)
17711 .arg(base_dev)
17712 .arg(&base_plus)
17713 .arg(&scale)
17714 .arg(&nspm)
17715 .arg(&spk)
17716 .arg(&ktb)
17717 .arg(&vtb)
17718 .arg(&wini);
17719 unsafe {
17720 b.launch(cfg)?;
17721 }
17722 }
17723 } else {
17724 if fa_v4_at(window) && Self::pdl_on() && Self::pdl_wb_on() {
17725 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
17727 use cudarc::driver::{DevicePtr, DevicePtrMut};
17728 let s = &self.gpu.stream();
17729 let (pq, _b0) = q.device_ptr(s);
17730 let (pk, _b1) = k.device_ptr(s);
17731 let (pv, _b2) = v.device_ptr(s);
17732 let (po, _b3) = part_o.device_ptr_mut(s);
17733 let (pm, _b4) = part_m.device_ptr_mut(s);
17734 let (pl, _b5) = part_l.device_ptr_mut(s);
17735 let (pb, _b6) = base_dev.device_ptr(s);
17736 let mut ps = [
17737 &pq as *const _ as *mut std::ffi::c_void,
17738 &pk as *const _ as *mut _,
17739 &pv as *const _ as *mut _,
17740 &po as *const _ as *mut _,
17741 &pm as *const _ as *mut _,
17742 &pl as *const _ as *mut _,
17743 &hd as *const _ as *mut _,
17744 &nh as *const _ as *mut _,
17745 &nhkv as *const _ as *mut _,
17746 &pb as *const _ as *mut _,
17747 &base_plus as *const _ as *mut _,
17748 &scale as *const _ as *mut _,
17749 &nspm as *const _ as *mut _,
17750 &spk as *const _ as *mut _,
17751 &ktb as *const _ as *mut _,
17752 &vtb as *const _ as *mut _,
17753 &wini as *const _ as *mut _,
17754 ];
17755 unsafe {
17756 self.launch_pdl_flash(
17757 wg,
17758 "fa_decode_vec_q_rows_v4_w",
17759 (n_head_kv as u32, n_splits_max as u32, t as u32),
17760 (32, gqa, 1),
17761 sh,
17762 &mut ps,
17763 )?;
17764 }
17765 } else {
17766 let pick = |name: &str| {
17767 if wg {
17768 self.func_g(name)
17769 } else {
17770 self.func(name)
17771 }
17772 };
17773 let (f, sh) = if fa_v4_at(window) {
17774 let f = pick("fa_decode_vec_q_rows_v4_w");
17775 (f, (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32)
17776 } else if smem_tkv > 0 && window >= smem_tkv {
17777 (
17780 pick("fa_decode_vec_q_rows_smem_w"),
17781 (2 * 32 * head_dim * 2) as u32,
17782 )
17783 } else {
17784 (pick("fa_decode_vec_q_rows_reg_w"), 0u32)
17785 };
17786 f.set_attribute(
17787 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17788 sh as i32,
17789 )?;
17790 let cfg = LaunchConfig {
17791 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
17792 block_dim: (32, gqa, 1),
17793 shared_mem_bytes: sh,
17794 };
17795 let __s_b = self.gpu.stream();
17796 let mut b = __s_b.launch_builder(&f);
17797 b.arg(q)
17798 .arg(k)
17799 .arg(v)
17800 .arg(&mut *part_o)
17801 .arg(&mut *part_m)
17802 .arg(&mut *part_l)
17803 .arg(&hd)
17804 .arg(&nh)
17805 .arg(&nhkv)
17806 .arg(base_dev)
17807 .arg(&base_plus)
17808 .arg(&scale)
17809 .arg(&nspm)
17810 .arg(&spk)
17811 .arg(&ktb)
17812 .arg(&vtb)
17813 .arg(&wini);
17814 unsafe {
17815 b.launch(cfg)?;
17816 }
17817 }
17818 }
17819 let cfg2 = LaunchConfig {
17820 grid_dim: (n_head as u32, t as u32, 1),
17821 block_dim: (head_dim as u32, 1, 1),
17822 shared_mem_bytes: 0,
17823 };
17824 if let Some((oq, od)) = q8_out {
17825 if Self::pdl_on() && Self::pdl_wb_on() {
17828 use cudarc::driver::{DevicePtr, DevicePtrMut};
17830 let s = &self.gpu.stream();
17831 let (po, _g0) = part_o.device_ptr(s);
17832 let (pm, _g1) = part_m.device_ptr(s);
17833 let (pl, _g2) = part_l.device_ptr(s);
17834 let (pq, _g3) = oq.device_ptr_mut(s);
17835 let (pd, _g4) = od.device_ptr_mut(s);
17836 let mut ps = [
17837 &po as *const _ as *mut std::ffi::c_void,
17838 &pm as *const _ as *mut _,
17839 &pl as *const _ as *mut _,
17840 &pq as *const _ as *mut _,
17841 &pd as *const _ as *mut _,
17842 &hd as *const _ as *mut _,
17843 &nh as *const _ as *mut _,
17844 &nspm as *const _ as *mut _,
17845 &spk as *const _ as *mut _,
17846 &wini as *const _ as *mut _,
17847 ];
17848 unsafe {
17849 self.launch_pdl_flash(
17850 wg,
17851 "fa_decode_combine_rows_w_q8_1",
17852 cfg2.grid_dim,
17853 cfg2.block_dim,
17854 0,
17855 &mut ps,
17856 )?;
17857 }
17858 return Ok(());
17859 }
17860 let fc = if wg {
17861 self.func_g("fa_decode_combine_rows_w_q8_1")
17862 } else {
17863 self.func("fa_decode_combine_rows_w_q8_1")
17864 };
17865 let __s_b2 = self.gpu.stream();
17866 let mut b2 = __s_b2.launch_builder(&fc);
17867 b2.arg(&*part_o)
17868 .arg(&*part_m)
17869 .arg(&*part_l)
17870 .arg(oq)
17871 .arg(od)
17872 .arg(&hd)
17873 .arg(&nh)
17874 .arg(&nspm)
17875 .arg(&spk)
17876 .arg(&wini);
17877 unsafe {
17878 b2.launch(cfg2)?;
17879 }
17880 return Ok(());
17881 }
17882 let fc = if wg {
17883 self.func_g("fa_decode_combine_rows_w")
17884 } else {
17885 self.func("fa_decode_combine_rows_w")
17886 };
17887 let __s_b2 = self.gpu.stream();
17888 let mut b2 = __s_b2.launch_builder(&fc);
17889 b2.arg(&*part_o)
17890 .arg(&*part_m)
17891 .arg(&*part_l)
17892 .arg(o)
17893 .arg(&hd)
17894 .arg(&nh)
17895 .arg(&nspm)
17896 .arg(&spk)
17897 .arg(&wini);
17898 unsafe {
17899 b2.launch(cfg2)?;
17900 }
17901 Ok(())
17902 }
17903
17904 #[allow(clippy::too_many_arguments)]
17910 pub fn fa_decode_rows_dc(
17911 &self,
17912 q: &CudaSlice<f32>,
17913 k: &cudarc::driver::CudaView<u8>,
17914 v: &cudarc::driver::CudaView<u8>,
17915 o: &mut CudaSlice<f32>,
17916 head_dim: usize,
17917 n_head: usize,
17918 n_head_kv: usize,
17919 base_dev: &CudaSlice<i32>,
17920 t_kv_upper: usize,
17921 t: usize,
17922 scale: f32,
17923 k_tok_bytes: usize,
17924 v_tok_bytes: usize,
17925 base_plus: i32,
17926 g: bool,
17927 ) -> Result<(), Box<dyn std::error::Error>> {
17928 let v4 = head_dim == 256 && fa_v4_at(t_kv_upper);
17929 assert!(
17930 v4 || fa_v3_active(head_dim),
17931 "stream fa rows requires the v3 or v4 lane"
17932 );
17933 assert!(v4 || base_plus == 0, "v3_dc kernel takes no plus arg");
17934 if v4 {
17935 let sp = fa_split_keys(t_kv_upper, n_head_kv);
17936 let n_splits_max = (t_kv_upper + sp - 1) / sp;
17937 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17938 let (nspm, spk) = (n_splits_max as i32, sp as i32);
17939 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17940 let gqa = (n_head / n_head_kv).max(1) as u32;
17941 let o_len = t * n_head * n_splits_max * head_dim;
17942 let ml_len = t * n_head * n_splits_max;
17943 let mut part_guard = self.fa_part_pool.lock().unwrap();
17944 if part_guard
17945 .as_ref()
17946 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17947 .unwrap_or(true)
17948 {
17949 let old = part_guard.take();
17960 let (co, cm) = old
17961 .as_ref()
17962 .map(|pp| (pp.0.len(), pp.1.len()))
17963 .unwrap_or((0, 0));
17964 if let Some(old) = old {
17965 self.fa_part_retired.lock().unwrap().push(old);
17966 }
17967 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17968 eprintln!(
17969 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17970 co, o_len, cm, ml_len
17971 );
17972 }
17973 *part_guard = Some((
17974 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17975 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17976 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17977 ));
17978 }
17979 let pg = part_guard.as_mut().unwrap();
17980 self.gpu
17981 .stream()
17982 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17983 self.gpu
17984 .stream()
17985 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17986 self.gpu
17987 .stream()
17988 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17989 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17990 let f = if g {
17991 self.func_g("fa_decode_vec_q_rows_v4_dc")
17992 } else {
17993 self.func("fa_decode_vec_q_rows_v4_dc")
17994 };
17995 let sh = (11520 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
17996 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17997 f.set_attribute(
17998 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17999 sh as i32,
18000 )?;
18001 let cfg = LaunchConfig {
18002 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18003 block_dim: (32, gqa, 1),
18004 shared_mem_bytes: sh,
18005 };
18006 let __s_b = self.gpu.stream();
18007 let mut b = __s_b.launch_builder(&f);
18008 b.arg(q)
18009 .arg(k)
18010 .arg(v)
18011 .arg(&mut *part_o)
18012 .arg(&mut *part_m)
18013 .arg(&mut *part_l)
18014 .arg(&hd)
18015 .arg(&nh)
18016 .arg(&nhkv)
18017 .arg(base_dev)
18018 .arg(&base_plus)
18019 .arg(&scale)
18020 .arg(&nspm)
18021 .arg(&spk)
18022 .arg(&ktb)
18023 .arg(&vtb);
18024 unsafe {
18025 b.launch(cfg)?;
18026 }
18027 let fc = self.func("fa_decode_combine_rows_dc");
18028 let cfg2 = LaunchConfig {
18029 grid_dim: (n_head as u32, t as u32, 1),
18030 block_dim: (head_dim as u32, 1, 1),
18031 shared_mem_bytes: 0,
18032 };
18033 let __s_b2 = self.gpu.stream();
18034 let mut b2 = __s_b2.launch_builder(&fc);
18035 b2.arg(&*part_o)
18036 .arg(&*part_m)
18037 .arg(&*part_l)
18038 .arg(o)
18039 .arg(&hd)
18040 .arg(&nh)
18041 .arg(base_dev)
18042 .arg(&base_plus)
18043 .arg(&nspm)
18044 .arg(&spk);
18045 unsafe {
18046 b2.launch(cfg2)?;
18047 }
18048 return Ok(());
18049 }
18050 let sp = fa_split_keys(t_kv_upper, n_head_kv);
18051 let n_splits_max = (t_kv_upper + sp - 1) / sp;
18052 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18053 let (nspm, spk) = (n_splits_max as i32, sp as i32);
18054 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18055 let gqa = (n_head / n_head_kv).max(1) as u32;
18056 let o_len = t * n_head * n_splits_max * head_dim;
18057 let ml_len = t * n_head * n_splits_max;
18058 let mut part_guard = self.fa_part_pool.lock().unwrap();
18059 if part_guard
18060 .as_ref()
18061 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18062 .unwrap_or(true)
18063 {
18064 let old = part_guard.take();
18075 let (co, cm) = old
18076 .as_ref()
18077 .map(|pp| (pp.0.len(), pp.1.len()))
18078 .unwrap_or((0, 0));
18079 if let Some(old) = old {
18080 self.fa_part_retired.lock().unwrap().push(old);
18081 }
18082 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18083 eprintln!(
18084 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18085 co, o_len, cm, ml_len
18086 );
18087 }
18088 *part_guard = Some((
18089 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18090 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18091 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18092 ));
18093 }
18094 let pg = part_guard.as_mut().unwrap();
18095 self.gpu
18096 .stream()
18097 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18098 self.gpu
18099 .stream()
18100 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18101 self.gpu
18102 .stream()
18103 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18104 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18105 let f = self.func("fa_decode_vec_q_rows_v3_dc");
18106 let sh = (32 * head_dim * 2) as u32;
18107 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18108 f.set_attribute(
18109 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18110 sh as i32,
18111 )?;
18112 let cfg = LaunchConfig {
18113 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18114 block_dim: (32, gqa, 1),
18115 shared_mem_bytes: sh,
18116 };
18117 let __s_b = self.gpu.stream();
18118 let mut b = __s_b.launch_builder(&f);
18119 b.arg(q)
18120 .arg(k)
18121 .arg(v)
18122 .arg(&mut *part_o)
18123 .arg(&mut *part_m)
18124 .arg(&mut *part_l)
18125 .arg(&hd)
18126 .arg(&nh)
18127 .arg(&nhkv)
18128 .arg(base_dev)
18129 .arg(&scale)
18130 .arg(&nspm)
18131 .arg(&spk)
18132 .arg(&ktb)
18133 .arg(&vtb);
18134 unsafe {
18135 b.launch(cfg)?;
18136 }
18137 let fc = self.func("fa_decode_combine_rows_dc");
18138 let cfg2 = LaunchConfig {
18139 grid_dim: (n_head as u32, t as u32, 1),
18140 block_dim: (head_dim as u32, 1, 1),
18141 shared_mem_bytes: 0,
18142 };
18143 let plus0 = 0i32;
18144 let __s_b2 = self.gpu.stream();
18145 let mut b2 = __s_b2.launch_builder(&fc);
18146 b2.arg(&*part_o)
18147 .arg(&*part_m)
18148 .arg(&*part_l)
18149 .arg(o)
18150 .arg(&hd)
18151 .arg(&nh)
18152 .arg(base_dev)
18153 .arg(&plus0)
18154 .arg(&nspm)
18155 .arg(&spk);
18156 unsafe {
18157 b2.launch(cfg2)?;
18158 }
18159 Ok(())
18160 }
18161
18162 pub fn fa_decode_dc(
18173 &self,
18174 q: &CudaSlice<f32>,
18175 k: &cudarc::driver::CudaView<u8>,
18176 v: &cudarc::driver::CudaView<u8>,
18177 o: &mut CudaSlice<f32>,
18178 head_dim: usize,
18179 n_head: usize,
18180 n_head_kv: usize,
18181 t_kv_dev: &CudaSlice<i32>,
18182 bucket_max: usize,
18183 scale: f32,
18184 k_tok_bytes: usize,
18185 v_tok_bytes: usize,
18186 g: bool,
18187 ) -> Result<(), Box<dyn std::error::Error>> {
18188 self.fa_decode_dc_q8(
18189 q,
18190 k,
18191 v,
18192 o,
18193 head_dim,
18194 n_head,
18195 n_head_kv,
18196 t_kv_dev,
18197 bucket_max,
18198 scale,
18199 k_tok_bytes,
18200 v_tok_bytes,
18201 g,
18202 None,
18203 )
18204 }
18205
18206 #[allow(clippy::too_many_arguments)]
18209 pub fn fa_decode_dc_q8(
18210 &self,
18211 q: &CudaSlice<f32>,
18212 k: &cudarc::driver::CudaView<u8>,
18213 v: &cudarc::driver::CudaView<u8>,
18214 o: &mut CudaSlice<f32>,
18215 head_dim: usize,
18216 n_head: usize,
18217 n_head_kv: usize,
18218 t_kv_dev: &CudaSlice<i32>,
18219 bucket_max: usize,
18220 scale: f32,
18221 k_tok_bytes: usize,
18222 v_tok_bytes: usize,
18223 g: bool,
18224 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
18225 ) -> Result<(), Box<dyn std::error::Error>> {
18226 let mut fa_vec =
18234 std::env::var("MEMRA_NO_FA_VEC").is_err() && bucket_max >= fa_vec_min_tkv();
18235 if g && head_dim == 256 && !fa_v4_at(bucket_max) {
18236 fa_vec = false;
18237 } let sp = fa_split_keys(bucket_max, n_head_kv);
18239 let n_splits = if fa_vec {
18240 ((bucket_max + sp - 1) / sp).max(1)
18241 } else {
18242 ((bucket_max + 255) / 256).max(1)
18243 };
18244 let o_len = n_head * n_splits * head_dim;
18245 let ml_len = n_head * n_splits;
18246 let mut part_guard = self.fa_part_pool.lock().unwrap();
18247 if part_guard
18248 .as_ref()
18249 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18250 .unwrap_or(true)
18251 {
18252 let old = part_guard.take();
18263 let (co, cm) = old
18264 .as_ref()
18265 .map(|pp| (pp.0.len(), pp.1.len()))
18266 .unwrap_or((0, 0));
18267 if let Some(old) = old {
18268 self.fa_part_retired.lock().unwrap().push(old);
18269 }
18270 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18271 eprintln!(
18272 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18273 co, o_len, cm, ml_len
18274 );
18275 }
18276 *part_guard = Some((
18277 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18278 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18279 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18280 ));
18281 }
18282 let pg = part_guard.as_mut().unwrap();
18283 self.gpu
18284 .stream()
18285 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18286 self.gpu
18287 .stream()
18288 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18289 self.gpu
18290 .stream()
18291 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18292 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18293 let (hd, nh, nhkv, nsp) = (
18294 head_dim as i32,
18295 n_head as i32,
18296 n_head_kv as i32,
18297 n_splits as i32,
18298 );
18299 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18300 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
18301 let deep = fa_vec
18304 && head_dim == 256
18305 && fa_v4_at(bucket_max)
18306 && !g
18307 && fa_deep_at(bucket_max)
18308 && !matches!(fa_v4_mode(), "noB3" | "stage");
18309 let (f, cfg) = if fa_vec
18310 && head_dim == 512
18311 && bucket_max >= {
18312 static FA512_MIN_DC: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18313 *FA512_MIN_DC.get_or_init(|| {
18314 std::env::var("MEMRA_FA512_MIN")
18315 .ok()
18316 .and_then(|v| v.parse().ok())
18317 .unwrap_or(512)
18318 })
18319 } {
18320 let gqa = (n_head / n_head_kv).max(1) as u32;
18322 (
18323 self.fa_func("fa_decode_vec_q_dpl16_dc", head_dim),
18324 LaunchConfig {
18325 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18326 block_dim: (32, gqa, 1),
18327 shared_mem_bytes: 0,
18328 },
18329 )
18330 } else if fa_vec && head_dim == 512 {
18331 let q_view = q.as_view();
18334 let mut o_view = o.as_view_mut();
18335 return self.fa_decode_scalar_unified(
18336 &q_view,
18337 k,
18338 v,
18339 &mut o_view,
18340 head_dim,
18341 n_head,
18342 n_head_kv,
18343 0,
18344 Some(t_kv_dev),
18345 scale,
18346 n_splits,
18347 sp,
18348 k_tok_bytes,
18349 v_tok_bytes,
18350 g,
18351 &mut *part_o,
18352 &mut *part_m,
18353 &mut *part_l,
18354 q8_out,
18355 );
18356 } else if fa_vec && head_dim == 256 && fa_v4_at(bucket_max) {
18357 let gqa = (n_head / n_head_kv).max(1) as u32;
18360 let fv = if g {
18361 self.func_g("fa_decode_vec_q_v4_dc")
18362 } else if deep {
18363 self.func("fa_decode_vec_q_v4_deep_dc")
18364 } else {
18365 self.func("fa_decode_vec_q_v4_dc")
18366 };
18367 let shmem =
18368 (if deep { 12160 } else { 11520 } + 32 * head_dim * if g { 1 } else { 2 }) as u32;
18369 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18370 fv.set_attribute(
18371 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18372 shmem as i32,
18373 )?;
18374 (
18375 fv,
18376 LaunchConfig {
18377 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18378 block_dim: (32, gqa, 1),
18379 shared_mem_bytes: shmem,
18380 },
18381 )
18382 } else if fa_vec && fa_v3_active(head_dim) {
18383 let gqa = (n_head / n_head_kv).max(1) as u32;
18386 let fv = if g {
18387 self.func_g("fa_decode_vec_q_v3_dc")
18388 } else {
18389 self.func("fa_decode_vec_q_v3_dc")
18390 };
18391 let shmem = (32 * head_dim * 2) as u32; (
18393 fv,
18394 LaunchConfig {
18395 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18396 block_dim: (32, gqa, 1),
18397 shared_mem_bytes: shmem,
18398 },
18399 )
18400 } else if fa_vec && fa_v2_on() {
18401 let gqa = (n_head / n_head_kv).max(1) as u32;
18405 let fv = if g {
18406 self.func_g("fa_decode_vec_q_v2_dc")
18407 } else {
18408 self.func("fa_decode_vec_q_v2_dc")
18409 };
18410 let shmem = (2 * 32 * head_dim * 2) as u32; (
18412 fv,
18413 LaunchConfig {
18414 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18415 block_dim: (32, gqa, 1),
18416 shared_mem_bytes: shmem,
18417 },
18418 )
18419 } else if fa_vec {
18420 let gqa = (n_head / n_head_kv).max(1) as u32;
18421 let fv = if g {
18423 self.func_g("fa_decode_vec_q_dc")
18424 } else {
18425 self.func("fa_decode_vec_q_dc")
18426 };
18427 (
18428 fv,
18429 LaunchConfig {
18430 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18431 block_dim: (32, gqa, 1),
18432 shared_mem_bytes: 0,
18433 },
18434 )
18435 } else {
18436 let q_view = q.as_view();
18437 let mut o_view = o.as_view_mut();
18438 return self.fa_decode_scalar_unified(
18439 &q_view,
18440 k,
18441 v,
18442 &mut o_view,
18443 head_dim,
18444 n_head,
18445 n_head_kv,
18446 0,
18447 Some(t_kv_dev),
18448 scale,
18449 n_splits,
18450 if fa_vec { sp } else { 256 },
18451 k_tok_bytes,
18452 v_tok_bytes,
18453 g,
18454 &mut *part_o,
18455 &mut *part_m,
18456 &mut *part_l,
18457 q8_out,
18458 );
18459 };
18460 let ski = sp as i32; let __s_b = self.gpu.stream();
18462 let mut b = __s_b.launch_builder(&f);
18463 b.arg(q)
18464 .arg(k)
18465 .arg(v)
18466 .arg(&mut *part_o)
18467 .arg(&mut *part_m)
18468 .arg(&mut *part_l)
18469 .arg(&hd)
18470 .arg(&nh)
18471 .arg(&nhkv)
18472 .arg(t_kv_dev)
18473 .arg(&scale)
18474 .arg(&nsp)
18475 .arg(&ski)
18476 .arg(&ktb)
18477 .arg(&vtb);
18478 unsafe {
18479 b.launch(cfg)?;
18480 }
18481 let cfg2 = LaunchConfig {
18482 grid_dim: (n_head as u32, 1, 1),
18483 block_dim: (head_dim as u32, 1, 1),
18484 shared_mem_bytes: 0,
18485 };
18486 if let Some((oq, od)) = q8_out {
18487 let fc = if g {
18488 self.func_g("fa_decode_combine_q8_1")
18489 } else {
18490 self.fa_func("fa_decode_combine_q8_1", head_dim)
18491 };
18492 let __s_b2 = self.gpu.stream();
18493 let mut b2 = __s_b2.launch_builder(&fc);
18494 b2.arg(&*part_o)
18495 .arg(&*part_m)
18496 .arg(&*part_l)
18497 .arg(oq)
18498 .arg(od)
18499 .arg(&hd)
18500 .arg(&nh)
18501 .arg(&nsp);
18502 unsafe {
18503 b2.launch(cfg2)?;
18504 }
18505 return Ok(());
18506 }
18507 let fc = if g {
18508 self.func_g("fa_decode_combine_f32")
18509 } else {
18510 self.fa_func("fa_decode_combine_f32", head_dim)
18511 };
18512 let __s_b2 = self.gpu.stream();
18513 let mut b2 = __s_b2.launch_builder(&fc);
18514 b2.arg(&*part_o)
18515 .arg(&*part_m)
18516 .arg(&*part_l)
18517 .arg(o)
18518 .arg(&hd)
18519 .arg(&nh)
18520 .arg(&nsp);
18521 unsafe {
18522 b2.launch(cfg2)?;
18523 }
18524 Ok(())
18525 }
18526
18527 pub fn fa_geom_eager(
18533 &self,
18534 t_kv: usize,
18535 head_dim: usize,
18536 n_head_kv: usize,
18537 g: bool,
18538 ) -> (bool, usize) {
18539 let fa_ok = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
18543 let vec512 = fa_ok && head_dim == 512 && t_kv >= fa512_min_tkv();
18549 let mut fa_vec = vec512 || (fa_ok && head_dim <= 256 && head_dim % 32 == 0);
18550 if g && head_dim == 256 && !fa_v4_at(t_kv) {
18556 fa_vec = false;
18557 }
18558 let sp = fa_split_keys(t_kv, n_head_kv);
18559 let n_splits = if fa_vec {
18560 ((t_kv + sp - 1) / sp).max(1)
18561 } else {
18562 ((t_kv + 255) / 256).max(1)
18563 };
18564 (fa_vec, n_splits)
18565 }
18566
18567 pub fn fa_bucket_key(
18573 &self,
18574 t_kv: usize,
18575 head_dim: usize,
18576 n_head_kv: usize,
18577 g: bool,
18578 ) -> (bool, usize) {
18579 self.fa_geom_eager(t_kv, head_dim, n_head_kv, g)
18580 }
18581
18582 pub fn capture_graph_retained<F>(
18594 &self,
18595 step: F,
18596 ) -> Result<
18597 (
18598 cudarc::driver::CudaGraph,
18599 Vec<Box<dyn std::any::Any + Send>>,
18600 ),
18601 Box<dyn std::error::Error>,
18602 >
18603 where
18604 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
18605 {
18606 use cudarc::driver::sys::CUgraphInstantiate_flags;
18607 self.capture_graph_retained_flags(
18608 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
18609 step,
18610 )
18611 }
18612
18613 pub fn capture_graph_retained_flags<F>(
18618 &self,
18619 flags: cudarc::driver::sys::CUgraphInstantiate_flags,
18620 mut step: F,
18621 ) -> Result<
18622 (
18623 cudarc::driver::CudaGraph,
18624 Vec<Box<dyn std::any::Any + Send>>,
18625 ),
18626 Box<dyn std::error::Error>,
18627 >
18628 where
18629 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
18630 {
18631 use cudarc::driver::sys::CUstreamCaptureMode;
18632 self.capture_keep.lock().unwrap().clear();
18640 let was_tracking = self.gpu.ctx.is_event_tracking();
18641 if was_tracking {
18642 unsafe {
18643 self.gpu.ctx.disable_event_tracking();
18644 }
18645 }
18646 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
18647 self.capture_keep_on
18648 .store(true, std::sync::atomic::Ordering::Relaxed);
18649 let w = (|| {
18650 step(self)?;
18651 step(self)
18652 })();
18653 self.capture_keep_on
18654 .store(false, std::sync::atomic::Ordering::Relaxed);
18655 w?;
18656 self.gpu.stream().synchronize()?;
18657 self.gpu
18658 .stream()
18659 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
18660 let r = step(self);
18661 let g = self.gpu.stream().end_capture(flags);
18662 r?;
18663 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
18664 graph.upload()?;
18665 Ok(graph)
18666 };
18667 let result = run();
18668 self.capture_keep_on
18669 .store(false, std::sync::atomic::Ordering::Relaxed);
18670 if was_tracking {
18671 unsafe {
18672 self.gpu.ctx.enable_event_tracking();
18673 }
18674 }
18675 let keeper = std::mem::take(&mut *self.capture_keep.lock().unwrap());
18676 Ok((result?, keeper))
18677 }
18678
18679 pub fn capture_graph<F>(
18680 &self,
18681 mut step: F,
18682 ) -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>>
18683 where
18684 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
18685 {
18686 use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
18687 let was_tracking = self.gpu.ctx.is_event_tracking();
18695 if was_tracking {
18696 unsafe {
18697 self.gpu.ctx.disable_event_tracking();
18698 }
18699 }
18700 let iflag = {
18707 static F: std::sync::OnceLock<CUgraphInstantiate_flags> = std::sync::OnceLock::new();
18708 *F.get_or_init(|| match std::env::var("MEMRA_GRAPH_IFLAG").as_deref() {
18709 Ok("upload") => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_UPLOAD,
18712 Ok("priority") => {
18713 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY
18714 }
18715 _ => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
18716 })
18717 };
18718 let ct = {
18725 static T: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
18726 *T.get_or_init(|| std::env::var("MEMRA_GRAPH_CAPTIME").as_deref() == Ok("1"))
18727 };
18728 let warmups = {
18751 static W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18752 *W.get_or_init(|| {
18753 std::env::var("MEMRA_GRAPH_WARMUPS")
18754 .ok()
18755 .and_then(|v| v.parse().ok())
18756 .filter(|n| *n >= 1)
18757 .unwrap_or(1)
18758 })
18759 };
18760 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
18761 let t_w = std::time::Instant::now();
18762 for _ in 0..warmups {
18764 step(self)?;
18765 }
18766 self.gpu.stream().synchronize()?;
18767 let ms_warm = t_w.elapsed().as_secs_f64() * 1e3;
18768 let t_c = std::time::Instant::now();
18770 self.gpu
18771 .stream()
18772 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
18773 let r = step(self);
18776 let ms_body = t_c.elapsed().as_secs_f64() * 1e3;
18777 let t_i = std::time::Instant::now();
18778 let g = self.gpu.stream().end_capture(iflag);
18779 let ms_inst = t_i.elapsed().as_secs_f64() * 1e3;
18780 r?;
18781 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
18782 let t_u = std::time::Instant::now();
18783 graph.upload()?;
18784 if ct {
18785 println!(
18786 "[graph-captime] warmup2x {ms_warm:.2} ms capture-body {ms_body:.2} ms \
18787 instantiate {ms_inst:.2} ms upload {:.2} ms",
18788 t_u.elapsed().as_secs_f64() * 1e3
18789 );
18790 }
18791 Ok(graph)
18792 };
18793 let result = run();
18794 if was_tracking {
18795 unsafe {
18796 self.gpu.ctx.enable_event_tracking();
18797 }
18798 }
18799 result
18800 }
18801
18802 pub fn gdn_scan_s128_view(
18804 &self,
18805 q: &CudaSlice<f32>,
18806 k: &CudaSlice<f32>,
18807 v: &CudaSlice<f32>,
18808 g: &CudaSlice<f32>,
18809 beta: &CudaSlice<f32>,
18810 state_in: &cudarc::driver::CudaView<f32>,
18811 state_out: &mut cudarc::driver::CudaViewMut<f32>,
18812 o: &mut CudaSlice<f32>,
18813 n_head: usize,
18814 t: usize,
18815 scale: f32,
18816 ) -> Result<(), Box<dyn std::error::Error>> {
18817 let f = self.func("gdn_scan_s128");
18818 const S_V: u32 = 128;
18819 const WARP: u32 = 32;
18820 const COLS: u32 = 4;
18821 let cfg = LaunchConfig {
18822 grid_dim: (n_head as u32, 1, S_V / COLS),
18823 block_dim: (WARP, COLS, 1),
18824 shared_mem_bytes: 0,
18825 };
18826 let (h, ti) = (n_head as i32, t as i32);
18827 let __s_b = self.gpu.stream();
18828 let mut b = __s_b.launch_builder(&f);
18829 b.arg(q)
18830 .arg(k)
18831 .arg(v)
18832 .arg(g)
18833 .arg(beta)
18834 .arg(state_in)
18835 .arg(state_out)
18836 .arg(o)
18837 .arg(&h)
18838 .arg(&ti)
18839 .arg(&scale);
18840 unsafe {
18841 b.launch(cfg)?;
18842 }
18843 Ok(())
18844 }
18845
18846 pub fn ssm_conv1d_view(
18848 &self,
18849 x: &cudarc::driver::CudaView<f32>,
18850 w: &CudaSlice<f32>,
18851 y: &mut CudaSlice<f32>,
18852 conv_dim: usize,
18853 t: usize,
18854 d_conv: usize,
18855 silu: bool,
18856 ) -> Result<(), Box<dyn std::error::Error>> {
18857 let f = self.func("ssm_conv1d_silu_f32");
18858 let cfg = LaunchConfig {
18860 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
18861 block_dim: (256, 1, 1),
18862 shared_mem_bytes: 0,
18863 };
18864 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
18865 let __s_b = self.gpu.stream();
18866 let mut b = __s_b.launch_builder(&f);
18867 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
18868 unsafe {
18869 b.launch(cfg)?;
18870 }
18871 Ok(())
18872 }
18873
18874 pub fn ssm_conv1d_tm(
18881 &self,
18882 qkv_tm: &CudaSlice<f32>,
18883 w: &CudaSlice<f32>,
18884 y: &mut CudaSlice<f32>,
18885 conv_dim: usize,
18886 t: usize,
18887 d_conv: usize,
18888 ) -> Result<(), Box<dyn std::error::Error>> {
18889 let f = self.func("ssm_conv1d_tm_f32");
18890 let cfg = LaunchConfig {
18891 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
18892 block_dim: (256, 1, 1),
18893 shared_mem_bytes: 0,
18894 };
18895 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
18896 let __s_b = self.gpu.stream();
18897 let mut b = __s_b.launch_builder(&f);
18898 b.arg(qkv_tm).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc);
18899 unsafe {
18900 b.launch(cfg)?;
18901 }
18902 Ok(())
18903 }
18904
18905 pub fn ssm_conv1d_tm_state(
18913 &self,
18914 qkv_tm: &CudaSlice<f32>,
18915 conv_state: &mut CudaSlice<f32>,
18916 w: &CudaSlice<f32>,
18917 y: &mut CudaSlice<f32>,
18918 conv_dim: usize,
18919 t: usize,
18920 d_conv: usize,
18921 ) -> Result<(), Box<dyn std::error::Error>> {
18922 self.ssm_conv1d_tm_state_pad(qkv_tm, conv_state, w, y, conv_dim, t, d_conv, None)
18923 }
18924
18925 #[allow(clippy::too_many_arguments)]
18928 pub fn ssm_conv1d_tm_state_pad(
18929 &self,
18930 qkv_tm: &CudaSlice<f32>,
18931 conv_state: &mut CudaSlice<f32>,
18932 w: &CudaSlice<f32>,
18933 y: &mut CudaSlice<f32>,
18934 conv_dim: usize,
18935 t: usize,
18936 d_conv: usize,
18937 pad_len: Option<&CudaSlice<i32>>,
18938 ) -> Result<(), Box<dyn std::error::Error>> {
18939 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
18940 let ring_old = if t < d_conv - 1 {
18944 Some(self.clone_dtod(conv_state)?)
18945 } else {
18946 None
18947 };
18948 {
18949 let f = self.func("ssm_conv1d_tm_state_f32");
18950 let cfg = LaunchConfig {
18951 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
18952 block_dim: (256, 1, 1),
18953 shared_mem_bytes: 0,
18954 };
18955 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
18956 let __s_b = self.gpu.stream();
18957 let mut b = __s_b.launch_builder(&f);
18958 b.arg(qkv_tm)
18959 .arg(&*conv_state)
18960 .arg(w)
18961 .arg(y)
18962 .arg(&cd)
18963 .arg(&ti)
18964 .arg(&dc);
18965 unsafe {
18966 b.launch(cfg)?;
18967 }
18968 }
18969 match (ring_old, pad_len) {
18970 (None, Some(len_d)) => {
18971 let f = self.func("ssm_conv_ring_update_dev_f32");
18972 let n = conv_dim * (d_conv - 1);
18973 let cfg = LaunchConfig::for_num_elems(n as u32);
18974 let (cd, dc) = (conv_dim as i32, d_conv as i32);
18975 let __s_b = self.gpu.stream();
18976 let mut b = __s_b.launch_builder(&f);
18977 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
18978 unsafe {
18979 b.launch(cfg)?;
18980 }
18981 }
18982 (None, None) => {
18983 let f = self.func("ssm_conv_ring_update_f32");
18984 let n = conv_dim * (d_conv - 1);
18985 let cfg = LaunchConfig::for_num_elems(n as u32);
18986 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
18987 let __s_b = self.gpu.stream();
18988 let mut b = __s_b.launch_builder(&f);
18989 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
18990 unsafe {
18991 b.launch(cfg)?;
18992 }
18993 }
18994 (Some(old), _) => {
18995 self.ssm_conv_ring_rebuild(qkv_tm, &old, conv_state, conv_dim, t, d_conv)?
18996 }
18997 }
18998 Ok(())
18999 }
19000
19001 pub fn ssm_conv1d_tm_state_pad_v(
19003 &self,
19004 qkv_tm: &cudarc::driver::CudaView<f32>,
19005 conv_state: &mut CudaSlice<f32>,
19006 w: &CudaSlice<f32>,
19007 y: &mut CudaSlice<f32>,
19008 conv_dim: usize,
19009 t: usize,
19010 d_conv: usize,
19011 pad_len: Option<&CudaSlice<i32>>,
19012 ) -> Result<(), Box<dyn std::error::Error>> {
19013 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
19014 let ring_old = if t < d_conv - 1 {
19018 Some(self.clone_dtod(conv_state)?)
19019 } else {
19020 None
19021 };
19022 {
19023 let f = self.func("ssm_conv1d_tm_state_f32");
19024 let cfg = LaunchConfig {
19025 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19026 block_dim: (256, 1, 1),
19027 shared_mem_bytes: 0,
19028 };
19029 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19030 let __s_b = self.gpu.stream();
19031 let mut b = __s_b.launch_builder(&f);
19032 b.arg(qkv_tm)
19033 .arg(&*conv_state)
19034 .arg(w)
19035 .arg(y)
19036 .arg(&cd)
19037 .arg(&ti)
19038 .arg(&dc);
19039 unsafe {
19040 b.launch(cfg)?;
19041 }
19042 }
19043 match (ring_old, pad_len) {
19044 (None, Some(len_d)) => {
19045 let f = self.func("ssm_conv_ring_update_dev_f32");
19046 let n = conv_dim * (d_conv - 1);
19047 let cfg = LaunchConfig::for_num_elems(n as u32);
19048 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19049 let __s_b = self.gpu.stream();
19050 let mut b = __s_b.launch_builder(&f);
19051 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
19052 unsafe {
19053 b.launch(cfg)?;
19054 }
19055 }
19056 (None, None) => {
19057 let f = self.func("ssm_conv_ring_update_f32");
19058 let n = conv_dim * (d_conv - 1);
19059 let cfg = LaunchConfig::for_num_elems(n as u32);
19060 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19061 let __s_b = self.gpu.stream();
19062 let mut b = __s_b.launch_builder(&f);
19063 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
19064 unsafe {
19065 b.launch(cfg)?;
19066 }
19067 }
19068 (Some(_), _) => unreachable!(
19069 "ssm_conv1d_tm_state_pad_v: T < d_conv-1 has no view path (PRIME_MIN_T gates it)"
19070 ),
19071 }
19072 Ok(())
19073 }
19074
19075 pub fn ssm_conv_ring_rebuild(
19080 &self,
19081 qkv_tm: &CudaSlice<f32>,
19082 ring_old: &CudaSlice<f32>,
19083 conv_state: &mut CudaSlice<f32>,
19084 conv_dim: usize,
19085 tc: usize,
19086 d_conv: usize,
19087 ) -> Result<(), Box<dyn std::error::Error>> {
19088 let f = self.func("ssm_conv_ring_rebuild_f32");
19089 let n = conv_dim * (d_conv - 1);
19090 let cfg = LaunchConfig::for_num_elems(n as u32);
19091 let (cd, ti, dc) = (conv_dim as i32, tc as i32, d_conv as i32);
19092 let __s_b = self.gpu.stream();
19093 let mut b = __s_b.launch_builder(&f);
19094 b.arg(qkv_tm)
19095 .arg(ring_old)
19096 .arg(conv_state)
19097 .arg(&cd)
19098 .arg(&ti)
19099 .arg(&dc);
19100 unsafe {
19101 b.launch(cfg)?;
19102 }
19103 Ok(())
19104 }
19105
19106 #[allow(clippy::too_many_arguments)]
19111 pub fn gdn_prep_decode(
19112 &self,
19113 conv_out: &CudaSlice<f32>,
19114 beta_raw: &CudaSlice<f32>,
19115 alpha: &CudaSlice<f32>,
19116 dt_bias: &CudaSlice<f32>,
19117 a: &CudaSlice<f32>,
19118 q_l2: &mut CudaSlice<f32>,
19119 k_l2: &mut CudaSlice<f32>,
19120 v_g: &mut CudaSlice<f32>,
19121 beta: &mut CudaSlice<f32>,
19122 g_log: &mut CudaSlice<f32>,
19123 d_state: usize,
19124 num_v: usize,
19125 num_k: usize,
19126 key_dim: usize,
19127 eps: f32,
19128 ) -> Result<(), Box<dyn std::error::Error>> {
19129 let f = self.func("gdn_prep_decode_f32");
19130 let cfg = LaunchConfig {
19131 grid_dim: (num_v as u32, 1, 1),
19132 block_dim: (32, 4, 1),
19133 shared_mem_bytes: 0,
19134 };
19135 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
19136 let __s_b = self.gpu.stream();
19137 let mut b = __s_b.launch_builder(&f);
19138 b.arg(conv_out)
19139 .arg(beta_raw)
19140 .arg(alpha)
19141 .arg(dt_bias)
19142 .arg(a)
19143 .arg(q_l2)
19144 .arg(k_l2)
19145 .arg(v_g)
19146 .arg(beta)
19147 .arg(g_log)
19148 .arg(&ds)
19149 .arg(&nv)
19150 .arg(&nk)
19151 .arg(&kd)
19152 .arg(&eps);
19153 unsafe {
19154 b.launch(cfg)?;
19155 }
19156 Ok(())
19157 }
19158
19159 #[allow(clippy::too_many_arguments)]
19163 pub fn ssm_conv1d_gdn(
19164 &self,
19165 qkv_tm: &CudaSlice<f32>,
19166 w: &CudaSlice<f32>,
19167 q_g: &mut CudaSlice<f32>,
19168 k_g: &mut CudaSlice<f32>,
19169 v_g: &mut CudaSlice<f32>,
19170 conv_dim: usize,
19171 t: usize,
19172 d_conv: usize,
19173 d_state: usize,
19174 num_v: usize,
19175 num_k: usize,
19176 key_dim: usize,
19177 ) -> Result<(), Box<dyn std::error::Error>> {
19178 let f = self.func("ssm_conv1d_gdn_f32");
19179 let cfg = LaunchConfig {
19180 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19181 block_dim: (256, 1, 1),
19182 shared_mem_bytes: 0,
19183 };
19184 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19185 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
19186 let __s_b = self.gpu.stream();
19187 let mut b = __s_b.launch_builder(&f);
19188 b.arg(qkv_tm)
19189 .arg(w)
19190 .arg(q_g)
19191 .arg(k_g)
19192 .arg(v_g)
19193 .arg(&cd)
19194 .arg(&ti)
19195 .arg(&dc)
19196 .arg(&ds)
19197 .arg(&nv)
19198 .arg(&nk)
19199 .arg(&kd);
19200 unsafe {
19201 b.launch(cfg)?;
19202 }
19203 Ok(())
19204 }
19205
19206 pub fn ssm_conv1d(
19207 &self,
19208 x: &CudaSlice<f32>,
19209 w: &CudaSlice<f32>,
19210 y: &mut CudaSlice<f32>,
19211 conv_dim: usize,
19212 t: usize,
19213 d_conv: usize,
19214 silu: bool,
19215 ) -> Result<(), Box<dyn std::error::Error>> {
19216 let f = self.func("ssm_conv1d_silu_f32");
19217 let cfg = LaunchConfig {
19218 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
19219 block_dim: (256, 1, 1),
19220 shared_mem_bytes: 0,
19221 };
19222 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
19223 let __s_b = self.gpu.stream();
19224 let mut b = __s_b.launch_builder(&f);
19225 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
19226 unsafe {
19227 b.launch(cfg)?;
19228 }
19229 Ok(())
19230 }
19231
19232 pub fn gdn_scan_s128(
19235 &self,
19236 q: &CudaSlice<f32>,
19237 k: &CudaSlice<f32>,
19238 v: &CudaSlice<f32>,
19239 g: &CudaSlice<f32>,
19240 beta: &CudaSlice<f32>,
19241 state_in: &CudaSlice<f32>,
19242 state_out: &mut CudaSlice<f32>,
19243 o: &mut CudaSlice<f32>,
19244 n_head: usize,
19245 t: usize,
19246 scale: f32,
19247 ) -> Result<(), Box<dyn std::error::Error>> {
19248 let f = self.func("gdn_scan_s128");
19249 const S_V: u32 = 128;
19250 const WARP: u32 = 32;
19251 const COLS_PER_BLOCK: u32 = 4;
19252 let cfg = LaunchConfig {
19253 grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
19254 block_dim: (WARP, COLS_PER_BLOCK, 1),
19255 shared_mem_bytes: 0,
19256 };
19257 let (h, ti) = (n_head as i32, t as i32);
19258 let __s_b = self.gpu.stream();
19259 let mut b = __s_b.launch_builder(&f);
19260 b.arg(q)
19261 .arg(k)
19262 .arg(v)
19263 .arg(g)
19264 .arg(beta)
19265 .arg(state_in)
19266 .arg(state_out)
19267 .arg(o)
19268 .arg(&h)
19269 .arg(&ti)
19270 .arg(&scale);
19271 unsafe {
19272 b.launch(cfg)?;
19273 }
19274 Ok(())
19275 }
19276
19277 #[allow(clippy::too_many_arguments)]
19282 pub fn ssm_conv1d_fused_decode_b(
19283 &self,
19284 qkv_cols: &CudaSlice<f32>,
19285 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
19286 w: &CudaSlice<f32>,
19287 conv_outs: &mut CudaSlice<f32>,
19288 conv_dim: usize,
19289 d_conv: usize,
19290 b_n: usize,
19291 ) -> Result<(), Box<dyn std::error::Error>> {
19292 let f = self.func("ssm_conv1d_fused_decode_b_f32");
19293 let cfg = LaunchConfig {
19294 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
19295 block_dim: (256, 1, 1),
19296 shared_mem_bytes: 0,
19297 };
19298 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19299 let __s_b = self.gpu.stream();
19300 let mut b = __s_b.launch_builder(&f);
19301 b.arg(qkv_cols)
19302 .arg(conv_state_ptrs)
19303 .arg(w)
19304 .arg(conv_outs)
19305 .arg(&cd)
19306 .arg(&dc);
19307 unsafe {
19308 b.launch(cfg)?;
19309 }
19310 Ok(())
19311 }
19312
19313 #[allow(clippy::too_many_arguments)]
19314 pub fn gdn_prep_decode_b(
19315 &self,
19316 conv_outs: &CudaSlice<f32>,
19317 beta_raws: &CudaSlice<f32>,
19318 alphas: &CudaSlice<f32>,
19319 dt_bias: &CudaSlice<f32>,
19320 a: &CudaSlice<f32>,
19321 q_l2: &mut CudaSlice<f32>,
19322 k_l2: &mut CudaSlice<f32>,
19323 v_g: &mut CudaSlice<f32>,
19324 beta: &mut CudaSlice<f32>,
19325 g_log: &mut CudaSlice<f32>,
19326 d_state: usize,
19327 num_v: usize,
19328 num_k: usize,
19329 key_dim: usize,
19330 eps: f32,
19331 conv_dim: usize,
19332 b_n: usize,
19333 ) -> Result<(), Box<dyn std::error::Error>> {
19334 let f = self.func("gdn_prep_decode_b_f32");
19335 let cfg = LaunchConfig {
19336 grid_dim: (num_v as u32, 1, b_n as u32),
19337 block_dim: (32, 4, 1),
19338 shared_mem_bytes: 0,
19339 };
19340 let (ds, nv, nk, kd, cd) = (
19341 d_state as i32,
19342 num_v as i32,
19343 num_k as i32,
19344 key_dim as i32,
19345 conv_dim as i32,
19346 );
19347 let __s_b = self.gpu.stream();
19348 let mut b = __s_b.launch_builder(&f);
19349 b.arg(conv_outs)
19350 .arg(beta_raws)
19351 .arg(alphas)
19352 .arg(dt_bias)
19353 .arg(a)
19354 .arg(q_l2)
19355 .arg(k_l2)
19356 .arg(v_g)
19357 .arg(beta)
19358 .arg(g_log)
19359 .arg(&ds)
19360 .arg(&nv)
19361 .arg(&nk)
19362 .arg(&kd)
19363 .arg(&eps)
19364 .arg(&cd);
19365 unsafe {
19366 b.launch(cfg)?;
19367 }
19368 Ok(())
19369 }
19370
19371 #[allow(clippy::too_many_arguments)]
19372 pub fn gdn_scan_s128_batched(
19373 &self,
19374 q: &CudaSlice<f32>,
19375 k: &CudaSlice<f32>,
19376 v: &CudaSlice<f32>,
19377 g: &CudaSlice<f32>,
19378 beta: &CudaSlice<f32>,
19379 state_in_ptrs: &cudarc::driver::CudaView<u64>,
19380 state_out_ptrs: &cudarc::driver::CudaView<u64>,
19381 o: &mut CudaSlice<f32>,
19382 n_head: usize,
19383 b_n: usize,
19384 scale: f32,
19385 ) -> Result<(), Box<dyn std::error::Error>> {
19386 let f = self.func("gdn_scan_s128_b");
19387 const S_V: u32 = 128;
19388 const WARP: u32 = 32;
19389 const COLS_PER_BLOCK: u32 = 4;
19390 let cfg = LaunchConfig {
19391 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
19392 block_dim: (WARP, COLS_PER_BLOCK, 1),
19393 shared_mem_bytes: 0,
19394 };
19395 let h = n_head as i32;
19396 let __s_b = self.gpu.stream();
19397 let mut b = __s_b.launch_builder(&f);
19398 b.arg(q)
19399 .arg(k)
19400 .arg(v)
19401 .arg(g)
19402 .arg(beta)
19403 .arg(state_in_ptrs)
19404 .arg(state_out_ptrs)
19405 .arg(o)
19406 .arg(&h)
19407 .arg(&scale);
19408 unsafe {
19409 b.launch(cfg)?;
19410 }
19411 Ok(())
19412 }
19413
19414 pub fn gdn_chunked_enabled() -> bool {
19423 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19424 *E.get_or_init(|| {
19425 std::env::var("MEMRA_GDN_CHUNKED")
19426 .map(|v| v != "0")
19427 .unwrap_or(true)
19428 })
19429 }
19430
19431 pub fn gdn_chunk_size() -> usize {
19436 static C: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
19437 *C.get_or_init(|| {
19438 let c: usize = std::env::var("MEMRA_GDN_CHUNK")
19439 .ok()
19440 .and_then(|v| v.parse().ok())
19441 .unwrap_or(32);
19442 c.clamp(32, 128) / 32 * 32
19443 })
19444 }
19445
19446 #[allow(clippy::too_many_arguments)]
19451 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
19454 #[allow(clippy::too_many_arguments)]
19455 pub fn gdn_chunk_k123(
19456 &self,
19457 q: &CudaSlice<f32>,
19458 k: &CudaSlice<f32>,
19459 v: &CudaSlice<f32>,
19460 g: &CudaSlice<f32>,
19461 beta: &CudaSlice<f32>,
19462 wb16: Option<&mut CudaSlice<u8>>,
19463 n_head: usize,
19464 t: usize,
19465 c: usize,
19466 hk: usize,
19467 k2w: Option<(&CudaSlice<u8>, &CudaSlice<u8>, &mut CudaSlice<u8>)>,
19468 ) -> Result<
19469 (
19470 CudaSlice<f32>,
19471 CudaSlice<f32>,
19472 CudaSlice<f32>,
19473 CudaSlice<f32>,
19474 ),
19475 Box<dyn std::error::Error>,
19476 > {
19477 const D: usize = 128;
19478 let h = n_head;
19479 let nc = (t + c - 1) / c;
19480 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
19481 let mut gcum = self.uninit(t * h)?;
19482 let mut a = self.uninit(nc * h * c * c)?;
19483 let mut p = self.uninit(nc * h * c * c)?;
19484 let mut u = self.uninit(nc * h * c * D)?;
19485 let mut w = self.uninit(nc * h * c * D)?;
19486 {
19487 let f = self.func("gdn_chunk_cumgate_f32");
19489 let cfg = LaunchConfig {
19490 grid_dim: (nc as u32, h as u32, 1),
19491 block_dim: (32, 1, 1),
19492 shared_mem_bytes: 0,
19493 };
19494 let __s_b = self.gpu.stream();
19495 let mut b = __s_b.launch_builder(&f);
19496 b.arg(g).arg(&mut gcum).arg(&hi).arg(&ti).arg(&ci);
19497 unsafe {
19498 b.launch(cfg)?;
19499 }
19500 }
19501 if let Some((qb, kb, pb)) = k2w {
19502 assert!(c == 32, "gdn_k2_wgmma is a C==32 tile");
19505 let f = self.func("gdn_k2_wgmma");
19506 let cfg = LaunchConfig {
19507 grid_dim: (nc as u32, h as u32, 1),
19508 block_dim: (128, 1, 1),
19509 shared_mem_bytes: 0,
19510 };
19511 let hki = hk as i32;
19512 let __s_b = self.gpu.stream();
19513 let mut b = __s_b.launch_builder(&f);
19514 b.arg(qb)
19515 .arg(kb)
19516 .arg(&gcum)
19517 .arg(beta)
19518 .arg(&mut a)
19519 .arg(&mut *pb)
19520 .arg(&hi)
19521 .arg(&ti)
19522 .arg(&ci)
19523 .arg(&hki);
19524 unsafe {
19525 b.launch(cfg)?;
19526 }
19527 } else if c <= 64 && !portable_mma_gated() {
19528 let f = self.func("gdn_chunk_attn_f32");
19530 let jt = ((c + 31) / 32) as u32;
19531 let cfg = LaunchConfig {
19532 grid_dim: (nc as u32, h as u32, jt),
19533 block_dim: (256, 1, 1),
19534 shared_mem_bytes: 0,
19535 };
19536 let hki = hk as i32;
19537 let __s_b = self.gpu.stream();
19538 let mut b = __s_b.launch_builder(&f);
19539 b.arg(q)
19540 .arg(k)
19541 .arg(&gcum)
19542 .arg(beta)
19543 .arg(&mut a)
19544 .arg(&mut p)
19545 .arg(&hi)
19546 .arg(&ti)
19547 .arg(&ci)
19548 .arg(&hki);
19549 unsafe {
19550 b.launch(cfg)?;
19551 }
19552 } else {
19553 assert!(
19555 hk == h,
19556 "generic K2 is broadcast-only (de-broadcast rides C==32)"
19557 );
19558 let f = self.func("gdn_chunk_attn_g_f32");
19559 let cfg = LaunchConfig {
19560 grid_dim: (nc as u32, h as u32, 1),
19561 block_dim: (32, 8, 1),
19562 shared_mem_bytes: 0,
19563 };
19564 let __s_b = self.gpu.stream();
19565 let mut b = __s_b.launch_builder(&f);
19566 b.arg(q)
19567 .arg(k)
19568 .arg(&gcum)
19569 .arg(beta)
19570 .arg(&mut a)
19571 .arg(&mut p)
19572 .arg(&hi)
19573 .arg(&ti)
19574 .arg(&ci);
19575 unsafe {
19576 b.launch(cfg)?;
19577 }
19578 }
19579 {
19580 let cfg = LaunchConfig {
19582 grid_dim: (nc as u32, h as u32, 1),
19583 block_dim: (256, 1, 1),
19584 shared_mem_bytes: 0,
19585 };
19586 match c {
19587 32 | 64 => {
19588 let f = self.func(if c == 32 {
19589 "gdn_chunk_solve32_f32"
19590 } else {
19591 "gdn_chunk_solve64_f32"
19592 });
19593 let wb: u64 = match wb16 {
19595 Some(d) => self.addr_u8(d),
19596 None => 0,
19597 };
19598 let hki = hk as i32;
19599 let __s_b = self.gpu.stream();
19600 let mut b = __s_b.launch_builder(&f);
19601 b.arg(v)
19602 .arg(k)
19603 .arg(&a)
19604 .arg(&gcum)
19605 .arg(&mut u)
19606 .arg(&mut w)
19607 .arg(&wb)
19608 .arg(&hi)
19609 .arg(&ti)
19610 .arg(&hki);
19611 unsafe {
19612 b.launch(cfg)?;
19613 }
19614 }
19615 _ => {
19616 assert!(hk == h, "generic K3 is broadcast-only");
19617 let f = self.func("gdn_chunk_solve_f32");
19618 let __s_b = self.gpu.stream();
19619 let mut b = __s_b.launch_builder(&f);
19620 b.arg(v)
19621 .arg(k)
19622 .arg(&a)
19623 .arg(&gcum)
19624 .arg(&mut u)
19625 .arg(&mut w)
19626 .arg(&hi)
19627 .arg(&ti)
19628 .arg(&ci);
19629 unsafe {
19630 b.launch(cfg)?;
19631 }
19632 }
19633 }
19634 }
19635 Ok((gcum, p, u, w))
19636 }
19637
19638 pub fn gdn_db_on() -> bool {
19642 std::env::var("MEMRA_GDN_DB").as_deref() != Ok("0")
19643 }
19644
19645 pub fn gdn_mma_enabled(&self, c: usize) -> bool {
19648 !portable_mma_gated()
19649 && c == 32
19650 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
19651 Ok("1") => true,
19652 Ok("0") => false,
19653 _ => cfg!(memra_hopper_mma),
19654 }
19655 }
19656
19657 pub fn gdn_wgmma_on(&self, c: usize) -> bool {
19660 self.gdn_mma_enabled(c)
19661 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
19662 Ok("0") => false,
19663 Ok("1") => true,
19664 _ => cfg!(memra_hopper_mma),
19665 }
19666 }
19667
19668 #[allow(clippy::too_many_arguments)]
19673 pub fn ssm_conv1d_gdn_state_pad(
19674 &self,
19675 qkv_tm: &cudarc::driver::CudaView<f32>,
19676 conv_state: &mut CudaSlice<f32>,
19677 w: &CudaSlice<f32>,
19678 q_g: &mut CudaSlice<f32>,
19679 k_g: &mut CudaSlice<f32>,
19680 v_g: &mut CudaSlice<f32>,
19681 conv_dim: usize,
19682 t: usize,
19683 d_conv: usize,
19684 d_state: usize,
19685 num_v: usize,
19686 num_k: usize,
19687 key_dim: usize,
19688 hk: usize,
19689 pad_len: Option<&CudaSlice<i32>>,
19690 ) -> Result<(), Box<dyn std::error::Error>> {
19691 assert!(
19692 t >= d_conv - 1,
19693 "fused state conv requires T >= pad (PRIME_MIN_T gates)"
19694 );
19695 {
19696 let f = self.func("ssm_conv1d_gdn_state_f32");
19697 let cfg = LaunchConfig {
19698 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19699 block_dim: (256, 1, 1),
19700 shared_mem_bytes: 0,
19701 };
19702 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19703 let (ds, nv, nk, kd, hki) = (
19704 d_state as i32,
19705 num_v as i32,
19706 num_k as i32,
19707 key_dim as i32,
19708 hk as i32,
19709 );
19710 let __s_b = self.gpu.stream();
19711 let mut b = __s_b.launch_builder(&f);
19712 b.arg(qkv_tm)
19713 .arg(&*conv_state)
19714 .arg(w)
19715 .arg(q_g)
19716 .arg(k_g)
19717 .arg(v_g)
19718 .arg(&cd)
19719 .arg(&ti)
19720 .arg(&dc)
19721 .arg(&ds)
19722 .arg(&nv)
19723 .arg(&nk)
19724 .arg(&kd)
19725 .arg(&hki);
19726 unsafe {
19727 b.launch(cfg)?;
19728 }
19729 }
19730 match pad_len {
19731 Some(len_d) => {
19732 let f = self.func("ssm_conv_ring_update_dev_f32");
19733 let n = conv_dim * (d_conv - 1);
19734 let cfg = LaunchConfig::for_num_elems(n as u32);
19735 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19736 let __s_b = self.gpu.stream();
19737 let mut b = __s_b.launch_builder(&f);
19738 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
19739 unsafe {
19740 b.launch(cfg)?;
19741 }
19742 }
19743 None => {
19744 let f = self.func("ssm_conv_ring_update_f32");
19745 let n = conv_dim * (d_conv - 1);
19746 let cfg = LaunchConfig::for_num_elems(n as u32);
19747 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19748 let __s_b = self.gpu.stream();
19749 let mut b = __s_b.launch_builder(&f);
19750 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
19751 unsafe {
19752 b.launch(cfg)?;
19753 }
19754 }
19755 }
19756 Ok(())
19757 }
19758
19759 pub fn gdn_chunk_alloc(
19763 &self,
19764 n_head: usize,
19765 t: usize,
19766 c: usize,
19767 hk: usize,
19768 ) -> Result<GdnChunkBufs, Box<dyn std::error::Error>> {
19769 const D: usize = 128;
19770 assert!(
19771 c == 32,
19772 "gdn_chunk_alloc: varlen chain is the C==32 mma pair"
19773 );
19774 let h = n_head;
19775 let nc = (t + c - 1) / c;
19776 Ok(GdnChunkBufs {
19777 gcum: self.uninit(t * h)?,
19778 a: self.uninit(nc * h * c * c)?,
19779 p: self.uninit(nc * h * c * c)?,
19780 u: self.uninit(nc * h * c * D)?,
19781 w: self.uninit(nc * h * c * D)?,
19782 kb16: self.alloc_u8_uninit(t * hk * D * 2)?,
19783 wb16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
19784 y16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
19785 ssnap16: self.alloc_u8_uninit(nc * h * D * D * 2)?,
19786 qb16: self.alloc_u8_uninit(t * hk * D * 2)?,
19787 pb16: self.alloc_u8_uninit(nc * h * c * c * 2)?,
19788 o: self.uninit(D * h * t)?,
19789 t,
19790 nc,
19791 })
19792 }
19793
19794 pub fn f32_to_bf16_v(
19796 &self,
19797 x: &cudarc::driver::CudaView<f32>,
19798 dst: &mut CudaSlice<u8>,
19799 n: usize,
19800 ) -> Result<(), Box<dyn std::error::Error>> {
19801 let f = self.func("f32_to_bf16_bulk");
19802 let ni = n as i64;
19803 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
19804 let __s_b = self.gpu.stream();
19805 let mut b = __s_b.launch_builder(&f);
19806 b.arg(x).arg(dst).arg(&ni);
19807 unsafe {
19808 b.launch(cfg)?;
19809 }
19810 Ok(())
19811 }
19812
19813 pub fn f32_to_bf16_into(
19815 &self,
19816 x: &CudaSlice<f32>,
19817 dst: &mut CudaSlice<u8>,
19818 n: usize,
19819 ) -> Result<(), Box<dyn std::error::Error>> {
19820 let f = self.func("f32_to_bf16_bulk");
19821 let ni = n as i64;
19822 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
19823 let __s_b = self.gpu.stream();
19824 let mut b = __s_b.launch_builder(&f);
19825 b.arg(x).arg(dst).arg(&ni);
19826 unsafe {
19827 b.launch(cfg)?;
19828 }
19829 Ok(())
19830 }
19831
19832 pub fn gdn_chunk_k123_vl8(
19835 &self,
19836 seqs: &[GdnSeqVl],
19837 n_head: usize,
19838 hk: usize,
19839 wq: Option<&GdnWVl8>,
19840 ) -> Result<(), Box<dyn std::error::Error>> {
19841 let b = seqs.len();
19842 assert!(b >= 1 && b <= 8, "gdn_chunk_k123_vl8: 1..=8 sequences");
19843 let mut packed = [GdnSeqVl::default(); 8];
19844 packed[..b].copy_from_slice(seqs);
19845 let v = GdnVl8(packed);
19846 let (hi, ci) = (n_head as i32, 32i32);
19847 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
19848 {
19849 let f = self.func("gdn_chunk_cumgate_vl");
19850 let cfg = LaunchConfig {
19851 grid_dim: (max_nc, n_head as u32, b as u32),
19852 block_dim: (32, 1, 1),
19853 shared_mem_bytes: 0,
19854 };
19855 let __s_lb = self.gpu.stream();
19856 let mut lb = __s_lb.launch_builder(&f);
19857 lb.arg(&v).arg(&hi).arg(&ci);
19858 unsafe {
19859 lb.launch(cfg)?;
19860 }
19861 }
19862 let hki = hk as i32;
19863 if let Some(w) = wq {
19864 let f = self.func("gdn_k2_wgmma_vl");
19866 let cfg = LaunchConfig {
19867 grid_dim: (max_nc, n_head as u32, b as u32),
19868 block_dim: (128, 1, 1),
19869 shared_mem_bytes: 0,
19870 };
19871 let __s_lb = self.gpu.stream();
19872 let mut lb = __s_lb.launch_builder(&f);
19873 lb.arg(&v).arg(w).arg(&hi).arg(&ci).arg(&hki);
19874 unsafe {
19875 lb.launch(cfg)?;
19876 }
19877 } else {
19878 let f = self.func("gdn_chunk_attn_vl");
19879 let cfg = LaunchConfig {
19880 grid_dim: (max_nc, n_head as u32, b as u32),
19881 block_dim: (256, 1, 1),
19882 shared_mem_bytes: 0,
19883 };
19884 let __s_lb = self.gpu.stream();
19885 let mut lb = __s_lb.launch_builder(&f);
19886 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
19887 unsafe {
19888 lb.launch(cfg)?;
19889 }
19890 }
19891 {
19892 let f = self.func("gdn_chunk_solve32_vl");
19893 let cfg = LaunchConfig {
19894 grid_dim: (max_nc, n_head as u32, b as u32),
19895 block_dim: (256, 1, 1),
19896 shared_mem_bytes: 0,
19897 };
19898 let __s_lb = self.gpu.stream();
19899 let mut lb = __s_lb.launch_builder(&f);
19900 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
19901 unsafe {
19902 lb.launch(cfg)?;
19903 }
19904 }
19905 Ok(())
19906 }
19907
19908 #[allow(clippy::too_many_arguments)]
19912 pub fn gdn_prep_vl8(
19913 &self,
19914 seqs: &[GdnPrepVl],
19915 conv_w: &CudaSlice<f32>,
19916 dt_bias: &CudaSlice<f32>,
19917 a: &CudaSlice<f32>,
19918 conv_dim: usize,
19919 d_conv: usize,
19920 d_state: usize,
19921 num_v: usize,
19922 num_k: usize,
19923 key_dim: usize,
19924 hk: usize,
19925 eps: f32,
19926 ) -> Result<(), Box<dyn std::error::Error>> {
19927 let b = seqs.len();
19928 assert!(b >= 1 && b <= 8);
19929 let mut packed = [GdnPrepVl::default(); 8];
19930 packed[..b].copy_from_slice(seqs);
19931 let v = GdnPrepVl8(packed);
19932 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
19933 let (cdi, dci) = (conv_dim as i32, d_conv as i32);
19934 let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
19935 assert!(
19936 conv_fuse || hk == num_v,
19937 "de-broadcast requires the fused conv"
19938 );
19939 if conv_fuse {
19940 let f = self.func("ssm_conv1d_gdn_state_vl");
19941 let cfg = LaunchConfig {
19942 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
19943 block_dim: (256, 1, 1),
19944 shared_mem_bytes: 0,
19945 };
19946 let (dsi, nvi, nki, kdi, hki) = (
19947 d_state as i32,
19948 num_v as i32,
19949 num_k as i32,
19950 key_dim as i32,
19951 hk as i32,
19952 );
19953 let __s_lb = self.gpu.stream();
19954 let mut lb = __s_lb.launch_builder(&f);
19955 lb.arg(&v)
19956 .arg(conv_w)
19957 .arg(&cdi)
19958 .arg(&dci)
19959 .arg(&dsi)
19960 .arg(&nvi)
19961 .arg(&nki)
19962 .arg(&kdi)
19963 .arg(&hki);
19964 unsafe {
19965 lb.launch(cfg)?;
19966 }
19967 } else {
19968 let f = self.func("ssm_conv1d_tm_state_vl");
19969 let cfg = LaunchConfig {
19970 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
19971 block_dim: (256, 1, 1),
19972 shared_mem_bytes: 0,
19973 };
19974 let __s_lb = self.gpu.stream();
19975 let mut lb = __s_lb.launch_builder(&f);
19976 lb.arg(&v).arg(conv_w).arg(&cdi).arg(&dci);
19977 unsafe {
19978 lb.launch(cfg)?;
19979 }
19980 }
19981 {
19982 let f = self.func("ssm_conv_ring_update_vl");
19983 let n = (conv_dim * (d_conv - 1)) as u32;
19984 let cfg = LaunchConfig {
19985 grid_dim: (n.div_ceil(256), 1, b as u32),
19986 block_dim: (256, 1, 1),
19987 shared_mem_bytes: 0,
19988 };
19989 let __s_lb = self.gpu.stream();
19990 let mut lb = __s_lb.launch_builder(&f);
19991 lb.arg(&v).arg(&cdi).arg(&dci);
19992 unsafe {
19993 lb.launch(cfg)?;
19994 }
19995 }
19996 if !conv_fuse {
19997 let f = self.func("qkv_to_gdn_repack_vl");
19998 let n = max_t * (num_v * d_state) as u32;
19999 let cfg = LaunchConfig {
20000 grid_dim: (n.div_ceil(256), 1, b as u32),
20001 block_dim: (256, 1, 1),
20002 shared_mem_bytes: 0,
20003 };
20004 let (dsi, nvi, nki, kdi) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
20005 let __s_lb = self.gpu.stream();
20006 let mut lb = __s_lb.launch_builder(&f);
20007 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&nki).arg(&kdi);
20008 unsafe {
20009 lb.launch(cfg)?;
20010 }
20011 }
20012 if Self::l2_v2_on(d_state) {
20013 let f = self.func("gdn_l2_v2_vl");
20014 let cfg = LaunchConfig {
20015 grid_dim: ((max_t * hk as u32).div_ceil(8), 2, b as u32),
20016 block_dim: (256, 1, 1),
20017 shared_mem_bytes: 0,
20018 };
20019 let (dsi, nvi) = (d_state as i32, hk as i32);
20020 let __s_lb = self.gpu.stream();
20021 let mut lb = __s_lb.launch_builder(&f);
20022 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
20023 unsafe {
20024 lb.launch(cfg)?;
20025 }
20026 } else {
20027 let f = self.func("gdn_l2_vl");
20028 let cfg = LaunchConfig {
20029 grid_dim: (max_t * hk as u32, 2, b as u32),
20030 block_dim: (256, 1, 1),
20031 shared_mem_bytes: 0,
20032 };
20033 let (dsi, nvi) = (d_state as i32, hk as i32);
20034 let __s_lb = self.gpu.stream();
20035 let mut lb = __s_lb.launch_builder(&f);
20036 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
20037 unsafe {
20038 lb.launch(cfg)?;
20039 }
20040 }
20041 {
20042 let f = self.func("gdn_gate_prep_vl");
20043 let n = max_t * num_v as u32;
20044 let cfg = LaunchConfig {
20045 grid_dim: (n.div_ceil(256), 1, b as u32),
20046 block_dim: (256, 1, 1),
20047 shared_mem_bytes: 0,
20048 };
20049 let nvi = num_v as i32;
20050 let __s_lb = self.gpu.stream();
20051 let mut lb = __s_lb.launch_builder(&f);
20052 lb.arg(&v).arg(dt_bias).arg(a).arg(&nvi);
20053 unsafe {
20054 lb.launch(cfg)?;
20055 }
20056 }
20057 Ok(())
20058 }
20059
20060 pub fn gdn_mirror_vl8(
20062 &self,
20063 seqs: &[GdnSeqVl],
20064 n_head: usize,
20065 which: i32,
20066 hk: usize,
20067 ) -> Result<(), Box<dyn std::error::Error>> {
20068 let b = seqs.len();
20069 assert!(b >= 1 && b <= 8);
20070 let mut packed = [GdnSeqVl::default(); 8];
20071 packed[..b].copy_from_slice(seqs);
20072 let v = GdnVl8(packed);
20073 let ept = (if which == 0 { hk } else { n_head } * 128) as i32;
20074 let max_n = seqs
20075 .iter()
20076 .map(|s| {
20077 if which == 0 {
20078 s.t as i64 * ept as i64
20079 } else {
20080 s.nc as i64 * ept as i64 * 32
20081 }
20082 })
20083 .max()
20084 .unwrap();
20085 let f = self.func("gdn_mirror_vl");
20086 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
20087 let cfg = LaunchConfig {
20088 grid_dim: (blocks, 1, b as u32),
20089 block_dim: (256, 1, 1),
20090 shared_mem_bytes: 0,
20091 };
20092 let __s_lb = self.gpu.stream();
20093 let mut lb = __s_lb.launch_builder(&f);
20094 lb.arg(&v).arg(&ept).arg(&which);
20095 unsafe {
20096 lb.launch(cfg)?;
20097 }
20098 Ok(())
20099 }
20100
20101 pub fn gdn_tail_vl8(
20103 &self,
20104 seqs: &[GdnPrepVl],
20105 norm_w: &CudaSlice<f32>,
20106 d_state: usize,
20107 num_v: usize,
20108 eps: f32,
20109 ) -> Result<(), Box<dyn std::error::Error>> {
20110 let b = seqs.len();
20111 assert!(b >= 1 && b <= 8);
20112 let mut packed = [GdnPrepVl::default(); 8];
20113 packed[..b].copy_from_slice(seqs);
20114 let v = GdnPrepVl8(packed);
20115 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
20116 let f = self.func("gated_rmsnorm_f16out_vl");
20117 let cfg = LaunchConfig {
20119 grid_dim: (max_t * num_v as u32, 1, b as u32),
20120 block_dim: (128, 1, 1),
20121 shared_mem_bytes: 0,
20122 };
20123 let (dsi, nvi) = (d_state as i32, num_v as i32);
20124 let __s_lb = self.gpu.stream();
20125 let mut lb = __s_lb.launch_builder(&f);
20126 lb.arg(&v).arg(norm_w).arg(&dsi).arg(&nvi).arg(&eps);
20127 unsafe {
20128 lb.launch(cfg)?;
20129 }
20130 Ok(())
20131 }
20132
20133 pub fn addr_f32(&self, x: &CudaSlice<f32>) -> u64 {
20136 use cudarc::driver::DevicePtr;
20137 let s = self.gpu.stream();
20138 let (p, _g) = x.device_ptr(&s);
20139 p as u64
20140 }
20141 pub fn addr_f32_mut(&self, x: &mut CudaSlice<f32>) -> u64 {
20142 use cudarc::driver::DevicePtrMut;
20143 let s = self.gpu.stream();
20144 let (p, _g) = x.device_ptr_mut(&s);
20145 p as u64
20146 }
20147 pub fn addr_f32v(&self, x: &cudarc::driver::CudaView<f32>) -> u64 {
20148 use cudarc::driver::DevicePtr;
20149 let s = self.gpu.stream();
20150 let (p, _g) = x.device_ptr(&s);
20151 p as u64
20152 }
20153 pub fn addr_u8(&self, x: &CudaSlice<u8>) -> u64 {
20154 use cudarc::driver::DevicePtr;
20155 let s = self.gpu.stream();
20156 let (p, _g) = x.device_ptr(&s);
20157 p as u64
20158 }
20159
20160 pub fn gdn_chunk_vl8(
20164 &self,
20165 seqs: &[GdnSeqVl],
20166 n_head: usize,
20167 scale: f32,
20168 hk: usize,
20169 wq: Option<&GdnWVl8>,
20170 ) -> Result<(), Box<dyn std::error::Error>> {
20171 const NSPLIT: u32 = 4;
20172 let b = seqs.len();
20173 assert!(b >= 1 && b <= 8, "gdn_chunk_vl8: 1..=8 sequences");
20174 let mut packed = [GdnSeqVl::default(); 8];
20175 packed[..b].copy_from_slice(seqs);
20176 let v = GdnVl8(packed);
20177 let (hi, ci) = (n_head as i32, 32i32);
20178 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
20179 let hki = hk as i32;
20180 if let Some(w) = wq {
20181 let f = self.func("gdn_k45_wgmma_vl");
20183 let cfg = LaunchConfig {
20184 grid_dim: (n_head as u32, NSPLIT, b as u32),
20185 block_dim: (256, 1, 1),
20186 shared_mem_bytes: 0,
20187 };
20188 let __s_lb = self.gpu.stream();
20189 let mut lb = __s_lb.launch_builder(&f);
20190 lb.arg(&v).arg(w).arg(&scale).arg(&hi).arg(&ci).arg(&hki);
20191 unsafe {
20192 lb.launch(cfg)?;
20193 }
20194 let _ = max_nc;
20195 return Ok(());
20196 }
20197 {
20198 let f = self.func("gdn_chunk_state_mma_vl");
20199 let cfg = LaunchConfig {
20200 grid_dim: (n_head as u32, NSPLIT, b as u32),
20201 block_dim: (256, 1, 1),
20202 shared_mem_bytes: 0,
20203 };
20204 let __s_lb = self.gpu.stream();
20205 let mut lb = __s_lb.launch_builder(&f);
20206 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
20207 unsafe {
20208 lb.launch(cfg)?;
20209 }
20210 }
20211 {
20212 let f = self.func("gdn_chunk_output_mma_vl");
20213 let cfg = LaunchConfig {
20214 grid_dim: (max_nc, n_head as u32, b as u32),
20215 block_dim: (256, 1, 1),
20216 shared_mem_bytes: 0,
20217 };
20218 let __s_lb = self.gpu.stream();
20219 let mut lb = __s_lb.launch_builder(&f);
20220 lb.arg(&v).arg(&hi).arg(&ci).arg(&scale).arg(&hki);
20221 unsafe {
20222 lb.launch(cfg)?;
20223 }
20224 }
20225 Ok(())
20226 }
20227 pub fn gdn_scan_chunked(
20228 &self,
20229 q: &CudaSlice<f32>,
20230 k: &CudaSlice<f32>,
20231 v: &CudaSlice<f32>,
20232 g: &CudaSlice<f32>,
20233 beta: &CudaSlice<f32>,
20234 kb16_pre: Option<&CudaSlice<u8>>,
20235 qb16_pre: Option<&CudaSlice<u8>>,
20236 state_in: &CudaSlice<f32>,
20237 state_out: &mut CudaSlice<f32>,
20238 o: &mut CudaSlice<f32>,
20239 n_head: usize,
20240 t: usize,
20241 scale: f32,
20242 c: usize,
20243 hk: usize,
20244 ) -> Result<(), Box<dyn std::error::Error>> {
20245 const D: usize = 128;
20246 const NSPLIT: u32 = 4;
20247 assert!(c >= 1 && c <= 128, "gdn_scan_chunked: C must be in 1..=128");
20248 let h = n_head;
20249 let nc = (t + c - 1) / c;
20250 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
20251 let gdn_mma_pre = !portable_mma_gated()
20255 && c == 32
20256 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
20257 Ok("1") => true,
20258 Ok("0") => false,
20259 _ => cfg!(memra_hopper_mma),
20260 };
20261 let mut wb16_pre: Option<CudaSlice<u8>> = if gdn_mma_pre {
20262 Some(self.alloc_u8_uninit(nc * h * c * D * 2)?)
20263 } else {
20264 None
20265 };
20266 let gdn_wgmma_pre = gdn_mma_pre
20270 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
20271 Ok("0") => false,
20272 Ok("1") => true,
20273 _ => cfg!(memra_hopper_mma),
20274 };
20275 let nk = t * hk * D;
20276 let mut kb16_local: Option<CudaSlice<u8>> = None;
20277 if gdn_mma_pre && kb16_pre.is_none() {
20278 let mut kb = self.alloc_u8_uninit(nk * 2)?;
20279 let f = self.func("f32_to_bf16_bulk");
20280 let n2 = nk as i64;
20281 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
20282 let __s_b = self.gpu.stream();
20283 let mut b = __s_b.launch_builder(&f);
20284 b.arg(k).arg(&mut kb).arg(&n2);
20285 unsafe {
20286 b.launch(cfg2)?;
20287 }
20288 kb16_local = Some(kb);
20289 }
20290 let kb16_ref0: Option<&CudaSlice<u8>> = kb16_local.as_ref().or(kb16_pre);
20291 if let Some(kb) = kb16_pre {
20292 assert!(kb.len() >= nk * 2, "kb16_pre too small");
20293 }
20294 let mut qb16: Option<CudaSlice<u8>> = None;
20295 let mut pb16: Option<CudaSlice<u8>> = None;
20296 if gdn_wgmma_pre {
20297 if qb16_pre.is_none() {
20300 let mut qb = self.alloc_u8_uninit(nk * 2)?;
20301 let f = self.func("f32_to_bf16_bulk");
20302 let n2 = nk as i64;
20303 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
20304 let __s_b = self.gpu.stream();
20305 let mut b = __s_b.launch_builder(&f);
20306 b.arg(q).arg(&mut qb).arg(&n2);
20307 unsafe {
20308 b.launch(cfg2)?;
20309 }
20310 qb16 = Some(qb);
20311 } else if let Some(qb) = qb16_pre {
20312 assert!(qb.len() >= nk * 2, "qb16_pre too small");
20313 }
20314 pb16 = Some(self.alloc_u8_uninit(nc * h * c * c * 2)?);
20315 }
20316 let qb16_ref0: Option<&CudaSlice<u8>> = qb16.as_ref().or(qb16_pre);
20317 let k2w = if gdn_wgmma_pre {
20318 Some((
20319 *qb16_ref0.as_ref().unwrap(),
20320 *kb16_ref0.as_ref().unwrap(),
20321 pb16.as_mut().unwrap(),
20322 ))
20323 } else {
20324 None
20325 };
20326 let (gcum, p, u, w) =
20327 self.gdn_chunk_k123(q, k, v, g, beta, wb16_pre.as_mut(), n_head, t, c, hk, k2w)?;
20328 let _ = &w;
20329 let mut y = self.uninit(nc * h * c * D)?;
20330 let mut ssnap = self.uninit(nc * h * D * D)?; let gdn_mma = !portable_mma_gated()
20342 && c == 32
20343 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
20344 Ok("1") => true,
20345 Ok("0") => false,
20346 _ => cfg!(memra_hopper_mma),
20347 };
20348 if gdn_mma {
20349 let wb16 = wb16_pre
20350 .take()
20351 .expect("mma path pre-allocates wb16 (K3 store fold)");
20352 let kb16_ref: &CudaSlice<u8> = kb16_ref0.expect("mma path pre-builds kb16 above K123");
20353 if gdn_wgmma_pre {
20365 let qb16 = qb16_ref0.unwrap();
20367 let pb16 = pb16.as_ref().unwrap();
20368 {
20369 let f = self.func("gdn_k45_wgmma");
20370 let cfg = LaunchConfig {
20371 grid_dim: (h as u32, 4, 1),
20372 block_dim: (256, 1, 1),
20373 shared_mem_bytes: 0,
20374 };
20375 let hki = hk as i32;
20376 let __s_b = self.gpu.stream();
20377 let mut b = __s_b.launch_builder(&f);
20378 b.arg(kb16_ref)
20379 .arg(&gcum)
20380 .arg(beta)
20381 .arg(&u)
20382 .arg(&wb16)
20383 .arg(qb16)
20384 .arg(pb16)
20385 .arg(o)
20386 .arg(&scale)
20387 .arg(state_in)
20388 .arg(&mut *state_out)
20389 .arg(&hi)
20390 .arg(&ti)
20391 .arg(&ci)
20392 .arg(&hki);
20393 unsafe {
20394 b.launch(cfg)?;
20395 }
20396 }
20397 return Ok(());
20398 }
20399 let mut y16 = self.alloc_u8_uninit(nc * h * c * D * 2)?;
20403 let mut ssnap16 = self.alloc_u8_uninit(nc * h * D * D * 2)?;
20404 {
20405 let f = self.func("gdn_chunk_state_mma");
20406 let cfg = LaunchConfig {
20407 grid_dim: (h as u32, NSPLIT, 1),
20408 block_dim: (256, 1, 1),
20409 shared_mem_bytes: 0,
20410 };
20411 let hki = hk as i32;
20412 let __s_b = self.gpu.stream();
20413 let mut b = __s_b.launch_builder(&f);
20414 b.arg(kb16_ref)
20415 .arg(&gcum)
20416 .arg(beta)
20417 .arg(&u)
20418 .arg(&wb16)
20419 .arg(&mut y16)
20420 .arg(&mut ssnap16)
20421 .arg(state_in)
20422 .arg(&mut *state_out)
20423 .arg(&hi)
20424 .arg(&ti)
20425 .arg(&ci)
20426 .arg(&hki);
20427 unsafe {
20428 b.launch(cfg)?;
20429 }
20430 }
20431 {
20432 let f = self.func("gdn_chunk_output_mma");
20434 let jt = ((c + 31) / 32) as u32;
20435 let cfg = LaunchConfig {
20436 grid_dim: (nc as u32, h as u32, jt),
20437 block_dim: (256, 1, 1),
20438 shared_mem_bytes: 0,
20439 };
20440 let hki = hk as i32;
20441 let __s_b = self.gpu.stream();
20442 let mut b = __s_b.launch_builder(&f);
20443 b.arg(q)
20444 .arg(&gcum)
20445 .arg(&p)
20446 .arg(&y16)
20447 .arg(&ssnap16)
20448 .arg(o)
20449 .arg(&hi)
20450 .arg(&ti)
20451 .arg(&ci)
20452 .arg(&scale)
20453 .arg(&hki);
20454 unsafe {
20455 b.launch(cfg)?;
20456 }
20457 }
20458 return Ok(());
20459 }
20460 {
20461 let f = self.func("gdn_chunk_state_f32");
20463 let cfg = LaunchConfig {
20464 grid_dim: (h as u32, NSPLIT, 1),
20465 block_dim: (256, 1, 1),
20466 shared_mem_bytes: 0,
20467 };
20468 let __s_b = self.gpu.stream();
20469 let mut b = __s_b.launch_builder(&f);
20470 b.arg(k)
20471 .arg(&gcum)
20472 .arg(beta)
20473 .arg(&u)
20474 .arg(&w)
20475 .arg(&mut y)
20476 .arg(&mut ssnap)
20477 .arg(state_in)
20478 .arg(&mut *state_out)
20479 .arg(&hi)
20480 .arg(&ti)
20481 .arg(&ci);
20482 unsafe {
20483 b.launch(cfg)?;
20484 }
20485 }
20486 {
20487 let f = self.func("gdn_chunk_output_f32");
20489 let jt = ((c + 31) / 32) as u32;
20490 let cfg = LaunchConfig {
20491 grid_dim: (nc as u32, h as u32, jt),
20492 block_dim: (256, 1, 1),
20493 shared_mem_bytes: 0,
20494 };
20495 let __s_b = self.gpu.stream();
20496 let mut b = __s_b.launch_builder(&f);
20497 b.arg(q)
20498 .arg(&gcum)
20499 .arg(&p)
20500 .arg(&y)
20501 .arg(&ssnap)
20502 .arg(o)
20503 .arg(&hi)
20504 .arg(&ti)
20505 .arg(&ci)
20506 .arg(&scale);
20507 unsafe {
20508 b.launch(cfg)?;
20509 }
20510 }
20511 Ok(())
20512 }
20513
20514 #[allow(clippy::too_many_arguments)]
20523 #[allow(clippy::too_many_arguments)]
20524 pub fn gdn_scan_prefill(
20525 &self,
20526 q: &CudaSlice<f32>,
20527 k: &CudaSlice<f32>,
20528 v: &CudaSlice<f32>,
20529 g: &CudaSlice<f32>,
20530 beta: &CudaSlice<f32>,
20531 kb16_pre: Option<&CudaSlice<u8>>,
20532 qb16_pre: Option<&CudaSlice<u8>>,
20533 state_in: &CudaSlice<f32>,
20534 state_out: &mut CudaSlice<f32>,
20535 o: &mut CudaSlice<f32>,
20536 n_head: usize,
20537 t: usize,
20538 scale: f32,
20539 hk: usize,
20540 ) -> Result<(), Box<dyn std::error::Error>> {
20541 if std::env::var("MEMRA_GDN_DIFF").is_ok() && t >= 16 {
20542 assert!(hk == n_head, "GDN_DIFF oracle is broadcast-only");
20543 return self.gdn_scan_diff(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale);
20544 }
20545 if Self::gdn_chunked_enabled() && t >= 16 {
20546 self.gdn_scan_chunked(
20547 q,
20548 k,
20549 v,
20550 g,
20551 beta,
20552 kb16_pre,
20553 qb16_pre,
20554 state_in,
20555 state_out,
20556 o,
20557 n_head,
20558 t,
20559 scale,
20560 Self::gdn_chunk_size(),
20561 hk,
20562 )
20563 } else {
20564 assert!(
20565 hk == n_head,
20566 "s128 scan is broadcast-only (prep guarantees by predicate)"
20567 );
20568 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)
20569 }
20570 }
20571
20572 #[allow(clippy::too_many_arguments)]
20574 fn gdn_scan_diff(
20575 &self,
20576 q: &CudaSlice<f32>,
20577 k: &CudaSlice<f32>,
20578 v: &CudaSlice<f32>,
20579 g: &CudaSlice<f32>,
20580 beta: &CudaSlice<f32>,
20581 state_in: &CudaSlice<f32>,
20582 state_out: &mut CudaSlice<f32>,
20583 o: &mut CudaSlice<f32>,
20584 n_head: usize,
20585 t: usize,
20586 scale: f32,
20587 ) -> Result<(), Box<dyn std::error::Error>> {
20588 static CALL: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
20589 let call = CALL.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
20590 let mut o_c = self.uninit(o.len())?;
20591 let mut st_c = self.uninit(state_out.len())?;
20592 self.gdn_scan_chunked(
20593 q,
20594 k,
20595 v,
20596 g,
20597 beta,
20598 None,
20599 None,
20600 state_in,
20601 &mut st_c,
20602 &mut o_c,
20603 n_head,
20604 t,
20605 scale,
20606 Self::gdn_chunk_size(),
20607 n_head,
20608 )?;
20609 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)?;
20610 let (oh_s, oh_c) = (self.dtoh(o)?, self.dtoh(&o_c)?);
20611 let (sh_s, sh_c) = (self.dtoh(state_out)?, self.dtoh(&st_c)?);
20612 let stats = |a: &[f32], b: &[f32]| -> (f32, f32, f64) {
20613 let mut max_abs = 0f32;
20614 let mut max_rel = 0f32;
20615 let mut sum_rel = 0f64;
20616 for (x, y) in a.iter().zip(b) {
20617 let ad = (x - y).abs();
20618 let rel = ad / x.abs().max(y.abs()).max(1e-3);
20619 if ad > max_abs {
20620 max_abs = ad;
20621 }
20622 if rel > max_rel {
20623 max_rel = rel;
20624 }
20625 sum_rel += rel as f64;
20626 }
20627 (max_abs, max_rel, sum_rel / a.len() as f64)
20628 };
20629 let (o_ma, o_mr, o_mean) = stats(&oh_s, &oh_c);
20630 let (s_ma, s_mr, s_mean) = stats(&sh_s, &sh_c);
20631 println!(
20632 "[gdn-diff call {call:3} T={t} C={}] out: max_abs={o_ma:.3e} max_rel={o_mr:.3e} mean_rel={o_mean:.3e} | \
20633 state: max_abs={s_ma:.3e} max_rel={s_mr:.3e} mean_rel={s_mean:.3e}",
20634 Self::gdn_chunk_size()
20635 );
20636 Ok(())
20637 }
20638
20639 pub fn gdn_glog(
20641 &self,
20642 alpha: &CudaSlice<f32>,
20643 dt_bias: &CudaSlice<f32>,
20644 a: &CudaSlice<f32>,
20645 g_log: &mut CudaSlice<f32>,
20646 n_head: usize,
20647 t: usize,
20648 ) -> Result<(), Box<dyn std::error::Error>> {
20649 let f = self.func("gdn_glog_f32");
20650 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
20651 let (h, ti) = (n_head as i32, t as i32);
20652 let __s_b = self.gpu.stream();
20653 let mut b = __s_b.launch_builder(&f);
20654 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
20655 unsafe {
20656 b.launch(cfg)?;
20657 }
20658 Ok(())
20659 }
20660
20661 pub fn sigmoid_v(
20664 &self,
20665 x: &cudarc::driver::CudaView<f32>,
20666 y: &mut CudaSlice<f32>,
20667 n: usize,
20668 ) -> Result<(), Box<dyn std::error::Error>> {
20669 let f = self.func("sigmoid_f32");
20670 let cfg = LaunchConfig::for_num_elems(n as u32);
20671 let ni = n as i32;
20672 let __s_b = self.gpu.stream();
20673 let mut b = __s_b.launch_builder(&f);
20674 b.arg(x).arg(y).arg(&ni);
20675 unsafe {
20676 b.launch(cfg)?;
20677 }
20678 Ok(())
20679 }
20680
20681 pub fn gdn_glog_v(
20682 &self,
20683 alpha: &cudarc::driver::CudaView<f32>,
20684 dt_bias: &CudaSlice<f32>,
20685 a: &CudaSlice<f32>,
20686 g_log: &mut CudaSlice<f32>,
20687 n_head: usize,
20688 t: usize,
20689 ) -> Result<(), Box<dyn std::error::Error>> {
20690 let f = self.func("gdn_glog_f32");
20691 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
20692 let (h, ti) = (n_head as i32, t as i32);
20693 let __s_b = self.gpu.stream();
20694 let mut b = __s_b.launch_builder(&f);
20695 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
20696 unsafe {
20697 b.launch(cfg)?;
20698 }
20699 Ok(())
20700 }
20701
20702 pub fn sigmoid(
20703 &self,
20704 x: &CudaSlice<f32>,
20705 y: &mut CudaSlice<f32>,
20706 n: usize,
20707 ) -> Result<(), Box<dyn std::error::Error>> {
20708 let f = self.func("sigmoid_f32");
20709 let cfg = LaunchConfig::for_num_elems(n as u32);
20710 let ni = n as i32;
20711 let __s_b = self.gpu.stream();
20712 let mut b = __s_b.launch_builder(&f);
20713 b.arg(x).arg(y).arg(&ni);
20714 unsafe {
20715 b.launch(cfg)?;
20716 }
20717 Ok(())
20718 }
20719
20720 pub fn sig_mul_f16out(
20723 &self,
20724 a: &CudaSlice<f32>,
20725 g: &CudaSlice<f32>,
20726 dst: &mut CudaSlice<f32>,
20727 dst16: &mut CudaSlice<u8>,
20728 n: usize,
20729 ) -> Result<(), Box<dyn std::error::Error>> {
20730 let f = self.func("sig_mul_f16out_f32");
20731 let cfg = LaunchConfig::for_num_elems(n as u32);
20732 let ni = n as i32;
20733 let __s_b = self.gpu.stream();
20734 let mut b = __s_b.launch_builder(&f);
20735 b.arg(a).arg(g).arg(dst).arg(dst16).arg(&ni);
20736 unsafe {
20737 b.launch(cfg)?;
20738 }
20739 Ok(())
20740 }
20741
20742 #[allow(clippy::too_many_arguments)]
20751 pub fn attn_head_gate(
20752 &self,
20753 a: &CudaSlice<f32>,
20754 g: &CudaSlice<f32>,
20755 dst: &mut CudaSlice<f32>,
20756 dst16: Option<&mut CudaSlice<u8>>,
20757 head_dim: usize,
20758 n_head: usize,
20759 t: usize,
20760 ) -> Result<(), Box<dyn std::error::Error>> {
20761 let f = self.func("attn_head_gate_f32");
20762 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
20763 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
20764 let d16: u64 = match dst16 {
20766 Some(d) => self.addr_u8(d),
20767 None => 0,
20768 };
20769 let __s_b = self.gpu.stream();
20770 let mut b = __s_b.launch_builder(&f);
20771 b.arg(a)
20772 .arg(g)
20773 .arg(dst)
20774 .arg(&d16)
20775 .arg(&hd)
20776 .arg(&nh)
20777 .arg(&ti);
20778 unsafe {
20779 b.launch(cfg)?;
20780 }
20781 Ok(())
20782 }
20783
20784 #[allow(clippy::too_many_arguments)]
20793 pub fn swiglu_clamped_mul_scaled(
20794 &self,
20795 gate: &CudaSlice<f32>,
20796 up: &CudaSlice<f32>,
20797 gs: f32,
20798 us: f32,
20799 limit: f32,
20800 dst: &mut CudaSlice<f32>,
20801 n: usize,
20802 ) -> Result<(), Box<dyn std::error::Error>> {
20803 debug_assert!(
20804 limit > 1e-6,
20805 "swiglu_clamped needs a live limit; use silu_mul_scaled"
20806 );
20807 let f = self.func("swiglu_clamped_mul_scaled_f32");
20808 let cfg = LaunchConfig::for_num_elems(n as u32);
20809 let ni = n as i32;
20810 let __s_b = self.gpu.stream();
20811 let mut b = __s_b.launch_builder(&f);
20812 b.arg(gate)
20813 .arg(up)
20814 .arg(&gs)
20815 .arg(&us)
20816 .arg(&limit)
20817 .arg(dst)
20818 .arg(&ni);
20819 unsafe {
20820 b.launch(cfg)?;
20821 }
20822 Ok(())
20823 }
20824
20825 pub fn gated_rmsnorm(
20827 &self,
20828 o: &CudaSlice<f32>,
20829 w: &CudaSlice<f32>,
20830 z: &CudaSlice<f32>,
20831 dst: &mut CudaSlice<f32>,
20832 ncols: usize,
20833 nrows: usize,
20834 eps: f32,
20835 ) -> Result<(), Box<dyn std::error::Error>> {
20836 let f = self.func("gated_rmsnorm_f32");
20837 let cfg = LaunchConfig {
20838 grid_dim: (nrows as u32, 1, 1),
20839 block_dim: (128, 1, 1),
20840 shared_mem_bytes: 0,
20841 };
20842 let (nc, e) = (ncols as i32, eps);
20843 let __s_b = self.gpu.stream();
20844 let mut b = __s_b.launch_builder(&f);
20845 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
20846 unsafe {
20847 b.launch(cfg)?;
20848 }
20849 Ok(())
20850 }
20851
20852 pub fn gated_rmsnorm_f16out(
20855 &self,
20856 o: &CudaSlice<f32>,
20857 w: &CudaSlice<f32>,
20858 z: &CudaSlice<f32>,
20859 dst: &mut CudaSlice<f32>,
20860 dst16: &mut CudaSlice<u8>,
20861 ncols: usize,
20862 nrows: usize,
20863 eps: f32,
20864 ) -> Result<(), Box<dyn std::error::Error>> {
20865 let f = self.func("gated_rmsnorm_f16out_f32");
20866 let cfg = LaunchConfig {
20868 grid_dim: (nrows as u32, 1, 1),
20869 block_dim: (128, 1, 1),
20870 shared_mem_bytes: 0,
20871 };
20872 let (nc, e) = (ncols as i32, eps);
20873 let __s_b = self.gpu.stream();
20874 let mut b = __s_b.launch_builder(&f);
20875 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
20876 unsafe {
20877 b.launch(cfg)?;
20878 }
20879 Ok(())
20880 }
20881
20882 #[allow(clippy::too_many_arguments)]
20886 pub fn add_rms_norm_zq8(
20887 &self,
20888 a: &CudaSlice<f32>,
20889 b_in: &CudaSlice<f32>,
20890 w: &CudaSlice<f32>,
20891 res: &mut CudaSlice<f32>,
20892 z: &mut CudaSlice<f32>,
20893 ncols: usize,
20894 nrows: usize,
20895 eps: f32,
20896 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
20897 assert!(ncols % 32 == 0);
20898 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
20899 let mut d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
20900 let f = self.func("add_rms_norm_zq8");
20901 let cfg = LaunchConfig {
20902 grid_dim: (nrows as u32, 1, 1),
20903 block_dim: (1024, 1, 1),
20904 shared_mem_bytes: 0,
20905 };
20906 let (nc, ep) = (ncols as i32, eps);
20907 let __s_b = self.gpu.stream();
20908 let mut b = __s_b.launch_builder(&f);
20909 b.arg(a)
20910 .arg(b_in)
20911 .arg(w)
20912 .arg(res)
20913 .arg(z)
20914 .arg(&mut q)
20915 .arg(&mut d)
20916 .arg(&nc)
20917 .arg(&ep);
20918 unsafe {
20919 b.launch(cfg)?;
20920 }
20921 Ok((q, d))
20922 }
20923
20924 pub fn gated_rmsnorm_zv(
20929 &self,
20930 o: &CudaSlice<f32>,
20931 w: &CudaSlice<f32>,
20932 z: &cudarc::driver::CudaView<f32>,
20933 dst: &mut CudaSlice<f32>,
20934 ncols: usize,
20935 nrows: usize,
20936 eps: f32,
20937 ) -> Result<(), Box<dyn std::error::Error>> {
20938 let f = self.func("gated_rmsnorm_f32");
20939 let cfg = LaunchConfig {
20940 grid_dim: (nrows as u32, 1, 1),
20941 block_dim: (128, 1, 1),
20942 shared_mem_bytes: 0,
20943 };
20944 let (nc, e) = (ncols as i32, eps);
20945 let __s_b = self.gpu.stream();
20946 let mut b = __s_b.launch_builder(&f);
20947 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
20948 unsafe {
20949 b.launch(cfg)?;
20950 }
20951 Ok(())
20952 }
20953
20954 pub fn gated_rmsnorm_f16out_zv(
20955 &self,
20956 o: &CudaSlice<f32>,
20957 w: &CudaSlice<f32>,
20958 z: &cudarc::driver::CudaView<f32>,
20959 dst: &mut CudaSlice<f32>,
20960 dst16: &mut CudaSlice<u8>,
20961 ncols: usize,
20962 nrows: usize,
20963 eps: f32,
20964 ) -> Result<(), Box<dyn std::error::Error>> {
20965 let f = self.func("gated_rmsnorm_f16out_f32");
20966 let cfg = LaunchConfig {
20968 grid_dim: (nrows as u32, 1, 1),
20969 block_dim: (128, 1, 1),
20970 shared_mem_bytes: 0,
20971 };
20972 let (nc, e) = (ncols as i32, eps);
20973 let __s_b = self.gpu.stream();
20974 let mut b = __s_b.launch_builder(&f);
20975 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
20976 unsafe {
20977 b.launch(cfg)?;
20978 }
20979 Ok(())
20980 }
20981
20982 pub fn gated_rmsnorm_q8_1(
20983 &self,
20984 o: &CudaSlice<f32>,
20985 w: &CudaSlice<f32>,
20986 z: &CudaSlice<f32>,
20987 ncols: usize,
20988 nrows: usize,
20989 eps: f32,
20990 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
20991 assert!(ncols % 32 == 0);
20992 let f = self.func("gated_rmsnorm_q8_1");
20993 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
20994 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
20995 let cfg = LaunchConfig {
20996 grid_dim: (nrows as u32, 1, 1),
20997 block_dim: (128, 1, 1),
20998 shared_mem_bytes: 0,
20999 };
21000 let (nc, ep) = (ncols as i32, eps);
21001 let __s_b = self.gpu.stream();
21002 let mut b = __s_b.launch_builder(&f);
21003 b.arg(o)
21004 .arg(w)
21005 .arg(z)
21006 .arg(&mut out_q)
21007 .arg(&mut out_d)
21008 .arg(&nc)
21009 .arg(&ep);
21010 unsafe {
21011 b.launch(cfg)?;
21012 }
21013 Ok((out_q, out_d))
21014 }
21015
21016 pub fn transpose(
21018 &self,
21019 inp: &CudaSlice<f32>,
21020 rows: usize,
21021 cols: usize,
21022 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21023 let f = self.func("transpose_f32");
21024 let mut out = self.zeros(rows * cols)?;
21025 let cfg = LaunchConfig::for_num_elems((rows * cols) as u32);
21026 let (r, c) = (rows as i32, cols as i32);
21027 let __s_b = self.gpu.stream();
21028 let mut b = __s_b.launch_builder(&f);
21029 b.arg(inp).arg(&mut out).arg(&r).arg(&c);
21030 unsafe {
21031 b.launch(cfg)?;
21032 }
21033 Ok(out)
21034 }
21035
21036 pub fn repeat_heads(
21038 &self,
21039 inp: &CudaSlice<f32>,
21040 out: &mut CudaSlice<f32>,
21041 head_dim: usize,
21042 n_in: usize,
21043 n_out: usize,
21044 t: usize,
21045 ) -> Result<(), Box<dyn std::error::Error>> {
21046 let f = self.func("repeat_heads_f32");
21047 let cfg = LaunchConfig::for_num_elems((head_dim * n_out * t) as u32);
21048 let (hd, ni, no, ti) = (head_dim as i32, n_in as i32, n_out as i32, t as i32);
21049 let __s_b = self.gpu.stream();
21050 let mut b = __s_b.launch_builder(&f);
21051 b.arg(inp).arg(out).arg(&hd).arg(&ni).arg(&no).arg(&ti);
21052 unsafe {
21053 b.launch(cfg)?;
21054 }
21055 Ok(())
21056 }
21057
21058 pub fn q_gate_split(
21061 &self,
21062 qf: &CudaSlice<f32>,
21063 q_out: &mut CudaSlice<f32>,
21064 gate_out: &mut CudaSlice<f32>,
21065 head_dim: usize,
21066 n_head: usize,
21067 t: usize,
21068 ) -> Result<(), Box<dyn std::error::Error>> {
21069 let f = self.func("q_gate_split_f32");
21070 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
21071 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
21072 let __s_b = self.gpu.stream();
21073 let mut b = __s_b.launch_builder(&f);
21074 b.arg(qf)
21075 .arg(q_out)
21076 .arg(gate_out)
21077 .arg(&hd)
21078 .arg(&nh)
21079 .arg(&ti);
21080 unsafe {
21081 b.launch(cfg)?;
21082 }
21083 Ok(())
21084 }
21085
21086 pub fn qkv_to_gdn_repack(
21090 &self,
21091 conv_out: &CudaSlice<f32>,
21092 q_g: &mut CudaSlice<f32>,
21093 k_g: &mut CudaSlice<f32>,
21094 v_g: &mut CudaSlice<f32>,
21095 d_state: usize,
21096 num_v: usize,
21097 num_k: usize,
21098 key_dim: usize,
21099 t: usize,
21100 ) -> Result<(), Box<dyn std::error::Error>> {
21101 let f = self.func("qkv_to_gdn_repack_f32");
21102 let cfg = LaunchConfig::for_num_elems((d_state * num_v * t) as u32);
21103 let (ds, nv, nk, kd, ti) = (
21104 d_state as i32,
21105 num_v as i32,
21106 num_k as i32,
21107 key_dim as i32,
21108 t as i32,
21109 );
21110 let __s_b = self.gpu.stream();
21111 let mut b = __s_b.launch_builder(&f);
21112 b.arg(conv_out)
21113 .arg(q_g)
21114 .arg(k_g)
21115 .arg(v_g)
21116 .arg(&ds)
21117 .arg(&nv)
21118 .arg(&nk)
21119 .arg(&kd)
21120 .arg(&ti);
21121 unsafe {
21122 b.launch(cfg)?;
21123 }
21124 Ok(())
21125 }
21126
21127 pub fn conv_left_pad(
21130 &self,
21131 src: &CudaSlice<f32>,
21132 dst: &mut CudaSlice<f32>,
21133 conv_dim: usize,
21134 t: usize,
21135 pad: usize,
21136 ) -> Result<(), Box<dyn std::error::Error>> {
21137 let f = self.func("conv_left_pad_f32");
21138 let cfg = LaunchConfig::for_num_elems((conv_dim * t) as u32);
21139 let (cd, ti, p) = (conv_dim as i32, t as i32, pad as i32);
21140 let __s_b = self.gpu.stream();
21141 let mut b = __s_b.launch_builder(&f);
21142 b.arg(src).arg(dst).arg(&cd).arg(&ti).arg(&p);
21143 unsafe {
21144 b.launch(cfg)?;
21145 }
21146 Ok(())
21147 }
21148
21149 pub fn conv_assemble_and_roll(
21153 &self,
21154 qkv_col: &CudaSlice<f32>,
21155 conv_state: &mut CudaSlice<f32>,
21156 conv_in: &mut CudaSlice<f32>,
21157 conv_dim: usize,
21158 pad: usize,
21159 ) -> Result<(), Box<dyn std::error::Error>> {
21160 let f = self.func("conv_assemble_and_roll_f32");
21161 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
21162 let (cd, p) = (conv_dim as i32, pad as i32);
21163 let __s_b = self.gpu.stream();
21164 let mut b = __s_b.launch_builder(&f);
21165 b.arg(qkv_col).arg(conv_state).arg(conv_in).arg(&cd).arg(&p);
21166 unsafe {
21167 b.launch(cfg)?;
21168 }
21169 Ok(())
21170 }
21171
21172 pub fn ssm_conv1d_fused_decode(
21178 &self,
21179 qkv_col: &CudaSlice<f32>,
21180 conv_state: &mut CudaSlice<f32>,
21181 w: &CudaSlice<f32>,
21182 conv_out: &mut CudaSlice<f32>,
21183 conv_dim: usize,
21184 d_conv: usize,
21185 ) -> Result<(), Box<dyn std::error::Error>> {
21186 let f = self.func("ssm_conv1d_fused_decode_f32");
21187 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
21188 let (cd, dc) = (conv_dim as i32, d_conv as i32);
21189 let __s_b = self.gpu.stream();
21190 let mut b = __s_b.launch_builder(&f);
21191 b.arg(qkv_col)
21192 .arg(conv_state)
21193 .arg(w)
21194 .arg(conv_out)
21195 .arg(&cd)
21196 .arg(&dc);
21197 unsafe {
21198 b.launch(cfg)?;
21199 }
21200 Ok(())
21201 }
21202
21203 pub fn slice_range(
21206 &self,
21207 src: &CudaSlice<f32>,
21208 start: usize,
21209 len: usize,
21210 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21211 let host = self.gpu.stream().clone_dtoh(src)?;
21212 self.gpu.stream().synchronize()?;
21213 Ok(self.htod(&host[start..start + len])?)
21214 }
21215}
21216
21217#[cfg(test)]
21218mod target_dispatch_tests {
21219 use super::legacy_quant_gemm_allowed;
21220
21221 #[test]
21222 fn legacy_quant_gemm_arch_policy_honors_the_escape_hatch() {
21223 assert!(legacy_quant_gemm_allowed(false, false, false));
21225 assert!(!legacy_quant_gemm_allowed(false, false, true));
21226 assert!(!legacy_quant_gemm_allowed(true, false, false));
21228 assert!(!legacy_quant_gemm_allowed(true, false, true));
21229 assert!(legacy_quant_gemm_allowed(true, true, false));
21231 assert!(!legacy_quant_gemm_allowed(true, true, true));
21232 }
21233
21234 #[cfg(all(memra_portable_cuda, not(memra_hopper_mma)))]
21235 #[test]
21236 fn portable_build_disables_legacy_quant_gemm_without_an_env_override() {
21237 assert!(!legacy_quant_gemm_allowed(
21238 cfg!(memra_portable_cuda),
21239 cfg!(memra_hopper_mma),
21240 false
21241 ));
21242 }
21243
21244 #[cfg(memra_hopper_mma)]
21245 #[test]
21246 fn hopper_mma_build_re_admits_legacy_quant_gemm() {
21247 assert!(legacy_quant_gemm_allowed(
21248 cfg!(memra_portable_cuda),
21249 cfg!(memra_hopper_mma),
21250 false
21251 ));
21252 assert!(super::portable_mma_gated() == false);
21253 }
21254}
21255
21256impl memra_kv::KvDev for Engine {
21259 fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21260 Engine::zeros(self, n)
21261 }
21262 fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21263 Engine::uninit(self, n)
21264 }
21265 fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
21266 Engine::alloc_u8(self, n)
21267 }
21268 fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
21269 Engine::htod_i32(self, v)
21270 }
21271 fn clone_dtod(
21272 &self,
21273 src: &CudaSlice<f32>,
21274 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21275 Engine::clone_dtod(self, src)
21276 }
21277 fn copy_into(
21278 &self,
21279 dst: &mut CudaSlice<f32>,
21280 off: usize,
21281 src: &CudaSlice<f32>,
21282 len: usize,
21283 ) -> Result<(), Box<dyn std::error::Error>> {
21284 Engine::copy_into(self, dst, off, src, len)
21285 }
21286 fn set_i32_one(
21287 &self,
21288 d: &mut CudaSlice<i32>,
21289 v: i32,
21290 ) -> Result<(), Box<dyn std::error::Error>> {
21291 Engine::set_i32_one(self, d, v)
21292 }
21293}