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 #[allow(clippy::too_many_arguments)]
2948 pub fn gumbel_perturb_filtered_col(
2949 &self,
2950 x: &CudaSlice<f32>,
2951 col: usize,
2952 y: &mut CudaSlice<f32>,
2953 n: usize,
2954 seed: u64,
2955 stream_pos: u32,
2956 temp: f32,
2957 stat_max: &CudaSlice<f32>,
2958 stat_th: &CudaSlice<f32>,
2959 stat_idx: usize,
2960 ) -> Result<(), Box<dyn std::error::Error>> {
2961 let f = self.func("gumbel_perturb_filtered_col_f32");
2962 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2963 let (ci, si) = (col as i32, stat_idx as i32);
2964 let cfg = LaunchConfig {
2965 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2966 block_dim: (256, 1, 1),
2967 shared_mem_bytes: 0,
2968 };
2969 let __s_b = self.gpu.stream();
2970 let mut b = __s_b.launch_builder(&f);
2971 b.arg(x)
2972 .arg(&ci)
2973 .arg(&mut *y)
2974 .arg(&ni)
2975 .arg(&slo)
2976 .arg(&shi)
2977 .arg(&stream_pos)
2978 .arg(&temp)
2979 .arg(stat_max)
2980 .arg(stat_th)
2981 .arg(&si);
2982 unsafe {
2983 b.launch(cfg)?;
2984 }
2985 Ok(())
2986 }
2987
2988 pub fn sctr_inc(&self, ctr: &mut CudaSlice<u32>) -> Result<(), Box<dyn std::error::Error>> {
2993 let f = self.func("memra_sctr_inc");
2994 let cfg = LaunchConfig {
2995 grid_dim: (1, 1, 1),
2996 block_dim: (1, 1, 1),
2997 shared_mem_bytes: 0,
2998 };
2999 let __s_b = self.gpu.stream();
3000 let mut b = __s_b.launch_builder(&f);
3001 b.arg(&mut *ctr);
3002 unsafe {
3003 b.launch(cfg)?;
3004 }
3005 Ok(())
3006 }
3007
3008 pub fn gumbel_perturb_ctr(
3013 &self,
3014 x: &CudaSlice<f32>,
3015 y: &mut CudaSlice<f32>,
3016 n: usize,
3017 seed: u64,
3018 ctr: &CudaSlice<u32>,
3019 temp: f32,
3020 ) -> Result<(), Box<dyn std::error::Error>> {
3021 let f = self.func("gumbel_perturb_ctr_f32");
3022 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3023 let cfg = LaunchConfig {
3024 grid_dim: (n.div_ceil(256) as u32, 1, 1),
3025 block_dim: (256, 1, 1),
3026 shared_mem_bytes: 0,
3027 };
3028 let __s_b = self.gpu.stream();
3029 let mut b = __s_b.launch_builder(&f);
3030 b.arg(x)
3031 .arg(&mut *y)
3032 .arg(&ni)
3033 .arg(&slo)
3034 .arg(&shi)
3035 .arg(ctr)
3036 .arg(&temp);
3037 unsafe {
3038 b.launch(cfg)?;
3039 }
3040 Ok(())
3041 }
3042
3043 pub fn softmax_gather(
3047 &self,
3048 x: &CudaSlice<f32>,
3049 row_stride: usize,
3050 ids: &CudaSlice<u32>,
3051 rows: &CudaSlice<i32>,
3052 out: &mut CudaSlice<f32>,
3053 n: usize,
3054 npair: usize,
3055 temp: f32,
3056 ) -> Result<(), Box<dyn std::error::Error>> {
3057 let f = self.func("softmax_gather_f32");
3058 let (ni, rs) = (n as i32, row_stride as i64);
3059 let np = npair as i32;
3060 let cfg = LaunchConfig {
3061 grid_dim: (npair as u32, 1, 1),
3062 block_dim: (256, 1, 1),
3063 shared_mem_bytes: 0,
3064 };
3065 let __s_b = self.gpu.stream();
3066 let mut b = __s_b.launch_builder(&f);
3067 b.arg(x)
3068 .arg(&rs)
3069 .arg(ids)
3070 .arg(rows)
3071 .arg(&mut *out)
3072 .arg(&ni)
3073 .arg(&np)
3074 .arg(&temp);
3075 unsafe {
3076 b.launch(cfg)?;
3077 }
3078 Ok(())
3079 }
3080
3081 pub fn residual_sample(
3085 &self,
3086 p: &CudaSlice<f32>,
3087 q: Option<&CudaSlice<f32>>,
3088 n: usize,
3089 temp: f32,
3090 seed: u64,
3091 stream_pos: u32,
3092 out_tok: &mut CudaSlice<u32>,
3093 ) -> Result<(), Box<dyn std::error::Error>> {
3094 let f = self.func("residual_sample_f32");
3095 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3096 let nth = 1024u32;
3097 let cfg = LaunchConfig {
3098 grid_dim: (1, 1, 1),
3099 block_dim: (nth, 1, 1),
3100 shared_mem_bytes: 0,
3101 };
3102 let has_q: i32 = q.is_some() as i32;
3103 let qbuf = q.unwrap_or(p); let __s_b = self.gpu.stream();
3105 let mut b = __s_b.launch_builder(&f);
3106 b.arg(p)
3107 .arg(qbuf)
3108 .arg(&has_q)
3109 .arg(&ni)
3110 .arg(&temp)
3111 .arg(&slo)
3112 .arg(&shi)
3113 .arg(&stream_pos)
3114 .arg(&mut *out_tok);
3115 unsafe {
3116 b.launch(cfg)?;
3117 }
3118 Ok(())
3119 }
3120
3121 pub fn with_moe_cache<R>(
3126 &self,
3127 max_block_bytes: usize,
3128 f: impl FnOnce(
3129 &mut crate::moe_cache::MoeSlotCache,
3130 &Engine,
3131 ) -> Result<R, Box<dyn std::error::Error>>,
3132 ) -> Result<R, Box<dyn std::error::Error>> {
3133 let mut guard = self.moe_cache.lock().unwrap();
3134 if guard.is_none() {
3135 *guard = Some(crate::moe_cache::MoeSlotCache::new(self, max_block_bytes)?);
3136 }
3137 let cache = guard.as_mut().unwrap();
3138 f(cache, self)
3139 }
3140
3141 pub fn freeze_moe_cache(&self) {
3144 if let Some(cache) = self.moe_cache.lock().unwrap().as_mut() {
3145 cache.freeze();
3146 }
3147 }
3148
3149 pub fn export_moe_residency(&self) -> Option<Vec<(u16, u8, u16)>> {
3152 self.moe_cache
3153 .lock()
3154 .unwrap()
3155 .as_ref()
3156 .map(crate::moe_cache::MoeSlotCache::export_residency)
3157 }
3158
3159 pub(crate) fn moe_cache_frozen(&self) -> bool {
3160 self.moe_cache
3161 .lock()
3162 .unwrap()
3163 .as_ref()
3164 .is_some_and(crate::moe_cache::MoeSlotCache::is_frozen)
3165 }
3166
3167 pub fn frozen_cpu_experts_prefer_tokenwise_prime(&self) -> bool {
3174 crate::cpu_experts::configured()
3175 && self.moe_cache_frozen()
3176 && std::env::var("MEMRA_CPU_EXPERT_BATCHED_PRIME").as_deref() != Ok("1")
3177 }
3178
3179 pub(crate) fn configure_moe_cache_layout(&self, block_bytes: Vec<usize>) {
3181 assert!(
3182 self.moe_cache.lock().unwrap().is_none(),
3183 "MoE cache layout configured after cache construction"
3184 );
3185 *self.moe_cache_layout.lock().unwrap() = Some(block_bytes);
3186 }
3187
3188 pub(crate) fn moe_cache_layout(&self) -> Option<Vec<usize>> {
3189 self.moe_cache_layout.lock().unwrap().clone()
3190 }
3191
3192 pub fn moe_cache_enabled() -> bool {
3194 std::env::var("MEMRA_MOE_CACHE").as_deref() != Ok("0")
3195 }
3196
3197 pub fn moe_cache_stats(&self) -> Option<(u64, u64, u64, usize)> {
3200 let guard = self.moe_cache.lock().unwrap();
3201 guard
3202 .as_ref()
3203 .map(|c| (c.hits, c.misses, c.staged_bytes, c.n_slots()))
3204 }
3205
3206 pub fn cpu_expert_stats(
3210 &self,
3211 ) -> Option<(u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64)> {
3212 crate::cpu_experts::configured().then(crate::cpu_experts::stats)
3213 }
3214
3215 pub fn cpu_expert_predictor_stats(&self) -> (u64, u64) {
3218 crate::cpu_experts::predictor_stats()
3219 }
3220
3221 pub fn cpu_expert_exposed_wait_ns(&self) -> Option<u64> {
3222 crate::cpu_experts::configured().then(crate::cpu_experts::exposed_wait_ns)
3223 }
3224
3225 pub fn cpu_expert_gpu_residency_stats(&self) -> Option<(u64, u64, u64)> {
3228 crate::cpu_experts::configured().then(crate::cpu_experts::incomplete_gpu_residency_stats)
3229 }
3230
3231 pub fn moe_pread_stats(&self) -> Option<(u64, u64, u64, u64, u64, u64, u64)> {
3234 let guard = self.moe_cache.lock().unwrap();
3235 guard
3236 .as_ref()
3237 .and_then(|cache| cache.pread_stats())
3238 .map(|stats| {
3239 (
3240 stats.reads,
3241 stats.bytes,
3242 stats.read_errors,
3243 stats.short_reads,
3244 stats.fallbacks,
3245 stats.buffer_waits,
3246 stats.ring_full,
3247 )
3248 })
3249 }
3250
3251 pub fn spill_config_fallbacks(&self) -> u64 {
3253 crate::spill_pread::config_fallbacks()
3254 }
3255
3256 pub fn moe_cache_reset_counters(&self) {
3258 if let Some(c) = self.moe_cache.lock().unwrap().as_mut() {
3259 c.reset_counters();
3260 }
3261 }
3262
3263 pub fn htod_bytes(&self, v: &[u8]) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3264 Ok(self.gpu.stream().clone_htod(v)?)
3265 }
3266
3267 pub fn htod_bytes_padded(
3271 &self,
3272 v: &[u8],
3273 pad: usize,
3274 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3275 let mut d = self.alloc_u8_uninit(v.len() + pad)?;
3276 {
3277 let mut view = d.slice_mut(0..v.len());
3278 self.gpu.stream().memcpy_htod(v, &mut view)?;
3279 }
3280 Ok(d)
3281 }
3282
3283 pub fn copy_into(
3285 &self,
3286 dst: &mut CudaSlice<f32>,
3287 off: usize,
3288 src: &CudaSlice<f32>,
3289 len: usize,
3290 ) -> Result<(), Box<dyn std::error::Error>> {
3291 let mut view = dst.slice_mut(off..off + len);
3292 self.gpu
3293 .stream()
3294 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3295 Ok(())
3296 }
3297
3298 pub fn copy_u8_into(
3301 &self,
3302 dst: &mut CudaSlice<u8>,
3303 off: usize,
3304 src: &CudaSlice<u8>,
3305 len: usize,
3306 ) -> Result<(), Box<dyn std::error::Error>> {
3307 let mut view = dst.slice_mut(off..off + len);
3308 self.gpu
3309 .stream()
3310 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3311 Ok(())
3312 }
3313
3314 pub fn copy_u8_range_into(
3316 &self,
3317 dst: &mut CudaSlice<u8>,
3318 dst_off: usize,
3319 src: &CudaSlice<u8>,
3320 src_off: usize,
3321 len: usize,
3322 ) -> Result<(), Box<dyn std::error::Error>> {
3323 let mut dst_view = dst.slice_mut(dst_off..dst_off + len);
3324 self.gpu
3325 .stream()
3326 .memcpy_dtod(&src.slice(src_off..src_off + len), &mut dst_view)?;
3327 Ok(())
3328 }
3329
3330 pub fn prepare_kv_append(
3334 &self,
3335 kv: &mut crate::cache::KvLayer,
3336 retain_from: usize,
3337 append_rows: usize,
3338 ) -> Result<usize, Box<dyn std::error::Error>> {
3339 let Some(plan) = kv
3340 .ring
3341 .as_ref()
3342 .map(|ring| ring.append_plan(kv.len, retain_from, append_rows))
3343 .transpose()?
3344 else {
3345 return Ok(kv.len);
3346 };
3347 match plan {
3348 crate::cache::KvRingAppend::Contiguous { write_row } => Ok(write_row),
3349 crate::cache::KvRingAppend::Rebase {
3350 src_row,
3351 keep_rows,
3352 new_base,
3353 write_row,
3354 } => {
3355 if keep_rows > 0 {
3356 let k_len = keep_rows * kv.k_tok_bytes;
3357 let v_len = keep_rows * kv.v_tok_bytes;
3358 let mut k_tmp = self.alloc_u8_uninit(k_len)?;
3359 let mut v_tmp = self.alloc_u8_uninit(v_len)?;
3360 self.copy_u8_range_into(&mut k_tmp, 0, &kv.k, src_row * kv.k_tok_bytes, k_len)?;
3361 self.copy_u8_range_into(&mut v_tmp, 0, &kv.v, src_row * kv.v_tok_bytes, v_len)?;
3362 self.copy_u8_into(&mut kv.k, 0, &k_tmp, k_len)?;
3363 self.copy_u8_into(&mut kv.v, 0, &v_tmp, v_len)?;
3364 }
3365 kv.ring.as_mut().unwrap().apply_rebase(new_base);
3366 Ok(write_row)
3367 }
3368 }
3369 }
3370
3371 pub fn htod_u8_into(
3374 &self,
3375 dst: &mut CudaSlice<u8>,
3376 off: usize,
3377 src: &[u8],
3378 ) -> Result<(), Box<dyn std::error::Error>> {
3379 let mut view = dst.slice_mut(off..off + src.len());
3380 self.gpu.stream().memcpy_htod(src, &mut view)?;
3381 Ok(())
3382 }
3383
3384 pub fn view<'a>(&self, b: &'a CudaSlice<f32>, len: usize) -> cudarc::driver::CudaView<'a, f32> {
3385 b.slice(0..len)
3386 }
3387
3388 pub fn view_u8_range<'a>(
3391 &self,
3392 b: &'a CudaSlice<u8>,
3393 start: usize,
3394 end: usize,
3395 ) -> cudarc::driver::CudaView<'a, u8> {
3396 b.slice(start..end)
3397 }
3398 pub fn view_u8<'a>(
3399 &self,
3400 b: &'a CudaSlice<u8>,
3401 len: usize,
3402 ) -> cudarc::driver::CudaView<'a, u8> {
3403 b.slice(0..len)
3404 }
3405
3406 pub fn append_kv_quantized(
3410 &self,
3411 k_row: &CudaSlice<f32>,
3412 v_row: &CudaSlice<f32>,
3413 kc: &mut CudaSlice<u8>,
3414 vc: &mut CudaSlice<u8>,
3415 t: usize,
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 f = if g {
3423 self.func_g("append_quantize_kv_q8_0_q5_1")
3424 } else {
3425 self.func("append_quantize_kv_q8_0_q5_1")
3426 };
3427 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3428 let cfg = LaunchConfig {
3429 grid_dim: (nblk, 1, 1),
3430 block_dim: (32, 1, 1),
3431 shared_mem_bytes: 0,
3432 };
3433 let (ti, kdk, kdv) = (t as i32, kv_dim_k as i32, kv_dim_v as i32);
3434 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3435 let __s_b = self.gpu.stream();
3436 let mut b = __s_b.launch_builder(&f);
3437 b.arg(k_row)
3438 .arg(v_row)
3439 .arg(kc)
3440 .arg(vc)
3441 .arg(&ti)
3442 .arg(&kdk)
3443 .arg(&kdv)
3444 .arg(&ktb)
3445 .arg(&vtb);
3446 unsafe {
3447 b.launch(cfg)?;
3448 }
3449 Ok(())
3450 }
3451
3452 pub fn append_kv_quantized_dc(
3456 &self,
3457 k_row: &CudaSlice<f32>,
3458 v_row: &CudaSlice<f32>,
3459 kc: &mut CudaSlice<u8>,
3460 vc: &mut CudaSlice<u8>,
3461 t_dev: &CudaSlice<i32>,
3462 kv_dim_k: usize,
3463 kv_dim_v: usize,
3464 k_tok_bytes: usize,
3465 v_tok_bytes: usize,
3466 g: bool,
3467 ) -> Result<(), Box<dyn std::error::Error>> {
3468 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3469 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
3470 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3471 if Self::pdl_on() && Self::pdl_wb_on() {
3473 use cudarc::driver::{DevicePtr, DevicePtrMut};
3474 let s = &self.gpu.stream();
3475 let (pk, _g0) = k_row.device_ptr(s);
3476 let (pv, _g1) = v_row.device_ptr(s);
3477 let (pkc, _g2) = kc.device_ptr_mut(s);
3478 let (pvc, _g3) = vc.device_ptr_mut(s);
3479 let (pt, _g4) = t_dev.device_ptr(s);
3480 let mut ps = [
3481 &pk as *const _ as *mut std::ffi::c_void,
3482 &pv as *const _ as *mut _,
3483 &pkc as *const _ as *mut _,
3484 &pvc as *const _ as *mut _,
3485 &pt as *const _ as *mut _,
3486 &kdk as *const _ as *mut _,
3487 &kdv as *const _ as *mut _,
3488 &ktb as *const _ as *mut _,
3489 &vtb as *const _ as *mut _,
3490 ];
3491 unsafe {
3492 self.launch_pdl_flash(
3493 g,
3494 "append_quantize_kv_q8_0_q5_1_dc",
3495 (nblk, 1, 1),
3496 (32, 1, 1),
3497 0,
3498 &mut ps,
3499 )?;
3500 }
3501 return Ok(());
3502 }
3503 let f = if g {
3504 self.func_g("append_quantize_kv_q8_0_q5_1_dc")
3505 } else {
3506 self.func("append_quantize_kv_q8_0_q5_1_dc")
3507 };
3508 let cfg = LaunchConfig {
3509 grid_dim: (nblk, 1, 1),
3510 block_dim: (32, 1, 1),
3511 shared_mem_bytes: 0,
3512 };
3513 let __s_b = self.gpu.stream();
3514 let mut b = __s_b.launch_builder(&f);
3515 b.arg(k_row)
3516 .arg(v_row)
3517 .arg(kc)
3518 .arg(vc)
3519 .arg(t_dev)
3520 .arg(&kdk)
3521 .arg(&kdv)
3522 .arg(&ktb)
3523 .arg(&vtb);
3524 unsafe {
3525 b.launch(cfg)?;
3526 }
3527 Ok(())
3528 }
3529
3530 #[allow(clippy::too_many_arguments)]
3537 pub fn append_kv_quantized_rows(
3538 &self,
3539 k_rows: &CudaSlice<f32>,
3540 v_rows: &CudaSlice<f32>,
3541 kc: &mut CudaSlice<u8>,
3542 vc: &mut CudaSlice<u8>,
3543 t0: usize,
3544 t: usize,
3545 kv_dim_k: usize,
3546 kv_dim_v: usize,
3547 k_tok_bytes: usize,
3548 v_tok_bytes: usize,
3549 g: bool,
3550 ) -> Result<(), Box<dyn std::error::Error>> {
3551 if std::env::var("MEMRA_PRIME_APPEND_LOOP").is_ok() {
3552 for i in 0..t {
3553 let k_row = k_rows.slice(i * kv_dim_k..(i + 1) * kv_dim_k);
3554 let v_row = v_rows.slice(i * kv_dim_v..(i + 1) * kv_dim_v);
3555 self.append_kv_quantized_view(
3556 &k_row,
3557 &v_row,
3558 kc,
3559 vc,
3560 t0 + i,
3561 kv_dim_k,
3562 kv_dim_v,
3563 k_tok_bytes,
3564 v_tok_bytes,
3565 g,
3566 )?;
3567 }
3568 return Ok(());
3569 }
3570 let f = if g {
3571 self.func_g("append_quantize_kv_q8_0_q5_1_rows")
3572 } else {
3573 self.func("append_quantize_kv_q8_0_q5_1_rows")
3574 };
3575 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3576 let cfg = LaunchConfig {
3577 grid_dim: (nblk, t as u32, 1),
3578 block_dim: (32, 1, 1),
3579 shared_mem_bytes: 0,
3580 };
3581 let (t0i, kdk, kdv) = (t0 as i32, kv_dim_k as i32, kv_dim_v as i32);
3582 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3583 let __s_b = self.gpu.stream();
3584 let mut b = __s_b.launch_builder(&f);
3585 b.arg(k_rows)
3586 .arg(v_rows)
3587 .arg(kc)
3588 .arg(vc)
3589 .arg(&t0i)
3590 .arg(&kdk)
3591 .arg(&kdv)
3592 .arg(&ktb)
3593 .arg(&vtb);
3594 unsafe {
3595 b.launch(cfg)?;
3596 }
3597 Ok(())
3598 }
3599
3600 pub fn inc_seqlen(&self, p: &mut CudaSlice<i32>) -> Result<(), Box<dyn std::error::Error>> {
3604 let f = self.func("inc_i32");
3605 let cfg = LaunchConfig {
3606 grid_dim: (1, 1, 1),
3607 block_dim: (1, 1, 1),
3608 shared_mem_bytes: 0,
3609 };
3610 let __s_b = self.gpu.stream();
3611 let mut b = __s_b.launch_builder(&f);
3612 b.arg(p);
3613 unsafe {
3614 b.launch(cfg)?;
3615 }
3616 Ok(())
3617 }
3618
3619 pub fn append_kv_quantized_view(
3622 &self,
3623 k_row: &cudarc::driver::CudaView<f32>,
3624 v_row: &cudarc::driver::CudaView<f32>,
3625 kc: &mut CudaSlice<u8>,
3626 vc: &mut CudaSlice<u8>,
3627 t: usize,
3628 kv_dim_k: usize,
3629 kv_dim_v: usize,
3630 k_tok_bytes: usize,
3631 v_tok_bytes: usize,
3632 g: bool,
3633 ) -> Result<(), Box<dyn std::error::Error>> {
3634 let f = if g {
3635 self.func_g("append_quantize_kv_q8_0_q5_1")
3636 } else {
3637 self.func("append_quantize_kv_q8_0_q5_1")
3638 };
3639 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3640 let cfg = LaunchConfig {
3641 grid_dim: (nblk, 1, 1),
3642 block_dim: (32, 1, 1),
3643 shared_mem_bytes: 0,
3644 };
3645 let (ti, kdk, kdv) = (t as i32, kv_dim_k as i32, kv_dim_v as i32);
3646 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3647 let __s_b = self.gpu.stream();
3648 let mut b = __s_b.launch_builder(&f);
3649 b.arg(k_row)
3650 .arg(v_row)
3651 .arg(kc)
3652 .arg(vc)
3653 .arg(&ti)
3654 .arg(&kdk)
3655 .arg(&kdv)
3656 .arg(&ktb)
3657 .arg(&vtb);
3658 unsafe {
3659 b.launch(cfg)?;
3660 }
3661 Ok(())
3662 }
3663
3664 pub fn copy_view_into(
3667 &self,
3668 dst: &mut CudaSlice<f32>,
3669 off: usize,
3670 src: &cudarc::driver::CudaView<f32>,
3671 len: usize,
3672 ) -> Result<(), Box<dyn std::error::Error>> {
3673 let mut view = dst.slice_mut(off..off + len);
3674 self.gpu
3675 .stream()
3676 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3677 Ok(())
3678 }
3679
3680 pub fn clone_dtod(
3684 &self,
3685 src: &CudaSlice<f32>,
3686 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3687 let mut dst = self.gpu.stream().alloc_zeros::<f32>(src.len())?;
3688 self.gpu.stream().memcpy_dtod(src, &mut dst)?;
3689 Ok(dst)
3690 }
3691
3692 pub fn dtod_copy_view(
3695 &self,
3696 src: &cudarc::driver::CudaView<f32>,
3697 dst: &mut CudaSlice<f32>,
3698 ) -> Result<(), Box<dyn std::error::Error>> {
3699 self.gpu.stream().memcpy_dtod(src, dst)?;
3700 Ok(())
3701 }
3702
3703 pub fn dtod_copy_view_i8(
3705 &self,
3706 src: &cudarc::driver::CudaView<i8>,
3707 dst: &mut CudaSlice<i8>,
3708 ) -> Result<(), Box<dyn std::error::Error>> {
3709 self.gpu.stream().memcpy_dtod(src, dst)?;
3710 Ok(())
3711 }
3712
3713 pub fn dtod_copy_into(
3715 &self,
3716 src: &CudaSlice<f32>,
3717 dst: &mut CudaSlice<f32>,
3718 offset: usize,
3719 ) -> Result<(), Box<dyn std::error::Error>> {
3720 let n = src.len();
3721 let mut dv = dst.slice_mut(offset..offset + n);
3722 self.gpu.stream().memcpy_dtod(src, &mut dv)?;
3723 Ok(())
3724 }
3725
3726 pub fn uninit_i8(&self, n: usize) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
3728 self.alloc_uninit::<i8>(n)
3729 }
3730
3731 pub fn qmatvec(
3733 &self,
3734 w: &CudaSlice<u8>,
3735 x: &CudaSlice<f32>,
3736 m: usize,
3737 in_f: usize,
3738 out_f: usize,
3739 qtype: i32,
3740 row_bytes: usize,
3741 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3742 let f = self.func("qmatvec_f32");
3743 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
3745 grid_dim: (out_f as u32, m as u32, 1),
3746 block_dim: (256, 1, 1),
3747 shared_mem_bytes: 0,
3748 };
3749 let (inf, outf, mi, qt, rb) =
3750 (in_f as i32, out_f as i32, m as i32, qtype, row_bytes as i64);
3751 let __s_b = self.gpu.stream();
3752 let mut b = __s_b.launch_builder(&f);
3753 b.arg(w)
3754 .arg(x)
3755 .arg(&mut y)
3756 .arg(&inf)
3757 .arg(&outf)
3758 .arg(&mi)
3759 .arg(&qt)
3760 .arg(&rb);
3761 unsafe {
3762 b.launch(cfg)?;
3763 }
3764 Ok(y)
3765 }
3766
3767 pub fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3769 let s = self.gpu.stream().alloc_zeros::<u8>(n)?;
3770 self.keep_if_capturing(&s);
3771 Ok(s)
3772 }
3773
3774 pub fn alloc_u8_uninit(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3778 let s = unsafe { self.gpu.stream().alloc::<u8>(n)? };
3779 self.keep_if_capturing(&s);
3780 Ok(s)
3781 }
3782
3783 pub fn memset_zeros_view(
3786 &self,
3787 dst: &mut cudarc::driver::CudaViewMut<f32>,
3788 ) -> Result<(), Box<dyn std::error::Error>> {
3789 self.gpu.stream().memset_zeros(dst)?;
3790 Ok(())
3791 }
3792
3793 pub fn stage_expert(
3799 &self,
3800 host_bytes: &[u8],
3801 scratch: &mut CudaSlice<u8>,
3802 off: usize,
3803 ) -> Result<(), Box<dyn std::error::Error>> {
3804 let mut dst = scratch.slice_mut(off..off + host_bytes.len()); self.gpu.stream().memcpy_htod(host_bytes, &mut dst)?; Ok(())
3807 }
3808
3809 pub fn moe_router_topk(
3815 &self,
3816 logits: &CudaSlice<f32>,
3817 t: usize,
3818 n_expert: usize,
3819 n_used: usize,
3820 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
3821 let f = self.func("moe_router_topk_f32");
3822 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 {
3825 grid_dim: (t as u32, 1, 1),
3826 block_dim: (n_expert as u32, 1, 1),
3827 shared_mem_bytes: 0,
3828 };
3829 let (ne, nu) = (n_expert as i32, n_used as i32);
3830 let __s_b = self.gpu.stream();
3831 let mut b = __s_b.launch_builder(&f);
3832 b.arg(logits)
3833 .arg(&mut sel_idx)
3834 .arg(&mut sel_w)
3835 .arg(&ne)
3836 .arg(&nu);
3837 unsafe {
3838 b.launch(cfg)?;
3839 }
3840 Ok((sel_idx, sel_w))
3841 }
3842
3843 pub fn moe_router_topk_scaled(
3846 &self,
3847 logits: &CudaSlice<f32>,
3848 t: usize,
3849 n_expert: usize,
3850 n_used: usize,
3851 ex_scale: &CudaSlice<f32>,
3852 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
3853 let f = self.func("moe_router_topk_scaled_f32");
3858 let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?;
3859 let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?;
3860 let cfg = LaunchConfig {
3861 grid_dim: (t as u32, 1, 1),
3862 block_dim: (n_expert as u32, 1, 1),
3863 shared_mem_bytes: 0,
3864 };
3865 let (ne, nu) = (n_expert as i32, n_used as i32);
3866 let __s_b = self.gpu.stream();
3867 let mut b = __s_b.launch_builder(&f);
3868 b.arg(logits)
3869 .arg(&mut sel_idx)
3870 .arg(&mut sel_w)
3871 .arg(&ne)
3872 .arg(&nu)
3873 .arg(ex_scale);
3874 unsafe {
3875 b.launch(cfg)?;
3876 }
3877 Ok((sel_idx, sel_w))
3878 }
3879
3880 pub fn moe_router_topk_host(
3888 &self,
3889 logits: &CudaSlice<f32>,
3890 t: usize,
3891 n_expert: usize,
3892 n_used: usize,
3893 ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
3894 let f = self.func("moe_router_topk_f32");
3895 let n = t * n_used;
3896 let mut sel_idx = self.alloc_uninit::<i32>(n)?;
3897 let mut sel_w = self.alloc_uninit::<f32>(n)?;
3898 let cfg = LaunchConfig {
3899 grid_dim: (t as u32, 1, 1),
3900 block_dim: (n_expert as u32, 1, 1),
3901 shared_mem_bytes: 0,
3902 };
3903 let (ne, nu) = (n_expert as i32, n_used as i32);
3904 let __s_b = self.gpu.stream();
3905 let mut b = __s_b.launch_builder(&f);
3906 b.arg(logits)
3907 .arg(&mut sel_idx)
3908 .arg(&mut sel_w)
3909 .arg(&ne)
3910 .arg(&nu);
3911 unsafe {
3912 b.launch(cfg)?;
3913 }
3914 let bytes = n * 8;
3916 let mut guard = self.router_stage.lock().unwrap();
3917 if guard.as_ref().map(|p| p.cap < bytes).unwrap_or(true) {
3918 *guard = Some(PinnedStage::new(bytes.max(4096))?);
3919 }
3920 let stage = guard.as_mut().unwrap();
3921 let (si, sw) = unsafe {
3922 (
3923 std::slice::from_raw_parts_mut(stage.ptr as *mut i32, n),
3924 std::slice::from_raw_parts_mut(stage.ptr.add(n * 4) as *mut f32, n),
3925 )
3926 };
3927 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()))
3931 }
3932
3933 #[allow(clippy::too_many_arguments)]
3937 pub fn moe_router_sigmoid_topk(
3938 &self,
3939 logits: &CudaSlice<f32>,
3940 t: usize,
3941 n_expert: usize,
3942 n_used: usize,
3943 active_count: usize,
3944 correction_bias: &CudaSlice<f32>,
3945 active: &CudaSlice<u8>,
3946 scaling_factor: f32,
3947 route_norm: bool,
3948 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
3949 crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
3950 if n_expert == 0 || n_expert > 1024 || n_used == 0 || n_used > n_expert {
3951 return Err(format!(
3952 "sigmoid router shape unsupported: n_expert={n_expert}, n_used={n_used}",
3953 )
3954 .into());
3955 }
3956 if logits.len() < t * n_expert
3957 || correction_bias.len() != n_expert
3958 || active.len() != n_expert
3959 {
3960 return Err(format!(
3961 "sigmoid router buffer mismatch: logits={} bias={} active={} expected logits>={} row={}",
3962 logits.len(), correction_bias.len(), active.len(), t * n_expert, n_expert,
3963 ).into());
3964 }
3965 let f = self.func("moe_router_sigmoid_topk_f32");
3966 let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?;
3967 let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?;
3968 let threads = n_expert.div_ceil(32) * 32;
3969 let cfg = LaunchConfig {
3970 grid_dim: (t as u32, 1, 1),
3971 block_dim: (threads as u32, 1, 1),
3972 shared_mem_bytes: 0,
3973 };
3974 let (ne, nu, rn) = (n_expert as i32, n_used as i32, i32::from(route_norm));
3975 let __s_b = self.gpu.stream();
3976 let mut b = __s_b.launch_builder(&f);
3977 b.arg(logits)
3978 .arg(correction_bias)
3979 .arg(active)
3980 .arg(&mut sel_idx)
3981 .arg(&mut sel_w)
3982 .arg(&ne)
3983 .arg(&nu)
3984 .arg(&scaling_factor)
3985 .arg(&rn);
3986 unsafe {
3987 b.launch(cfg)?;
3988 }
3989 Ok((sel_idx, sel_w))
3990 }
3991
3992 #[allow(clippy::too_many_arguments)]
3995 pub fn moe_router_sigmoid_topk_host(
3996 &self,
3997 logits: &CudaSlice<f32>,
3998 t: usize,
3999 n_expert: usize,
4000 n_used: usize,
4001 active_count: usize,
4002 correction_bias: &CudaSlice<f32>,
4003 active: &CudaSlice<u8>,
4004 scaling_factor: f32,
4005 route_norm: bool,
4006 ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
4007 let (sel_idx, sel_w) = self.moe_router_sigmoid_topk(
4008 logits,
4009 t,
4010 n_expert,
4011 n_used,
4012 active_count,
4013 correction_bias,
4014 active,
4015 scaling_factor,
4016 route_norm,
4017 )?;
4018 let n = t * n_used;
4019 let bytes = n * 8;
4020 let mut guard = self.router_stage.lock().unwrap();
4021 if guard.as_ref().map(|p| p.cap < bytes).unwrap_or(true) {
4022 *guard = Some(PinnedStage::new(bytes.max(4096))?);
4023 }
4024 let stage = guard.as_mut().unwrap();
4025 let (si, sw) = unsafe {
4026 (
4027 std::slice::from_raw_parts_mut(stage.ptr as *mut i32, n),
4028 std::slice::from_raw_parts_mut(stage.ptr.add(n * 4) as *mut f32, n),
4029 )
4030 };
4031 self.gpu.stream().memcpy_dtoh(&sel_idx, si)?;
4032 self.gpu.stream().memcpy_dtoh(&sel_w, sw)?;
4033 self.gpu.stream().synchronize()?;
4034 Ok((si.iter().map(|&i| i as u32).collect(), sw.to_vec()))
4035 }
4036
4037 pub fn stage_expert_async(
4041 &self,
4042 host_bytes: &[u8],
4043 scratch: &mut CudaSlice<u8>,
4044 off: usize,
4045 ) -> Result<cudarc::driver::CudaEvent, Box<dyn std::error::Error>> {
4046 let mut dst = scratch.slice_mut(off..off + host_bytes.len());
4047 self.copy_stream.memcpy_htod(host_bytes, &mut dst)?;
4048 Ok(self.copy_stream.record_event(None)?)
4049 }
4050
4051 pub fn compute_wait(
4053 &self,
4054 ev: &cudarc::driver::CudaEvent,
4055 ) -> Result<(), Box<dyn std::error::Error>> {
4056 self.gpu.stream().wait(ev)?;
4057 Ok(())
4058 }
4059
4060 pub fn qmatvec_view(
4065 &self,
4066 w: &CudaSlice<u8>,
4067 range: std::ops::Range<usize>,
4068 x: &cudarc::driver::CudaView<f32>,
4069 m: usize,
4070 in_f: usize,
4071 out_f: usize,
4072 qtype: i32,
4073 row_bytes: usize,
4074 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4075 let f = self.func("qmatvec_f32");
4076 let wv = w.slice(range); let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
4079 grid_dim: (out_f as u32, m as u32, 1),
4080 block_dim: (256, 1, 1),
4081 shared_mem_bytes: 0,
4082 };
4083 let (inf, outf, mi, qt, rb) =
4084 (in_f as i32, out_f as i32, m as i32, qtype, row_bytes as i64);
4085 let __s_b = self.gpu.stream();
4086 let mut b = __s_b.launch_builder(&f);
4087 b.arg(&wv)
4088 .arg(x)
4089 .arg(&mut y)
4090 .arg(&inf)
4091 .arg(&outf)
4092 .arg(&mi)
4093 .arg(&qt)
4094 .arg(&rb);
4095 unsafe {
4096 b.launch(cfg)?;
4097 }
4098 Ok(y)
4099 }
4100
4101 #[allow(clippy::too_many_arguments)]
4108 pub fn moe_gate_up_silu8_q8(
4112 &self,
4113 gp: WPtr8,
4114 up: WPtr8,
4115 aq: &CudaSlice<i8>,
4116 ad: &CudaSlice<f32>,
4117 in_f: usize,
4118 n_ff: usize,
4119 n_used: usize,
4120 qt_g: i32,
4121 qt_u: i32,
4122 rb_g: usize,
4123 rb_u: usize,
4124 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4125 let f = self.func("moe_gate_up_silu8_q8");
4126 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
4127 let cfg = LaunchConfig {
4128 grid_dim: (n_ff as u32, n_used as u32, 1),
4129 block_dim: (32, 1, 1),
4130 shared_mem_bytes: 0,
4131 };
4132 let (inf, nff, rbg, rbu) = (in_f as i32, n_ff as i32, rb_g as i64, rb_u as i64);
4133 let __s_b = self.gpu.stream();
4134 let mut b = __s_b.launch_builder(&f);
4135 b.arg(&gp)
4136 .arg(&up)
4137 .arg(aq)
4138 .arg(ad)
4139 .arg(&mut act)
4140 .arg(&inf)
4141 .arg(&nff)
4142 .arg(&qt_g)
4143 .arg(&qt_u)
4144 .arg(&rbg)
4145 .arg(&rbu);
4146 unsafe {
4147 b.launch(cfg)?;
4148 }
4149 Ok(act)
4150 }
4151
4152 #[allow(clippy::too_many_arguments)]
4153 pub fn moe_down8_fma_q8(
4154 &self,
4155 dp: WPtr8,
4156 w: F32x8,
4157 aq2: &CudaSlice<i8>,
4158 ad2: &CudaSlice<f32>,
4159 dst: &mut cudarc::driver::CudaViewMut<f32>,
4160 in_f: usize,
4161 out_f: usize,
4162 n_used: usize,
4163 qt: i32,
4164 rb: usize,
4165 ) -> Result<(), Box<dyn std::error::Error>> {
4166 let f = self.func("moe_down8_fma_q8");
4167 let cfg = LaunchConfig {
4168 grid_dim: (out_f as u32, 1, 1),
4169 block_dim: (32, 1, 1),
4170 shared_mem_bytes: 0,
4171 };
4172 let (inf, outf, nu, rbi) = (in_f as i32, out_f as i32, n_used as i32, rb as i64);
4173 let __s_b = self.gpu.stream();
4174 let mut b = __s_b.launch_builder(&f);
4175 b.arg(&dp)
4176 .arg(&w)
4177 .arg(aq2)
4178 .arg(ad2)
4179 .arg(dst)
4180 .arg(&inf)
4181 .arg(&outf)
4182 .arg(&nu)
4183 .arg(&qt)
4184 .arg(&rbi);
4185 unsafe {
4186 b.launch(cfg)?;
4187 }
4188 Ok(())
4189 }
4190
4191 pub fn qmatvec_expert_q8(
4193 &self,
4194 w: &CudaSlice<u8>,
4195 range: std::ops::Range<usize>,
4196 aq: &CudaSlice<i8>,
4197 ad: &CudaSlice<f32>,
4198 m: usize,
4199 in_f: usize,
4200 out_f: usize,
4201 qtype: i32,
4202 row_bytes: usize,
4203 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4204 let f = self.func("qmatvec_expert_q8");
4205 let wv = w.slice(range);
4206 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
4207 const ROWS: u32 = 4; let cfg = LaunchConfig {
4209 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, m as u32, 1),
4210 block_dim: (32, ROWS, 1),
4211 shared_mem_bytes: 0,
4212 };
4213 let (inf, outf, mi, rbi) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
4214 let __s_b = self.gpu.stream();
4215 let mut b = __s_b.launch_builder(&f);
4216 b.arg(&wv)
4217 .arg(aq)
4218 .arg(ad)
4219 .arg(&mut y)
4220 .arg(&inf)
4221 .arg(&outf)
4222 .arg(&mi)
4223 .arg(&qtype)
4224 .arg(&rbi);
4225 unsafe {
4226 b.launch(cfg)?;
4227 }
4228 Ok(y)
4229 }
4230
4231 pub fn moe_gate_up_silu8(
4232 &self,
4233 gp: WPtr8,
4234 up: WPtr8,
4235 x: &cudarc::driver::CudaView<f32>,
4236 in_f: usize,
4237 n_ff: usize,
4238 n_used: usize,
4239 qt_g: i32,
4240 qt_u: i32,
4241 rb_g: usize,
4242 rb_u: usize,
4243 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4244 let f = self.func("moe_gate_up_silu8_f32");
4245 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?; let cfg = LaunchConfig {
4247 grid_dim: (n_ff as u32, n_used as u32, 1),
4248 block_dim: (256, 1, 1),
4249 shared_mem_bytes: 0,
4250 };
4251 let (inf, nff, rbg, rbu) = (in_f as i32, n_ff as i32, rb_g as i64, rb_u as i64);
4252 let __s_b = self.gpu.stream();
4253 let mut b = __s_b.launch_builder(&f);
4254 b.arg(&gp)
4255 .arg(&up)
4256 .arg(x)
4257 .arg(&mut act)
4258 .arg(&inf)
4259 .arg(&nff)
4260 .arg(&qt_g)
4261 .arg(&qt_u)
4262 .arg(&rbg)
4263 .arg(&rbu);
4264 unsafe {
4265 b.launch(cfg)?;
4266 }
4267 Ok(act)
4268 }
4269
4270 #[allow(clippy::too_many_arguments)]
4276 pub fn moe_down8_fma_into(
4277 &self,
4278 dp: WPtr8,
4279 w: F32x8,
4280 act: &CudaSlice<f32>,
4281 dst: &mut cudarc::driver::CudaViewMut<f32>,
4282 in_f: usize,
4283 out_f: usize,
4284 n_used: usize,
4285 qt: i32,
4286 rb: usize,
4287 ) -> Result<(), Box<dyn std::error::Error>> {
4288 let f = self.func("moe_down8_fma_f32");
4289 let cfg = LaunchConfig {
4290 grid_dim: (out_f as u32, 1, 1),
4291 block_dim: (256, 1, 1),
4292 shared_mem_bytes: 0,
4293 };
4294 let (inf, outf, nu, rbv) = (in_f as i32, out_f as i32, n_used as i32, rb as i64);
4295 let __s_b = self.gpu.stream();
4296 let mut b = __s_b.launch_builder(&f);
4297 b.arg(&dp)
4298 .arg(&w)
4299 .arg(act)
4300 .arg(dst)
4301 .arg(&inf)
4302 .arg(&outf)
4303 .arg(&nu)
4304 .arg(&qt)
4305 .arg(&rbv);
4306 unsafe {
4307 b.launch(cfg)?;
4308 }
4309 Ok(())
4310 }
4311
4312 #[allow(clippy::too_many_arguments)]
4317 #[allow(clippy::too_many_arguments)]
4332 #[allow(clippy::too_many_arguments)]
4334 pub fn moe_pairs_matvec_q8(
4335 &self,
4336 table: &CudaSlice<u64>,
4337 proj: i32,
4338 pair_tok: &CudaSlice<i32>,
4339 pair_ex: &CudaSlice<i32>,
4340 aq: &CudaSlice<i8>,
4341 ad: &CudaSlice<f32>,
4342 in_f: usize,
4343 out_f: usize,
4344 n_expert: usize,
4345 n_pairs: usize,
4346 qtype: i32,
4347 row_bytes: usize,
4348 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4349 let f = self.func("moe_pairs_matvec_q8");
4350 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4351 const ROWS: u32 = 4;
4352 let cfg = LaunchConfig {
4353 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_pairs as u32, 1),
4354 block_dim: (32, ROWS, 1),
4355 shared_mem_bytes: 0,
4356 };
4357 let (inf, outf, ne, np, rbi) = (
4358 in_f as i32,
4359 out_f as i32,
4360 n_expert as i32,
4361 n_pairs as i32,
4362 row_bytes as i64,
4363 );
4364 let __s_b = self.gpu.stream();
4365 let mut b = __s_b.launch_builder(&f);
4366 b.arg(table)
4367 .arg(&proj)
4368 .arg(pair_tok)
4369 .arg(pair_ex)
4370 .arg(aq)
4371 .arg(ad)
4372 .arg(&mut y)
4373 .arg(&inf)
4374 .arg(&outf)
4375 .arg(&ne)
4376 .arg(&np)
4377 .arg(&qtype)
4378 .arg(&rbi);
4379 unsafe {
4380 b.launch(cfg)?;
4381 }
4382 Ok(y)
4383 }
4384
4385 #[allow(clippy::too_many_arguments)]
4387 pub fn moe_pairs_matvec_q8_em(
4388 &self,
4389 table: &CudaSlice<u64>,
4390 proj: i32,
4391 ex_ids: &CudaSlice<i32>,
4392 ex_off: &CudaSlice<i32>,
4393 ex_pairs: &CudaSlice<i32>,
4394 pair_tok: &CudaSlice<i32>,
4395 aq: &CudaSlice<i8>,
4396 ad: &CudaSlice<f32>,
4397 in_f: usize,
4398 out_f: usize,
4399 n_expert: usize,
4400 n_active: usize,
4401 n_pairs: usize,
4402 qtype: i32,
4403 row_bytes: usize,
4404 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4405 let f = self.func("moe_pairs_matvec_q8_em");
4406 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4407 const ROWS: u32 = 4;
4408 let cfg = LaunchConfig {
4409 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_active as u32, 1),
4410 block_dim: (32, ROWS, 1),
4411 shared_mem_bytes: 0,
4412 };
4413 let (inf, outf, ne, na, rbi) = (
4414 in_f as i32,
4415 out_f as i32,
4416 n_expert as i32,
4417 n_active as i32,
4418 row_bytes as i64,
4419 );
4420 let __s_b = self.gpu.stream();
4421 let mut b = __s_b.launch_builder(&f);
4422 b.arg(table)
4423 .arg(&proj)
4424 .arg(ex_ids)
4425 .arg(ex_off)
4426 .arg(ex_pairs)
4427 .arg(pair_tok)
4428 .arg(aq)
4429 .arg(ad)
4430 .arg(&mut y)
4431 .arg(&inf)
4432 .arg(&outf)
4433 .arg(&ne)
4434 .arg(&na)
4435 .arg(&qtype)
4436 .arg(&rbi);
4437 unsafe {
4438 b.launch(cfg)?;
4439 }
4440 Ok(y)
4441 }
4442
4443 #[allow(clippy::too_many_arguments)]
4446 pub fn moe_pairs_matvec_q8_dec(
4447 &self,
4448 table: &CudaSlice<u64>,
4449 proj: i32,
4450 ex_ids: &CudaSlice<i32>,
4451 ex_off: &CudaSlice<i32>,
4452 ex_pairs: &CudaSlice<i32>,
4453 pair_tok: &CudaSlice<i32>,
4454 aq: &CudaSlice<i8>,
4455 ad: &CudaSlice<f32>,
4456 in_f: usize,
4457 out_f: usize,
4458 n_expert: usize,
4459 n_active: usize,
4460 n_pairs: usize,
4461 qtype: i32,
4462 row_bytes: usize,
4463 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4464 let f = self.func("moe_pairs_matvec_q8_dec");
4465 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4466 const ROWS: u32 = 4;
4467 let cfg = LaunchConfig {
4468 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_active as u32, 1),
4469 block_dim: (32, ROWS, 1),
4470 shared_mem_bytes: 0,
4471 };
4472 let (inf, outf, ne, na, rbi) = (
4473 in_f as i32,
4474 out_f as i32,
4475 n_expert as i32,
4476 n_active as i32,
4477 row_bytes as i64,
4478 );
4479 let __s_b = self.gpu.stream();
4480 let mut b = __s_b.launch_builder(&f);
4481 b.arg(table)
4482 .arg(&proj)
4483 .arg(ex_ids)
4484 .arg(ex_off)
4485 .arg(ex_pairs)
4486 .arg(pair_tok)
4487 .arg(aq)
4488 .arg(ad)
4489 .arg(&mut y)
4490 .arg(&inf)
4491 .arg(&outf)
4492 .arg(&ne)
4493 .arg(&na)
4494 .arg(&qtype)
4495 .arg(&rbi);
4496 unsafe {
4497 b.launch(cfg)?;
4498 }
4499 Ok(y)
4500 }
4501
4502 pub fn moe_pairs_gelu_mul(
4503 &self,
4504 gate: &CudaSlice<f32>,
4505 up: &CudaSlice<f32>,
4506 n: usize,
4507 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4508 let f = self.func("moe_pairs_gelu_mul");
4509 let mut act = self.alloc_uninit::<f32>(n)?;
4510 let cfg = LaunchConfig::for_num_elems(n as u32);
4511 let nl = n as i64;
4512 let __s_b = self.gpu.stream();
4513 let mut b = __s_b.launch_builder(&f);
4514 b.arg(gate).arg(up).arg(&mut act).arg(&nl);
4515 unsafe {
4516 b.launch(cfg)?;
4517 }
4518 Ok(act)
4519 }
4520
4521 pub fn moe_pairs_silu_mul(
4522 &self,
4523 gate: &CudaSlice<f32>,
4524 up: &CudaSlice<f32>,
4525 n: usize,
4526 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4527 let f = self.func("moe_pairs_silu_mul");
4528 let mut act = self.alloc_uninit::<f32>(n)?;
4529 let cfg = LaunchConfig::for_num_elems(n as u32);
4530 let nl = n as i64;
4531 let __s_b = self.gpu.stream();
4532 let mut b = __s_b.launch_builder(&f);
4533 b.arg(gate).arg(up).arg(&mut act).arg(&nl);
4534 unsafe {
4535 b.launch(cfg)?;
4536 }
4537 Ok(act)
4538 }
4539
4540 #[allow(clippy::too_many_arguments)]
4541 pub fn moe_pairs_scatter(
4542 &self,
4543 y_down: &CudaSlice<f32>,
4544 pair_w: &CudaSlice<f32>,
4545 tok_pair_off: &CudaSlice<i32>,
4546 tok_pair_ids: &CudaSlice<i32>,
4547 moe_out: &mut CudaSlice<f32>,
4548 t: usize,
4549 n_embd: usize,
4550 ) -> Result<(), Box<dyn std::error::Error>> {
4551 let f = self.func("moe_pairs_scatter");
4552 let cfg = LaunchConfig {
4553 grid_dim: (((n_embd + 255) / 256) as u32, t as u32, 1),
4554 block_dim: (256, 1, 1),
4555 shared_mem_bytes: 0,
4556 };
4557 let ne = n_embd as i32;
4558 let __s_b = self.gpu.stream();
4559 let mut b = __s_b.launch_builder(&f);
4560 b.arg(y_down)
4561 .arg(pair_w)
4562 .arg(tok_pair_off)
4563 .arg(tok_pair_ids)
4564 .arg(moe_out)
4565 .arg(&ne);
4566 unsafe {
4567 b.launch(cfg)?;
4568 }
4569 Ok(())
4570 }
4571
4572 #[allow(clippy::too_many_arguments)]
4576 pub fn moe_gate_up_gelu8_dev_q8(
4577 &self,
4578 table: &CudaSlice<u64>,
4579 sel: &cudarc::driver::CudaView<i32>,
4580 aq: &CudaSlice<i8>,
4581 ad: &CudaSlice<f32>,
4582 in_f: usize,
4583 n_ff: usize,
4584 n_used: usize,
4585 n_expert: usize,
4586 qt_g: i32,
4587 qt_u: i32,
4588 rb_g: usize,
4589 rb_u: usize,
4590 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4591 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
4592 let (inf, nff, ne, rbg, rbu) = (
4593 in_f as i32,
4594 n_ff as i32,
4595 n_expert as i32,
4596 rb_g as i64,
4597 rb_u as i64,
4598 );
4599 let f = self.func("moe_gate_up_gelu8_dev_q8");
4600 let cfg = LaunchConfig {
4601 grid_dim: (n_ff as u32, n_used as u32, 1),
4602 block_dim: (32, 1, 1),
4603 shared_mem_bytes: 0,
4604 };
4605 let __s_b = self.gpu.stream();
4606 let mut b = __s_b.launch_builder(&f);
4607 b.arg(table)
4608 .arg(sel)
4609 .arg(aq)
4610 .arg(ad)
4611 .arg(&mut act)
4612 .arg(&inf)
4613 .arg(&nff)
4614 .arg(&ne)
4615 .arg(&qt_g)
4616 .arg(&qt_u)
4617 .arg(&rbg)
4618 .arg(&rbu);
4619 unsafe {
4620 b.launch(cfg)?;
4621 }
4622 Ok(act)
4623 }
4624
4625 #[allow(clippy::too_many_arguments)]
4627 pub fn moe_gate_up_gelu8_dev_q8_rows(
4628 &self,
4629 table: &CudaSlice<u64>,
4630 sel: &CudaSlice<i32>,
4631 aq: &CudaSlice<i8>,
4632 ad: &CudaSlice<f32>,
4633 t: usize,
4634 in_f: usize,
4635 n_ff: usize,
4636 n_used: usize,
4637 n_expert: usize,
4638 qt_g: i32,
4639 qt_u: i32,
4640 rb_g: usize,
4641 rb_u: usize,
4642 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4643 let mut act = self.alloc_uninit::<f32>(t * n_used * n_ff)?;
4644 let (inf, nff, ne, rbg, rbu, nu) = (
4645 in_f as i32,
4646 n_ff as i32,
4647 n_expert as i32,
4648 rb_g as i64,
4649 rb_u as i64,
4650 n_used as i32,
4651 );
4652 let f = self.func("moe_gate_up_gelu8_dev_q8_rows");
4653 let cfg = LaunchConfig {
4654 grid_dim: (n_ff as u32, n_used as u32, t as u32),
4655 block_dim: (32, 1, 1),
4656 shared_mem_bytes: 0,
4657 };
4658 let __s_b = self.gpu.stream();
4659 let mut b = __s_b.launch_builder(&f);
4660 b.arg(table)
4661 .arg(sel)
4662 .arg(aq)
4663 .arg(ad)
4664 .arg(&mut act)
4665 .arg(&inf)
4666 .arg(&nff)
4667 .arg(&ne)
4668 .arg(&qt_g)
4669 .arg(&qt_u)
4670 .arg(&rbg)
4671 .arg(&rbu)
4672 .arg(&nu);
4673 unsafe {
4674 b.launch(cfg)?;
4675 }
4676 Ok(act)
4677 }
4678
4679 #[allow(clippy::too_many_arguments)]
4681 pub fn moe_gate_up_gelu8_dev_q8_csr(
4682 &self,
4683 table: &CudaSlice<u64>,
4684 sel: &CudaSlice<i32>,
4685 aq: &CudaSlice<i8>,
4686 ad: &CudaSlice<f32>,
4687 n_pairs: usize,
4688 in_f: usize,
4689 n_ff: usize,
4690 n_used: usize,
4691 n_expert: usize,
4692 qt_g: i32,
4693 qt_u: i32,
4694 rb_g: usize,
4695 rb_u: usize,
4696 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4697 let mut act = self.alloc_uninit::<f32>(n_pairs * n_ff)?;
4698 let (inf, nff, ne, rbg, rbu, nu, npi) = (
4699 in_f as i32,
4700 n_ff as i32,
4701 n_expert as i32,
4702 rb_g as i64,
4703 rb_u as i64,
4704 n_used as i32,
4705 n_pairs as i32,
4706 );
4707 let f = self.func("moe_gate_up_gelu8_dev_q8_csr");
4708 let cfg = LaunchConfig {
4709 grid_dim: (n_ff as u32, n_pairs as u32, 1),
4710 block_dim: (32, 1, 1),
4711 shared_mem_bytes: 0,
4712 };
4713 let __s_b = self.gpu.stream();
4714 let mut b = __s_b.launch_builder(&f);
4715 b.arg(table)
4716 .arg(sel)
4717 .arg(aq)
4718 .arg(ad)
4719 .arg(&mut act)
4720 .arg(&inf)
4721 .arg(&nff)
4722 .arg(&ne)
4723 .arg(&qt_g)
4724 .arg(&qt_u)
4725 .arg(&rbg)
4726 .arg(&rbu)
4727 .arg(&nu)
4728 .arg(&npi);
4729 unsafe {
4730 b.launch(cfg)?;
4731 }
4732 Ok(act)
4733 }
4734
4735 #[allow(clippy::too_many_arguments)]
4737 pub fn moe_down8_fma_dev_q8_rows_g(
4738 &self,
4739 table: &CudaSlice<u64>,
4740 sel: &CudaSlice<i32>,
4741 w: &CudaSlice<f32>,
4742 aq2: &CudaSlice<i8>,
4743 ad2: &CudaSlice<f32>,
4744 dst: &mut CudaSlice<f32>,
4745 t: usize,
4746 in_f: usize,
4747 out_f: usize,
4748 n_used: usize,
4749 n_expert: usize,
4750 qt: i32,
4751 rb: usize,
4752 ) -> Result<(), Box<dyn std::error::Error>> {
4753 let (inf, outf, nu, ne, rbi) = (
4754 in_f as i32,
4755 out_f as i32,
4756 n_used as i32,
4757 n_expert as i32,
4758 rb as i64,
4759 );
4760 let step_b1_w8 = t == 1 && in_f == 1280 && out_f == 4096 && n_used == 8 && qt == QT_IQ4_XS;
4764 let f = self.func(if step_b1_w8 {
4765 "moe_down8_fma_dev_q8_rows_w8"
4766 } else {
4767 "moe_down8_fma_dev_q8_rows_g"
4768 });
4769 let cfg = LaunchConfig {
4770 grid_dim: (out_f as u32, 1, t as u32),
4771 block_dim: (32, if step_b1_w8 { 8 } else { 1 }, 1),
4772 shared_mem_bytes: 0,
4773 };
4774 let __s_b = self.gpu.stream();
4775 let mut b = __s_b.launch_builder(&f);
4776 b.arg(table)
4777 .arg(sel)
4778 .arg(w)
4779 .arg(aq2)
4780 .arg(ad2)
4781 .arg(dst)
4782 .arg(&inf)
4783 .arg(&outf)
4784 .arg(&nu)
4785 .arg(&ne)
4786 .arg(&qt)
4787 .arg(&rbi);
4788 unsafe {
4789 b.launch(cfg)?;
4790 }
4791 Ok(())
4792 }
4793
4794 pub fn rp_probe_q4(&self, m: usize) -> Result<(f64, f64), Box<dyn std::error::Error>> {
4798 let (out_f, in_f) = (2048usize, 2816usize);
4799 let nblk = in_f / 32;
4800 let mut seed = 0x9E3779B97F4A7C15u64;
4801 let mut rng = move || {
4802 seed = seed
4803 .wrapping_mul(6364136223846793005)
4804 .wrapping_add(1442695040888963407);
4805 (seed >> 33) as u8
4806 };
4807 let mut w = vec![0u8; out_f * nblk * 18];
4808 for b in w.iter_mut() {
4809 *b = rng();
4810 }
4811 for r in 0..out_f {
4812 for g in 0..nblk {
4813 let off = (r * nblk + g) * 18;
4814 w[off] = 0x00;
4815 w[off + 1] = 0x2C; }
4817 }
4818 let qplane = out_f * nblk * 16;
4819 let mut wrp = vec![0u8; w.len()];
4820 for r in 0..out_f {
4821 for g in 0..nblk {
4822 let src = &w[(r * nblk + g) * 18..(r * nblk + g) * 18 + 18];
4823 wrp[qplane + (r * nblk + g) * 2..qplane + (r * nblk + g) * 2 + 2]
4824 .copy_from_slice(&src[0..2]);
4825 wrp[(r * nblk + g) * 16..(r * nblk + g) * 16 + 16].copy_from_slice(&src[2..18]);
4826 }
4827 }
4828 let w_d = self.htod_bytes(&w)?;
4829 let wrp_d = self.htod_bytes(&wrp)?;
4830 let mut aq = vec![0i8; m * in_f];
4831 for v in aq.iter_mut() {
4832 *v = rng() as i8;
4833 }
4834 let aq_d = self.htod_i8(&aq)?;
4835 let ad_d = self.htod(&vec![0.03125f32; m * nblk])?;
4836 let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
4837 let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
4838 const RPB: u32 = 4;
4839 let cfg = LaunchConfig {
4840 grid_dim: ((out_f as u32).div_ceil(RPB), 1, 1),
4841 block_dim: (32, RPB, 1),
4842 shared_mem_bytes: 0,
4843 };
4844 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
4845 let (rb, qp) = ((nblk * 18) as i64, qplane as i64);
4846 let fb = self.func("qmatvec_q4_0_mmvq_b4");
4847 let fr = self.func("qmatvec_q4_0_mmvq_b4_rp");
4848 {
4849 let __s_b = self.gpu.stream();
4850 let mut b = __s_b.launch_builder(&fb);
4851 b.arg(&w_d)
4852 .arg(&aq_d)
4853 .arg(&ad_d)
4854 .arg(&mut y0)
4855 .arg(&inf)
4856 .arg(&outf)
4857 .arg(&mi)
4858 .arg(&rb);
4859 unsafe {
4860 b.launch(cfg)?;
4861 }
4862 let __s_b = self.gpu.stream();
4863 let mut b = __s_b.launch_builder(&fr);
4864 b.arg(&wrp_d)
4865 .arg(&aq_d)
4866 .arg(&ad_d)
4867 .arg(&mut y1)
4868 .arg(&inf)
4869 .arg(&outf)
4870 .arg(&mi)
4871 .arg(&qp);
4872 unsafe {
4873 b.launch(cfg)?;
4874 }
4875 }
4876 self.gpu.stream().synchronize()?;
4877 let (h0, h1) = (self.dtoh(&y0)?, self.dtoh(&y1)?);
4878 let nd = h0
4879 .iter()
4880 .zip(&h1)
4881 .filter(|(a, b)| a.to_bits() != b.to_bits())
4882 .count();
4883 if nd != 0 {
4884 return Err(format!("rp twin not bitwise: {nd}/{} diffs", h0.len()).into());
4885 }
4886 let mut time = |rp: bool| -> Result<f64, Box<dyn std::error::Error>> {
4887 self.gpu.stream().synchronize()?;
4888 let t0 = std::time::Instant::now();
4889 for _ in 0..500 {
4890 if rp {
4891 let __s_b = self.gpu.stream();
4892 let mut b = __s_b.launch_builder(&fr);
4893 b.arg(&wrp_d)
4894 .arg(&aq_d)
4895 .arg(&ad_d)
4896 .arg(&mut y1)
4897 .arg(&inf)
4898 .arg(&outf)
4899 .arg(&mi)
4900 .arg(&qp);
4901 unsafe {
4902 b.launch(cfg)?;
4903 }
4904 } else {
4905 let __s_b = self.gpu.stream();
4906 let mut b = __s_b.launch_builder(&fb);
4907 b.arg(&w_d)
4908 .arg(&aq_d)
4909 .arg(&ad_d)
4910 .arg(&mut y0)
4911 .arg(&inf)
4912 .arg(&outf)
4913 .arg(&mi)
4914 .arg(&rb);
4915 unsafe {
4916 b.launch(cfg)?;
4917 }
4918 }
4919 }
4920 self.gpu.stream().synchronize()?;
4921 Ok(t0.elapsed().as_secs_f64() * 1e6 / 500.0)
4922 };
4923 let _ = time(false)?;
4924 let _ = time(true)?; Ok((time(false)?, time(true)?))
4926 }
4927
4928 pub fn build_q4_rp4(
4933 &self,
4934 t: &mut crate::model::GpuTensor,
4935 ) -> Result<(), Box<dyn std::error::Error>> {
4936 use crate::model::GpuTensor;
4937 let GpuTensor::Quant {
4938 bytes,
4939 qtype,
4940 row_bytes,
4941 ne,
4942 rp4,
4943 ..
4944 } = t
4945 else {
4946 return Ok(());
4947 };
4948 if *qtype != QT_Q4_0 || rp4.is_some() || ne.len() != 2 {
4949 return Ok(());
4950 }
4951 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
4952 if in_f % 32 != 0 || *row_bytes != (in_f / 32) * 18 {
4953 return Ok(());
4954 }
4955 let nblk = in_f / 32;
4956 let mut dst = self.alloc_uninit::<u8>(out_f * nblk * 18)?;
4957 let f = self.func("q4_0_split_rp_build");
4958 let n = (out_f * nblk) as i32;
4959 let cfg = LaunchConfig {
4960 grid_dim: (((out_f * nblk) as u32).div_ceil(256), 1, 1),
4961 block_dim: (256, 1, 1),
4962 shared_mem_bytes: 0,
4963 };
4964 let (of, nb) = (out_f as i32, nblk as i32);
4965 let _ = n;
4966 let __s_b = self.gpu.stream();
4967 let mut b = __s_b.launch_builder(&f);
4968 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
4969 unsafe {
4970 b.launch(cfg)?;
4971 }
4972 *rp4 = Some(dst);
4973 Ok(())
4974 }
4975
4976 pub fn build_q8_rp4(
4981 &self,
4982 t: &mut crate::model::GpuTensor,
4983 ) -> Result<(), Box<dyn std::error::Error>> {
4984 use crate::model::GpuTensor;
4985 let GpuTensor::Quant {
4986 bytes,
4987 qtype,
4988 row_bytes,
4989 ne,
4990 rp4,
4991 ..
4992 } = t
4993 else {
4994 return Ok(());
4995 };
4996 if *qtype != QT_Q8_0 || rp4.is_some() || ne.len() != 2 {
4997 return Ok(());
4998 }
4999 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5000 if in_f % 32 != 0 || *row_bytes != (in_f / 32) * 34 {
5001 return Ok(());
5002 }
5003 *rp4 = Some(self.build_q8_rp4_raw(bytes, in_f, out_f)?);
5004 Ok(())
5005 }
5006
5007 pub fn build_q8_rp4_raw(
5010 &self,
5011 bytes: &CudaSlice<u8>,
5012 in_f: usize,
5013 out_f: usize,
5014 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
5015 assert!(in_f % 32 == 0);
5016 let nblk = in_f / 32;
5017 let mut dst = self.alloc_uninit::<u8>(out_f * nblk * 34)?;
5018 let f = self.func("q8_0_split_rp_build");
5019 let cfg = LaunchConfig {
5020 grid_dim: (((out_f * nblk) as u32).div_ceil(256), 1, 1),
5021 block_dim: (256, 1, 1),
5022 shared_mem_bytes: 0,
5023 };
5024 let (of, nb) = (out_f as i32, nblk as i32);
5025 let __s_b = self.gpu.stream();
5026 let mut b = __s_b.launch_builder(&f);
5027 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
5028 unsafe {
5029 b.launch(cfg)?;
5030 }
5031 Ok(dst)
5032 }
5033
5034 pub fn build_q4k_rp4(
5042 &self,
5043 t: &mut crate::model::GpuTensor,
5044 ) -> Result<(), Box<dyn std::error::Error>> {
5045 use crate::model::GpuTensor;
5046 let GpuTensor::Quant {
5047 bytes,
5048 qtype,
5049 row_bytes,
5050 ne,
5051 rp4,
5052 ..
5053 } = t
5054 else {
5055 return Ok(());
5056 };
5057 if *qtype != QT_Q4_K || rp4.is_some() || ne.len() != 2 {
5058 return Ok(());
5059 }
5060 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5061 if in_f % 256 != 0 || *row_bytes != (in_f / 256) * 144 {
5062 return Ok(());
5063 }
5064 *rp4 = Some(self.build_kq_rp4_raw(bytes, in_f, out_f, QT_Q4_K)?);
5065 Ok(())
5066 }
5067
5068 pub fn build_q6k_rp4(
5069 &self,
5070 t: &mut crate::model::GpuTensor,
5071 ) -> Result<(), Box<dyn std::error::Error>> {
5072 use crate::model::GpuTensor;
5073 let GpuTensor::Quant {
5074 bytes,
5075 qtype,
5076 row_bytes,
5077 ne,
5078 rp4,
5079 ..
5080 } = t
5081 else {
5082 return Ok(());
5083 };
5084 if *qtype != QT_Q6_K || rp4.is_some() || ne.len() != 2 {
5085 return Ok(());
5086 }
5087 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5088 if in_f % 256 != 0 || *row_bytes != (in_f / 256) * 210 {
5089 return Ok(());
5090 }
5091 *rp4 = Some(self.build_kq_rp4_raw(bytes, in_f, out_f, QT_Q6_K)?);
5092 Ok(())
5093 }
5094
5095 pub fn build_kq_rp4_raw(
5097 &self,
5098 bytes: &CudaSlice<u8>,
5099 in_f: usize,
5100 out_f: usize,
5101 qtype: i32,
5102 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
5103 assert!(in_f % 256 == 0);
5104 let nsbk = in_f / 256;
5105 let (sb_bytes, kname) = match qtype {
5106 QT_Q4_K => (144usize, "q4_K_split_rp_build"),
5107 QT_Q6_K => (210usize, "q6_K_split_rp_build"),
5108 _ => return Err(format!("build_kq_rp4_raw: qtype {qtype} has no rp mirror").into()),
5109 };
5110 let mut dst = self.alloc_uninit::<u8>(out_f * nsbk * sb_bytes)?;
5111 let f = self.func(kname);
5112 let cfg = LaunchConfig {
5113 grid_dim: (((out_f * nsbk) as u32).div_ceil(256), 1, 1),
5114 block_dim: (256, 1, 1),
5115 shared_mem_bytes: 0,
5116 };
5117 let (of, nb) = (out_f as i32, nsbk as i32);
5118 let __s_b = self.gpu.stream();
5119 let mut b = __s_b.launch_builder(&f);
5120 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
5121 unsafe {
5122 b.launch(cfg)?;
5123 }
5124 Ok(dst)
5125 }
5126
5127 pub fn kqrp_enabled() -> bool {
5131 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5132 *ON.get_or_init(|| match std::env::var("MEMRA_KQRP").as_deref() {
5133 Ok("0") => false,
5134 Ok(_) => true,
5135 Err(_) => cfg!(memra_hopper_mma),
5136 })
5137 }
5138
5139 pub fn build_q4_rp_swap(
5145 &self,
5146 t: &mut crate::model::GpuTensor,
5147 ) -> Result<bool, Box<dyn std::error::Error>> {
5148 self.build_q4_rp4(t)?;
5149 self.gpu.stream().synchronize()?; use crate::model::GpuTensor;
5151 let GpuTensor::Quant { bytes, rp4, rp, .. } = t else {
5152 return Ok(false);
5153 };
5154 match rp4.take() {
5155 Some(split) => {
5156 *bytes = split; *rp = true;
5158 Ok(true)
5159 }
5160 None => Ok(false),
5161 }
5162 }
5163
5164 pub fn q4rp_enabled() -> bool {
5166 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5167 *ON.get_or_init(|| {
5168 std::env::var("MEMRA_Q4RP")
5169 .map(|v| v != "0")
5170 .unwrap_or(true)
5171 })
5172 }
5173
5174 pub fn copy_rows_strided(
5177 &self,
5178 src: &CudaSlice<f32>,
5179 dst: &mut CudaSlice<f32>,
5180 row_elems: usize,
5181 n_rows: usize,
5182 src_stride: usize,
5183 src_off: usize,
5184 ) -> Result<(), Box<dyn std::error::Error>> {
5185 let f = self.func("copy_rows_strided_f32");
5186 let cfg = LaunchConfig {
5187 grid_dim: (((row_elems as u32 + 255) / 256).max(1), n_rows as u32, 1),
5188 block_dim: (256, 1, 1),
5189 shared_mem_bytes: 0,
5190 };
5191 let (re, nr) = (row_elems as i32, n_rows as i32);
5192 let (st, off) = (src_stride as i64, src_off as i64);
5193 let __s_b = self.gpu.stream();
5194 let mut b = __s_b.launch_builder(&f);
5195 b.arg(src)
5196 .arg(&mut *dst)
5197 .arg(&re)
5198 .arg(&nr)
5199 .arg(&st)
5200 .arg(&off);
5201 unsafe {
5202 b.launch(cfg)?;
5203 }
5204 Ok(())
5205 }
5206
5207 pub fn u32_set_k(
5209 &self,
5210 dst: &mut CudaSlice<u32>,
5211 v: u32,
5212 idx: usize,
5213 ) -> Result<(), Box<dyn std::error::Error>> {
5214 let f = self.func("u32_set_k");
5215 let cfg = LaunchConfig {
5216 grid_dim: (1, 1, 1),
5217 block_dim: (1, 1, 1),
5218 shared_mem_bytes: 0,
5219 };
5220 let ii = idx as i32;
5221 let __s_b = self.gpu.stream();
5222 let mut b = __s_b.launch_builder(&f);
5223 b.arg(dst).arg(&v).arg(&ii);
5224 unsafe {
5225 b.launch(cfg)?;
5226 }
5227 Ok(())
5228 }
5229
5230 pub fn i32_add_k(
5232 &self,
5233 d: &mut CudaSlice<i32>,
5234 v: i32,
5235 ) -> Result<(), Box<dyn std::error::Error>> {
5236 let f = self.func("i32_add_k");
5237 let cfg = LaunchConfig {
5238 grid_dim: (1, 1, 1),
5239 block_dim: (32, 1, 1),
5240 shared_mem_bytes: 0,
5241 };
5242 let __s_b = self.gpu.stream();
5243 let mut b = __s_b.launch_builder(&f);
5244 b.arg(d).arg(&v);
5245 unsafe {
5246 b.launch(cfg)?;
5247 }
5248 Ok(())
5249 }
5250
5251 pub fn i32_iota_from(
5253 &self,
5254 ctr: &CudaSlice<i32>,
5255 dst: &mut CudaSlice<i32>,
5256 n: usize,
5257 ) -> Result<(), Box<dyn std::error::Error>> {
5258 let f = self.func("i32_iota_from");
5259 let cfg = LaunchConfig::for_num_elems(n as u32);
5260 let ni = n as i32;
5261 let __s_b = self.gpu.stream();
5262 let mut b = __s_b.launch_builder(&f);
5263 b.arg(ctr).arg(dst).arg(&ni);
5264 unsafe {
5265 b.launch(cfg)?;
5266 }
5267 Ok(())
5268 }
5269
5270 pub fn u32_map_k(
5272 &self,
5273 buf: &mut CudaSlice<u32>,
5274 map: &CudaSlice<u32>,
5275 idx: usize,
5276 ) -> Result<(), Box<dyn std::error::Error>> {
5277 let f = self.func("u32_map_k");
5278 let cfg = LaunchConfig {
5279 grid_dim: (1, 1, 1),
5280 block_dim: (1, 1, 1),
5281 shared_mem_bytes: 0,
5282 };
5283 let ii = idx as i32;
5284 let __s_b = self.gpu.stream();
5285 let mut b = __s_b.launch_builder(&f);
5286 b.arg(buf).arg(map).arg(&ii);
5287 unsafe {
5288 b.launch(cfg)?;
5289 }
5290 Ok(())
5291 }
5292
5293 #[allow(clippy::too_many_arguments)]
5295 pub fn u32_pack2(
5296 &self,
5297 a: &CudaSlice<u32>,
5298 off_a: usize,
5299 n1: usize,
5300 b_in: &CudaSlice<u32>,
5301 n2: usize,
5302 out: &mut CudaSlice<u32>,
5303 ) -> Result<(), Box<dyn std::error::Error>> {
5304 let f = self.func("u32_pack2");
5305 let cfg = LaunchConfig::for_num_elems((n1 + n2) as u32);
5306 let (oa, i1, i2) = (off_a as i32, n1 as i32, n2 as i32);
5307 let __s_b = self.gpu.stream();
5308 let mut b = __s_b.launch_builder(&f);
5309 b.arg(a).arg(&oa).arg(&i1).arg(b_in).arg(&i2).arg(out);
5310 unsafe {
5311 b.launch(cfg)?;
5312 }
5313 Ok(())
5314 }
5315
5316 pub fn moe_w_exscale(
5318 &self,
5319 w: &mut CudaSlice<f32>,
5320 sel: &CudaSlice<i32>,
5321 s: &CudaSlice<f32>,
5322 n: usize,
5323 ) -> Result<(), Box<dyn std::error::Error>> {
5324 let f = self.func("moe_w_exscale");
5325 let cfg = LaunchConfig::for_num_elems(n as u32);
5326 let ni = n as i32;
5327 let __s_b = self.gpu.stream();
5328 let mut b = __s_b.launch_builder(&f);
5329 b.arg(w).arg(sel).arg(s).arg(&ni);
5330 unsafe {
5331 b.launch(cfg)?;
5332 }
5333 Ok(())
5334 }
5335
5336 pub fn moe_w_scale_by_expert(
5339 &self,
5340 w: &mut CudaSlice<f32>,
5341 sel: &CudaSlice<i32>,
5342 macros: &CudaSlice<f32>,
5343 n_expert: usize,
5344 n: usize,
5345 ) -> Result<(), Box<dyn std::error::Error>> {
5346 let f = self.func("moe_w_scale_by_expert");
5347 let cfg = LaunchConfig {
5348 grid_dim: (n.div_ceil(64) as u32, 1, 1),
5349 block_dim: (64, 1, 1),
5350 shared_mem_bytes: 0,
5351 };
5352 let (ne, nn) = (n_expert as i32, n as i32);
5353 let __s_b = self.gpu.stream();
5354 let mut b = __s_b.launch_builder(&f);
5355 b.arg(w).arg(sel).arg(macros).arg(&ne).arg(&nn);
5356 unsafe {
5357 b.launch(cfg)?;
5358 }
5359 Ok(())
5360 }
5361
5362 pub fn moe_gate_up_silu8_dev_q8(
5363 &self,
5364 table: &CudaSlice<u64>,
5365 sel: &cudarc::driver::CudaView<i32>,
5366 aq: &CudaSlice<i8>,
5367 ad: &CudaSlice<f32>,
5368 in_f: usize,
5369 n_ff: usize,
5370 n_used: usize,
5371 n_expert: usize,
5372 qt_g: i32,
5373 qt_u: i32,
5374 rb_g: usize,
5375 rb_u: usize,
5376 macros: &CudaSlice<f32>,
5377 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5378 static GU: std::sync::OnceLock<(String, u32)> = std::sync::OnceLock::new();
5379 let (mode, wpb) = GU.get_or_init(|| {
5380 let mode = std::env::var("MEMRA_MOE_DEVQ8_GU").unwrap_or_default();
5381 let wpb = std::env::var("MEMRA_MOE_DEVQ8_WPB")
5382 .ok()
5383 .and_then(|v| v.parse().ok())
5384 .unwrap_or(4u32)
5385 .clamp(1, 16);
5386 (mode, wpb)
5387 });
5388 let (mode, wpb) = (mode.as_str(), *wpb);
5389 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
5390 let (inf, nff, ne, rbg, rbu) = (
5391 in_f as i32,
5392 n_ff as i32,
5393 n_expert as i32,
5394 rb_g as i64,
5395 rb_u as i64,
5396 );
5397 let (f, cfg) = match mode {
5398 "1" | "2" | "4" => {
5399 let rpw: u32 = mode.parse().unwrap();
5400 let f = self.func(match rpw {
5401 1 => "moe_gate_up_silu8_dev_q8_r1",
5402 2 => "moe_gate_up_silu8_dev_q8_r2",
5403 _ => "moe_gate_up_silu8_dev_q8_r4",
5404 });
5405 let rows_per_block = (rpw * wpb) as usize;
5406 let gx = n_ff.div_ceil(rows_per_block) as u32;
5407 (
5408 f,
5409 LaunchConfig {
5410 grid_dim: (gx, n_used as u32, 1),
5411 block_dim: (32, wpb, 1),
5412 shared_mem_bytes: 0,
5413 },
5414 )
5415 }
5416 "j8" if n_used <= 32 => (
5417 self.func("moe_gate_up_silu8_dev_q8_j8"),
5418 LaunchConfig {
5419 grid_dim: (n_ff as u32, 1, 1),
5420 block_dim: (32, n_used as u32, 1),
5421 shared_mem_bytes: 0,
5422 },
5423 ),
5424 "vsm2" => {
5426 let f = self.func("moe_gate_up_silu8_dev_q8_vsm2");
5427 let sh = (rb_g + rb_u) as u32;
5428 use cudarc::driver::sys::CUfunction_attribute_enum as A;
5429 f.set_attribute(
5430 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
5431 sh as i32,
5432 )?;
5433 (
5434 f,
5435 LaunchConfig {
5436 grid_dim: (n_ff as u32, n_used as u32, 1),
5437 block_dim: (32, 1, 1),
5438 shared_mem_bytes: sh,
5439 },
5440 )
5441 }
5442 "vsm" => {
5443 let f = self.func("moe_gate_up_silu8_dev_q8_vsm");
5444 let sh = (rb_g + rb_u) as u32;
5445 use cudarc::driver::sys::CUfunction_attribute_enum as A;
5446 f.set_attribute(
5447 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
5448 sh as i32,
5449 )?;
5450 (
5451 f,
5452 LaunchConfig {
5453 grid_dim: (n_ff as u32, n_used as u32, 1),
5454 block_dim: (32, 1, 1),
5455 shared_mem_bytes: sh,
5456 },
5457 )
5458 }
5459 "sg" => (
5460 self.func("moe_gate_up_silu8_dev_q8_sg"),
5461 LaunchConfig {
5462 grid_dim: (n_ff as u32, n_used as u32, 1),
5463 block_dim: (32, 1, 1),
5464 shared_mem_bytes: 0,
5465 },
5466 ),
5467 "j8sg" if n_used <= 32 => (
5468 self.func("moe_gate_up_silu8_dev_q8_j8sg"),
5469 LaunchConfig {
5470 grid_dim: (n_ff as u32, 1, 1),
5471 block_dim: (32, n_used as u32, 1),
5472 shared_mem_bytes: 0,
5473 },
5474 ),
5475 "u64" if in_f == 2048 => (
5476 self.func("moe_gate_up_silu8_dev_q8_u64"),
5477 LaunchConfig {
5478 grid_dim: (n_ff as u32, n_used as u32, 1),
5479 block_dim: (32, 1, 1),
5480 shared_mem_bytes: 0,
5481 },
5482 ),
5483 "gs4" if in_f == 2048 => (
5484 self.func("moe_gate_up_silu8_dev_q8_gs4"),
5485 LaunchConfig {
5486 grid_dim: (n_ff as u32, n_used as u32, 1),
5487 block_dim: (32, 4, 1),
5488 shared_mem_bytes: 0,
5489 },
5490 ),
5491 "v" | "" => (
5493 self.func("moe_gate_up_silu8_dev_q8_v"),
5494 LaunchConfig {
5495 grid_dim: (n_ff as u32, n_used as u32, 1),
5496 block_dim: (32, 1, 1),
5497 shared_mem_bytes: 0,
5498 },
5499 ),
5500 "s2" => (
5501 self.func("moe_gate_up_silu8_dev_q8_s2"),
5502 LaunchConfig {
5503 grid_dim: (n_ff as u32, n_used as u32, 1),
5504 block_dim: (32, 2, 1),
5505 shared_mem_bytes: 0,
5506 },
5507 ),
5508 "s2z" => {
5509 let rz = wpb.min(16); (
5511 self.func("moe_gate_up_silu8_dev_q8_s2z"),
5512 LaunchConfig {
5513 grid_dim: (n_ff.div_ceil(rz as usize) as u32, n_used as u32, 1),
5514 block_dim: (32, 2, rz),
5515 shared_mem_bytes: 0,
5516 },
5517 )
5518 }
5519 _ => (
5520 self.func("moe_gate_up_silu8_dev_q8"),
5521 LaunchConfig {
5522 grid_dim: (n_ff as u32, n_used as u32, 1),
5523 block_dim: (32, 1, 1),
5524 shared_mem_bytes: 0,
5525 },
5526 ),
5527 };
5528 let __s_b = self.gpu.stream();
5529 let mut b = __s_b.launch_builder(&f);
5530 b.arg(table)
5531 .arg(sel)
5532 .arg(aq)
5533 .arg(ad)
5534 .arg(&mut act)
5535 .arg(&inf)
5536 .arg(&nff)
5537 .arg(&ne)
5538 .arg(&qt_g)
5539 .arg(&qt_u)
5540 .arg(&rbg)
5541 .arg(&rbu)
5542 .arg(macros);
5543 unsafe {
5544 b.launch(cfg)?;
5545 }
5546 Ok(act)
5547 }
5548
5549 #[allow(clippy::too_many_arguments)]
5550 pub fn moe_down8_fma_dev_q8(
5551 &self,
5552 table: &CudaSlice<u64>,
5553 sel: &cudarc::driver::CudaView<i32>,
5554 w: &cudarc::driver::CudaView<f32>,
5555 aq2: &CudaSlice<i8>,
5556 ad2: &CudaSlice<f32>,
5557 dst: &mut cudarc::driver::CudaViewMut<f32>,
5558 in_f: usize,
5559 out_f: usize,
5560 n_used: usize,
5561 n_expert: usize,
5562 qt: i32,
5563 rb: usize,
5564 ) -> Result<(), Box<dyn std::error::Error>> {
5565 static DOWN: std::sync::OnceLock<String> = std::sync::OnceLock::new();
5566 let mode = DOWN.get_or_init(|| std::env::var("MEMRA_MOE_DEVQ8_DOWN").unwrap_or_default());
5567 let (inf, outf, nu, ne, rbi) = (
5568 in_f as i32,
5569 out_f as i32,
5570 n_used as i32,
5571 n_expert as i32,
5572 rb as i64,
5573 );
5574 let (f, cfg) = match mode.as_str() {
5577 m @ ("1" | "2" | "4") if n_used <= 8 => {
5578 let rpw: usize = m.parse().unwrap();
5579 let f = self.func(match rpw {
5580 1 => "moe_down8_fma_dev_q8_w8r1",
5581 2 => "moe_down8_fma_dev_q8_w8r2",
5582 _ => "moe_down8_fma_dev_q8_w8r4",
5583 });
5584 (
5585 f,
5586 LaunchConfig {
5587 grid_dim: (out_f.div_ceil(rpw) as u32, 1, 1),
5588 block_dim: (32, n_used as u32, 1),
5589 shared_mem_bytes: 0,
5590 },
5591 )
5592 }
5593 "h2" if in_f == 512 => (
5594 self.func("moe_down8_fma_dev_q8_h2"),
5595 LaunchConfig {
5596 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5597 block_dim: (32, 1, 1),
5598 shared_mem_bytes: 0,
5599 },
5600 ),
5601 "" if in_f == 704 && n_used <= 8 => (
5604 self.func("moe_down8_fma_dev_q8_w8r2"),
5605 LaunchConfig {
5606 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5607 block_dim: (32, n_used as u32, 1),
5608 shared_mem_bytes: 0,
5609 },
5610 ),
5611 "w8h2v" | "" if in_f == 512 && n_used <= 8 => (
5615 self.func("moe_down8_fma_dev_q8_w8h2v"),
5616 LaunchConfig {
5617 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5618 block_dim: (32, n_used as u32, 1),
5619 shared_mem_bytes: 0,
5620 },
5621 ),
5622 "w8h2r2v" if in_f == 512 && n_used <= 8 => (
5623 self.func("moe_down8_fma_dev_q8_w8h2r2v"),
5624 LaunchConfig {
5625 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
5626 block_dim: (32, n_used as u32, 1),
5627 shared_mem_bytes: 0,
5628 },
5629 ),
5630 "w8h2r2" if in_f == 512 && n_used <= 8 => (
5631 self.func("moe_down8_fma_dev_q8_w8h2r2"),
5632 LaunchConfig {
5633 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
5634 block_dim: (32, n_used as u32, 1),
5635 shared_mem_bytes: 0,
5636 },
5637 ),
5638 "w8h2" if in_f == 512 && n_used <= 8 => (
5639 self.func("moe_down8_fma_dev_q8_w8h2"),
5640 LaunchConfig {
5641 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5642 block_dim: (32, n_used as u32, 1),
5643 shared_mem_bytes: 0,
5644 },
5645 ),
5646 _ => (
5647 self.func("moe_down8_fma_dev_q8"),
5648 LaunchConfig {
5649 grid_dim: (out_f as u32, 1, 1),
5650 block_dim: (32, 1, 1),
5651 shared_mem_bytes: 0,
5652 },
5653 ),
5654 };
5655 let __s_b = self.gpu.stream();
5656 let mut b = __s_b.launch_builder(&f);
5657 b.arg(table)
5658 .arg(sel)
5659 .arg(w)
5660 .arg(aq2)
5661 .arg(ad2)
5662 .arg(dst)
5663 .arg(&inf)
5664 .arg(&outf)
5665 .arg(&nu)
5666 .arg(&ne)
5667 .arg(&qt)
5668 .arg(&rbi);
5669 unsafe {
5670 b.launch(cfg)?;
5671 }
5672 Ok(())
5673 }
5674
5675 #[allow(clippy::too_many_arguments)]
5682 pub fn moe_gate_up_silu8_dev_q8_rows(
5683 &self,
5684 table: &CudaSlice<u64>,
5685 sel: &CudaSlice<i32>,
5686 aq: &CudaSlice<i8>,
5687 ad: &CudaSlice<f32>,
5688 t: usize,
5689 in_f: usize,
5690 n_ff: usize,
5691 n_used: usize,
5692 n_expert: usize,
5693 qt_g: i32,
5694 qt_u: i32,
5695 rb_g: usize,
5696 rb_u: usize,
5697 macros: &CudaSlice<f32>,
5698 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5699 let f = self.func("moe_gate_up_silu8_dev_q8_v_rows");
5700 let mut act = self.alloc_uninit::<f32>(t * n_used * n_ff)?;
5701 let cfg = LaunchConfig {
5702 grid_dim: (n_ff as u32, n_used as u32, t as u32),
5703 block_dim: (32, 1, 1),
5704 shared_mem_bytes: 0,
5705 };
5706 let (inf, nff, ne, nu, rbg, rbu) = (
5707 in_f as i32,
5708 n_ff as i32,
5709 n_expert as i32,
5710 n_used as i32,
5711 rb_g as i64,
5712 rb_u as i64,
5713 );
5714 let __s_b = self.gpu.stream();
5715 let mut b = __s_b.launch_builder(&f);
5716 b.arg(table)
5717 .arg(sel)
5718 .arg(aq)
5719 .arg(ad)
5720 .arg(&mut act)
5721 .arg(&inf)
5722 .arg(&nff)
5723 .arg(&ne)
5724 .arg(&qt_g)
5725 .arg(&qt_u)
5726 .arg(&rbg)
5727 .arg(&rbu)
5728 .arg(&nu)
5729 .arg(macros);
5730 unsafe {
5731 b.launch(cfg)?;
5732 }
5733 Ok(act)
5734 }
5735
5736 #[allow(clippy::too_many_arguments)]
5741 pub fn moe_down8_fma_dev_q8_rows(
5742 &self,
5743 table: &CudaSlice<u64>,
5744 sel: &CudaSlice<i32>,
5745 w: &CudaSlice<f32>,
5746 aq2: &CudaSlice<i8>,
5747 ad2: &CudaSlice<f32>,
5748 dst: &mut CudaSlice<f32>,
5749 t: usize,
5750 in_f: usize,
5751 out_f: usize,
5752 n_used: usize,
5753 n_expert: usize,
5754 qt: i32,
5755 rb: usize,
5756 ) -> Result<(), Box<dyn std::error::Error>> {
5757 assert!(
5758 in_f == 512 && n_used <= 8,
5759 "down rows twin is w8h2v shape-gated"
5760 );
5761 let f = self.func("moe_down8_fma_dev_q8_w8h2v_rows");
5762 let cfg = LaunchConfig {
5763 grid_dim: (out_f.div_ceil(2) as u32, 1, t as u32),
5764 block_dim: (32, n_used as u32, 1),
5765 shared_mem_bytes: 0,
5766 };
5767 let (inf, outf, nu, ne, rbi) = (
5768 in_f as i32,
5769 out_f as i32,
5770 n_used as i32,
5771 n_expert as i32,
5772 rb as i64,
5773 );
5774 let __s_b = self.gpu.stream();
5775 let mut b = __s_b.launch_builder(&f);
5776 b.arg(table)
5777 .arg(sel)
5778 .arg(w)
5779 .arg(aq2)
5780 .arg(ad2)
5781 .arg(dst)
5782 .arg(&inf)
5783 .arg(&outf)
5784 .arg(&nu)
5785 .arg(&ne)
5786 .arg(&qt)
5787 .arg(&rbi);
5788 unsafe {
5789 b.launch(cfg)?;
5790 }
5791 Ok(())
5792 }
5793
5794 #[allow(clippy::too_many_arguments)]
5798 pub fn moe_gate_up_silu8_dev_q8_csr(
5799 &self,
5800 table: &CudaSlice<u64>,
5801 sel: &CudaSlice<i32>,
5802 aq: &CudaSlice<i8>,
5803 ad: &CudaSlice<f32>,
5804 n_pairs: usize,
5805 in_f: usize,
5806 n_ff: usize,
5807 n_used: usize,
5808 n_expert: usize,
5809 qt_g: i32,
5810 qt_u: i32,
5811 rb_g: usize,
5812 rb_u: usize,
5813 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5814 let f = self.func("moe_gate_up_silu8_dev_q8_csr_iq4");
5815 let mut act = self.alloc_uninit::<f32>(n_pairs * n_ff)?;
5816 let cfg = LaunchConfig {
5817 grid_dim: (n_ff as u32, n_pairs as u32, 1),
5818 block_dim: (32, 1, 1),
5819 shared_mem_bytes: 0,
5820 };
5821 let (inf, nff, ne, nu, npi, rbg, rbu) = (
5822 in_f as i32,
5823 n_ff as i32,
5824 n_expert as i32,
5825 n_used as i32,
5826 n_pairs as i32,
5827 rb_g as i64,
5828 rb_u as i64,
5829 );
5830 let __s_b = self.gpu.stream();
5831 let mut b = __s_b.launch_builder(&f);
5832 b.arg(table)
5833 .arg(sel)
5834 .arg(aq)
5835 .arg(ad)
5836 .arg(&mut act)
5837 .arg(&inf)
5838 .arg(&nff)
5839 .arg(&ne)
5840 .arg(&qt_g)
5841 .arg(&qt_u)
5842 .arg(&rbg)
5843 .arg(&rbu)
5844 .arg(&nu)
5845 .arg(&npi);
5846 unsafe {
5847 b.launch(cfg)?;
5848 }
5849 Ok(act)
5850 }
5851
5852 #[allow(clippy::too_many_arguments)]
5856 pub fn moe_down8_fma_dev_q8_variant(
5857 &self,
5858 variant: &str,
5859 table: &CudaSlice<u64>,
5860 sel: &cudarc::driver::CudaView<i32>,
5861 w: &cudarc::driver::CudaView<f32>,
5862 aq2: &CudaSlice<i8>,
5863 ad2: &CudaSlice<f32>,
5864 dst: &mut cudarc::driver::CudaViewMut<f32>,
5865 in_f: usize,
5866 out_f: usize,
5867 n_used: usize,
5868 n_expert: usize,
5869 qt: i32,
5870 rb: usize,
5871 ) -> Result<(), Box<dyn std::error::Error>> {
5872 let (inf, outf, nu, ne, rbi) = (
5873 in_f as i32,
5874 out_f as i32,
5875 n_used as i32,
5876 n_expert as i32,
5877 rb as i64,
5878 );
5879 let (f, cfg) = match variant {
5880 "w8h2" | "w8h2v" => (
5881 self.func(if variant == "w8h2" {
5882 "moe_down8_fma_dev_q8_w8h2"
5883 } else {
5884 "moe_down8_fma_dev_q8_w8h2v"
5885 }),
5886 LaunchConfig {
5887 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5888 block_dim: (32, n_used as u32, 1),
5889 shared_mem_bytes: 0,
5890 },
5891 ),
5892 "w8h2r2" | "w8h2r2v" => (
5893 self.func(if variant == "w8h2r2" {
5894 "moe_down8_fma_dev_q8_w8h2r2"
5895 } else {
5896 "moe_down8_fma_dev_q8_w8h2r2v"
5897 }),
5898 LaunchConfig {
5899 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
5900 block_dim: (32, n_used as u32, 1),
5901 shared_mem_bytes: 0,
5902 },
5903 ),
5904 _ => (
5905 self.func("moe_down8_fma_dev_q8"),
5906 LaunchConfig {
5907 grid_dim: (out_f as u32, 1, 1),
5908 block_dim: (32, 1, 1),
5909 shared_mem_bytes: 0,
5910 },
5911 ),
5912 };
5913 let __s_b = self.gpu.stream();
5914 let mut b = __s_b.launch_builder(&f);
5915 b.arg(table)
5916 .arg(sel)
5917 .arg(w)
5918 .arg(aq2)
5919 .arg(ad2)
5920 .arg(dst)
5921 .arg(&inf)
5922 .arg(&outf)
5923 .arg(&nu)
5924 .arg(&ne)
5925 .arg(&qt)
5926 .arg(&rbi);
5927 unsafe {
5928 b.launch(cfg)?;
5929 }
5930 Ok(())
5931 }
5932
5933 #[allow(clippy::too_many_arguments)]
5935 pub fn moe_gate_up_silu8_dev_q8_variant(
5936 &self,
5937 variant: &str,
5938 table: &CudaSlice<u64>,
5939 sel: &cudarc::driver::CudaView<i32>,
5940 aq: &CudaSlice<i8>,
5941 ad: &CudaSlice<f32>,
5942 in_f: usize,
5943 n_ff: usize,
5944 n_used: usize,
5945 n_expert: usize,
5946 qt_g: i32,
5947 qt_u: i32,
5948 rb_g: usize,
5949 rb_u: usize,
5950 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5951 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
5952 let (inf, nff, ne, rbg, rbu) = (
5953 in_f as i32,
5954 n_ff as i32,
5955 n_expert as i32,
5956 rb_g as i64,
5957 rb_u as i64,
5958 );
5959 let f = self.func(if variant == "v" {
5960 "moe_gate_up_silu8_dev_q8_v"
5961 } else {
5962 "moe_gate_up_silu8_dev_q8"
5963 });
5964 let cfg = LaunchConfig {
5965 grid_dim: (n_ff as u32, n_used as u32, 1),
5966 block_dim: (32, 1, 1),
5967 shared_mem_bytes: 0,
5968 };
5969 let __s_b = self.gpu.stream();
5970 let mut b = __s_b.launch_builder(&f);
5971 b.arg(table)
5972 .arg(sel)
5973 .arg(aq)
5974 .arg(ad)
5975 .arg(&mut act)
5976 .arg(&inf)
5977 .arg(&nff)
5978 .arg(&ne)
5979 .arg(&qt_g)
5980 .arg(&qt_u)
5981 .arg(&rbg)
5982 .arg(&rbu);
5983 unsafe {
5984 b.launch(cfg)?;
5985 }
5986 Ok(act)
5987 }
5988
5989 pub fn moe_gate_up_silu8_dev(
5990 &self,
5991 table: &CudaSlice<u64>,
5992 sel: &cudarc::driver::CudaView<i32>,
5993 x: &cudarc::driver::CudaView<f32>,
5994 in_f: usize,
5995 n_ff: usize,
5996 n_used: usize,
5997 n_expert: usize,
5998 qt_g: i32,
5999 qt_u: i32,
6000 rb_g: usize,
6001 rb_u: usize,
6002 macros: &CudaSlice<f32>,
6003 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6004 let f = self.func("moe_gate_up_silu8_dev");
6005 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?; let cfg = LaunchConfig {
6007 grid_dim: (n_ff as u32, n_used as u32, 1),
6008 block_dim: (256, 1, 1),
6009 shared_mem_bytes: 0,
6010 };
6011 let (inf, nff, ne, rbg, rbu) = (
6012 in_f as i32,
6013 n_ff as i32,
6014 n_expert as i32,
6015 rb_g as i64,
6016 rb_u as i64,
6017 );
6018 let __s_b = self.gpu.stream();
6019 let mut b = __s_b.launch_builder(&f);
6020 b.arg(table)
6021 .arg(sel)
6022 .arg(x)
6023 .arg(&mut act)
6024 .arg(&inf)
6025 .arg(&nff)
6026 .arg(&ne)
6027 .arg(&qt_g)
6028 .arg(&qt_u)
6029 .arg(&rbg)
6030 .arg(&rbu)
6031 .arg(macros);
6032 unsafe {
6033 b.launch(cfg)?;
6034 }
6035 Ok(act)
6036 }
6037
6038 #[allow(clippy::too_many_arguments)]
6041 pub fn moe_down8_fma_dev(
6042 &self,
6043 table: &CudaSlice<u64>,
6044 sel: &cudarc::driver::CudaView<i32>,
6045 w: &cudarc::driver::CudaView<f32>,
6046 act: &CudaSlice<f32>,
6047 dst: &mut cudarc::driver::CudaViewMut<f32>,
6048 in_f: usize,
6049 out_f: usize,
6050 n_used: usize,
6051 n_expert: usize,
6052 qt: i32,
6053 rb: usize,
6054 ) -> Result<(), Box<dyn std::error::Error>> {
6055 let f = self.func("moe_down8_fma_dev");
6056 let cfg = LaunchConfig {
6057 grid_dim: (out_f as u32, 1, 1),
6058 block_dim: (256, 1, 1),
6059 shared_mem_bytes: 0,
6060 };
6061 let (inf, outf, nu, ne, rbv) = (
6062 in_f as i32,
6063 out_f as i32,
6064 n_used as i32,
6065 n_expert as i32,
6066 rb as i64,
6067 );
6068 let __s_b = self.gpu.stream();
6069 let mut b = __s_b.launch_builder(&f);
6070 b.arg(table)
6071 .arg(sel)
6072 .arg(w)
6073 .arg(act)
6074 .arg(dst)
6075 .arg(&inf)
6076 .arg(&outf)
6077 .arg(&nu)
6078 .arg(&ne)
6079 .arg(&qt)
6080 .arg(&rbv);
6081 unsafe {
6082 b.launch(cfg)?;
6083 }
6084 Ok(())
6085 }
6086
6087 pub fn axpy_into(
6089 &self,
6090 src: &CudaSlice<f32>,
6091 alpha: f32,
6092 dst: &mut cudarc::driver::CudaViewMut<f32>,
6093 n: usize,
6094 ) -> Result<(), Box<dyn std::error::Error>> {
6095 let f = self.func("axpy_f32");
6096 let cfg = LaunchConfig::for_num_elems(n as u32);
6097 let (a, ni) = (alpha, n as i32);
6098 let __s_b = self.gpu.stream();
6099 let mut b = __s_b.launch_builder(&f);
6100 b.arg(src).arg(dst).arg(&a).arg(&ni);
6101 unsafe {
6102 b.launch(cfg)?;
6103 }
6104 Ok(())
6105 }
6106
6107 pub fn add_scaled_rows(
6109 &self,
6110 src: &CudaSlice<f32>,
6111 scale: &CudaSlice<f32>,
6112 dst: &mut CudaSlice<f32>,
6113 ncols: usize,
6114 nrows: usize,
6115 ) -> Result<(), Box<dyn std::error::Error>> {
6116 let f = self.func("add_scaled_rows_f32");
6117 let cfg = LaunchConfig::for_num_elems((ncols * nrows) as u32);
6118 let (nc, nr) = (ncols as i32, nrows as i32);
6119 let __s_b = self.gpu.stream();
6120 let mut b = __s_b.launch_builder(&f);
6121 b.arg(src).arg(scale).arg(dst).arg(&nc).arg(&nr);
6122 unsafe {
6123 b.launch(cfg)?;
6124 }
6125 Ok(())
6126 }
6127
6128 pub fn gather_rows(
6132 &self,
6133 src: &CudaSlice<f32>,
6134 idx: &CudaSlice<i32>,
6135 dst: &mut CudaSlice<f32>,
6136 ncols: usize,
6137 m_e: usize,
6138 ) -> Result<(), Box<dyn std::error::Error>> {
6139 let f = self.func("gather_rows_f32");
6140 let cfg = LaunchConfig::for_num_elems((m_e * ncols) as u32);
6141 let (nc, me) = (ncols as i32, m_e as i32);
6142 let __s_b = self.gpu.stream();
6143 let mut b = __s_b.launch_builder(&f);
6144 b.arg(src).arg(idx).arg(dst).arg(&nc).arg(&me);
6145 unsafe {
6146 b.launch(cfg)?;
6147 }
6148 Ok(())
6149 }
6150
6151 pub fn scatter_slot(
6156 &self,
6157 src: &CudaSlice<f32>,
6158 tok_idx: &CudaSlice<i32>,
6159 slot_idx: &CudaSlice<i32>,
6160 weight: &CudaSlice<f32>,
6161 dst: &mut CudaSlice<f32>,
6162 wbuf: &mut CudaSlice<f32>,
6163 ncols: usize,
6164 n_used: usize,
6165 m_e: usize,
6166 ) -> Result<(), Box<dyn std::error::Error>> {
6167 let f = self.func("scatter_add_slot_f32");
6168 let cfg = LaunchConfig::for_num_elems((m_e * ncols) as u32);
6169 let (nc, nu, me) = (ncols as i32, n_used as i32, m_e as i32);
6170 let __s_b = self.gpu.stream();
6171 let mut b = __s_b.launch_builder(&f);
6172 b.arg(src)
6173 .arg(tok_idx)
6174 .arg(slot_idx)
6175 .arg(weight)
6176 .arg(dst)
6177 .arg(wbuf)
6178 .arg(&nc)
6179 .arg(&nu)
6180 .arg(&me);
6181 unsafe {
6182 b.launch(cfg)?;
6183 }
6184 Ok(())
6185 }
6186
6187 pub fn reduce_slots(
6191 &self,
6192 slots: &CudaSlice<f32>,
6193 wbuf: &CudaSlice<f32>,
6194 dst: &mut CudaSlice<f32>,
6195 ncols: usize,
6196 n_used: usize,
6197 t: usize,
6198 ) -> Result<(), Box<dyn std::error::Error>> {
6199 let f = self.func("reduce_slots_f32");
6200 let cfg = LaunchConfig::for_num_elems((t * ncols) as u32);
6201 let (nc, nu, ti) = (ncols as i32, n_used as i32, t as i32);
6202 let __s_b = self.gpu.stream();
6203 let mut b = __s_b.launch_builder(&f);
6204 b.arg(slots).arg(wbuf).arg(dst).arg(&nc).arg(&nu).arg(&ti);
6205 unsafe {
6206 b.launch(cfg)?;
6207 }
6208 Ok(())
6209 }
6210
6211 pub fn quantize_q8_1_view(
6218 &self,
6219 x: &cudarc::driver::CudaView<f32>,
6220 m: usize,
6221 in_f: usize,
6222 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
6223 let f = self.func("quantize_q8_1");
6224 let nblk = in_f / 32;
6225 let mut q = self.alloc_uninit::<i8>(m * in_f)?;
6226 let mut d = self.alloc_uninit::<f32>(m * nblk)?;
6227 let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
6228 let (inf, mi) = (in_f as i32, m as i32);
6229 let __s_b = self.gpu.stream();
6230 let mut b = __s_b.launch_builder(&f);
6231 b.arg(x).arg(&mut q).arg(&mut d).arg(&inf).arg(&mi);
6232 unsafe {
6233 b.launch(cfg)?;
6234 }
6235 Ok((q, d))
6236 }
6237
6238 pub fn quantize_q8_1(
6239 &self,
6240 x: &CudaSlice<f32>,
6241 m: usize,
6242 in_f: usize,
6243 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
6244 let nblk = in_f / 32;
6245 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);
6249 let (inf, mi) = (in_f as i32, m as i32);
6250 if Self::pdl_on() && Self::pdl_wb_on() {
6251 {
6252 use cudarc::driver::{DevicePtr, DevicePtrMut};
6253 let s = &self.gpu.stream();
6254 let (px, _g0) = x.device_ptr(s);
6255 let (pq, _g1) = q.device_ptr_mut(s);
6256 let (pd, _g2) = d.device_ptr_mut(s);
6257 let mut ps = [
6258 &px as *const _ as *mut std::ffi::c_void,
6259 &pq as *const _ as *mut _,
6260 &pd as *const _ as *mut _,
6261 &inf as *const _ as *mut _,
6262 &mi as *const _ as *mut _,
6263 ];
6264 unsafe {
6265 self.launch_pdl("quantize_q8_1", cfg.grid_dim, cfg.block_dim, &mut ps)?;
6266 }
6267 }
6268 return Ok((q, d));
6269 }
6270 let f = self.func("quantize_q8_1");
6271 let __s_b = self.gpu.stream();
6272 let mut b = __s_b.launch_builder(&f);
6273 b.arg(x).arg(&mut q).arg(&mut d).arg(&inf).arg(&mi);
6274 unsafe {
6275 b.launch(cfg)?;
6276 }
6277 Ok((q, d))
6278 }
6279
6280 pub fn quantize_fp4_act(
6284 &self,
6285 x: &CudaSlice<f32>,
6286 m: usize,
6287 in_f: usize,
6288 ) -> Result<(CudaSlice<u32>, CudaSlice<u8>), Box<dyn std::error::Error>> {
6289 let f = self.func("quantize_fp4_act");
6290 let nb16 = in_f / 16;
6291 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);
6294 let (inf, mi) = (in_f as i32, m as i32);
6295 let __s_b = self.gpu.stream();
6296 let mut b = __s_b.launch_builder(&f);
6297 b.arg(x).arg(&mut aq4).arg(&mut ad4).arg(&inf).arg(&mi);
6298 unsafe {
6299 b.launch(cfg)?;
6300 }
6301 Ok((aq4, ad4))
6302 }
6303
6304 pub fn qmatvec_gemm_nvfp4_fp4(
6309 &self,
6310 bytes: &CudaSlice<u8>,
6311 x: &CudaSlice<f32>,
6312 m: usize,
6313 in_f: usize,
6314 out_f: usize,
6315 row_bytes: usize,
6316 scale: f32,
6317 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6318 assert!(
6319 in_f % 64 == 0,
6320 "FP4 GEMM requires in_f % 64 == 0, got {in_f}"
6321 );
6322 let (aq4, ad4) = self.quantize_fp4_act(x, m, in_f)?;
6323 let mut y = self.fp4_gemm_launch(bytes, &aq4, &ad4, m, in_f, out_f, row_bytes)?;
6324 if scale != 1.0 {
6325 self.scale_inplace(&mut y, scale, m * out_f)?;
6326 }
6327 Ok(y)
6328 }
6329
6330 fn fp4_gemm_launch(
6333 &self,
6334 bytes: &CudaSlice<u8>,
6335 aq4: &CudaSlice<u32>,
6336 ad4: &CudaSlice<u8>,
6337 m: usize,
6338 in_f: usize,
6339 out_f: usize,
6340 row_bytes: usize,
6341 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6342 let f = self.func("qmatvec_gemm_nvfp4_fp4");
6343 let mut y = self.alloc_uninit::<f32>(m * out_f)?; const BM: u32 = 64;
6345 const BN: u32 = 256;
6346 let cfg = LaunchConfig {
6347 grid_dim: ((out_f as u32 + BM - 1) / BM, (m as u32 + BN - 1) / BN, 1),
6348 block_dim: (32, 4, 1),
6349 shared_mem_bytes: 0,
6350 };
6351 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6352 let __s_b = self.gpu.stream();
6353 let mut b = __s_b.launch_builder(&f);
6354 b.arg(bytes)
6355 .arg(aq4)
6356 .arg(ad4)
6357 .arg(&mut y)
6358 .arg(&inf)
6359 .arg(&outf)
6360 .arg(&mi)
6361 .arg(&rb);
6362 unsafe {
6363 b.launch(cfg)?;
6364 }
6365 Ok(y)
6366 }
6367
6368 pub fn qmatvec_gemm_nvfp4_fp4_raw(
6370 &self,
6371 bytes: &CudaSlice<u8>,
6372 x: &CudaSlice<f32>,
6373 m: usize,
6374 in_f: usize,
6375 out_f: usize,
6376 row_bytes: usize,
6377 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6378 assert!(
6379 in_f % 64 == 0,
6380 "FP4 GEMM requires in_f % 64 == 0, got {in_f}"
6381 );
6382 let (aq4, ad4) = self.quantize_fp4_act(x, m, in_f)?;
6383 self.fp4_gemm_launch(bytes, &aq4, &ad4, m, in_f, out_f, row_bytes)
6384 }
6385
6386 pub fn qmatvec_q8_0_fast(
6388 &self,
6389 w: &CudaSlice<u8>,
6390 x: &CudaSlice<f32>,
6391 m: usize,
6392 in_f: usize,
6393 out_f: usize,
6394 row_bytes: usize,
6395 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6396 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6397 let f = self.func("qmatvec_q8_0_dp4a");
6398 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6400 grid_dim: (out_f as u32, m as u32, 1),
6401 block_dim: (128, 1, 1),
6402 shared_mem_bytes: 0,
6403 };
6404 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6405 let __s_b = self.gpu.stream();
6406 let mut b = __s_b.launch_builder(&f);
6407 b.arg(w)
6408 .arg(&aq)
6409 .arg(&ad)
6410 .arg(&mut y)
6411 .arg(&inf)
6412 .arg(&outf)
6413 .arg(&mi)
6414 .arg(&rb);
6415 unsafe {
6416 b.launch(cfg)?;
6417 }
6418 Ok(y)
6419 }
6420
6421 #[allow(non_snake_case)] pub fn qmatvec_q4_K_fast(
6424 &self,
6425 w: &CudaSlice<u8>,
6426 x: &CudaSlice<f32>,
6427 m: usize,
6428 in_f: usize,
6429 out_f: usize,
6430 row_bytes: usize,
6431 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6432 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6433 let f = self.func("qmatvec_q4_K_dp4a");
6434 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6436 grid_dim: (out_f as u32, m as u32, 1),
6437 block_dim: (128, 1, 1),
6438 shared_mem_bytes: 0,
6439 };
6440 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6441 let __s_b = self.gpu.stream();
6442 let mut b = __s_b.launch_builder(&f);
6443 b.arg(w)
6444 .arg(&aq)
6445 .arg(&ad)
6446 .arg(&mut y)
6447 .arg(&inf)
6448 .arg(&outf)
6449 .arg(&mi)
6450 .arg(&rb);
6451 unsafe {
6452 b.launch(cfg)?;
6453 }
6454 Ok(y)
6455 }
6456
6457 #[allow(non_snake_case)] pub fn qmatvec_q6_K_fast(
6460 &self,
6461 w: &CudaSlice<u8>,
6462 x: &CudaSlice<f32>,
6463 m: usize,
6464 in_f: usize,
6465 out_f: usize,
6466 row_bytes: usize,
6467 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6468 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6469 let f = self.func("qmatvec_q6_K_dp4a");
6470 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6472 grid_dim: (out_f as u32, m as u32, 1),
6473 block_dim: (128, 1, 1),
6474 shared_mem_bytes: 0,
6475 };
6476 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6477 let __s_b = self.gpu.stream();
6478 let mut b = __s_b.launch_builder(&f);
6479 b.arg(w)
6480 .arg(&aq)
6481 .arg(&ad)
6482 .arg(&mut y)
6483 .arg(&inf)
6484 .arg(&outf)
6485 .arg(&mi)
6486 .arg(&rb);
6487 unsafe {
6488 b.launch(cfg)?;
6489 }
6490 Ok(y)
6491 }
6492
6493 #[allow(non_snake_case)] pub fn qmatvec_q5_K_fast(
6496 &self,
6497 w: &CudaSlice<u8>,
6498 x: &CudaSlice<f32>,
6499 m: usize,
6500 in_f: usize,
6501 out_f: usize,
6502 row_bytes: usize,
6503 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6504 self.qmatvec_dp4a_named("qmatvec_q5_K_dp4a", w, x, m, in_f, out_f, row_bytes)
6505 }
6506 #[allow(non_snake_case)] pub fn qmatvec_q3_K_fast(
6509 &self,
6510 w: &CudaSlice<u8>,
6511 x: &CudaSlice<f32>,
6512 m: usize,
6513 in_f: usize,
6514 out_f: usize,
6515 row_bytes: usize,
6516 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6517 self.qmatvec_dp4a_named("qmatvec_q3_K_dp4a", w, x, m, in_f, out_f, row_bytes)
6518 }
6519 pub fn qmatvec_nvfp4_fast_rp(
6521 &self,
6522 w: &CudaSlice<u8>,
6523 x: &CudaSlice<f32>,
6524 m: usize,
6525 in_f: usize,
6526 out_f: usize,
6527 row_bytes: usize,
6528 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6529 assert!(
6530 in_f % 64 == 0,
6531 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
6532 );
6533 self.qmatvec_dp4a_named("qmatvec_nvfp4_dp4a_rp", w, x, m, in_f, out_f, row_bytes)
6534 }
6535 pub fn qmatvec_nvfp4_fast(
6537 &self,
6538 w: &CudaSlice<u8>,
6539 x: &CudaSlice<f32>,
6540 m: usize,
6541 in_f: usize,
6542 out_f: usize,
6543 row_bytes: usize,
6544 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6545 assert!(
6548 in_f % 64 == 0,
6549 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
6550 );
6551 self.qmatvec_dp4a_named("qmatvec_nvfp4_dp4a", w, x, m, in_f, out_f, row_bytes)
6552 }
6553 #[allow(non_snake_case)] pub fn qmatvec_iq4_XS_fast(
6556 &self,
6557 w: &CudaSlice<u8>,
6558 x: &CudaSlice<f32>,
6559 m: usize,
6560 in_f: usize,
6561 out_f: usize,
6562 row_bytes: usize,
6563 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6564 self.qmatvec_dp4a_named("qmatvec_iq4_XS_dp4a", w, x, m, in_f, out_f, row_bytes)
6565 }
6566
6567 fn qmatvec_dp4a_named(
6569 &self,
6570 name: &str,
6571 w: &CudaSlice<u8>,
6572 x: &CudaSlice<f32>,
6573 m: usize,
6574 in_f: usize,
6575 out_f: usize,
6576 row_bytes: usize,
6577 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6578 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6579 let f = self.func(name);
6580 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6582 grid_dim: (out_f as u32, m as u32, 1),
6583 block_dim: (128, 1, 1),
6584 shared_mem_bytes: 0,
6585 };
6586 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6587 let __s_b = self.gpu.stream();
6588 let mut b = __s_b.launch_builder(&f);
6589 b.arg(w)
6590 .arg(&aq)
6591 .arg(&ad)
6592 .arg(&mut y)
6593 .arg(&inf)
6594 .arg(&outf)
6595 .arg(&mi)
6596 .arg(&rb);
6597 unsafe {
6598 b.launch(cfg)?;
6599 }
6600 Ok(y)
6601 }
6602
6603 pub fn htod(&self, v: &[f32]) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6604 Ok(self.gpu.stream().clone_htod(v)?)
6605 }
6606 pub fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
6607 Ok(self.gpu.stream().clone_htod(v)?)
6608 }
6609 pub fn htod_i8(&self, v: &[i8]) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
6611 Ok(self.gpu.stream().clone_htod(v)?)
6612 }
6613 pub fn htod_u64(&self, v: &[u64]) -> Result<CudaSlice<u64>, Box<dyn std::error::Error>> {
6614 Ok(self.gpu.stream().clone_htod(v)?)
6615 }
6616 pub fn dtoh_view(
6618 &self,
6619 d: &cudarc::driver::CudaView<f32>,
6620 ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6621 let v = self.gpu.stream().clone_dtoh(d)?;
6622 self.gpu.stream().synchronize()?;
6623 Ok(v)
6624 }
6625 pub fn dtoh(&self, d: &CudaSlice<f32>) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6626 let v = self.gpu.stream().clone_dtoh(d)?;
6627 self.gpu.stream().synchronize()?;
6628 Ok(v)
6629 }
6630 pub fn dtoh_pair(
6634 &self,
6635 a: &CudaSlice<f32>,
6636 b: &CudaSlice<f32>,
6637 ) -> Result<(Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
6638 let av = self.gpu.stream().clone_dtoh(a)?;
6639 let bv = self.gpu.stream().clone_dtoh(b)?;
6640 self.gpu.stream().synchronize()?;
6641 Ok((av, bv))
6642 }
6643 pub fn dtoh_i32(&self, d: &CudaSlice<i32>) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
6645 let v = self.gpu.stream().clone_dtoh(d)?;
6646 self.gpu.stream().synchronize()?;
6647 Ok(v)
6648 }
6649 pub fn dtoh_u8(&self, d: &CudaSlice<u8>) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
6651 let v = self.gpu.stream().clone_dtoh(d)?;
6652 self.gpu.stream().synchronize()?;
6653 Ok(v)
6654 }
6655 pub fn dtoh_u8_view(
6656 &self,
6657 d: &cudarc::driver::CudaView<u8>,
6658 ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
6659 let v = self.gpu.stream().clone_dtoh(d)?;
6660 self.gpu.stream().synchronize()?;
6661 Ok(v)
6662 }
6663 pub fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6664 let s = self.gpu.stream().alloc_zeros::<f32>(n)?;
6665 self.keep_if_capturing(&s);
6666 Ok(s)
6667 }
6668
6669 pub fn prob_of_token_device(
6678 &self,
6679 logits: &CudaSlice<f32>,
6680 tok: &CudaSlice<u32>,
6681 n_vocab: usize,
6682 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6683 let nb = ARGMAX_NB;
6684 let mut part = self.alloc_uninit::<f32>(nb)?;
6685 let mut p = self.alloc_uninit::<f32>(1)?;
6686 let f1 = self.func("prob_of_token_partial_f32");
6687 let cfg1 = LaunchConfig {
6688 grid_dim: (nb as u32, 1, 1),
6689 block_dim: (256, 1, 1),
6690 shared_mem_bytes: 0,
6691 };
6692 let nv = n_vocab as i32;
6693 let __s_b1 = self.gpu.stream();
6694 let mut b1 = __s_b1.launch_builder(&f1);
6695 b1.arg(logits).arg(tok).arg(&mut part).arg(&nv);
6696 unsafe {
6697 b1.launch(cfg1)?;
6698 }
6699 let f2 = self.func("prob_of_token_final_f32");
6700 let cfg2 = LaunchConfig {
6701 grid_dim: (1, 1, 1),
6702 block_dim: (256, 1, 1),
6703 shared_mem_bytes: 0,
6704 };
6705 let nbi = nb as i32;
6706 let __s_b2 = self.gpu.stream();
6707 let mut b2 = __s_b2.launch_builder(&f2);
6708 b2.arg(&part).arg(&mut p).arg(&nbi);
6709 unsafe {
6710 b2.launch(cfg2)?;
6711 }
6712 Ok(p)
6713 }
6714
6715 pub fn prob_of_token_device_col(
6722 &self,
6723 logits: &CudaSlice<f32>,
6724 tok_all: &CudaSlice<u32>,
6725 tok_idx: usize,
6726 p_out: &mut CudaSlice<f32>,
6727 p_idx: usize,
6728 n_vocab: usize,
6729 ) -> Result<(), Box<dyn std::error::Error>> {
6730 let tok_v = tok_all.slice(tok_idx..tok_idx + 1);
6731 let mut p_v = p_out.slice_mut(p_idx..p_idx + 1);
6732 let nb = ARGMAX_NB;
6733 let mut part = self.alloc_uninit::<f32>(nb)?;
6734 let f1 = self.func("prob_of_token_partial_f32");
6735 let cfg1 = LaunchConfig {
6736 grid_dim: (nb as u32, 1, 1),
6737 block_dim: (256, 1, 1),
6738 shared_mem_bytes: 0,
6739 };
6740 let nv = n_vocab as i32;
6741 let __s_b1 = self.gpu.stream();
6742 let mut b1 = __s_b1.launch_builder(&f1);
6743 b1.arg(logits).arg(&tok_v).arg(&mut part).arg(&nv);
6744 unsafe {
6745 b1.launch(cfg1)?;
6746 }
6747 let f2 = self.func("prob_of_token_final_f32");
6748 let cfg2 = LaunchConfig {
6749 grid_dim: (1, 1, 1),
6750 block_dim: (256, 1, 1),
6751 shared_mem_bytes: 0,
6752 };
6753 let nbi = nb as i32;
6754 let __s_b2 = self.gpu.stream();
6755 let mut b2 = __s_b2.launch_builder(&f2);
6756 b2.arg(&part).arg(&mut p_v).arg(&nbi);
6757 unsafe {
6758 b2.launch(cfg2)?;
6759 }
6760 Ok(())
6761 }
6762
6763 pub fn prob_of_token_device_into(
6764 &self,
6765 logits: &CudaSlice<f32>,
6766 tok: &CudaSlice<u32>,
6767 p_out: &mut CudaSlice<f32>,
6768 n_vocab: usize,
6769 ) -> Result<(), Box<dyn std::error::Error>> {
6770 let nb = ARGMAX_NB;
6771 let mut part = self.alloc_uninit::<f32>(nb)?;
6772 let f1 = self.func("prob_of_token_partial_f32");
6773 let cfg1 = LaunchConfig {
6774 grid_dim: (nb as u32, 1, 1),
6775 block_dim: (256, 1, 1),
6776 shared_mem_bytes: 0,
6777 };
6778 let nv = n_vocab as i32;
6779 let __s_b1 = self.gpu.stream();
6780 let mut b1 = __s_b1.launch_builder(&f1);
6781 b1.arg(logits).arg(tok).arg(&mut part).arg(&nv);
6782 unsafe {
6783 b1.launch(cfg1)?;
6784 }
6785 let f2 = self.func("prob_of_token_final_f32");
6786 let cfg2 = LaunchConfig {
6787 grid_dim: (1, 1, 1),
6788 block_dim: (256, 1, 1),
6789 shared_mem_bytes: 0,
6790 };
6791 let nbi = nb as i32;
6792 let __s_b2 = self.gpu.stream();
6793 let mut b2 = __s_b2.launch_builder(&f2);
6794 b2.arg(&part).arg(p_out).arg(&nbi);
6795 unsafe {
6796 b2.launch(cfg2)?;
6797 }
6798 Ok(())
6799 }
6800
6801 pub fn argmax_token_device(
6802 &self,
6803 logits: &CudaSlice<f32>,
6804 n_vocab: usize,
6805 ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
6806 let mut tok = unsafe { self.gpu.stream().alloc::<u32>(1)? };
6807 self.argmax_token_device_into(logits, &mut tok, n_vocab)?;
6808 Ok(tok)
6809 }
6810 pub fn argmax_token_device_into(
6817 &self,
6818 logits: &CudaSlice<f32>,
6819 tok: &mut CudaSlice<u32>,
6820 n_vocab: usize,
6821 ) -> Result<(), Box<dyn std::error::Error>> {
6822 let nb = ARGMAX_NB;
6823 let f1 = self.func("argmax_partial_f32");
6824 let f2 = self.func("argmax_final_f32");
6825 let mut guard = self.argmax_partials.lock().unwrap();
6826 if guard.is_none() {
6827 let pv = self.gpu.stream().alloc_zeros::<f32>(nb)?;
6830 let pi = self.gpu.stream().alloc_zeros::<i32>(nb)?;
6831 *guard = Some((pv, pi));
6832 }
6833 let (part_v, part_i) = guard.as_mut().unwrap();
6834 let nv = n_vocab as i32;
6835 let nbi = nb as i32;
6836 let cfg1 = LaunchConfig {
6838 grid_dim: (nb as u32, 1, 1),
6839 block_dim: (256, 1, 1),
6840 shared_mem_bytes: 0,
6841 };
6842 let __s_b1 = self.gpu.stream();
6843 let mut b1 = __s_b1.launch_builder(&f1);
6844 b1.arg(logits).arg(&mut *part_v).arg(&mut *part_i).arg(&nv);
6845 unsafe {
6846 b1.launch(cfg1)?;
6847 }
6848 let cfg2 = LaunchConfig {
6850 grid_dim: (1, 1, 1),
6851 block_dim: (256, 1, 1),
6852 shared_mem_bytes: 0,
6853 };
6854 let __s_b2 = self.gpu.stream();
6855 let mut b2 = __s_b2.launch_builder(&f2);
6856 b2.arg(&*part_v).arg(&*part_i).arg(tok).arg(&nbi);
6857 unsafe {
6858 b2.launch(cfg2)?;
6859 }
6860 Ok(())
6861 }
6862 pub fn argmax_token_device_col(
6868 &self,
6869 logits: &CudaSlice<f32>,
6870 col: usize,
6871 n_vocab: usize,
6872 toks: &mut CudaSlice<u32>,
6873 out_idx: usize,
6874 ) -> Result<(), Box<dyn std::error::Error>> {
6875 let nb = ARGMAX_NB;
6876 let f1 = self.func("argmax_partial_f32");
6877 let f2 = self.func("argmax_final_f32");
6878 let mut guard = self.argmax_partials.lock().unwrap();
6879 if guard.is_none() {
6880 let pv = self.gpu.stream().alloc_zeros::<f32>(nb)?;
6881 let pi = self.gpu.stream().alloc_zeros::<i32>(nb)?;
6882 *guard = Some((pv, pi));
6883 }
6884 let (part_v, part_i) = guard.as_mut().unwrap();
6885 let col_view = logits.slice(col * n_vocab..(col + 1) * n_vocab);
6886 let nv = n_vocab as i32;
6887 let nbi = nb as i32;
6888 let cfg1 = LaunchConfig {
6889 grid_dim: (nb as u32, 1, 1),
6890 block_dim: (256, 1, 1),
6891 shared_mem_bytes: 0,
6892 };
6893 let __s_b1 = self.gpu.stream();
6894 let mut b1 = __s_b1.launch_builder(&f1);
6895 b1.arg(&col_view)
6896 .arg(&mut *part_v)
6897 .arg(&mut *part_i)
6898 .arg(&nv);
6899 unsafe {
6900 b1.launch(cfg1)?;
6901 }
6902 let mut tok_view = toks.slice_mut(out_idx..out_idx + 1);
6903 let cfg2 = LaunchConfig {
6904 grid_dim: (1, 1, 1),
6905 block_dim: (256, 1, 1),
6906 shared_mem_bytes: 0,
6907 };
6908 let __s_b2 = self.gpu.stream();
6909 let mut b2 = __s_b2.launch_builder(&f2);
6910 b2.arg(&*part_v).arg(&*part_i).arg(&mut tok_view).arg(&nbi);
6911 unsafe {
6912 b2.launch(cfg2)?;
6913 }
6914 Ok(())
6915 }
6916 pub fn htod_u32_v(&self, v: &[u32]) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
6918 Ok(self.gpu.stream().clone_htod(v)?)
6919 }
6920 pub fn dtoh_u32(&self, d: &CudaSlice<u32>) -> Result<Vec<u32>, Box<dyn std::error::Error>> {
6921 let v = self.gpu.stream().clone_dtoh(d)?;
6922 self.gpu.stream().synchronize()?;
6923 Ok(v)
6924 }
6925 pub fn htod_u32_into(
6929 &self,
6930 dst: &mut CudaSlice<u32>,
6931 src: &[u32],
6932 ) -> Result<(), Box<dyn std::error::Error>> {
6933 let mut view = dst.slice_mut(0..src.len());
6934 self.gpu.stream().memcpy_htod(src, &mut view)?;
6935 Ok(())
6936 }
6937
6938 pub fn htod_i32_into(
6941 &self,
6942 dst: &mut CudaSlice<i32>,
6943 src: &[i32],
6944 ) -> Result<(), Box<dyn std::error::Error>> {
6945 let mut view = dst.slice_mut(0..src.len());
6946 self.gpu.stream().memcpy_htod(src, &mut view)?;
6947 Ok(())
6948 }
6949
6950 pub fn alloc_u32_zeroed(&self, n: usize) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
6951 let s = self.gpu.stream().alloc_zeros::<u32>(n)?;
6952 self.keep_if_capturing(&s);
6953 Ok(s)
6954 }
6955 pub fn embed_gather_device_into(
6958 &self,
6959 embd: &CudaSlice<u8>,
6960 token_d: &CudaSlice<u32>,
6961 x_out: &mut CudaSlice<f32>,
6962 n_embd: usize,
6963 qtype: i32,
6964 row_bytes: usize,
6965 ) -> Result<(), Box<dyn std::error::Error>> {
6966 let f = self.func("embed_gather_u32");
6967 let cfg = LaunchConfig {
6968 grid_dim: (((n_embd as u32 + 255) / 256).max(1), 1, 1),
6969 block_dim: (256, 1, 1),
6970 shared_mem_bytes: 0,
6971 };
6972 let (ne, qt, rb) = (n_embd as i32, qtype, row_bytes as i64);
6973 let __s_b = self.gpu.stream();
6974 let mut b = __s_b.launch_builder(&f);
6975 b.arg(embd)
6976 .arg(token_d)
6977 .arg(x_out)
6978 .arg(&ne)
6979 .arg(&qt)
6980 .arg(&rb);
6981 unsafe {
6982 b.launch(cfg)?;
6983 }
6984 Ok(())
6985 }
6986 pub fn dtoh_i32_one(&self, d: &CudaSlice<i32>) -> Result<i32, Box<dyn std::error::Error>> {
6988 let v = self.gpu.stream().clone_dtoh(d)?;
6989 self.gpu.stream().synchronize()?;
6990 Ok(v[0])
6991 }
6992 pub fn i32_set_k(
6999 &self,
7000 dst: &mut CudaSlice<i32>,
7001 v: i32,
7002 ) -> Result<(), Box<dyn std::error::Error>> {
7003 let f = self.func("i32_set_k");
7004 let cfg = LaunchConfig {
7005 grid_dim: (1, 1, 1),
7006 block_dim: (1, 1, 1),
7007 shared_mem_bytes: 0,
7008 };
7009 let idx = 0i32;
7010 let __s_b = self.gpu.stream();
7011 let mut b = __s_b.launch_builder(&f);
7012 b.arg(dst).arg(&v).arg(&idx);
7013 unsafe {
7014 b.launch(cfg)?;
7015 }
7016 Ok(())
7017 }
7018
7019 pub fn set_i32_one(
7020 &self,
7021 d: &mut CudaSlice<i32>,
7022 v: i32,
7023 ) -> Result<(), Box<dyn std::error::Error>> {
7024 self.gpu.stream().memcpy_htod(&[v], d)?;
7025 Ok(())
7026 }
7027 pub fn set_u32_one(
7030 &self,
7031 d: &mut CudaSlice<u32>,
7032 v: u32,
7033 ) -> Result<(), Box<dyn std::error::Error>> {
7034 self.gpu.stream().memcpy_htod(&[v], d)?;
7035 Ok(())
7036 }
7037 pub fn dtoh_u32_one(&self, d: &CudaSlice<u32>) -> Result<u32, Box<dyn std::error::Error>> {
7039 let v = self.gpu.stream().clone_dtoh(d)?;
7040 self.gpu.stream().synchronize()?;
7041 Ok(v[0])
7042 }
7043 pub fn upload_u8(&self, bytes: &[u8]) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
7045 Ok(self.gpu.stream().clone_htod(bytes)?)
7046 }
7047 pub fn embed_gather_device(
7051 &self,
7052 embd: &CudaSlice<u8>,
7053 token_d: &CudaSlice<u32>,
7054 n_embd: usize,
7055 qtype: i32,
7056 row_bytes: usize,
7057 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7058 let f = self.func("embed_gather_u32");
7059 let mut x = self.alloc_uninit::<f32>(n_embd)?;
7060 let cfg = LaunchConfig {
7061 grid_dim: (((n_embd as u32 + 255) / 256).max(1), 1, 1),
7062 block_dim: (256, 1, 1),
7063 shared_mem_bytes: 0,
7064 };
7065 let (ne, qt, rb) = (n_embd as i32, qtype, row_bytes as i64);
7066 let __s_b = self.gpu.stream();
7067 let mut b = __s_b.launch_builder(&f);
7068 b.arg(embd)
7069 .arg(token_d)
7070 .arg(&mut x)
7071 .arg(&ne)
7072 .arg(&qt)
7073 .arg(&rb);
7074 unsafe {
7075 b.launch(cfg)?;
7076 }
7077 Ok(x)
7078 }
7079
7080 pub fn embed_gather_device_t(
7084 &self,
7085 embd: &CudaSlice<u8>,
7086 tokens: &[u32],
7087 n_embd: usize,
7088 qtype: i32,
7089 row_bytes: usize,
7090 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7091 let t = tokens.len();
7092 let tok_d = self.gpu.stream().clone_htod(tokens)?;
7093 let f = self.func("embed_gather_u32_t");
7094 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7095 let cfg = LaunchConfig {
7096 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7097 block_dim: (256, 1, 1),
7098 shared_mem_bytes: 0,
7099 };
7100 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7101 let __s_b = self.gpu.stream();
7102 let mut b = __s_b.launch_builder(&f);
7103 b.arg(embd)
7104 .arg(&tok_d)
7105 .arg(&mut x)
7106 .arg(&ne)
7107 .arg(&qt)
7108 .arg(&rb)
7109 .arg(&ti);
7110 unsafe {
7111 b.launch(cfg)?;
7112 }
7113 Ok(x)
7114 }
7115
7116 pub fn embed_gather_device_tv(
7121 &self,
7122 embd: &CudaSlice<u8>,
7123 tok_v: &cudarc::driver::CudaView<u32>,
7124 t: usize,
7125 n_embd: usize,
7126 qtype: i32,
7127 row_bytes: usize,
7128 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7129 let f = self.func("embed_gather_u32_t");
7130 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7131 let cfg = LaunchConfig {
7132 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7133 block_dim: (256, 1, 1),
7134 shared_mem_bytes: 0,
7135 };
7136 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7137 let __s_b = self.gpu.stream();
7138 let mut b = __s_b.launch_builder(&f);
7139 b.arg(embd)
7140 .arg(tok_v)
7141 .arg(&mut x)
7142 .arg(&ne)
7143 .arg(&qt)
7144 .arg(&rb)
7145 .arg(&ti);
7146 unsafe {
7147 b.launch(cfg)?;
7148 }
7149 Ok(x)
7150 }
7151
7152 pub fn embed_gather_device_td(
7153 &self,
7154 embd: &CudaSlice<u8>,
7155 tok_d: &CudaSlice<u32>,
7156 t: usize,
7157 n_embd: usize,
7158 qtype: i32,
7159 row_bytes: usize,
7160 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7161 let f = self.func("embed_gather_u32_t");
7162 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7163 let cfg = LaunchConfig {
7164 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7165 block_dim: (256, 1, 1),
7166 shared_mem_bytes: 0,
7167 };
7168 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7169 let __s_b = self.gpu.stream();
7170 let mut b = __s_b.launch_builder(&f);
7171 b.arg(embd)
7172 .arg(tok_d)
7173 .arg(&mut x)
7174 .arg(&ne)
7175 .arg(&qt)
7176 .arg(&rb)
7177 .arg(&ti);
7178 unsafe {
7179 b.launch(cfg)?;
7180 }
7181 Ok(x)
7182 }
7183
7184 #[inline]
7190 fn keep_if_capturing<T: cudarc::driver::DeviceRepr + Send + 'static>(&self, s: &CudaSlice<T>) {
7192 if self
7193 .capture_keep_on
7194 .load(std::sync::atomic::Ordering::Relaxed)
7195 {
7196 self.capture_keep.lock().unwrap().push(Box::new(s.clone()));
7197 }
7198 }
7199
7200 fn alloc_uninit<T: cudarc::driver::DeviceRepr + Send + 'static>(
7201 &self,
7202 n: usize,
7203 ) -> Result<CudaSlice<T>, Box<dyn std::error::Error>> {
7204 let mut s = unsafe { self.gpu.stream().alloc::<T>(n)? };
7205 {
7209 static Z: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7210 if *Z.get_or_init(|| std::env::var("MEMRA_DEBUG_ZERO_ALLOCS").as_deref() == Ok("1")) {
7211 use cudarc::driver::DevicePtrMut;
7213 let n_bytes = s.len() * std::mem::size_of::<T>();
7214 let stream = self.gpu.stream();
7215 let (p_, _g) = s.device_ptr_mut(&stream);
7216 unsafe {
7217 cudarc::driver::sys::cuMemsetD8Async(p_, 0, n_bytes, stream.cu_stream())
7218 .result()?;
7219 }
7220 }
7221 }
7222 self.keep_if_capturing(&s);
7223 Ok(s)
7224 }
7225
7226 pub fn uninit_q8_pair(
7231 &self,
7232 n: usize,
7233 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7234 Ok((
7235 self.alloc_uninit::<i8>(n)?,
7236 self.alloc_uninit::<f32>(n / 32)?,
7237 ))
7238 }
7239
7240 pub fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7241 self.alloc_uninit::<f32>(n)
7242 }
7243
7244 pub fn alloc_i8_uninit(&self, n: usize) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
7246 self.alloc_uninit::<i8>(n)
7247 }
7248
7249 #[allow(clippy::too_many_arguments)]
7253 pub fn rms_norm3(
7254 &self,
7255 x: &CudaSlice<f32>,
7256 w0: &CudaSlice<f32>,
7257 w1: &CudaSlice<f32>,
7258 w2: &CudaSlice<f32>,
7259 d0: &mut CudaSlice<f32>,
7260 d1: &mut CudaSlice<f32>,
7261 d2: &mut CudaSlice<f32>,
7262 ncols: usize,
7263 nrows: usize,
7264 eps: f32,
7265 ) -> Result<(), Box<dyn std::error::Error>> {
7266 let f = self.func("rms_norm3_f32");
7267 let cfg = LaunchConfig {
7268 grid_dim: (nrows as u32, 1, 1),
7269 block_dim: (rms_block(), 1, 1),
7270 shared_mem_bytes: 0,
7271 };
7272 let (nc, e) = (ncols as i32, eps);
7273 let __s_b = self.gpu.stream();
7274 let mut b = __s_b.launch_builder(&f);
7275 b.arg(x)
7276 .arg(w0)
7277 .arg(w1)
7278 .arg(w2)
7279 .arg(d0)
7280 .arg(d1)
7281 .arg(d2)
7282 .arg(&nc)
7283 .arg(&e);
7284 unsafe {
7285 b.launch(cfg)?;
7286 }
7287 Ok(())
7288 }
7289
7290 #[allow(clippy::too_many_arguments)]
7292 pub fn qkvnorm_w_on_prefill(rows: usize, ncols: usize) -> bool {
7295 static WARP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7296 *WARP_ON.get_or_init(|| {
7297 std::env::var("MEMRA_QKVNORM_W")
7298 .map(|v| v != "0")
7299 .unwrap_or(true)
7300 }) && ncols % 4 == 0
7301 && rows >= 64
7302 }
7303
7304 #[allow(clippy::too_many_arguments)]
7307 pub fn rms_norm_qkv_w4b(
7308 &self,
7309 q: &CudaSlice<f32>,
7310 k: &CudaSlice<f32>,
7311 v: &CudaSlice<f32>,
7312 wq: &CudaSlice<f32>,
7313 wk: &CudaSlice<f32>,
7314 wv: &CudaSlice<f32>,
7315 dq: &mut CudaSlice<f32>,
7316 dk: &mut CudaSlice<f32>,
7317 dv: &mut CudaSlice<f32>,
7318 dvb: &mut CudaSlice<u8>,
7319 ncols: usize,
7320 rq: usize,
7321 rk: usize,
7322 eps: f32,
7323 vf16: bool,
7324 ) -> Result<(), Box<dyn std::error::Error>> {
7325 assert!(ncols % 4 == 0 && rq + 2 * rk >= 64);
7326 let f = self.func("rms_norm_qkv_w4b_f32");
7327 let rows = (rq + 2 * rk) as u32;
7328 let cfg = LaunchConfig {
7329 grid_dim: (rows.div_ceil(8), 1, 1),
7330 block_dim: (256, 1, 1),
7331 shared_mem_bytes: 0,
7332 };
7333 let (nc, rqi, rki, rvi, e) = (ncols as i32, rq as i32, rk as i32, rk as i32, eps);
7334 let vf = vf16 as i32;
7335 let __s_b = self.gpu.stream();
7336 let mut b = __s_b.launch_builder(&f);
7337 b.arg(q)
7338 .arg(k)
7339 .arg(v)
7340 .arg(wq)
7341 .arg(wk)
7342 .arg(wv)
7343 .arg(dq)
7344 .arg(dk)
7345 .arg(dv)
7346 .arg(&mut *dvb)
7347 .arg(&nc)
7348 .arg(&rqi)
7349 .arg(&rki)
7350 .arg(&rvi)
7351 .arg(&e)
7352 .arg(&vf);
7353 unsafe {
7354 b.launch(cfg)?;
7355 }
7356 Ok(())
7357 }
7358
7359 pub fn rms_norm_qkv(
7360 &self,
7361 q: &CudaSlice<f32>,
7362 k: &CudaSlice<f32>,
7363 v: &CudaSlice<f32>,
7364 wq: &CudaSlice<f32>,
7365 wk: &CudaSlice<f32>,
7366 wv: &CudaSlice<f32>,
7367 dq: &mut CudaSlice<f32>,
7368 dk: &mut CudaSlice<f32>,
7369 dv: &mut CudaSlice<f32>,
7370 ncols: usize,
7371 rq: usize,
7372 rk: usize,
7373 eps: f32,
7374 ) -> Result<(), Box<dyn std::error::Error>> {
7375 static WARP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7379 let warp_on = *WARP_ON.get_or_init(|| {
7380 std::env::var("MEMRA_QKVNORM_W")
7381 .map(|v| v != "0")
7382 .unwrap_or(true)
7383 });
7384 if warp_on && ncols % 4 == 0 && rq + 2 * rk >= 64 {
7387 let f = self.func("rms_norm_qkv_w4_f32");
7388 let rows = (rq + 2 * rk) as u32;
7389 let cfg = LaunchConfig {
7390 grid_dim: (rows.div_ceil(8), 1, 1),
7391 block_dim: (256, 1, 1),
7392 shared_mem_bytes: 0,
7393 };
7394 let (nc, rqi, rki, rvi, e) = (ncols as i32, rq as i32, rk as i32, rk as i32, eps);
7395 let __s_b = self.gpu.stream();
7396 let mut b = __s_b.launch_builder(&f);
7397 b.arg(q)
7398 .arg(k)
7399 .arg(v)
7400 .arg(wq)
7401 .arg(wk)
7402 .arg(wv)
7403 .arg(dq)
7404 .arg(dk)
7405 .arg(dv)
7406 .arg(&nc)
7407 .arg(&rqi)
7408 .arg(&rki)
7409 .arg(&rvi)
7410 .arg(&e);
7411 unsafe {
7412 b.launch(cfg)?;
7413 }
7414 return Ok(());
7415 }
7416 let f = self.func("rms_norm_qkv_f32");
7417 let grid = (rq + 2 * rk) as u32;
7418 let cfg = LaunchConfig {
7419 grid_dim: (grid, 1, 1),
7420 block_dim: (rms_block(), 1, 1),
7421 shared_mem_bytes: 0,
7422 };
7423 let (nc, rqi, rki, e) = (ncols as i32, rq as i32, rk as i32, eps);
7424 let __s_b = self.gpu.stream();
7425 let mut b = __s_b.launch_builder(&f);
7426 b.arg(q)
7427 .arg(k)
7428 .arg(v)
7429 .arg(wq)
7430 .arg(wk)
7431 .arg(wv)
7432 .arg(dq)
7433 .arg(dk)
7434 .arg(dv)
7435 .arg(&nc)
7436 .arg(&rqi)
7437 .arg(&rki)
7438 .arg(&e);
7439 unsafe {
7440 b.launch(cfg)?;
7441 }
7442 Ok(())
7443 }
7444
7445 #[allow(clippy::too_many_arguments)]
7447 pub fn rms_norm2x(
7448 &self,
7449 a: &CudaSlice<f32>,
7450 bb: &CudaSlice<f32>,
7451 wa: &CudaSlice<f32>,
7452 wb: &CudaSlice<f32>,
7453 da: &mut CudaSlice<f32>,
7454 db: &mut CudaSlice<f32>,
7455 ncols: usize,
7456 nrows: usize,
7457 eps: f32,
7458 ) -> Result<(), Box<dyn std::error::Error>> {
7459 let f = self.func("rms_norm2x_f32");
7460 let cfg = LaunchConfig {
7461 grid_dim: (2 * nrows as u32, 1, 1),
7462 block_dim: (rms_block(), 1, 1),
7463 shared_mem_bytes: 0,
7464 };
7465 let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
7466 let __s_b = self.gpu.stream();
7467 let mut b = __s_b.launch_builder(&f);
7468 b.arg(a)
7469 .arg(bb)
7470 .arg(wa)
7471 .arg(wb)
7472 .arg(da)
7473 .arg(db)
7474 .arg(&nc)
7475 .arg(&nr)
7476 .arg(&e);
7477 unsafe {
7478 b.launch(cfg)?;
7479 }
7480 Ok(())
7481 }
7482
7483 pub fn softcap(
7485 &self,
7486 y: &mut CudaSlice<f32>,
7487 cap: f32,
7488 n: usize,
7489 ) -> Result<(), Box<dyn std::error::Error>> {
7490 let f = self.func("softcap_f32");
7491 let cfg = LaunchConfig::for_num_elems(n as u32);
7492 let ni = n as i32;
7493 let __s_b = self.gpu.stream();
7494 let mut b = __s_b.launch_builder(&f);
7495 b.arg(y).arg(&cap).arg(&ni);
7496 unsafe {
7497 b.launch(cfg)?;
7498 }
7499 Ok(())
7500 }
7501
7502 pub fn mask_ids_rows(
7505 &self,
7506 y: &mut CudaSlice<f32>,
7507 ids: &CudaSlice<i32>,
7508 n_ids: usize,
7509 n_vocab: usize,
7510 t: usize,
7511 ) -> Result<(), Box<dyn std::error::Error>> {
7512 let f = self.func("mask_ids_rows_f32");
7513 let cfg = LaunchConfig::for_num_elems((n_ids * t) as u32);
7514 let (ni, nv, ti) = (n_ids as i32, n_vocab as i32, t as i32);
7515 let __s_b = self.gpu.stream();
7516 let mut b = __s_b.launch_builder(&f);
7517 b.arg(y).arg(ids).arg(&ni).arg(&nv).arg(&ti);
7518 unsafe {
7519 b.launch(cfg)?;
7520 }
7521 Ok(())
7522 }
7523
7524 #[allow(clippy::too_many_arguments)]
7526 pub fn add_scale_rms_norm(
7527 &self,
7528 a: &CudaSlice<f32>,
7529 b_in: &CudaSlice<f32>,
7530 c: f32,
7531 w: &CudaSlice<f32>,
7532 res: &mut CudaSlice<f32>,
7533 dst: &mut CudaSlice<f32>,
7534 ncols: usize,
7535 nrows: usize,
7536 eps: f32,
7537 ) -> Result<(), Box<dyn std::error::Error>> {
7538 let f = self.func("add_scale_rms_norm_f32");
7539 let cfg = LaunchConfig {
7540 grid_dim: (nrows as u32, 1, 1),
7541 block_dim: (rms_block(), 1, 1),
7542 shared_mem_bytes: 0,
7543 };
7544 let (nc, e2) = (ncols as i32, eps);
7545 let __s_b = self.gpu.stream();
7546 let mut b = __s_b.launch_builder(&f);
7547 b.arg(a)
7548 .arg(b_in)
7549 .arg(&c)
7550 .arg(w)
7551 .arg(res)
7552 .arg(dst)
7553 .arg(&nc)
7554 .arg(&e2);
7555 unsafe {
7556 b.launch(cfg)?;
7557 }
7558 Ok(())
7559 }
7560
7561 #[allow(clippy::too_many_arguments)]
7564 pub fn add_scale_rms_norm_q8_1(
7565 &self,
7566 a: &CudaSlice<f32>,
7567 b_in: &CudaSlice<f32>,
7568 c: f32,
7569 w: &CudaSlice<f32>,
7570 res: &mut CudaSlice<f32>,
7571 ncols: usize,
7572 nrows: usize,
7573 eps: f32,
7574 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7575 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7576 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7577 let (nc, e2) = (ncols as i32, eps);
7578 if Self::pdl_on() && Self::pdl_wb_on() {
7579 {
7580 use cudarc::driver::{DevicePtr, DevicePtrMut};
7581 let s = &self.gpu.stream();
7582 let (pa, _g0) = a.device_ptr(s);
7583 let (pb, _g1) = b_in.device_ptr(s);
7584 let (pw, _g2) = w.device_ptr(s);
7585 let (pr, _g3) = res.device_ptr_mut(s);
7586 let (pq, _g4) = out_q.device_ptr_mut(s);
7587 let (pd, _g5) = out_d.device_ptr_mut(s);
7588 let mut ps = [
7589 &pa as *const _ as *mut std::ffi::c_void,
7590 &pb as *const _ as *mut _,
7591 &c as *const _ as *mut _,
7592 &pw as *const _ as *mut _,
7593 &pr as *const _ as *mut _,
7594 &pq as *const _ as *mut _,
7595 &pd as *const _ as *mut _,
7596 &nc as *const _ as *mut _,
7597 &e2 as *const _ as *mut _,
7598 ];
7599 unsafe {
7600 self.launch_pdl(
7601 "add_scale_rms_norm_q8_1",
7602 (nrows as u32, 1, 1),
7603 (rms_block(), 1, 1),
7604 &mut ps,
7605 )?;
7606 }
7607 }
7608 return Ok((out_q, out_d));
7609 }
7610 let f = self.func("add_scale_rms_norm_q8_1");
7611 let cfg = LaunchConfig {
7612 grid_dim: (nrows as u32, 1, 1),
7613 block_dim: (rms_block(), 1, 1),
7614 shared_mem_bytes: 0,
7615 };
7616 let __s_b = self.gpu.stream();
7617 let mut b = __s_b.launch_builder(&f);
7618 b.arg(a)
7619 .arg(b_in)
7620 .arg(&c)
7621 .arg(w)
7622 .arg(res)
7623 .arg(&mut out_q)
7624 .arg(&mut out_d)
7625 .arg(&nc)
7626 .arg(&e2);
7627 unsafe {
7628 b.launch(cfg)?;
7629 }
7630 Ok((out_q, out_d))
7631 }
7632
7633 #[allow(clippy::too_many_arguments)]
7635 pub fn add_scale_rms_norm_q8_1_into(
7636 &self,
7637 a: &CudaSlice<f32>,
7638 b_in: &CudaSlice<f32>,
7639 c: f32,
7640 w: &CudaSlice<f32>,
7641 res: &mut CudaSlice<f32>,
7642 ncols: usize,
7643 nrows: usize,
7644 eps: f32,
7645 out_q: &mut CudaSlice<i8>,
7646 out_d: &mut CudaSlice<f32>,
7647 ) -> Result<(), Box<dyn std::error::Error>> {
7648 debug_assert!(out_q.len() >= nrows * ncols && out_d.len() >= nrows * (ncols / 32));
7649 let (nc, e2) = (ncols as i32, eps);
7650 if Self::pdl_on() && Self::pdl_wb_on() {
7651 use cudarc::driver::{DevicePtr, DevicePtrMut};
7652 let s = &self.gpu.stream();
7653 let (pa, _g0) = a.device_ptr(s);
7654 let (pb, _g1) = b_in.device_ptr(s);
7655 let (pw, _g2) = w.device_ptr(s);
7656 let (pr, _g3) = res.device_ptr_mut(s);
7657 let (pq, _g4) = out_q.device_ptr_mut(s);
7658 let (pd, _g5) = out_d.device_ptr_mut(s);
7659 let mut ps = [
7660 &pa as *const _ as *mut std::ffi::c_void,
7661 &pb as *const _ as *mut _,
7662 &c as *const _ as *mut _,
7663 &pw as *const _ as *mut _,
7664 &pr as *const _ as *mut _,
7665 &pq as *const _ as *mut _,
7666 &pd as *const _ as *mut _,
7667 &nc as *const _ as *mut _,
7668 &e2 as *const _ as *mut _,
7669 ];
7670 unsafe {
7671 self.launch_pdl(
7672 "add_scale_rms_norm_q8_1",
7673 (nrows as u32, 1, 1),
7674 (rms_block(), 1, 1),
7675 &mut ps,
7676 )?;
7677 }
7678 return Ok(());
7679 }
7680 let f = self.func("add_scale_rms_norm_q8_1");
7681 let cfg = LaunchConfig {
7682 grid_dim: (nrows as u32, 1, 1),
7683 block_dim: (rms_block(), 1, 1),
7684 shared_mem_bytes: 0,
7685 };
7686 let __s_b = self.gpu.stream();
7687 let mut b = __s_b.launch_builder(&f);
7688 b.arg(a)
7689 .arg(b_in)
7690 .arg(&c)
7691 .arg(w)
7692 .arg(res)
7693 .arg(&mut *out_q)
7694 .arg(&mut *out_d)
7695 .arg(&nc)
7696 .arg(&e2);
7697 unsafe {
7698 b.launch(cfg)?;
7699 }
7700 Ok(())
7701 }
7702
7703 #[allow(clippy::too_many_arguments)]
7706 pub fn rms_pre_add_scale_rms_norm_q8_1(
7707 &self,
7708 a: &CudaSlice<f32>,
7709 wa: &CudaSlice<f32>,
7710 b_in: &CudaSlice<f32>,
7711 c: f32,
7712 w: &CudaSlice<f32>,
7713 res: &mut CudaSlice<f32>,
7714 ncols: usize,
7715 nrows: usize,
7716 eps: f32,
7717 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7718 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7719 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7720 let (nc, e2) = (ncols as i32, eps);
7721 if Self::pdl_on() {
7722 {
7723 use cudarc::driver::{DevicePtr, DevicePtrMut};
7724 let s = &self.gpu.stream();
7725 let (pa, _g0) = a.device_ptr(s);
7726 let (pwa, _g1) = wa.device_ptr(s);
7727 let (pb, _g2) = b_in.device_ptr(s);
7728 let (pw, _g3) = w.device_ptr(s);
7729 let (pr, _g4) = res.device_ptr_mut(s);
7730 let (pq, _g5) = out_q.device_ptr_mut(s);
7731 let (pd, _g6) = out_d.device_ptr_mut(s);
7732 let mut ps = [
7733 &pa as *const _ as *mut std::ffi::c_void,
7734 &pwa as *const _ as *mut _,
7735 &pb as *const _ as *mut _,
7736 &c as *const _ as *mut _,
7737 &pw as *const _ as *mut _,
7738 &pr as *const _ as *mut _,
7739 &pq as *const _ as *mut _,
7740 &pd as *const _ as *mut _,
7741 &nc as *const _ as *mut _,
7742 &e2 as *const _ as *mut _,
7743 ];
7744 unsafe {
7745 self.launch_pdl(
7746 "rms_pre_add_scale_rms_norm_q8_1",
7747 (nrows as u32, 1, 1),
7748 (rms_block(), 1, 1),
7749 &mut ps,
7750 )?;
7751 }
7752 }
7753 return Ok((out_q, out_d));
7754 }
7755 let f = self.func("rms_pre_add_scale_rms_norm_q8_1");
7756 let cfg = LaunchConfig {
7757 grid_dim: (nrows as u32, 1, 1),
7758 block_dim: (rms_block(), 1, 1),
7759 shared_mem_bytes: 0,
7760 };
7761 let __s_b = self.gpu.stream();
7762 let mut b = __s_b.launch_builder(&f);
7763 b.arg(a)
7764 .arg(wa)
7765 .arg(b_in)
7766 .arg(&c)
7767 .arg(w)
7768 .arg(res)
7769 .arg(&mut out_q)
7770 .arg(&mut out_d)
7771 .arg(&nc)
7772 .arg(&e2);
7773 unsafe {
7774 b.launch(cfg)?;
7775 }
7776 Ok((out_q, out_d))
7777 }
7778
7779 pub fn gelu_tanh_mul_q8_1(
7782 &self,
7783 gate: &CudaSlice<f32>,
7784 up: &cudarc::driver::CudaView<f32>,
7785 act: &mut CudaSlice<f32>,
7786 ncols: usize,
7787 nrows: usize,
7788 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7789 debug_assert!(ncols % 128 == 0);
7790 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7791 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7792 let nc = ncols as i32;
7793 if Self::pdl_on() {
7794 {
7795 use cudarc::driver::{DevicePtr, DevicePtrMut};
7796 let s = &self.gpu.stream();
7797 let (pg, _g0) = gate.device_ptr(s);
7798 let (pu, _g1) = up.device_ptr(s);
7799 let (pact, _g2) = act.device_ptr_mut(s);
7800 let (pq, _g3) = out_q.device_ptr_mut(s);
7801 let (pd, _g4) = out_d.device_ptr_mut(s);
7802 let mut ps = [
7803 &pg as *const _ as *mut std::ffi::c_void,
7804 &pu as *const _ as *mut _,
7805 &pact as *const _ as *mut _,
7806 &pq as *const _ as *mut _,
7807 &pd as *const _ as *mut _,
7808 &nc as *const _ as *mut _,
7809 ];
7810 unsafe {
7811 self.launch_pdl(
7812 "gelu_tanh_mul_q8_1",
7813 (nrows as u32, 1, 1),
7814 (rms_block(), 1, 1),
7815 &mut ps,
7816 )?;
7817 }
7818 }
7819 return Ok((out_q, out_d));
7820 }
7821 let f = self.func("gelu_tanh_mul_q8_1");
7822 let cfg = LaunchConfig {
7823 grid_dim: (nrows as u32, 1, 1),
7824 block_dim: (rms_block(), 1, 1),
7825 shared_mem_bytes: 0,
7826 };
7827 let __s_b = self.gpu.stream();
7828 let mut b = __s_b.launch_builder(&f);
7829 b.arg(gate)
7830 .arg(up)
7831 .arg(act)
7832 .arg(&mut out_q)
7833 .arg(&mut out_d)
7834 .arg(&nc);
7835 unsafe {
7836 b.launch(cfg)?;
7837 }
7838 Ok((out_q, out_d))
7839 }
7840
7841 #[allow(clippy::too_many_arguments)]
7843 pub fn gelu_tanh_mul_q8_1_into(
7844 &self,
7845 gate: &CudaSlice<f32>,
7846 up: &cudarc::driver::CudaView<f32>,
7847 act: &mut CudaSlice<f32>,
7848 ncols: usize,
7849 nrows: usize,
7850 out_q: &mut CudaSlice<i8>,
7851 out_d: &mut CudaSlice<f32>,
7852 ) -> Result<(), Box<dyn std::error::Error>> {
7853 debug_assert!(ncols % 128 == 0);
7854 debug_assert!(out_q.len() >= nrows * ncols && out_d.len() >= nrows * (ncols / 32));
7855 let nc = ncols as i32;
7856 if Self::pdl_on() {
7857 use cudarc::driver::{DevicePtr, DevicePtrMut};
7858 let s = &self.gpu.stream();
7859 let (pg, _g0) = gate.device_ptr(s);
7860 let (pu, _g1) = up.device_ptr(s);
7861 let (pact, _g2) = act.device_ptr_mut(s);
7862 let (pq, _g3) = out_q.device_ptr_mut(s);
7863 let (pd, _g4) = out_d.device_ptr_mut(s);
7864 let mut ps = [
7865 &pg as *const _ as *mut std::ffi::c_void,
7866 &pu as *const _ as *mut _,
7867 &pact as *const _ as *mut _,
7868 &pq as *const _ as *mut _,
7869 &pd as *const _ as *mut _,
7870 &nc as *const _ as *mut _,
7871 ];
7872 unsafe {
7873 self.launch_pdl(
7874 "gelu_tanh_mul_q8_1",
7875 (nrows as u32, 1, 1),
7876 (rms_block(), 1, 1),
7877 &mut ps,
7878 )?;
7879 }
7880 return Ok(());
7881 }
7882 let f = self.func("gelu_tanh_mul_q8_1");
7883 let cfg = LaunchConfig {
7884 grid_dim: (nrows as u32, 1, 1),
7885 block_dim: (rms_block(), 1, 1),
7886 shared_mem_bytes: 0,
7887 };
7888 let __s_b = self.gpu.stream();
7889 let mut b = __s_b.launch_builder(&f);
7890 b.arg(gate)
7891 .arg(up)
7892 .arg(&mut *act)
7893 .arg(&mut *out_q)
7894 .arg(&mut *out_d)
7895 .arg(&nc);
7896 unsafe {
7897 b.launch(cfg)?;
7898 }
7899 Ok(())
7900 }
7901
7902 #[allow(clippy::too_many_arguments)]
7904 pub fn add_rms_norm3_q8z(
7905 &self,
7906 a: &CudaSlice<f32>,
7907 b_in: &CudaSlice<f32>,
7908 w0: &CudaSlice<f32>,
7909 w1: &CudaSlice<f32>,
7910 w2: &CudaSlice<f32>,
7911 res: &mut CudaSlice<f32>,
7912 out1: &mut CudaSlice<f32>,
7913 ncols: usize,
7914 nrows: usize,
7915 eps: f32,
7916 ) -> Result<
7917 (
7918 (CudaSlice<i8>, CudaSlice<f32>),
7919 (CudaSlice<i8>, CudaSlice<f32>),
7920 ),
7921 Box<dyn std::error::Error>,
7922 > {
7923 let mut q0 = self.alloc_uninit::<i8>(nrows * ncols)?;
7924 let mut d0 = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7925 let mut q2 = self.alloc_uninit::<i8>(nrows * ncols)?;
7926 let mut d2 = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7927 let f = self.func("add_rms_norm3_q8z_f32");
7928 let cfg = LaunchConfig {
7929 grid_dim: (nrows as u32, 1, 1),
7930 block_dim: (rms_block(), 1, 1),
7931 shared_mem_bytes: 0,
7932 };
7933 let (nc, e2) = (ncols as i32, eps);
7934 let __s_b = self.gpu.stream();
7935 let mut b = __s_b.launch_builder(&f);
7936 b.arg(a)
7937 .arg(b_in)
7938 .arg(w0)
7939 .arg(w1)
7940 .arg(w2)
7941 .arg(res)
7942 .arg(&mut q0)
7943 .arg(&mut d0)
7944 .arg(out1)
7945 .arg(&mut q2)
7946 .arg(&mut d2)
7947 .arg(&nc)
7948 .arg(&e2);
7949 unsafe {
7950 b.launch(cfg)?;
7951 }
7952 Ok(((q0, d0), (q2, d2)))
7953 }
7954
7955 #[allow(clippy::too_many_arguments)]
7957 pub fn add_rms_norm3(
7958 &self,
7959 a: &CudaSlice<f32>,
7960 b_in: &CudaSlice<f32>,
7961 w0: &CudaSlice<f32>,
7962 w1: &CudaSlice<f32>,
7963 w2: &CudaSlice<f32>,
7964 res: &mut CudaSlice<f32>,
7965 d0: &mut CudaSlice<f32>,
7966 d1: &mut CudaSlice<f32>,
7967 d2: &mut CudaSlice<f32>,
7968 ncols: usize,
7969 nrows: usize,
7970 eps: f32,
7971 ) -> Result<(), Box<dyn std::error::Error>> {
7972 let f = self.func("add_rms_norm3_f32");
7973 let cfg = LaunchConfig {
7974 grid_dim: (nrows as u32, 1, 1),
7975 block_dim: (rms_block(), 1, 1),
7976 shared_mem_bytes: 0,
7977 };
7978 let (nc, e2) = (ncols as i32, eps);
7979 let __s_b = self.gpu.stream();
7980 let mut b = __s_b.launch_builder(&f);
7981 b.arg(a)
7982 .arg(b_in)
7983 .arg(w0)
7984 .arg(w1)
7985 .arg(w2)
7986 .arg(res)
7987 .arg(d0)
7988 .arg(d1)
7989 .arg(d2)
7990 .arg(&nc)
7991 .arg(&e2);
7992 unsafe {
7993 b.launch(cfg)?;
7994 }
7995 Ok(())
7996 }
7997
7998 pub fn add_scale(
8000 &self,
8001 a: &CudaSlice<f32>,
8002 b_in: &CudaSlice<f32>,
8003 c: f32,
8004 dst: &mut CudaSlice<f32>,
8005 n: usize,
8006 ) -> Result<(), Box<dyn std::error::Error>> {
8007 let f = self.func("add_scale_f32");
8008 let cfg = LaunchConfig::for_num_elems(n as u32);
8009 let ni = n as i32;
8010 let __s_b = self.gpu.stream();
8011 let mut b = __s_b.launch_builder(&f);
8012 b.arg(a).arg(b_in).arg(&c).arg(dst).arg(&ni);
8013 unsafe {
8014 b.launch(cfg)?;
8015 }
8016 Ok(())
8017 }
8018
8019 pub fn rms_norm(
8020 &self,
8021 x: &CudaSlice<f32>,
8022 w: &CudaSlice<f32>,
8023 dst: &mut CudaSlice<f32>,
8024 ncols: usize,
8025 nrows: usize,
8026 eps: f32,
8027 ) -> Result<(), Box<dyn std::error::Error>> {
8028 let (nc, e) = (ncols as i32, eps);
8029 if Self::pdl_on() && Self::pdl_wb_on() {
8030 use cudarc::driver::{DevicePtr, DevicePtrMut};
8031 let s = &self.gpu.stream();
8032 let (px, _g0) = x.device_ptr(s);
8033 let (pw, _g1) = w.device_ptr(s);
8034 let (pd, _g2) = dst.device_ptr_mut(s);
8035 let mut ps = [
8036 &px as *const _ as *mut std::ffi::c_void,
8037 &pw as *const _ as *mut _,
8038 &pd as *const _ as *mut _,
8039 &nc as *const _ as *mut _,
8040 &e as *const _ as *mut _,
8041 ];
8042 unsafe {
8043 self.launch_pdl(
8044 "rms_norm_f32",
8045 (nrows as u32, 1, 1),
8046 (rms_block(), 1, 1),
8047 &mut ps,
8048 )?;
8049 }
8050 return Ok(());
8051 }
8052 let f = self.func("rms_norm_f32");
8053 let cfg = LaunchConfig {
8054 grid_dim: (nrows as u32, 1, 1),
8055 block_dim: (rms_block(), 1, 1),
8056 shared_mem_bytes: 0,
8057 };
8058 let __s_b = self.gpu.stream();
8059 let mut b = __s_b.launch_builder(&f);
8060 b.arg(x).arg(w).arg(dst).arg(&nc).arg(&e);
8061 unsafe {
8062 b.launch(cfg)?;
8063 }
8064 Ok(())
8065 }
8066
8067 pub fn rms_norm_decode(
8075 &self,
8076 x: &CudaSlice<f32>,
8077 w: &CudaSlice<f32>,
8078 dst: &mut CudaSlice<f32>,
8079 ncols: usize,
8080 nrows: usize,
8081 eps: f32,
8082 ) -> Result<(), Box<dyn std::error::Error>> {
8083 let f = self.func("rms_norm_f32");
8084 let cfg = LaunchConfig {
8085 grid_dim: (nrows as u32, 1, 1),
8086 block_dim: (1024, 1, 1),
8087 shared_mem_bytes: 0,
8088 };
8089 let (nc, e) = (ncols as i32, eps);
8090 let __s_b = self.gpu.stream();
8091 let mut b = __s_b.launch_builder(&f);
8092 b.arg(x).arg(w).arg(dst).arg(&nc).arg(&e);
8093 unsafe {
8094 b.launch(cfg)?;
8095 }
8096 Ok(())
8097 }
8098
8099 pub fn rms_norm_q8_1(
8103 &self,
8104 x: &CudaSlice<f32>,
8105 w: &CudaSlice<f32>,
8106 ncols: usize,
8107 nrows: usize,
8108 eps: f32,
8109 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8110 let nblk = ncols / 32;
8111 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
8112 let mut d = self.alloc_uninit::<f32>(nrows * nblk)?;
8113 let (nc, e) = (ncols as i32, eps);
8114 if Self::pdl_on() {
8115 {
8116 use cudarc::driver::{DevicePtr, DevicePtrMut};
8117 let s = &self.gpu.stream();
8118 let (px, _g0) = x.device_ptr(s);
8119 let (pw, _g1) = w.device_ptr(s);
8120 let (pq, _g2) = q.device_ptr_mut(s);
8121 let (pd, _g3) = d.device_ptr_mut(s);
8122 let mut ps = [
8123 &px as *const _ as *mut std::ffi::c_void,
8124 &pw as *const _ as *mut _,
8125 &pq as *const _ as *mut _,
8126 &pd as *const _ as *mut _,
8127 &nc as *const _ as *mut _,
8128 &e as *const _ as *mut _,
8129 ];
8130 unsafe {
8131 self.launch_pdl("rms_norm_q8_1", (nrows as u32, 1, 1), (1024, 1, 1), &mut ps)?;
8132 }
8133 }
8134 return Ok((q, d));
8135 }
8136 let f = self.func("rms_norm_q8_1");
8137 let cfg = LaunchConfig {
8140 grid_dim: (nrows as u32, 1, 1),
8141 block_dim: (1024, 1, 1),
8142 shared_mem_bytes: 0,
8143 };
8144 let __s_b = self.gpu.stream();
8145 let mut b = __s_b.launch_builder(&f);
8146 b.arg(x).arg(w).arg(&mut q).arg(&mut d).arg(&nc).arg(&e);
8147 unsafe {
8148 b.launch(cfg)?;
8149 }
8150 Ok((q, d))
8151 }
8152
8153 pub fn rms_norm_q8_1_into(
8156 &self,
8157 x: &CudaSlice<f32>,
8158 w: &CudaSlice<f32>,
8159 ncols: usize,
8160 nrows: usize,
8161 eps: f32,
8162 q: &mut CudaSlice<i8>,
8163 d: &mut CudaSlice<f32>,
8164 ) -> Result<(), Box<dyn std::error::Error>> {
8165 let nblk = ncols / 32;
8166 debug_assert!(q.len() >= nrows * ncols && d.len() >= nrows * nblk);
8167 let (nc, e) = (ncols as i32, eps);
8168 if Self::pdl_on() {
8169 use cudarc::driver::{DevicePtr, DevicePtrMut};
8170 let s = &self.gpu.stream();
8171 let (px, _g0) = x.device_ptr(s);
8172 let (pw, _g1) = w.device_ptr(s);
8173 let (pq, _g2) = q.device_ptr_mut(s);
8174 let (pd, _g3) = d.device_ptr_mut(s);
8175 let mut ps = [
8176 &px as *const _ as *mut std::ffi::c_void,
8177 &pw as *const _ as *mut _,
8178 &pq as *const _ as *mut _,
8179 &pd as *const _ as *mut _,
8180 &nc as *const _ as *mut _,
8181 &e as *const _ as *mut _,
8182 ];
8183 unsafe {
8184 self.launch_pdl("rms_norm_q8_1", (nrows as u32, 1, 1), (1024, 1, 1), &mut ps)?;
8185 }
8186 return Ok(());
8187 }
8188 let f = self.func("rms_norm_q8_1");
8189 let cfg = LaunchConfig {
8190 grid_dim: (nrows as u32, 1, 1),
8191 block_dim: (1024, 1, 1),
8192 shared_mem_bytes: 0,
8193 };
8194 let __s_b = self.gpu.stream();
8195 let mut b = __s_b.launch_builder(&f);
8196 b.arg(x).arg(w).arg(&mut *q).arg(&mut *d).arg(&nc).arg(&e);
8197 unsafe {
8198 b.launch(cfg)?;
8199 }
8200 Ok(())
8201 }
8202
8203 pub fn quantize_q8_1_into(
8205 &self,
8206 x: &CudaSlice<f32>,
8207 m: usize,
8208 in_f: usize,
8209 q: &mut CudaSlice<i8>,
8210 d: &mut CudaSlice<f32>,
8211 ) -> Result<(), Box<dyn std::error::Error>> {
8212 let nblk = in_f / 32;
8213 debug_assert!(q.len() >= m * in_f && d.len() >= m * nblk);
8214 let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
8215 let (inf, mi) = (in_f as i32, m as i32);
8216 if Self::pdl_on() && Self::pdl_wb_on() {
8217 use cudarc::driver::{DevicePtr, DevicePtrMut};
8218 let s = &self.gpu.stream();
8219 let (px, _g0) = x.device_ptr(s);
8220 let (pq, _g1) = q.device_ptr_mut(s);
8221 let (pd, _g2) = d.device_ptr_mut(s);
8222 let mut ps = [
8223 &px as *const _ as *mut std::ffi::c_void,
8224 &pq as *const _ as *mut _,
8225 &pd as *const _ as *mut _,
8226 &inf as *const _ as *mut _,
8227 &mi as *const _ as *mut _,
8228 ];
8229 unsafe {
8230 self.launch_pdl("quantize_q8_1", cfg.grid_dim, cfg.block_dim, &mut ps)?;
8231 }
8232 return Ok(());
8233 }
8234 let f = self.func("quantize_q8_1");
8235 let __s_b = self.gpu.stream();
8236 let mut b = __s_b.launch_builder(&f);
8237 b.arg(x).arg(&mut *q).arg(&mut *d).arg(&inf).arg(&mi);
8238 unsafe {
8239 b.launch(cfg)?;
8240 }
8241 Ok(())
8242 }
8243
8244 pub fn add_rms_norm_q8_1(
8248 &self,
8249 a: &CudaSlice<f32>,
8250 b_in: &CudaSlice<f32>,
8251 w: &CudaSlice<f32>,
8252 res: &mut CudaSlice<f32>,
8253 ncols: usize,
8254 nrows: usize,
8255 eps: f32,
8256 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8257 let nblk = ncols / 32;
8258 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
8259 let mut d = self.alloc_uninit::<f32>(nrows * nblk)?;
8260 let f = self.func("add_rms_norm_q8_1");
8261 let cfg = LaunchConfig {
8263 grid_dim: (nrows as u32, 1, 1),
8264 block_dim: (1024, 1, 1),
8265 shared_mem_bytes: 0,
8266 };
8267 let (nc, e) = (ncols as i32, eps);
8268 let __s_bld = self.gpu.stream();
8269 let mut bld = __s_bld.launch_builder(&f);
8270 bld.arg(a)
8271 .arg(b_in)
8272 .arg(w)
8273 .arg(res)
8274 .arg(&mut q)
8275 .arg(&mut d)
8276 .arg(&nc)
8277 .arg(&e);
8278 unsafe {
8279 bld.launch(cfg)?;
8280 }
8281 Ok((q, d))
8282 }
8283
8284 pub fn add_rms_norm(
8288 &self,
8289 a: &CudaSlice<f32>,
8290 b: &CudaSlice<f32>,
8291 w: &CudaSlice<f32>,
8292 res: &mut CudaSlice<f32>,
8293 dst: &mut CudaSlice<f32>,
8294 ncols: usize,
8295 nrows: usize,
8296 eps: f32,
8297 ) -> Result<(), Box<dyn std::error::Error>> {
8298 let (nc, e) = (ncols as i32, eps);
8299 if Self::pdl_on() && Self::pdl_wb_on() {
8300 use cudarc::driver::{DevicePtr, DevicePtrMut};
8301 let s = &self.gpu.stream();
8302 let (pa, _g0) = a.device_ptr(s);
8303 let (pb, _g1) = b.device_ptr(s);
8304 let (pw, _g2) = w.device_ptr(s);
8305 let (pr, _g3) = res.device_ptr_mut(s);
8306 let (pd, _g4) = dst.device_ptr_mut(s);
8307 let mut ps = [
8308 &pa as *const _ as *mut std::ffi::c_void,
8309 &pb as *const _ as *mut _,
8310 &pw as *const _ as *mut _,
8311 &pr as *const _ as *mut _,
8312 &pd as *const _ as *mut _,
8313 &nc as *const _ as *mut _,
8314 &e as *const _ as *mut _,
8315 ];
8316 unsafe {
8317 self.launch_pdl(
8318 "add_rms_norm_f32",
8319 (nrows as u32, 1, 1),
8320 (rms_block(), 1, 1),
8321 &mut ps,
8322 )?;
8323 }
8324 return Ok(());
8325 }
8326 let f = self.func("add_rms_norm_f32");
8327 let cfg = LaunchConfig {
8328 grid_dim: (nrows as u32, 1, 1),
8329 block_dim: (rms_block(), 1, 1),
8330 shared_mem_bytes: 0,
8331 };
8332 let __s_b2 = self.gpu.stream();
8333 let mut b2 = __s_b2.launch_builder(&f);
8334 b2.arg(a)
8335 .arg(b)
8336 .arg(w)
8337 .arg(&mut *res)
8338 .arg(&mut *dst)
8339 .arg(&nc)
8340 .arg(&e);
8341 unsafe {
8342 b2.launch(cfg)?;
8343 }
8344 Ok(())
8345 }
8346
8347 #[allow(clippy::too_many_arguments)]
8350 pub fn rms_pre_add_rms_norm(
8351 &self,
8352 a: &CudaSlice<f32>,
8353 wa: &CudaSlice<f32>,
8354 b: &CudaSlice<f32>,
8355 w: &CudaSlice<f32>,
8356 res: &mut CudaSlice<f32>,
8357 dst: &mut CudaSlice<f32>,
8358 ncols: usize,
8359 nrows: usize,
8360 eps: f32,
8361 ) -> Result<(), Box<dyn std::error::Error>> {
8362 let f = self.func("rms_pre_add_rms_norm_f32");
8363 let cfg = LaunchConfig {
8364 grid_dim: (nrows as u32, 1, 1),
8365 block_dim: (rms_block(), 1, 1),
8366 shared_mem_bytes: 0,
8367 };
8368 let (nc, e) = (ncols as i32, eps);
8369 let __s_b2 = self.gpu.stream();
8370 let mut b2 = __s_b2.launch_builder(&f);
8371 b2.arg(a)
8372 .arg(wa)
8373 .arg(b)
8374 .arg(w)
8375 .arg(&mut *res)
8376 .arg(&mut *dst)
8377 .arg(&nc)
8378 .arg(&e);
8379 unsafe {
8380 b2.launch(cfg)?;
8381 }
8382 Ok(())
8383 }
8384
8385 #[allow(clippy::too_many_arguments)]
8387 pub fn rms_pre_add_rms_norm_q8z(
8388 &self,
8389 a: &CudaSlice<f32>,
8390 wa: &CudaSlice<f32>,
8391 b: &CudaSlice<f32>,
8392 w: &CudaSlice<f32>,
8393 res: &mut CudaSlice<f32>,
8394 dst: &mut CudaSlice<f32>,
8395 ncols: usize,
8396 nrows: usize,
8397 eps: f32,
8398 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8399 debug_assert!(ncols % 128 == 0);
8400 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
8401 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
8402 let (nc, e) = (ncols as i32, eps);
8403 if Self::pdl_on() {
8404 {
8405 use cudarc::driver::{DevicePtr, DevicePtrMut};
8406 let s = &self.gpu.stream();
8407 let (pa, _g0) = a.device_ptr(s);
8408 let (pwa, _g1) = wa.device_ptr(s);
8409 let (pb, _g2) = b.device_ptr(s);
8410 let (pw, _g3) = w.device_ptr(s);
8411 let (pr, _g4) = res.device_ptr_mut(s);
8412 let (pdst, _g5) = dst.device_ptr_mut(s);
8413 let (pq, _g6) = out_q.device_ptr_mut(s);
8414 let (pd, _g7) = out_d.device_ptr_mut(s);
8415 let mut ps = [
8416 &pa as *const _ as *mut std::ffi::c_void,
8417 &pwa as *const _ as *mut _,
8418 &pb as *const _ as *mut _,
8419 &pw as *const _ as *mut _,
8420 &pr as *const _ as *mut _,
8421 &pdst as *const _ as *mut _,
8422 &pq as *const _ as *mut _,
8423 &pd as *const _ as *mut _,
8424 &nc as *const _ as *mut _,
8425 &e as *const _ as *mut _,
8426 ];
8427 unsafe {
8428 self.launch_pdl(
8429 "rms_pre_add_rms_norm_q8z_f32",
8430 (nrows as u32, 1, 1),
8431 (rms_block(), 1, 1),
8432 &mut ps,
8433 )?;
8434 }
8435 }
8436 return Ok((out_q, out_d));
8437 }
8438 let f = self.func("rms_pre_add_rms_norm_q8z_f32");
8439 let cfg = LaunchConfig {
8440 grid_dim: (nrows as u32, 1, 1),
8441 block_dim: (rms_block(), 1, 1),
8442 shared_mem_bytes: 0,
8443 };
8444 let __s_b2 = self.gpu.stream();
8445 let mut b2 = __s_b2.launch_builder(&f);
8446 b2.arg(a)
8447 .arg(wa)
8448 .arg(b)
8449 .arg(w)
8450 .arg(&mut *res)
8451 .arg(&mut *dst)
8452 .arg(&mut out_q)
8453 .arg(&mut out_d)
8454 .arg(&nc)
8455 .arg(&e);
8456 unsafe {
8457 b2.launch(cfg)?;
8458 }
8459 Ok((out_q, out_d))
8460 }
8461
8462 pub fn build_q4_out_concat3(
8466 &self,
8467 w0: &crate::model::GpuTensor,
8468 w1: &crate::model::GpuTensor,
8469 w2: &crate::model::GpuTensor,
8470 ) -> Result<Option<crate::model::GpuTensor>, Box<dyn std::error::Error>> {
8471 use crate::model::GpuTensor;
8472 let part = |w: &GpuTensor| -> Option<(usize, usize)> {
8473 match w {
8474 GpuTensor::Quant {
8475 qtype,
8476 row_bytes,
8477 rp,
8478 ..
8479 } if *qtype == QT_Q4_0 && !*rp => Some((*row_bytes, w.out_features())),
8480 _ => None,
8481 }
8482 };
8483 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (part(w0), part(w1), part(w2))
8484 else {
8485 return Ok(None);
8486 };
8487 if rb0 != rb1
8488 || rb0 != rb2
8489 || w0.in_features() != w1.in_features()
8490 || w0.in_features() != w2.in_features()
8491 {
8492 return Ok(None);
8493 }
8494 fn bytes_of(w: &crate::model::GpuTensor) -> &CudaSlice<u8> {
8495 match w {
8496 crate::model::GpuTensor::Quant { bytes, .. } => bytes,
8497 _ => unreachable!(),
8498 }
8499 }
8500 let (b0, b1, b2) = (bytes_of(w0), bytes_of(w1), bytes_of(w2));
8501 let total = rb0 * (o0 + o1 + o2);
8502 let mut cat = self.alloc_u8(total)?;
8503 self.copy_u8_into(&mut cat, 0, b0, rb0 * o0)?;
8504 self.copy_u8_into(&mut cat, rb0 * o0, b1, rb1 * o1)?;
8505 self.copy_u8_into(&mut cat, rb0 * (o0 + o1), b2, rb2 * o2)?;
8506 Ok(Some(GpuTensor::Quant {
8507 bytes: cat,
8508 qtype: QT_Q4_0,
8509 row_bytes: rb0,
8510 ne: vec![w0.in_features() as u64, (o0 + o1 + o2) as u64],
8511 scale: 1.0,
8512 rp: false,
8513 #[cfg(memra_cutlass)]
8514 cutlass: None,
8515 fp8: None,
8516 blk: None,
8517 rp4: None,
8518 f16: None,
8519 }))
8520 }
8521
8522 #[allow(clippy::too_many_arguments)]
8524 pub fn rms_norm_qkv_rope_cat(
8525 &self,
8526 qkv: &CudaSlice<f32>,
8527 wq: &CudaSlice<f32>,
8528 wk: &CudaSlice<f32>,
8529 wv: &CudaSlice<f32>,
8530 q: &mut CudaSlice<f32>,
8531 k: &mut CudaSlice<f32>,
8532 v: &mut CudaSlice<f32>,
8533 head_dim: usize,
8534 rq: usize,
8535 rk: usize,
8536 pos: &CudaSlice<i32>,
8537 nh_q: usize,
8538 nh_k: usize,
8539 base: f32,
8540 freq_scale: f32,
8541 ff: Option<&CudaSlice<f32>>,
8542 eps: f32,
8543 ) -> Result<(), Box<dyn std::error::Error>> {
8544 let rows = rq + rk + rk;
8545 let theta_scale = base.powf(-2.0 / head_dim as f32);
8546 let (nc, rqi, rki, nhq, nhk) = (
8547 head_dim as i32,
8548 rq as i32,
8549 rk as i32,
8550 nh_q as i32,
8551 nh_k as i32,
8552 );
8553 if Self::pdl_on() {
8554 use cudarc::driver::{DevicePtr, DevicePtrMut};
8555 let s = &self.gpu.stream();
8556 let (pqkv, _g0) = qkv.device_ptr(s);
8557 let (pwq, _g1) = wq.device_ptr(s);
8558 let (pwk, _g2) = wk.device_ptr(s);
8559 let (pwv, _g3) = wv.device_ptr(s);
8560 let (pq, _g4) = q.device_ptr_mut(s);
8561 let (pk, _g5) = k.device_ptr_mut(s);
8562 let (pv, _g6) = v.device_ptr_mut(s);
8563 let (ppos, _g7) = pos.device_ptr(s);
8564 let (pff, _g8) = match ff {
8565 Some(t) => {
8566 let (p, g) = t.device_ptr(s);
8567 (p, Some(g))
8568 }
8569 None => (0, None),
8570 };
8571 let mut ps = [
8572 &pqkv as *const _ as *mut std::ffi::c_void,
8573 &pwq as *const _ as *mut _,
8574 &pwk as *const _ as *mut _,
8575 &pwv as *const _ as *mut _,
8576 &pq as *const _ as *mut _,
8577 &pk as *const _ as *mut _,
8578 &pv as *const _ as *mut _,
8579 &nc as *const _ as *mut _,
8580 &rqi as *const _ as *mut _,
8581 &rki as *const _ as *mut _,
8582 &ppos as *const _ as *mut _,
8583 &nhq as *const _ as *mut _,
8584 &nhk as *const _ as *mut _,
8585 &theta_scale as *const _ as *mut _,
8586 &freq_scale as *const _ as *mut _,
8587 &pff as *const _ as *mut _,
8588 &eps as *const _ as *mut _,
8589 ];
8590 unsafe {
8591 self.launch_pdl(
8592 "rms_norm_qkv_rope_cat_f32",
8593 (rows as u32, 1, 1),
8594 (rms_block(), 1, 1),
8595 &mut ps,
8596 )?;
8597 }
8598 return Ok(());
8599 }
8600 let f = self.func("rms_norm_qkv_rope_cat_f32");
8601 let cfg = LaunchConfig {
8602 grid_dim: (rows as u32, 1, 1),
8603 block_dim: (rms_block(), 1, 1),
8604 shared_mem_bytes: 0,
8605 };
8606 let __s_b = self.gpu.stream();
8607 let mut b = __s_b.launch_builder(&f);
8608 match ff {
8609 Some(t) => {
8610 b.arg(qkv)
8611 .arg(wq)
8612 .arg(wk)
8613 .arg(wv)
8614 .arg(&mut *q)
8615 .arg(&mut *k)
8616 .arg(&mut *v)
8617 .arg(&nc)
8618 .arg(&rqi)
8619 .arg(&rki)
8620 .arg(pos)
8621 .arg(&nhq)
8622 .arg(&nhk)
8623 .arg(&theta_scale)
8624 .arg(&freq_scale)
8625 .arg(t)
8626 .arg(&eps);
8627 unsafe {
8628 b.launch(cfg)?;
8629 }
8630 }
8631 None => {
8632 let null: u64 = 0;
8633 b.arg(qkv)
8634 .arg(wq)
8635 .arg(wk)
8636 .arg(wv)
8637 .arg(&mut *q)
8638 .arg(&mut *k)
8639 .arg(&mut *v)
8640 .arg(&nc)
8641 .arg(&rqi)
8642 .arg(&rki)
8643 .arg(pos)
8644 .arg(&nhq)
8645 .arg(&nhk)
8646 .arg(&theta_scale)
8647 .arg(&freq_scale)
8648 .arg(&null)
8649 .arg(&eps);
8650 unsafe {
8651 b.launch(cfg)?;
8652 }
8653 }
8654 }
8655 Ok(())
8656 }
8657
8658 #[allow(clippy::too_many_arguments)]
8660 pub fn rms_norm_qkv_rope(
8661 &self,
8662 q0: &CudaSlice<f32>,
8663 k0: &CudaSlice<f32>,
8664 v0: &CudaSlice<f32>,
8665 wq: &CudaSlice<f32>,
8666 wk: &CudaSlice<f32>,
8667 wv: &CudaSlice<f32>,
8668 q: &mut CudaSlice<f32>,
8669 k: &mut CudaSlice<f32>,
8670 v: &mut CudaSlice<f32>,
8671 head_dim: usize,
8672 rq: usize,
8673 rk: usize,
8674 pos: &CudaSlice<i32>,
8675 nh_q: usize,
8676 nh_k: usize,
8677 base: f32,
8678 freq_scale: f32,
8679 ff: Option<&CudaSlice<f32>>,
8680 eps: f32,
8681 ) -> Result<(), Box<dyn std::error::Error>> {
8682 let f = self.func("rms_norm_qkv_rope_f32");
8683 let rows = rq + rk + rk; let cfg = LaunchConfig {
8685 grid_dim: (rows as u32, 1, 1),
8686 block_dim: (rms_block(), 1, 1),
8687 shared_mem_bytes: 0,
8688 };
8689 let theta_scale = base.powf(-2.0 / head_dim as f32);
8690 let (nc, rqi, rki, nhq, nhk) = (
8691 head_dim as i32,
8692 rq as i32,
8693 rk as i32,
8694 nh_q as i32,
8695 nh_k as i32,
8696 );
8697 let __s_b = self.gpu.stream();
8698 let mut b = __s_b.launch_builder(&f);
8699 match ff {
8700 Some(t) => {
8701 b.arg(q0)
8702 .arg(k0)
8703 .arg(v0)
8704 .arg(wq)
8705 .arg(wk)
8706 .arg(wv)
8707 .arg(&mut *q)
8708 .arg(&mut *k)
8709 .arg(&mut *v)
8710 .arg(&nc)
8711 .arg(&rqi)
8712 .arg(&rki)
8713 .arg(pos)
8714 .arg(&nhq)
8715 .arg(&nhk)
8716 .arg(&theta_scale)
8717 .arg(&freq_scale)
8718 .arg(t)
8719 .arg(&eps);
8720 unsafe {
8721 b.launch(cfg)?;
8722 }
8723 }
8724 None => {
8725 let null: u64 = 0;
8726 b.arg(q0)
8727 .arg(k0)
8728 .arg(v0)
8729 .arg(wq)
8730 .arg(wk)
8731 .arg(wv)
8732 .arg(&mut *q)
8733 .arg(&mut *k)
8734 .arg(&mut *v)
8735 .arg(&nc)
8736 .arg(&rqi)
8737 .arg(&rki)
8738 .arg(pos)
8739 .arg(&nhq)
8740 .arg(&nhk)
8741 .arg(&theta_scale)
8742 .arg(&freq_scale)
8743 .arg(&null)
8744 .arg(&eps);
8745 unsafe {
8746 b.launch(cfg)?;
8747 }
8748 }
8749 }
8750 Ok(())
8751 }
8752
8753 #[allow(clippy::too_many_arguments)]
8757 pub fn rms_norm_qkv_rope_append_dc(
8758 &self,
8759 q0: &CudaSlice<f32>,
8760 k0: &CudaSlice<f32>,
8761 v0: &CudaSlice<f32>,
8762 wq: &CudaSlice<f32>,
8763 wk: &CudaSlice<f32>,
8764 wv: &CudaSlice<f32>,
8765 q: &mut CudaSlice<f32>,
8766 k: &mut CudaSlice<f32>,
8767 v: &mut CudaSlice<f32>,
8768 head_dim: usize,
8769 rq: usize,
8770 rk: usize,
8771 pos: &CudaSlice<i32>,
8772 nh_q: usize,
8773 nh_k: usize,
8774 base: f32,
8775 freq_scale: f32,
8776 ff: Option<&CudaSlice<f32>>,
8777 eps: f32,
8778 kc: &mut CudaSlice<u8>,
8779 vc: &mut CudaSlice<u8>,
8780 t_dev: &CudaSlice<i32>,
8781 k_tok_bytes: usize,
8782 v_tok_bytes: usize,
8783 g: bool,
8784 ) -> Result<(), Box<dyn std::error::Error>> {
8785 let rows = rq + rk + rk;
8786 let theta_scale = base.powf(-2.0 / head_dim as f32);
8787 let (nc, rqi, rki, nhq, nhk) = (
8788 head_dim as i32,
8789 rq as i32,
8790 rk as i32,
8791 nh_q as i32,
8792 nh_k as i32,
8793 );
8794 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
8795 if Self::pdl_on() && Self::pdl_wb_on() {
8796 use cudarc::driver::{DevicePtr, DevicePtrMut};
8797 let s = &self.gpu.stream();
8798 let (p0, _a0) = q0.device_ptr(s);
8799 let (p1, _a1) = k0.device_ptr(s);
8800 let (p2, _a2) = v0.device_ptr(s);
8801 let (pwq, _a3) = wq.device_ptr(s);
8802 let (pwk, _a4) = wk.device_ptr(s);
8803 let (pwv, _a5) = wv.device_ptr(s);
8804 let (pq, _a6) = q.device_ptr_mut(s);
8805 let (pk, _a7) = k.device_ptr_mut(s);
8806 let (pv, _a8) = v.device_ptr_mut(s);
8807 let (pp, _a9) = pos.device_ptr(s);
8808 let pff: u64 = match ff {
8809 Some(t) => {
8810 let (p, _gg) = t.device_ptr(s);
8811 p as u64
8812 }
8813 None => 0,
8814 };
8815 let (pkc, _a10) = kc.device_ptr_mut(s);
8816 let (pvc, _a11) = vc.device_ptr_mut(s);
8817 let (pt, _a12) = t_dev.device_ptr(s);
8818 let mut ps = [
8819 &p0 as *const _ as *mut std::ffi::c_void,
8820 &p1 as *const _ as *mut _,
8821 &p2 as *const _ as *mut _,
8822 &pwq as *const _ as *mut _,
8823 &pwk as *const _ as *mut _,
8824 &pwv as *const _ as *mut _,
8825 &pq as *const _ as *mut _,
8826 &pk as *const _ as *mut _,
8827 &pv as *const _ as *mut _,
8828 &nc as *const _ as *mut _,
8829 &rqi as *const _ as *mut _,
8830 &rki as *const _ as *mut _,
8831 &pp as *const _ as *mut _,
8832 &nhq as *const _ as *mut _,
8833 &nhk as *const _ as *mut _,
8834 &theta_scale as *const _ as *mut _,
8835 &freq_scale as *const _ as *mut _,
8836 &pff as *const _ as *mut _,
8837 &eps as *const _ as *mut _,
8838 &pkc as *const _ as *mut _,
8839 &pvc as *const _ as *mut _,
8840 &pt as *const _ as *mut _,
8841 &ktb as *const _ as *mut _,
8842 &vtb as *const _ as *mut _,
8843 ];
8844 unsafe {
8845 self.launch_pdl_flash(
8846 g,
8847 "rms_norm_qkv_rope_append_dc_f32",
8848 (rows as u32, 1, 1),
8849 (rms_block(), 1, 1),
8850 0,
8851 &mut ps,
8852 )?;
8853 }
8854 return Ok(());
8855 }
8856 let f = if g {
8857 self.func_g("rms_norm_qkv_rope_append_dc_f32")
8858 } else {
8859 self.func("rms_norm_qkv_rope_append_dc_f32")
8860 };
8861 let cfg = LaunchConfig {
8862 grid_dim: (rows as u32, 1, 1),
8863 block_dim: (rms_block(), 1, 1),
8864 shared_mem_bytes: 0,
8865 };
8866 let __s_b = self.gpu.stream();
8867 let mut b = __s_b.launch_builder(&f);
8868 match ff {
8869 Some(t) => {
8870 b.arg(q0)
8871 .arg(k0)
8872 .arg(v0)
8873 .arg(wq)
8874 .arg(wk)
8875 .arg(wv)
8876 .arg(&mut *q)
8877 .arg(&mut *k)
8878 .arg(&mut *v)
8879 .arg(&nc)
8880 .arg(&rqi)
8881 .arg(&rki)
8882 .arg(pos)
8883 .arg(&nhq)
8884 .arg(&nhk)
8885 .arg(&theta_scale)
8886 .arg(&freq_scale)
8887 .arg(t)
8888 .arg(&eps)
8889 .arg(&mut *kc)
8890 .arg(&mut *vc)
8891 .arg(t_dev)
8892 .arg(&ktb)
8893 .arg(&vtb);
8894 unsafe {
8895 b.launch(cfg)?;
8896 }
8897 }
8898 None => {
8899 let null: u64 = 0;
8900 b.arg(q0)
8901 .arg(k0)
8902 .arg(v0)
8903 .arg(wq)
8904 .arg(wk)
8905 .arg(wv)
8906 .arg(&mut *q)
8907 .arg(&mut *k)
8908 .arg(&mut *v)
8909 .arg(&nc)
8910 .arg(&rqi)
8911 .arg(&rki)
8912 .arg(pos)
8913 .arg(&nhq)
8914 .arg(&nhk)
8915 .arg(&theta_scale)
8916 .arg(&freq_scale)
8917 .arg(&null)
8918 .arg(&eps)
8919 .arg(&mut *kc)
8920 .arg(&mut *vc)
8921 .arg(t_dev)
8922 .arg(&ktb)
8923 .arg(&vtb);
8924 unsafe {
8925 b.launch(cfg)?;
8926 }
8927 }
8928 }
8929 Ok(())
8930 }
8931
8932 pub fn add_q8_1(
8934 &self,
8935 a: &CudaSlice<f32>,
8936 b: &CudaSlice<f32>,
8937 res: &mut CudaSlice<f32>,
8938 ncols: usize,
8939 nrows: usize,
8940 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8941 debug_assert!(ncols % 128 == 0);
8942 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
8943 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
8944 let f = self.func("add_q8_1_f32");
8945 let cfg = LaunchConfig {
8946 grid_dim: (nrows as u32, 1, 1),
8947 block_dim: (rms_block(), 1, 1),
8948 shared_mem_bytes: 0,
8949 };
8950 let nc = ncols as i32;
8951 let __s_b2 = self.gpu.stream();
8952 let mut b2 = __s_b2.launch_builder(&f);
8953 b2.arg(a)
8954 .arg(b)
8955 .arg(&mut *res)
8956 .arg(&mut out_q)
8957 .arg(&mut out_d)
8958 .arg(&nc);
8959 unsafe {
8960 b2.launch(cfg)?;
8961 }
8962 Ok((out_q, out_d))
8963 }
8964
8965 pub fn rms_pre_add_q8_1(
8969 &self,
8970 a: &CudaSlice<f32>,
8971 wa: &CudaSlice<f32>,
8972 b: &CudaSlice<f32>,
8973 res: &mut CudaSlice<f32>,
8974 ncols: usize,
8975 nrows: usize,
8976 eps: f32,
8977 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8978 debug_assert!(ncols % 128 == 0);
8979 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
8980 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
8981 let f = self.func("rms_pre_add_q8_1_f32");
8982 let cfg = LaunchConfig {
8983 grid_dim: (nrows as u32, 1, 1),
8984 block_dim: (rms_block(), 1, 1),
8985 shared_mem_bytes: 0,
8986 };
8987 let (nc, ep) = (ncols as i32, eps);
8988 let __s_b2 = self.gpu.stream();
8989 let mut b2 = __s_b2.launch_builder(&f);
8990 b2.arg(a)
8991 .arg(wa)
8992 .arg(b)
8993 .arg(&mut *res)
8994 .arg(&mut out_q)
8995 .arg(&mut out_d)
8996 .arg(&nc)
8997 .arg(&ep);
8998 unsafe {
8999 b2.launch(cfg)?;
9000 }
9001 Ok((out_q, out_d))
9002 }
9003
9004 pub fn l2_v2_on(ncols: usize) -> bool {
9008 ncols == 128 && std::env::var("MEMRA_L2_V2").as_deref() != Ok("0")
9009 }
9010
9011 pub fn l2_norm_pp(
9012 &self,
9013 x: &CudaSlice<f32>,
9014 dst: &mut CudaSlice<f32>,
9015 dst16: Option<&mut CudaSlice<u8>>,
9016 ncols: usize,
9017 nrows: usize,
9018 eps: f32,
9019 ) -> Result<(), Box<dyn std::error::Error>> {
9020 if Self::l2_v2_on(ncols) {
9021 let f = self.func("l2_norm_pp_v2_f32");
9022 let rows_per_block = 8u32; let cfg = LaunchConfig {
9024 grid_dim: ((nrows as u32).div_ceil(rows_per_block), 1, 1),
9025 block_dim: (256, 1, 1),
9026 shared_mem_bytes: 0,
9027 };
9028 let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
9029 let d16: u64 = match dst16 {
9031 Some(d) => self.addr_u8(d),
9032 None => 0,
9033 };
9034 let __s_b = self.gpu.stream();
9035 let mut b = __s_b.launch_builder(&f);
9036 b.arg(x).arg(dst).arg(&d16).arg(&nc).arg(&nr).arg(&e);
9037 unsafe {
9038 b.launch(cfg)?;
9039 }
9040 return Ok(());
9041 }
9042 self.l2_norm(x, dst, ncols, nrows, eps)
9043 }
9044
9045 pub fn l2_norm(
9046 &self,
9047 x: &CudaSlice<f32>,
9048 dst: &mut CudaSlice<f32>,
9049 ncols: usize,
9050 nrows: usize,
9051 eps: f32,
9052 ) -> Result<(), Box<dyn std::error::Error>> {
9053 let f = self.func("l2_norm_f32");
9054 let cfg = LaunchConfig {
9055 grid_dim: (nrows as u32, 1, 1),
9056 block_dim: (256, 1, 1),
9057 shared_mem_bytes: 0,
9058 };
9059 let (nc, e) = (ncols as i32, eps);
9060 let __s_b = self.gpu.stream();
9061 let mut b = __s_b.launch_builder(&f);
9062 b.arg(x).arg(dst).arg(&nc).arg(&e);
9063 unsafe {
9064 b.launch(cfg)?;
9065 }
9066 Ok(())
9067 }
9068
9069 pub fn l2_norm_decode(
9075 &self,
9076 x: &CudaSlice<f32>,
9077 dst: &mut CudaSlice<f32>,
9078 ncols: usize,
9079 nrows: usize,
9080 eps: f32,
9081 ) -> Result<(), Box<dyn std::error::Error>> {
9082 let f = self.func("l2_norm_f32");
9083 let cfg = LaunchConfig {
9084 grid_dim: (nrows as u32, 1, 1),
9085 block_dim: (32, 1, 1),
9086 shared_mem_bytes: 0,
9087 };
9088 let (nc, e) = (ncols as i32, eps);
9089 let __s_b = self.gpu.stream();
9090 let mut b = __s_b.launch_builder(&f);
9091 b.arg(x).arg(dst).arg(&nc).arg(&e);
9092 unsafe {
9093 b.launch(cfg)?;
9094 }
9095 Ok(())
9096 }
9097
9098 pub fn rope_neox(
9100 &self,
9101 x: &mut CudaSlice<f32>,
9102 pos: &CudaSlice<i32>,
9103 head_dim: usize,
9104 n_dims: usize,
9105 n_heads: usize,
9106 n_tokens: usize,
9107 freq_base: f32,
9108 freq_scale: f32,
9109 ) -> Result<(), Box<dyn std::error::Error>> {
9110 let f = self.func("rope_neox_f32");
9111 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9112 let grid = (n_heads * n_tokens) as u32;
9113 let cfg = LaunchConfig {
9114 grid_dim: (grid, 1, 1),
9115 block_dim: ((head_dim / 2) as u32, 1, 1),
9116 shared_mem_bytes: 0,
9117 };
9118 let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
9119 let __s_b = self.gpu.stream();
9120 let mut b = __s_b.launch_builder(&f);
9121 b.arg(x)
9122 .arg(pos)
9123 .arg(&hd)
9124 .arg(&nd)
9125 .arg(&nh)
9126 .arg(&theta_scale)
9127 .arg(&freq_scale);
9128 unsafe {
9129 b.launch(cfg)?;
9130 }
9131 Ok(())
9132 }
9133
9134 pub fn rope_neox_ff(
9136 &self,
9137 x: &mut CudaSlice<f32>,
9138 pos: &CudaSlice<i32>,
9139 head_dim: usize,
9140 n_dims: usize,
9141 n_heads: usize,
9142 n_tokens: usize,
9143 freq_base: f32,
9144 freq_scale: f32,
9145 ff: &CudaSlice<f32>,
9146 ) -> Result<(), Box<dyn std::error::Error>> {
9147 let f = self.func("rope_neox_ff_f32");
9148 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9149 let grid = (n_heads * n_tokens) as u32;
9150 let cfg = LaunchConfig {
9151 grid_dim: (grid, 1, 1),
9152 block_dim: ((head_dim / 2) as u32, 1, 1),
9153 shared_mem_bytes: 0,
9154 };
9155 let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
9156 let __s_b = self.gpu.stream();
9157 let mut b = __s_b.launch_builder(&f);
9158 b.arg(x)
9159 .arg(pos)
9160 .arg(&hd)
9161 .arg(&nd)
9162 .arg(&nh)
9163 .arg(&theta_scale)
9164 .arg(&freq_scale)
9165 .arg(ff);
9166 unsafe {
9167 b.launch(cfg)?;
9168 }
9169 Ok(())
9170 }
9171
9172 #[allow(clippy::too_many_arguments)]
9174 pub fn rope_neox2(
9175 &self,
9176 q: &mut CudaSlice<f32>,
9177 k: &mut CudaSlice<f32>,
9178 pos: &CudaSlice<i32>,
9179 head_dim: usize,
9180 n_dims: usize,
9181 nh_q: usize,
9182 nh_k: usize,
9183 n_tokens: usize,
9184 freq_base: f32,
9185 freq_scale: f32,
9186 ff: Option<&CudaSlice<f32>>,
9187 ) -> Result<(), Box<dyn std::error::Error>> {
9188 let f = self.func("rope_neox2_f32");
9189 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9190 let grid = ((nh_q + nh_k) * n_tokens) as u32;
9191 let cfg = LaunchConfig {
9192 grid_dim: (grid, 1, 1),
9193 block_dim: ((head_dim / 2) as u32, 1, 1),
9194 shared_mem_bytes: 0,
9195 };
9196 let (hd, nd, nq, nk, nt) = (
9197 head_dim as i32,
9198 n_dims as i32,
9199 nh_q as i32,
9200 nh_k as i32,
9201 n_tokens as i32,
9202 );
9203 let __s_b = self.gpu.stream();
9204 let mut b = __s_b.launch_builder(&f);
9205 b.arg(q)
9206 .arg(k)
9207 .arg(pos)
9208 .arg(&hd)
9209 .arg(&nd)
9210 .arg(&nq)
9211 .arg(&nk)
9212 .arg(&nt)
9213 .arg(&theta_scale)
9214 .arg(&freq_scale);
9215 match ff {
9216 Some(ffv) => {
9217 b.arg(ffv);
9218 unsafe {
9219 b.launch(cfg)?;
9220 }
9221 }
9222 None => {
9223 let null: u64 = 0;
9224 b.arg(&null);
9225 unsafe {
9226 b.launch(cfg)?;
9227 }
9228 }
9229 }
9230 Ok(())
9231 }
9232
9233 pub fn gelu_tanh_mul(
9235 &self,
9236 gate: &CudaSlice<f32>,
9237 up: &CudaSlice<f32>,
9238 dst: &mut CudaSlice<f32>,
9239 n: usize,
9240 ) -> Result<(), Box<dyn std::error::Error>> {
9241 let f = self.func("gelu_tanh_mul_f32");
9242 let cfg = LaunchConfig::for_num_elems(n as u32);
9243 let ni = n as i32;
9244 let __s_b = self.gpu.stream();
9245 let mut b = __s_b.launch_builder(&f);
9246 b.arg(gate).arg(up).arg(dst).arg(&ni);
9247 unsafe {
9248 b.launch(cfg)?;
9249 }
9250 Ok(())
9251 }
9252
9253 pub fn silu_mul(
9254 &self,
9255 gate: &CudaSlice<f32>,
9256 up: &CudaSlice<f32>,
9257 dst: &mut CudaSlice<f32>,
9258 n: usize,
9259 ) -> Result<(), Box<dyn std::error::Error>> {
9260 let f = self.func("silu_mul_f32");
9261 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9263 let ni = n as i32;
9264 let __s_b = self.gpu.stream();
9265 let mut b = __s_b.launch_builder(&f);
9266 b.arg(gate).arg(up).arg(dst).arg(&ni);
9267 unsafe {
9268 b.launch(cfg)?;
9269 }
9270 Ok(())
9271 }
9272
9273 pub fn silu_mul_f16out(
9276 &self,
9277 gate: &CudaSlice<f32>,
9278 up: &CudaSlice<f32>,
9279 dst: &mut CudaSlice<f32>,
9280 dst16: &mut CudaSlice<u8>,
9281 n: usize,
9282 ) -> Result<(), Box<dyn std::error::Error>> {
9283 let f = self.func("silu_mul_f16out_f32");
9284 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9285 let ni = n as i32;
9286 let __s_b = self.gpu.stream();
9287 let mut b = __s_b.launch_builder(&f);
9288 b.arg(gate).arg(up).arg(dst).arg(dst16).arg(&ni);
9289 unsafe {
9290 b.launch(cfg)?;
9291 }
9292 Ok(())
9293 }
9294
9295 pub fn silu_mul_scaled(
9302 &self,
9303 gate: &CudaSlice<f32>,
9304 up: &CudaSlice<f32>,
9305 gs: f32,
9306 us: f32,
9307 dst: &mut CudaSlice<f32>,
9308 n: usize,
9309 ) -> Result<(), Box<dyn std::error::Error>> {
9310 let f = self.func("silu_mul_scaled_f32");
9311 let cfg = LaunchConfig::for_num_elems(n as u32);
9312 let ni = n as i32;
9313 let (gsf, usf) = (gs, us);
9314 let __s_b = self.gpu.stream();
9315 let mut b = __s_b.launch_builder(&f);
9316 b.arg(gate).arg(up).arg(&gsf).arg(&usf).arg(dst).arg(&ni);
9317 unsafe {
9318 b.launch(cfg)?;
9319 }
9320 Ok(())
9321 }
9322
9323 #[allow(clippy::too_many_arguments)]
9327 pub fn swigluoai_mul_scaled(
9328 &self,
9329 gate: &CudaSlice<f32>,
9330 up: &CudaSlice<f32>,
9331 gs: f32,
9332 us: f32,
9333 alpha: f32,
9334 limit: f32,
9335 dst: &mut CudaSlice<f32>,
9336 n: usize,
9337 ) -> Result<(), Box<dyn std::error::Error>> {
9338 let f = self.func("swigluoai_mul_scaled_f32");
9339 let cfg = LaunchConfig::for_num_elems(n as u32);
9340 let ni = n as i32;
9341 let __s_b = self.gpu.stream();
9342 let mut b = __s_b.launch_builder(&f);
9343 b.arg(gate)
9344 .arg(up)
9345 .arg(&gs)
9346 .arg(&us)
9347 .arg(&alpha)
9348 .arg(&limit)
9349 .arg(dst)
9350 .arg(&ni);
9351 unsafe {
9352 b.launch(cfg)?;
9353 }
9354 Ok(())
9355 }
9356
9357 pub fn silu_mul_scaled_q8_1(
9365 &self,
9366 gate: &CudaSlice<f32>,
9367 up: &CudaSlice<f32>,
9368 gs: f32,
9369 us: f32,
9370 n: usize,
9371 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9372 let f = self.func("silu_mul_scaled_q8_1");
9373 let nblk = n / 32;
9374 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);
9378 let (gsf, usf, ni) = (gs, us, n as i32);
9379 let __s_b = self.gpu.stream();
9380 let mut b = __s_b.launch_builder(&f);
9381 b.arg(gate)
9382 .arg(up)
9383 .arg(&gsf)
9384 .arg(&usf)
9385 .arg(&mut aq)
9386 .arg(&mut ad)
9387 .arg(&ni);
9388 unsafe {
9389 b.launch(cfg)?;
9390 }
9391 Ok((aq, ad))
9392 }
9393
9394 pub fn add(
9395 &self,
9396 a: &CudaSlice<f32>,
9397 b_in: &CudaSlice<f32>,
9398 dst: &mut CudaSlice<f32>,
9399 n: usize,
9400 ) -> Result<(), Box<dyn std::error::Error>> {
9401 let f = self.func("add_f32");
9402 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9404 let ni = n as i32;
9405 let __s_bld = self.gpu.stream();
9406 let mut bld = __s_bld.launch_builder(&f);
9407 bld.arg(a).arg(b_in).arg(dst).arg(&ni);
9408 unsafe {
9409 bld.launch(cfg)?;
9410 }
9411 Ok(())
9412 }
9413
9414 pub fn mul(
9415 &self,
9416 a: &CudaSlice<f32>,
9417 b_in: &CudaSlice<f32>,
9418 dst: &mut CudaSlice<f32>,
9419 n: usize,
9420 ) -> Result<(), Box<dyn std::error::Error>> {
9421 let f = self.func("mul_f32");
9422 let cfg = LaunchConfig::for_num_elems(n as u32);
9423 let ni = n as i32;
9424 let __s_bld = self.gpu.stream();
9425 let mut bld = __s_bld.launch_builder(&f);
9426 bld.arg(a).arg(b_in).arg(dst).arg(&ni);
9427 unsafe {
9428 bld.launch(cfg)?;
9429 }
9430 Ok(())
9431 }
9432
9433 pub fn matmul(
9436 &self,
9437 w: &crate::model::GpuTensor,
9438 x: &CudaSlice<f32>,
9439 m: usize,
9440 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9441 use crate::model::GpuTensor;
9442 let in_f = w.in_features();
9443 let out_f = w.out_features();
9444 #[allow(non_snake_case)]
9452 let GEMM_M_THRESHOLD = if self.verify_exact_on() {
9455 usize::MAX
9456 } else {
9457 16usize
9458 };
9459
9460 const GEMM_MIN_OUT_F: usize = 128; if m >= GEMM_M_THRESHOLD {
9485 if let Some(y) = self.try_fp8_gemm(w, x, m)? {
9486 return Ok(y);
9487 }
9488 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
9495 return Ok(y);
9496 }
9497 if let Some(y) = self.try_f16_gemm(w, x, m)? {
9500 return Ok(y);
9501 }
9502 }
9503 if let GpuTensor::Quant { qtype, .. } = w {
9518 if *qtype == QT_F8_E4M3_BLK {
9519 if m >= GEMM_M_THRESHOLD {
9520 if let Some(y) = self.try_e4m3_blk_prefill(w, x, m)? {
9521 return Ok(y);
9522 }
9523 }
9524 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9525 if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
9526 return Ok(y);
9527 }
9528 }
9529 }
9530 if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.mmq_supports(w) {
9531 return self.qmatvec_mmq(w, x, m);
9532 }
9533 if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.gemm_supports(w) {
9534 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9535 return self.qmatvec_gemm(w, &aq, &ad, m);
9536 }
9537 if m >= GEMM_M_THRESHOLD {
9540 if let Some(y) = self.try_fp4_gemm(w, x, m, in_f, out_f)? {
9541 return Ok(y);
9542 }
9543 }
9544 let fast = std::env::var("MEMRA_FAST").as_deref() != Ok("0");
9548 if m == 1 && fast {
9553 if let GpuTensor::Quant {
9554 bytes,
9555 qtype,
9556 row_bytes,
9557 rp,
9558 rp4,
9559 scale,
9560 ..
9561 } = w
9562 {
9563 if self.mmvq_supports(*qtype) {
9564 let (bytes, rp) = match rp4 {
9568 Some(m4) => (m4, true),
9569 None => (bytes, *rp),
9570 };
9571 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9572 return self.qmatvec_mmvq(
9573 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, rp,
9574 );
9575 }
9576 }
9577 }
9578 if (2..=16).contains(&m)
9594 && fast
9595 && std::env::var("MEMRA_NO_BATCHED").is_err()
9596 && (m <= 4 || Self::b8_enabled())
9597 {
9598 let m_ok = m <= 8
9608 || matches!(w, GpuTensor::Quant { qtype, .. }
9609 if *qtype == QT_Q4_0 || *qtype == QT_Q6_K || *qtype == QT_F8_E4M3
9610 || *qtype == QT_NVFP4 || *qtype == QT_Q4_K || *qtype == QT_Q5_K || *qtype == QT_Q8_0);
9611 if m_ok {
9612 if let GpuTensor::Quant {
9613 bytes,
9614 qtype,
9615 row_bytes,
9616 rp,
9617 rp4,
9618 ..
9619 } = w
9620 {
9621 if self.batched_supports(*qtype) && self.mmvq_supports(*qtype) {
9622 let (bytes, rp) = match rp4 {
9623 Some(m4) => (m4, true),
9624 None => (bytes, *rp),
9625 };
9626 let mcols = Self::batched_mcols(m);
9627 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9628 let mut y = self.qmatvec_mmvq_batched(
9629 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, mcols, 1.0, rp,
9630 )?;
9631 if let GpuTensor::Quant { scale, .. } = w {
9632 if *scale != 1.0 {
9633 self.scale_inplace(&mut y, *scale, m * out_f)?;
9634 }
9635 }
9636 return Ok(y);
9637 }
9638 }
9639 }
9640 }
9641 if fast {
9647 if let GpuTensor::Quant {
9648 bytes,
9649 qtype,
9650 row_bytes,
9651 scale,
9652 ..
9653 } = w
9654 {
9655 if *qtype == QT_F8_E4M3 {
9656 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9657 return self.qmatvec_mmvq(
9658 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, false,
9659 );
9660 }
9661 }
9662 }
9663 let mut y = match w {
9664 GpuTensor::Quant {
9665 bytes,
9666 qtype,
9667 row_bytes,
9668 ..
9669 } if fast && *qtype == QT_Q8_0 => {
9670 self.qmatvec_q8_0_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9671 }
9672 GpuTensor::Quant {
9673 bytes,
9674 qtype,
9675 row_bytes,
9676 ..
9677 } if fast && *qtype == QT_Q4_K => {
9678 self.qmatvec_q4_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9679 }
9680 GpuTensor::Quant {
9681 bytes,
9682 qtype,
9683 row_bytes,
9684 ..
9685 } if fast && *qtype == QT_Q6_K => {
9686 self.qmatvec_q6_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9687 }
9688 GpuTensor::Quant {
9689 bytes,
9690 qtype,
9691 row_bytes,
9692 ..
9693 } if fast && *qtype == QT_Q5_K => {
9694 self.qmatvec_q5_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9695 }
9696 GpuTensor::Quant {
9697 bytes,
9698 qtype,
9699 row_bytes,
9700 ..
9701 } if fast && *qtype == QT_Q3_K => {
9702 self.qmatvec_q3_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9703 }
9704 GpuTensor::Quant {
9705 bytes,
9706 qtype,
9707 row_bytes,
9708 rp,
9709 ..
9710 } if fast && *qtype == QT_NVFP4 => self.qmatvec_dp4a_named(
9711 if *rp {
9712 "qmatvec_nvfp4_dp4a_rp"
9713 } else {
9714 "qmatvec_nvfp4_dp4a"
9715 },
9716 bytes,
9717 x,
9718 m,
9719 in_f,
9720 out_f,
9721 *row_bytes,
9722 )?,
9723 GpuTensor::Quant {
9727 bytes,
9728 qtype,
9729 row_bytes,
9730 ..
9731 } if fast && *qtype == QT_IQ4_XS && Self::iq_fast_enabled() => {
9732 self.qmatvec_iq4_XS_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9733 }
9734 GpuTensor::Quant {
9739 bytes,
9740 qtype,
9741 row_bytes,
9742 rp,
9743 ..
9744 } =>
9745 {
9748 self.qmatvec(
9749 bytes,
9750 x,
9751 m,
9752 in_f,
9753 out_f,
9754 if *rp && *qtype == QT_NVFP4 {
9755 QT_NVFP4_RP
9756 } else {
9757 *qtype
9758 },
9759 *row_bytes,
9760 )?
9761 }
9762 GpuTensor::Float { data, .. } => self.linear(x, data, m, in_f, out_f)?,
9763 GpuTensor::FloatBf16 { data, .. } => {
9766 self.linear_bf16_chunked(x, data, m, in_f, out_f, false)?
9767 }
9768 };
9769 if let GpuTensor::Quant { scale, .. } = w {
9771 if *scale != 1.0 {
9772 self.scale_inplace(&mut y, *scale, m * out_f)?;
9773 }
9774 }
9775 Ok(y)
9776 }
9777
9778 pub fn uses_q8_1_fast(&self, w: &crate::model::GpuTensor) -> bool {
9781 use crate::model::GpuTensor;
9782 if std::env::var("MEMRA_FAST").as_deref() == Ok("0") {
9783 return false;
9784 }
9785 match w {
9786 GpuTensor::Quant { qtype, .. } => {
9793 matches!(
9794 *qtype,
9795 QT_Q8_0
9796 | QT_Q4_K
9797 | QT_Q6_K
9798 | QT_Q5_K
9799 | QT_Q3_K
9800 | QT_NVFP4
9801 | QT_F8_E4M3
9802 | QT_F8_E4M3_BLK
9803 | QT_Q4_0
9804 ) || (*qtype == QT_IQ4_XS && Self::iq_fast_enabled())
9805 }
9806 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
9807 }
9808 }
9809
9810 pub fn matmul_pre(
9815 &self,
9816 w: &crate::model::GpuTensor,
9817 aq: &CudaSlice<i8>,
9818 ad: &CudaSlice<f32>,
9819 x_fallback: &CudaSlice<f32>,
9820 m: usize,
9821 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9822 use crate::model::GpuTensor;
9823 let x_raw_ok = x_fallback.len() >= m * w.in_features();
9829 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
9832 if let Some(y) = self.try_fp8_gemm(w, x_fallback, m)? {
9833 return Ok(y);
9834 }
9835 if let Some(y) = self.try_fp8_blk_mmq(w, x_fallback, m)? {
9838 return Ok(y);
9839 }
9840 if let Some(y) = self.try_f16_gemm(w, x_fallback, m)? {
9842 return Ok(y);
9843 }
9844 }
9845 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
9851 if let Some(y) = self.try_e4m3_blk_prefill(w, x_fallback, m)? {
9852 return Ok(y);
9853 }
9854 }
9855 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
9856 return Ok(y);
9857 }
9858 if m >= 16
9863 && w.out_features() >= 128
9864 && self.mmq_supports(w)
9865 && !self.verify_exact_on()
9866 && x_raw_ok
9867 {
9868 return self.qmatvec_mmq(w, x_fallback, m);
9869 }
9870 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
9873 if let Some(y) =
9874 self.try_fp4_gemm(w, x_fallback, m, w.in_features(), w.out_features())?
9875 {
9876 return Ok(y);
9877 }
9878 }
9879 if m >= 16 && self.gemm_supports(w) && !self.verify_exact_on() {
9882 return self.qmatvec_gemm(w, aq, ad, m);
9883 }
9884 if !self.uses_q8_1_fast(w) {
9885 return self.matmul(w, x_fallback, m);
9886 }
9887 let in_f = w.in_features();
9888 let out_f = w.out_features();
9889 let (bytes, qtype, row_bytes, scale, rp) = match w {
9890 GpuTensor::Quant {
9891 bytes,
9892 qtype,
9893 row_bytes,
9894 scale,
9895 rp,
9896 ..
9897 } => (bytes, *qtype, *row_bytes, *scale, *rp),
9898 _ => unreachable!("uses_q8_1_fast guaranteed Quant"),
9899 };
9900 let (mbytes, mrp) = match w {
9903 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
9904 _ => (bytes, rp),
9905 };
9906 if m == 1 && self.mmvq_supports(qtype) {
9910 return self.qmatvec_mmvq(mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, mrp);
9911 }
9912 if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
9925 && std::env::var("MEMRA_NO_BATCHED").is_err()
9926 && (m <= 4 || Self::b8_enabled())
9927 && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_NVFP4
9931 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_F8_E4M3 || qtype == QT_Q8_0)
9932 {
9933 let mcols = Self::batched_mcols(m);
9934 return self.qmatvec_mmvq_batched(
9935 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, mrp,
9936 );
9937 }
9938 if qtype == QT_F8_E4M3 || qtype == QT_Q4_0 {
9944 let (b2, r2) = if qtype == QT_Q4_0 {
9945 (mbytes, mrp)
9946 } else {
9947 (bytes, rp)
9948 };
9949 return self.qmatvec_mmvq(b2, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, r2);
9950 }
9951 let name = match qtype {
9952 QT_Q8_0 => "qmatvec_q8_0_dp4a",
9953 QT_Q4_K => "qmatvec_q4_K_dp4a",
9954 QT_Q6_K => "qmatvec_q6_K_dp4a",
9955 QT_Q5_K => "qmatvec_q5_K_dp4a",
9956 QT_Q3_K => "qmatvec_q3_K_dp4a",
9957 QT_NVFP4 => {
9958 if rp {
9959 "qmatvec_nvfp4_dp4a_rp"
9960 } else {
9961 "qmatvec_nvfp4_dp4a"
9962 }
9963 }
9964 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
9965 _ => unreachable!(),
9966 };
9967 let f = self.func(name);
9968 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
9970 grid_dim: (out_f as u32, m as u32, 1),
9971 block_dim: (128, 1, 1),
9972 shared_mem_bytes: 0,
9973 };
9974 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
9975 let __s_b = self.gpu.stream();
9976 let mut b = __s_b.launch_builder(&f);
9977 b.arg(bytes)
9978 .arg(aq)
9979 .arg(ad)
9980 .arg(&mut y)
9981 .arg(&inf)
9982 .arg(&outf)
9983 .arg(&mi)
9984 .arg(&rb);
9985 unsafe {
9986 b.launch(cfg)?;
9987 }
9988 if scale != 1.0 {
9989 self.scale_inplace(&mut y, scale, m * out_f)?;
9990 }
9991 Ok(y)
9992 }
9993
9994 pub fn matmul_decode_exact(
10002 &self,
10003 w: &crate::model::GpuTensor,
10004 x: &CudaSlice<f32>,
10005 m: usize,
10006 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10007 use crate::model::GpuTensor;
10008 if let GpuTensor::Float { data, .. } = w {
10016 return self.linear_decode_exact(x, data, m, w.in_features(), w.out_features());
10017 }
10018 if let GpuTensor::FloatBf16 { data, .. } = w {
10021 let (in_f, out_f) = (w.in_features(), w.out_features());
10022 return self.linear_bf16_chunked(x, data, m, in_f, out_f, true);
10023 }
10024 if !self.uses_q8_1_fast(w) {
10025 return self.matmul(w, x, m);
10026 }
10027 let in_f = w.in_features();
10028 let out_f = w.out_features();
10029 let (bytes, qtype, row_bytes, scale, rp) = match w {
10030 GpuTensor::Quant {
10031 bytes,
10032 qtype,
10033 row_bytes,
10034 scale,
10035 rp,
10036 ..
10037 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10038 _ => return self.matmul(w, x, m),
10039 };
10040 let (bytes, rp) = match w {
10043 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10044 _ => (bytes, rp),
10045 };
10046 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10047 if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
10051 return Ok(y);
10052 }
10053 if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
10062 && std::env::var("MEMRA_NO_BATCHED").is_err()
10063 && (m <= 4 || Self::b8_enabled())
10064 && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_F8_E4M3
10067 || qtype == QT_NVFP4 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_Q8_0)
10068 {
10069 let mcols = Self::batched_mcols(m);
10070 return self.qmatvec_mmvq_batched(
10071 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
10072 );
10073 }
10074 if self.mmvq_supports(qtype) {
10075 return self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
10078 }
10079 self.matmul_pre(w, &aq, &ad, x, m)
10082 }
10083
10084 pub fn matmul_decode_exact_pre(
10094 &self,
10095 w: &crate::model::GpuTensor,
10096 aq: &CudaSlice<i8>,
10097 ad: &CudaSlice<f32>,
10098 m: usize,
10099 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10100 use crate::model::GpuTensor;
10101 debug_assert!(
10102 self.uses_q8_1_fast(w),
10103 "matmul_decode_exact_pre: caller must guarantee q8_1-fast"
10104 );
10105 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
10107 return Ok(y);
10108 }
10109 let in_f = w.in_features();
10110 let out_f = w.out_features();
10111 let (bytes, qtype, row_bytes, scale, rp) = match w {
10112 GpuTensor::Quant {
10113 bytes,
10114 qtype,
10115 row_bytes,
10116 scale,
10117 rp,
10118 ..
10119 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10120 _ => {
10121 return Err(
10122 "matmul_decode_exact_pre: Quant tensor required (q8_1-fast contract)".into(),
10123 );
10124 }
10125 };
10126 let (bytes, rp) = match w {
10128 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10129 _ => (bytes, rp),
10130 };
10131 if (2..=16).contains(&m)
10133 && self.batched_supports(qtype)
10134 && self.mmvq_supports(qtype)
10135 && std::env::var("MEMRA_NO_BATCHED").is_err()
10136 && (m <= 4 || Self::b8_enabled())
10137 && (m <= 8
10138 || qtype == QT_Q4_0
10139 || qtype == QT_Q6_K
10140 || qtype == QT_F8_E4M3
10141 || qtype == QT_NVFP4
10142 || qtype == QT_Q4_K
10143 || qtype == QT_Q5_K
10144 || qtype == QT_Q8_0)
10145 {
10146 let mcols = Self::batched_mcols(m);
10147 return self.qmatvec_mmvq_batched(
10148 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
10149 );
10150 }
10151 if self.mmvq_supports(qtype) {
10152 return self.qmatvec_mmvq(bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
10153 }
10154 let x0 = self.zeros(0)?;
10157 self.matmul_pre(w, aq, ad, &x0, m)
10158 }
10159
10160 pub fn matmul_decode_exact_dual_pre(
10169 &self,
10170 w0: &crate::model::GpuTensor,
10171 w1: &crate::model::GpuTensor,
10172 aq: &CudaSlice<i8>,
10173 ad: &CudaSlice<f32>,
10174 m: usize,
10175 ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
10176 {
10177 use crate::model::GpuTensor;
10178 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10179 let on = *ON.get_or_init(|| {
10180 std::env::var("MEMRA_SPEC_DUAL_T")
10181 .map(|v| v != "0")
10182 .unwrap_or(true)
10183 });
10184 if !on
10185 || !(2..=7).contains(&m)
10186 || std::env::var("MEMRA_NO_BATCHED").is_ok()
10187 || !self.uses_q8_1_fast(w0)
10188 || !self.uses_q8_1_fast(w1)
10189 {
10190 return Ok(None);
10191 }
10192 if !self.mmvq_supports(QT_NVFP4) {
10197 return Ok(None);
10198 }
10199 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10200 if w1.in_features() != in_f || w1.out_features() != out_f {
10201 return Ok(None);
10202 }
10203 let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
10204 (
10205 GpuTensor::Quant {
10206 bytes: b0,
10207 qtype: q0,
10208 row_bytes: rb0,
10209 scale: s0,
10210 rp: rp0,
10211 rp4: None,
10212 ..
10213 },
10214 GpuTensor::Quant {
10215 bytes: b1,
10216 qtype: q1,
10217 row_bytes: rb1,
10218 scale: s1,
10219 rp: rp1,
10220 rp4: None,
10221 ..
10222 },
10223 ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
10224 (b0, b1, *rb0, *s0, *s1, *rp0)
10225 }
10226 _ => return Ok(None),
10227 };
10228 if m > 4 && !(rp && Self::b8_enabled() && std::env::var("MEMRA_B567").as_deref() != Ok("0"))
10231 {
10232 return Ok(None);
10233 }
10234 let (y0, y1) =
10235 self.qmatvec_batched_dual_raw(b0, b1, aq, ad, m, in_f, out_f, row_bytes, rp)?;
10236 Ok(Some(((y0, s0), (y1, s1))))
10237 }
10238
10239 pub fn matmul_decode_exact_dual(
10255 &self,
10256 w0: &crate::model::GpuTensor,
10257 w1: &crate::model::GpuTensor,
10258 x: &CudaSlice<f32>,
10259 m: usize,
10260 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10261 use crate::model::GpuTensor;
10262 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10263 let on = *ON.get_or_init(|| {
10264 std::env::var("MEMRA_SPEC_DUAL_T")
10265 .map(|v| v != "0")
10266 .unwrap_or(true)
10267 });
10268 if !on
10269 || !(2..=4).contains(&m)
10270 || std::env::var("MEMRA_NO_BATCHED").is_ok()
10271 || !self.uses_q8_1_fast(w0)
10272 || !self.uses_q8_1_fast(w1)
10273 {
10274 return Ok(None);
10275 }
10276 if !self.mmvq_supports(QT_NVFP4) {
10281 return Ok(None);
10282 }
10283 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10284 if w1.in_features() != in_f || w1.out_features() != out_f {
10285 return Ok(None);
10286 }
10287 let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
10288 (
10289 GpuTensor::Quant {
10290 bytes: b0,
10291 qtype: q0,
10292 row_bytes: rb0,
10293 scale: s0,
10294 rp: rp0,
10295 rp4: None,
10296 ..
10297 },
10298 GpuTensor::Quant {
10299 bytes: b1,
10300 qtype: q1,
10301 row_bytes: rb1,
10302 scale: s1,
10303 rp: rp1,
10304 rp4: None,
10305 ..
10306 },
10307 ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
10308 (b0, b1, *rb0, *s0, *s1, *rp0)
10309 }
10310 _ => return Ok(None),
10311 };
10312 if std::env::var("MEMRA_DEBUG").is_ok() {
10315 static ONCE: std::sync::Once = std::sync::Once::new();
10316 ONCE.call_once(|| eprintln!("[memra] dual gate+up batched ENGAGED (m={m} rp={rp})"));
10317 }
10318 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10319 let (y0, y1) =
10320 self.qmatvec_batched_dual_raw(b0, b1, &aq, &ad, m, in_f, out_f, row_bytes, rp)?;
10321 let mut y0 = y0;
10322 let mut y1 = y1;
10323 if s0 != 1.0 {
10324 self.scale_inplace(&mut y0, s0, m * out_f)?;
10325 }
10326 if s1 != 1.0 {
10327 self.scale_inplace(&mut y1, s1, m * out_f)?;
10328 }
10329 Ok(Some((y0, y1)))
10330 }
10331
10332 #[allow(clippy::too_many_arguments)]
10337 pub fn qmatvec_batched_dual_raw(
10338 &self,
10339 b0: &CudaSlice<u8>,
10340 b1: &CudaSlice<u8>,
10341 aq: &CudaSlice<i8>,
10342 ad: &CudaSlice<f32>,
10343 m: usize,
10344 in_f: usize,
10345 out_f: usize,
10346 row_bytes: usize,
10347 rp: bool,
10348 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10349 const ROWS_PER_BLOCK: u32 = 4;
10350 let mcols = Self::batched_mcols(m);
10351 let tiny_rp1 = rp
10354 && mcols == 4
10355 && out_f <= 128
10356 && std::env::var("MEMRA_NVFP4_AUX_DUAL").as_deref() != Ok("0");
10357 let (name, rows_per_block) = if tiny_rp1 {
10358 ("qmatvec_nvfp4_mmvq_dual_b4_rp", ROWS_PER_BLOCK)
10359 } else {
10360 match (mcols, rp, m) {
10361 (2, false, _) => ("qmatvec_nvfp4_mmvq_dual_b2", ROWS_PER_BLOCK),
10362 (4, false, _) => ("qmatvec_nvfp4_mmvq_dual_b4_r2", ROWS_PER_BLOCK * 2),
10363 (2, true, _) => ("qmatvec_nvfp4_mmvq_dual_b2_rp", ROWS_PER_BLOCK),
10364 (4, true, _) => ("qmatvec_nvfp4_mmvq_dual_b4_rpr2", ROWS_PER_BLOCK * 2),
10365 (8, true, 5) => ("qmatvec_nvfp4_mmvq_dual_b5_rpr2", ROWS_PER_BLOCK * 2),
10366 (8, true, 6) => ("qmatvec_nvfp4_mmvq_dual_b6_rpr2", ROWS_PER_BLOCK * 2),
10367 (8, true, 7) => ("qmatvec_nvfp4_mmvq_dual_b7_rpr2", ROWS_PER_BLOCK * 2),
10368 _ => {
10369 return Err(
10370 format!("qmatvec_batched_dual_raw: no dual kernel for m {m}").into(),
10371 );
10372 }
10373 }
10374 };
10375 let f = self.func(name);
10376 let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
10377 let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
10378 let cfg = LaunchConfig {
10379 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
10380 block_dim: (32, ROWS_PER_BLOCK, 1),
10381 shared_mem_bytes: 0,
10382 };
10383 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
10384 let __s_b = self.gpu.stream();
10385 let mut b = __s_b.launch_builder(&f);
10386 b.arg(b0)
10387 .arg(b1)
10388 .arg(aq)
10389 .arg(ad)
10390 .arg(&mut y0)
10391 .arg(&mut y1)
10392 .arg(&inf)
10393 .arg(&outf)
10394 .arg(&mi)
10395 .arg(&rb);
10396 unsafe {
10397 b.launch(cfg)?;
10398 }
10399 Ok((y0, y1))
10400 }
10401
10402 pub fn matmul_pre_dual_noscale(
10414 &self,
10415 w0: &crate::model::GpuTensor,
10416 w1: &crate::model::GpuTensor,
10417 aq: &CudaSlice<i8>,
10418 ad: &CudaSlice<f32>,
10419 m: usize,
10420 ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
10421 {
10422 use crate::model::GpuTensor;
10423 if m != 1 || !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
10424 return Ok(None);
10425 }
10426 if !self.mmvq_supports(QT_NVFP4) {
10436 return Ok(None);
10437 }
10438 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10439 if w1.in_features() != in_f || w1.out_features() != out_f {
10440 return Ok(None);
10441 }
10442 let no_mirror =
10455 |w: &crate::model::GpuTensor| !matches!(w, GpuTensor::Quant { rp4: Some(_), .. });
10456 if self.q8_ffn_fuse2_on()
10457 && no_mirror(w0)
10458 && no_mirror(w1)
10459 && let Some([p0, p1]) = self.q8_fused_params(&[w0, w1])
10460 {
10461 let (y0, y1) = self.q8_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2)?;
10462 return Ok(Some(((y0, 1.0), (y1, 1.0))));
10463 }
10464 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
10474 let (y0, y1) =
10475 self.e4m3_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2, 1.0, 1.0)?;
10476 return Ok(Some(((y0, p0.3), (y1, p1.3))));
10477 }
10478 let (b0, q0, rb0, s0, rp0) = match w0 {
10479 GpuTensor::Quant {
10480 bytes,
10481 qtype,
10482 row_bytes,
10483 scale,
10484 rp,
10485 ..
10486 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10487 _ => return Ok(None),
10488 };
10489 let (b1, q1, rb1, s1, rp1) = match w1 {
10490 GpuTensor::Quant {
10491 bytes,
10492 qtype,
10493 row_bytes,
10494 scale,
10495 rp,
10496 ..
10497 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10498 _ => return Ok(None),
10499 };
10500 if q0 != QT_NVFP4 || q1 != QT_NVFP4 || rb0 != rb1 || rp0 != rp1 {
10501 return Ok(None);
10502 }
10503 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
10505 let rows_per_block = ROWS_PER_BLOCK * RPW;
10506 let f = self.func(if rp0 {
10507 "qmatvec_nvfp4_mmvq_dual_mr2_rp"
10508 } else {
10509 "qmatvec_nvfp4_mmvq_dual_mr2"
10510 });
10511 let mut y0 = self.alloc_uninit::<f32>(out_f)?;
10512 let mut y1 = self.alloc_uninit::<f32>(out_f)?;
10513 let cfg = LaunchConfig {
10514 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
10515 block_dim: (32, ROWS_PER_BLOCK, 1),
10516 shared_mem_bytes: 0,
10517 };
10518 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, rb0 as i64);
10519 let one = 1.0f32;
10522 let __s_b = self.gpu.stream();
10523 let mut b = __s_b.launch_builder(&f);
10524 b.arg(b0)
10525 .arg(b1)
10526 .arg(aq)
10527 .arg(ad)
10528 .arg(&mut y0)
10529 .arg(&mut y1)
10530 .arg(&inf)
10531 .arg(&outf)
10532 .arg(&mi)
10533 .arg(&rb)
10534 .arg(&one)
10535 .arg(&one);
10536 unsafe {
10537 b.launch(cfg)?;
10538 }
10539 Ok(Some(((y0, s0), (y1, s1))))
10540 }
10541
10542 #[allow(clippy::too_many_arguments)]
10550 pub fn matmul_nvfp4_fused3(
10551 &self,
10552 w0: &crate::model::GpuTensor,
10553 w1: &crate::model::GpuTensor,
10554 w2: &crate::model::GpuTensor,
10555 aq: &CudaSlice<i8>,
10556 ad: &CudaSlice<f32>,
10557 m: usize,
10558 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
10559 {
10560 use crate::model::GpuTensor;
10561 if m != 1
10565 || !self.mmvq_supports(QT_NVFP4)
10566 || !self.uses_q8_1_fast(w0)
10567 || !self.uses_q8_1_fast(w1)
10568 || !self.uses_q8_1_fast(w2)
10569 {
10570 return Ok(None);
10571 }
10572 let unpack = |w: &crate::model::GpuTensor| match w {
10573 GpuTensor::Quant {
10574 bytes,
10575 qtype,
10576 scale,
10577 rp,
10578 ..
10579 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
10580 _ => None,
10581 };
10582 let (Some(p0), Some(p1), Some(p2)) = (unpack(w0), unpack(w1), unpack(w2)) else {
10583 return Ok(None);
10584 };
10585 let in_f = w0.in_features();
10586 if w1.in_features() != in_f || w2.in_features() != in_f {
10587 return Ok(None);
10588 }
10589 let (o0, o1, o2) = (w0.out_features(), w1.out_features(), w2.out_features());
10590 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
10592 let rows_pb = ROWS_PER_BLOCK * RPW;
10593 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
10594 let f = self.func("qmatvec_nvfp4_mmvq_fused3_rp");
10595 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
10596 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
10597 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
10598 let cfg = LaunchConfig {
10599 grid_dim: (nb(o0) + nb(o1) + nb(o2), m as u32, 1),
10600 block_dim: (32, ROWS_PER_BLOCK, 1),
10601 shared_mem_bytes: 0,
10602 };
10603 let (inf, oi0, oi1, oi2, mi) = (in_f as i32, o0 as i32, o1 as i32, o2 as i32, m as i32);
10604 let (b0, b1, b2) = unsafe { (&*p0.0, &*p1.0, &*p2.0) };
10607 let __s_b = self.gpu.stream();
10608 let mut b = __s_b.launch_builder(&f);
10609 b.arg(b0)
10610 .arg(b1)
10611 .arg(b2)
10612 .arg(aq)
10613 .arg(ad)
10614 .arg(&mut y0)
10615 .arg(&mut y1)
10616 .arg(&mut y2)
10617 .arg(&inf)
10618 .arg(&oi0)
10619 .arg(&oi1)
10620 .arg(&oi2)
10621 .arg(&mi)
10622 .arg(&p0.1)
10623 .arg(&p1.1)
10624 .arg(&p2.1);
10625 unsafe {
10626 b.launch(cfg)?;
10627 }
10628 Ok(Some((y0, y1, y2)))
10629 }
10630
10631 pub fn matmul_q8_fused2(
10639 &self,
10640 w0: &crate::model::GpuTensor,
10641 w1: &crate::model::GpuTensor,
10642 aq: &CudaSlice<i8>,
10643 ad: &CudaSlice<f32>,
10644 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10645 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
10651 return Ok(Some(self.e4m3_fused2_core(
10652 p0.0,
10653 p1.0,
10654 aq,
10655 ad,
10656 w0.in_features(),
10657 p0.1,
10658 p1.1,
10659 p0.2,
10660 p0.3,
10661 p1.3,
10662 )?));
10663 }
10664 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
10665 return Ok(None);
10666 };
10667 Ok(Some(self.q8_fused2_core(
10668 p0.0,
10669 p1.0,
10670 aq,
10671 ad,
10672 w0.in_features(),
10673 p0.1,
10674 p1.1,
10675 p0.2,
10676 )?))
10677 }
10678
10679 #[allow(clippy::too_many_arguments)]
10680 fn q8_fused2_core(
10681 &self,
10682 b0: &CudaSlice<u8>,
10683 b1: &CudaSlice<u8>,
10684 aq: &CudaSlice<i8>,
10685 ad: &CudaSlice<f32>,
10686 in_f: usize,
10687 out0: usize,
10688 out1: usize,
10689 row_bytes: usize,
10690 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10691 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
10693 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
10694 let f = self.func("qmatvec_q8_0_mmvq_fused2");
10695 let mut y0 = self.alloc_uninit::<f32>(out0)?;
10696 let mut y1 = self.alloc_uninit::<f32>(out1)?;
10697 let cfg = LaunchConfig {
10698 grid_dim: (nb0 + nb1, 1, 1),
10699 block_dim: (32, ROWS_PER_BLOCK, 1),
10700 shared_mem_bytes: 0,
10701 };
10702 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
10703 let __s_b = self.gpu.stream();
10704 let mut b = __s_b.launch_builder(&f);
10705 b.arg(b0)
10706 .arg(b1)
10707 .arg(aq)
10708 .arg(ad)
10709 .arg(&mut y0)
10710 .arg(&mut y1)
10711 .arg(&inf)
10712 .arg(&o0)
10713 .arg(&o1)
10714 .arg(&rbl);
10715 unsafe {
10716 b.launch(cfg)?;
10717 }
10718 Ok((y0, y1))
10719 }
10720
10721 pub fn matmul_q8_fused2_x(
10727 &self,
10728 w0: &crate::model::GpuTensor,
10729 w1: &crate::model::GpuTensor,
10730 x: &CudaSlice<f32>,
10731 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10732 if !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
10733 return Ok(None);
10734 }
10735 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
10736 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
10737 return Ok(Some(self.e4m3_fused2_core(
10738 p0.0,
10739 p1.0,
10740 &aq,
10741 &ad,
10742 w0.in_features(),
10743 p0.1,
10744 p1.1,
10745 p0.2,
10746 p0.3,
10747 p1.3,
10748 )?));
10749 }
10750 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
10751 return Ok(None);
10752 };
10753 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
10754 Ok(Some(self.q8_fused2_core(
10755 p0.0,
10756 p1.0,
10757 &aq,
10758 &ad,
10759 w0.in_features(),
10760 p0.1,
10761 p1.1,
10762 p0.2,
10763 )?))
10764 }
10765
10766 #[allow(clippy::too_many_arguments)]
10769 pub fn qmatvec_q8_fused2_raw(
10770 &self,
10771 b0: &CudaSlice<u8>,
10772 b1: &CudaSlice<u8>,
10773 x: &CudaSlice<f32>,
10774 in_f: usize,
10775 out0: usize,
10776 out1: usize,
10777 row_bytes: usize,
10778 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10779 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
10780 self.q8_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes)
10781 }
10782
10783 pub fn matmul_q4_fused3(
10789 &self,
10790 w0: &crate::model::GpuTensor,
10791 w1: &crate::model::GpuTensor,
10792 w2: &crate::model::GpuTensor,
10793 aq: &CudaSlice<i8>,
10794 ad: &CudaSlice<f32>,
10795 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
10796 {
10797 use crate::model::GpuTensor;
10798 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
10799 match w {
10800 GpuTensor::Quant {
10801 qtype, row_bytes, ..
10802 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
10803 _ => None,
10804 }
10805 };
10806 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
10807 return Ok(None);
10808 };
10809 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
10810 return Ok(None);
10811 }
10812 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
10816 match w {
10817 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
10818 Some(m) => (m, true),
10819 None => (bytes, *rp),
10820 },
10821 _ => unreachable!(),
10822 }
10823 }
10824 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
10825 if rp0 != rp1 || rp1 != rp2 {
10826 return Ok(None);
10827 }
10828 let rp = rp0;
10829 let rpb: u32 = 4;
10830 let mr1 = rp && Self::q40_mr1_on();
10834 let nb = |o: usize| {
10835 if mr1 {
10836 (o as u32).div_ceil(rpb)
10837 } else {
10838 (o as u32).div_ceil(2).div_ceil(rpb)
10839 }
10840 };
10841 let grid = nb(o0) + nb(o1) + nb(o2);
10842 let mut y0 = self.alloc_uninit::<f32>(o0)?;
10843 let mut y1 = self.alloc_uninit::<f32>(o1)?;
10844 let mut y2 = self.alloc_uninit::<f32>(o2)?;
10845 let f = self.func(if mr1 {
10846 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
10847 } else if rp {
10848 "qmatvec_q4_0_mmvq_fused3_rp"
10849 } else {
10850 "qmatvec_q4_0_mmvq_fused3"
10851 });
10852 let cfg = LaunchConfig {
10853 grid_dim: (grid, 1, 1),
10854 block_dim: (32, rpb, 1),
10855 shared_mem_bytes: 0,
10856 };
10857 let inf = w0.in_features() as i32;
10858 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
10859 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
10860 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
10863 {
10864 use cudarc::driver::{DevicePtr, DevicePtrMut};
10865 let s = &self.gpu.stream();
10866 let (p0, _g0) = b0.device_ptr(s);
10867 let (p1, _g1) = b1.device_ptr(s);
10868 let (p2, _g2) = b2.device_ptr(s);
10869 let (paq, _g3) = aq.device_ptr(s);
10870 let (pad, _g4) = ad.device_ptr(s);
10871 let (py0, _g5) = y0.device_ptr_mut(s);
10872 let (py1, _g6) = y1.device_ptr_mut(s);
10873 let (py2, _g7) = y2.device_ptr_mut(s);
10874 let mut ps = [
10875 &p0 as *const _ as *mut std::ffi::c_void,
10876 &p1 as *const _ as *mut _,
10877 &p2 as *const _ as *mut _,
10878 &paq as *const _ as *mut _,
10879 &pad as *const _ as *mut _,
10880 &py0 as *const _ as *mut _,
10881 &py1 as *const _ as *mut _,
10882 &py2 as *const _ as *mut _,
10883 &inf as *const _ as *mut _,
10884 &oo0 as *const _ as *mut _,
10885 &oo1 as *const _ as *mut _,
10886 &oo2 as *const _ as *mut _,
10887 &r0 as *const _ as *mut _,
10888 &r1 as *const _ as *mut _,
10889 &r2 as *const _ as *mut _,
10890 ];
10891 unsafe {
10892 self.launch_pdl(
10893 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
10894 (grid, 1, 1),
10895 (32, rpb, 1),
10896 &mut ps,
10897 )?;
10898 }
10899 }
10900 return Ok(Some((y0, y1, y2)));
10901 }
10902 let __s_b = self.gpu.stream();
10903 let mut b = __s_b.launch_builder(&f);
10904 b.arg(b0)
10905 .arg(b1)
10906 .arg(b2)
10907 .arg(aq)
10908 .arg(ad)
10909 .arg(&mut y0)
10910 .arg(&mut y1)
10911 .arg(&mut y2)
10912 .arg(&inf)
10913 .arg(&oo0)
10914 .arg(&oo1)
10915 .arg(&oo2)
10916 .arg(&r0)
10917 .arg(&r1)
10918 .arg(&r2);
10919 unsafe {
10920 b.launch(cfg)?;
10921 }
10922 Ok(Some((y0, y1, y2)))
10923 }
10924
10925 #[allow(clippy::too_many_arguments)]
10928 pub fn matmul_q4_fused3_into(
10929 &self,
10930 w0: &crate::model::GpuTensor,
10931 w1: &crate::model::GpuTensor,
10932 w2: &crate::model::GpuTensor,
10933 aq: &CudaSlice<i8>,
10934 ad: &CudaSlice<f32>,
10935 y0: &mut CudaSlice<f32>,
10936 y1: &mut CudaSlice<f32>,
10937 y2: &mut CudaSlice<f32>,
10938 ) -> Result<bool, Box<dyn std::error::Error>> {
10939 use crate::model::GpuTensor;
10940 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
10941 match w {
10942 GpuTensor::Quant {
10943 qtype, row_bytes, ..
10944 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
10945 _ => None,
10946 }
10947 };
10948 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
10949 return Ok(false);
10950 };
10951 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
10952 return Ok(false);
10953 }
10954 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
10955 match w {
10956 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
10957 Some(m) => (m, true),
10958 None => (bytes, *rp),
10959 },
10960 _ => unreachable!(),
10961 }
10962 }
10963 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
10964 if rp0 != rp1 || rp1 != rp2 {
10965 return Ok(false);
10966 }
10967 let rp = rp0;
10968 let rpb: u32 = 4;
10969 let mr1 = rp && Self::q40_mr1_on();
10970 let nb = |o: usize| {
10971 if mr1 {
10972 (o as u32).div_ceil(rpb)
10973 } else {
10974 (o as u32).div_ceil(2).div_ceil(rpb)
10975 }
10976 };
10977 let grid = nb(o0) + nb(o1) + nb(o2);
10978 debug_assert!(y0.len() >= o0 && y1.len() >= o1 && y2.len() >= o2);
10979 let f = self.func(if mr1 {
10980 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
10981 } else if rp {
10982 "qmatvec_q4_0_mmvq_fused3_rp"
10983 } else {
10984 "qmatvec_q4_0_mmvq_fused3"
10985 });
10986 let cfg = LaunchConfig {
10987 grid_dim: (grid, 1, 1),
10988 block_dim: (32, rpb, 1),
10989 shared_mem_bytes: 0,
10990 };
10991 let inf = w0.in_features() as i32;
10992 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
10993 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
10994 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
10996 use cudarc::driver::{DevicePtr, DevicePtrMut};
10997 let s = &self.gpu.stream();
10998 let (p0, _g0) = b0.device_ptr(s);
10999 let (p1, _g1) = b1.device_ptr(s);
11000 let (p2, _g2) = b2.device_ptr(s);
11001 let (paq, _g3) = aq.device_ptr(s);
11002 let (pad, _g4) = ad.device_ptr(s);
11003 let (py0, _g5) = y0.device_ptr_mut(s);
11004 let (py1, _g6) = y1.device_ptr_mut(s);
11005 let (py2, _g7) = y2.device_ptr_mut(s);
11006 let mut ps = [
11007 &p0 as *const _ as *mut std::ffi::c_void,
11008 &p1 as *const _ as *mut _,
11009 &p2 as *const _ as *mut _,
11010 &paq as *const _ as *mut _,
11011 &pad as *const _ as *mut _,
11012 &py0 as *const _ as *mut _,
11013 &py1 as *const _ as *mut _,
11014 &py2 as *const _ as *mut _,
11015 &inf as *const _ as *mut _,
11016 &oo0 as *const _ as *mut _,
11017 &oo1 as *const _ as *mut _,
11018 &oo2 as *const _ as *mut _,
11019 &r0 as *const _ as *mut _,
11020 &r1 as *const _ as *mut _,
11021 &r2 as *const _ as *mut _,
11022 ];
11023 unsafe {
11024 self.launch_pdl(
11025 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
11026 (grid, 1, 1),
11027 (32, rpb, 1),
11028 &mut ps,
11029 )?;
11030 }
11031 return Ok(true);
11032 }
11033 let __s_b = self.gpu.stream();
11034 let mut b = __s_b.launch_builder(&f);
11035 b.arg(b0)
11036 .arg(b1)
11037 .arg(b2)
11038 .arg(aq)
11039 .arg(ad)
11040 .arg(&mut *y0)
11041 .arg(&mut *y1)
11042 .arg(&mut *y2)
11043 .arg(&inf)
11044 .arg(&oo0)
11045 .arg(&oo1)
11046 .arg(&oo2)
11047 .arg(&r0)
11048 .arg(&r1)
11049 .arg(&r2);
11050 unsafe {
11051 b.launch(cfg)?;
11052 }
11053 Ok(true)
11054 }
11055
11056 pub fn matmul_q4_fused2(
11058 &self,
11059 w0: &crate::model::GpuTensor,
11060 w1: &crate::model::GpuTensor,
11061 aq: &CudaSlice<i8>,
11062 ad: &CudaSlice<f32>,
11063 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11064 use crate::model::GpuTensor;
11065 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11066 match w {
11067 GpuTensor::Quant {
11068 qtype, row_bytes, ..
11069 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11070 _ => None,
11071 }
11072 };
11073 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
11074 return Ok(None);
11075 };
11076 if w0.in_features() != w1.in_features() {
11077 return Ok(None);
11078 }
11079 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11081 match w {
11082 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11083 Some(m) => (m, true),
11084 None => (bytes, *rp),
11085 },
11086 _ => unreachable!(),
11087 }
11088 }
11089 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11090 if rp0 != rp1 {
11091 return Ok(None);
11092 }
11093 let rp = rp0;
11094 let rpb: u32 = 4;
11095 let mr1 = rp && Self::q40_mr1_on();
11097 let nb = |o: usize| {
11098 if mr1 {
11099 (o as u32).div_ceil(rpb)
11100 } else {
11101 (o as u32).div_ceil(2).div_ceil(rpb)
11102 }
11103 };
11104 let grid = nb(o0) + nb(o1);
11105 let mut y0 = self.alloc_uninit::<f32>(o0)?;
11106 let mut y1 = self.alloc_uninit::<f32>(o1)?;
11107 let f = self.func(if mr1 {
11108 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
11109 } else if rp {
11110 "qmatvec_q4_0_mmvq_fused2_rp"
11111 } else {
11112 "qmatvec_q4_0_mmvq_fused2"
11113 });
11114 let cfg = LaunchConfig {
11115 grid_dim: (grid, 1, 1),
11116 block_dim: (32, rpb, 1),
11117 shared_mem_bytes: 0,
11118 };
11119 let inf = w0.in_features() as i32;
11120 let (oo0, oo1) = (o0 as i32, o1 as i32);
11121 let (r0, r1) = (rb0 as i64, rb1 as i64);
11122 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11124 {
11125 use cudarc::driver::{DevicePtr, DevicePtrMut};
11126 let s = &self.gpu.stream();
11127 let (p0, _g0) = b0.device_ptr(s);
11128 let (p1, _g1) = b1.device_ptr(s);
11129 let (paq, _g2) = aq.device_ptr(s);
11130 let (pad, _g3) = ad.device_ptr(s);
11131 let (py0, _g4) = y0.device_ptr_mut(s);
11132 let (py1, _g5) = y1.device_ptr_mut(s);
11133 let mut ps = [
11134 &p0 as *const _ as *mut std::ffi::c_void,
11135 &p1 as *const _ as *mut _,
11136 &paq as *const _ as *mut _,
11137 &pad as *const _ as *mut _,
11138 &py0 as *const _ as *mut _,
11139 &py1 as *const _ as *mut _,
11140 &inf as *const _ as *mut _,
11141 &oo0 as *const _ as *mut _,
11142 &oo1 as *const _ as *mut _,
11143 &r0 as *const _ as *mut _,
11144 &r1 as *const _ as *mut _,
11145 ];
11146 unsafe {
11147 self.launch_pdl(
11148 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
11149 (grid, 1, 1),
11150 (32, rpb, 1),
11151 &mut ps,
11152 )?;
11153 }
11154 }
11155 return Ok(Some((y0, y1)));
11156 }
11157 let __s_b = self.gpu.stream();
11158 let mut b = __s_b.launch_builder(&f);
11159 b.arg(b0)
11160 .arg(b1)
11161 .arg(aq)
11162 .arg(ad)
11163 .arg(&mut y0)
11164 .arg(&mut y1)
11165 .arg(&inf)
11166 .arg(&oo0)
11167 .arg(&oo1)
11168 .arg(&r0)
11169 .arg(&r1);
11170 unsafe {
11171 b.launch(cfg)?;
11172 }
11173 Ok(Some((y0, y1)))
11174 }
11175
11176 pub fn matmul_q4_fused2_into(
11178 &self,
11179 w0: &crate::model::GpuTensor,
11180 w1: &crate::model::GpuTensor,
11181 aq: &CudaSlice<i8>,
11182 ad: &CudaSlice<f32>,
11183 y0: &mut CudaSlice<f32>,
11184 y1: &mut CudaSlice<f32>,
11185 ) -> Result<bool, Box<dyn std::error::Error>> {
11186 use crate::model::GpuTensor;
11187 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11188 match w {
11189 GpuTensor::Quant {
11190 qtype, row_bytes, ..
11191 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11192 _ => None,
11193 }
11194 };
11195 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
11196 return Ok(false);
11197 };
11198 if w0.in_features() != w1.in_features() {
11199 return Ok(false);
11200 }
11201 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11202 match w {
11203 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11204 Some(m) => (m, true),
11205 None => (bytes, *rp),
11206 },
11207 _ => unreachable!(),
11208 }
11209 }
11210 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11211 if rp0 != rp1 {
11212 return Ok(false);
11213 }
11214 let rp = rp0;
11215 let rpb: u32 = 4;
11216 let mr1 = rp && Self::q40_mr1_on();
11217 let nb = |o: usize| {
11218 if mr1 {
11219 (o as u32).div_ceil(rpb)
11220 } else {
11221 (o as u32).div_ceil(2).div_ceil(rpb)
11222 }
11223 };
11224 let grid = nb(o0) + nb(o1);
11225 debug_assert!(y0.len() >= o0 && y1.len() >= o1);
11226 let f = self.func(if mr1 {
11227 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
11228 } else if rp {
11229 "qmatvec_q4_0_mmvq_fused2_rp"
11230 } else {
11231 "qmatvec_q4_0_mmvq_fused2"
11232 });
11233 let cfg = LaunchConfig {
11234 grid_dim: (grid, 1, 1),
11235 block_dim: (32, rpb, 1),
11236 shared_mem_bytes: 0,
11237 };
11238 let inf = w0.in_features() as i32;
11239 let (oo0, oo1) = (o0 as i32, o1 as i32);
11240 let (r0, r1) = (rb0 as i64, rb1 as i64);
11241 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11243 use cudarc::driver::{DevicePtr, DevicePtrMut};
11244 let s = &self.gpu.stream();
11245 let (p0, _g0) = b0.device_ptr(s);
11246 let (p1, _g1) = b1.device_ptr(s);
11247 let (paq, _g2) = aq.device_ptr(s);
11248 let (pad, _g3) = ad.device_ptr(s);
11249 let (py0, _g4) = y0.device_ptr_mut(s);
11250 let (py1, _g5) = y1.device_ptr_mut(s);
11251 let mut ps = [
11252 &p0 as *const _ as *mut std::ffi::c_void,
11253 &p1 as *const _ as *mut _,
11254 &paq as *const _ as *mut _,
11255 &pad as *const _ as *mut _,
11256 &py0 as *const _ as *mut _,
11257 &py1 as *const _ as *mut _,
11258 &inf as *const _ as *mut _,
11259 &oo0 as *const _ as *mut _,
11260 &oo1 as *const _ as *mut _,
11261 &r0 as *const _ as *mut _,
11262 &r1 as *const _ as *mut _,
11263 ];
11264 unsafe {
11265 self.launch_pdl(
11266 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
11267 (grid, 1, 1),
11268 (32, rpb, 1),
11269 &mut ps,
11270 )?;
11271 }
11272 return Ok(true);
11273 }
11274 let __s_b = self.gpu.stream();
11275 let mut b = __s_b.launch_builder(&f);
11276 b.arg(b0)
11277 .arg(b1)
11278 .arg(aq)
11279 .arg(ad)
11280 .arg(&mut *y0)
11281 .arg(&mut *y1)
11282 .arg(&inf)
11283 .arg(&oo0)
11284 .arg(&oo1)
11285 .arg(&r0)
11286 .arg(&r1);
11287 unsafe {
11288 b.launch(cfg)?;
11289 }
11290 Ok(true)
11291 }
11292
11293 pub fn matmul_q4_fused2_batched(
11298 &self,
11299 w0: &crate::model::GpuTensor,
11300 w1: &crate::model::GpuTensor,
11301 aq: &CudaSlice<i8>,
11302 ad: &CudaSlice<f32>,
11303 m: usize,
11304 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11305 use crate::model::GpuTensor;
11306 if m < 2 || m > 8 {
11307 return Ok(None);
11308 }
11309 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11310 match w {
11311 GpuTensor::Quant {
11312 qtype, row_bytes, ..
11313 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11314 _ => None,
11315 }
11316 };
11317 let (Some((rb0, o0)), Some((_rb1, o1))) = (q4(w0), q4(w1)) else {
11318 return Ok(None);
11319 };
11320 if w0.in_features() != w1.in_features() {
11321 return Ok(None);
11322 }
11323 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11324 match w {
11325 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11326 Some(mr) => (mr, true),
11327 None => (bytes, *rp),
11328 },
11329 _ => unreachable!(),
11330 }
11331 }
11332 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11333 if !rp0 || !rp1 {
11334 return Ok(None);
11335 }
11336 let mcols = Self::batched_mcols(m);
11337 let rpb: u32 = 4;
11338 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
11339 let grid = nb(o0) + nb(o1);
11340 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11341 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11342 let f = self.func(match mcols {
11343 2 => "qmatvec_q4_0_mmvq_b2_f2_rp",
11344 4 => "qmatvec_q4_0_mmvq_b4_f2_rp",
11345 _ => "qmatvec_q4_0_mmvq_b8_f2_rp",
11346 });
11347 let cfg = LaunchConfig {
11348 grid_dim: (grid, 1, 1),
11349 block_dim: (32, rpb, 1),
11350 shared_mem_bytes: 0,
11351 };
11352 let inf = w0.in_features() as i32;
11353 let (oo0, oo1, mi) = (o0 as i32, o1 as i32, m as i32);
11354 let rb = rb0 as i64;
11355 let __s_b = self.gpu.stream();
11356 let mut b = __s_b.launch_builder(&f);
11357 b.arg(b0)
11358 .arg(b1)
11359 .arg(aq)
11360 .arg(ad)
11361 .arg(&mut y0)
11362 .arg(&mut y1)
11363 .arg(&inf)
11364 .arg(&oo0)
11365 .arg(&oo1)
11366 .arg(&mi)
11367 .arg(&rb);
11368 unsafe {
11369 b.launch(cfg)?;
11370 }
11371 Ok(Some((y0, y1)))
11372 }
11373
11374 #[allow(clippy::too_many_arguments)]
11377 pub fn matmul_q4_fused3_batched(
11378 &self,
11379 w0: &crate::model::GpuTensor,
11380 w1: &crate::model::GpuTensor,
11381 w2: &crate::model::GpuTensor,
11382 aq: &CudaSlice<i8>,
11383 ad: &CudaSlice<f32>,
11384 m: usize,
11385 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11386 {
11387 use crate::model::GpuTensor;
11388 if m < 2 || m > 8 {
11389 return Ok(None);
11390 }
11391 let q4 = |w: &GpuTensor| -> Option<usize> {
11392 match w {
11393 GpuTensor::Quant { qtype, .. } if *qtype == QT_Q4_0 => Some(w.out_features()),
11394 _ => None,
11395 }
11396 };
11397 let (Some(o0), Some(o1), Some(o2)) = (q4(w0), q4(w1), q4(w2)) else {
11398 return Ok(None);
11399 };
11400 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
11401 return Ok(None);
11402 }
11403 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11404 match w {
11405 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11406 Some(mr) => (mr, true),
11407 None => (bytes, *rp),
11408 },
11409 _ => unreachable!(),
11410 }
11411 }
11412 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
11413 if !rp0 || !rp1 || !rp2 {
11414 return Ok(None);
11415 }
11416 let mcols = Self::batched_mcols(m);
11417 let rpb: u32 = 4;
11418 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
11419 let grid = nb(o0) + nb(o1) + nb(o2);
11420 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11421 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11422 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
11423 let f = self.func(match mcols {
11424 2 => "qmatvec_q4_0_mmvq_b2_f3_rp",
11425 4 => "qmatvec_q4_0_mmvq_b4_f3_rp",
11426 _ => "qmatvec_q4_0_mmvq_b8_f3_rp",
11427 });
11428 let cfg = LaunchConfig {
11429 grid_dim: (grid, 1, 1),
11430 block_dim: (32, rpb, 1),
11431 shared_mem_bytes: 0,
11432 };
11433 let inf = w0.in_features() as i32;
11434 let (oo0, oo1, oo2, mi) = (o0 as i32, o1 as i32, o2 as i32, m as i32);
11435 let rb = 0i64;
11436 let __s_b = self.gpu.stream();
11437 let mut b = __s_b.launch_builder(&f);
11438 b.arg(b0)
11439 .arg(b1)
11440 .arg(b2)
11441 .arg(aq)
11442 .arg(ad)
11443 .arg(&mut y0)
11444 .arg(&mut y1)
11445 .arg(&mut y2)
11446 .arg(&inf)
11447 .arg(&oo0)
11448 .arg(&oo1)
11449 .arg(&oo2)
11450 .arg(&mi)
11451 .arg(&rb);
11452 unsafe {
11453 b.launch(cfg)?;
11454 }
11455 Ok(Some((y0, y1, y2)))
11456 }
11457
11458 pub fn matmul_q8_fused3(
11459 &self,
11460 w0: &crate::model::GpuTensor,
11461 w1: &crate::model::GpuTensor,
11462 w2: &crate::model::GpuTensor,
11463 aq: &CudaSlice<i8>,
11464 ad: &CudaSlice<f32>,
11465 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11466 {
11467 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
11470 return Ok(Some(self.e4m3_fused3_core(
11471 p0.0,
11472 p1.0,
11473 p2.0,
11474 aq,
11475 ad,
11476 w0.in_features(),
11477 p0.1,
11478 p1.1,
11479 p2.1,
11480 p0.2,
11481 p0.3,
11482 p1.3,
11483 p2.3,
11484 )?));
11485 }
11486 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
11487 return Ok(None);
11488 };
11489 Ok(Some(self.q8_fused3_core(
11490 p0.0,
11491 p1.0,
11492 p2.0,
11493 aq,
11494 ad,
11495 w0.in_features(),
11496 p0.1,
11497 p1.1,
11498 p2.1,
11499 p0.2,
11500 )?))
11501 }
11502
11503 #[allow(clippy::too_many_arguments)]
11504 fn q8_fused3_core(
11505 &self,
11506 b0: &CudaSlice<u8>,
11507 b1: &CudaSlice<u8>,
11508 b2: &CudaSlice<u8>,
11509 aq: &CudaSlice<i8>,
11510 ad: &CudaSlice<f32>,
11511 in_f: usize,
11512 out0: usize,
11513 out1: usize,
11514 out2: usize,
11515 row_bytes: usize,
11516 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11517 const ROWS_PER_BLOCK: u32 = 4;
11518 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11519 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11520 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
11521 let f = self.func("qmatvec_q8_0_mmvq_fused3");
11522 let mut y0 = self.alloc_uninit::<f32>(out0)?;
11523 let mut y1 = self.alloc_uninit::<f32>(out1)?;
11524 let mut y2 = self.alloc_uninit::<f32>(out2)?;
11525 let cfg = LaunchConfig {
11526 grid_dim: (nb0 + nb1 + nb2, 1, 1),
11527 block_dim: (32, ROWS_PER_BLOCK, 1),
11528 shared_mem_bytes: 0,
11529 };
11530 let (inf, o0, o1, o2, rbl) = (
11531 in_f as i32,
11532 out0 as i32,
11533 out1 as i32,
11534 out2 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(b2)
11542 .arg(aq)
11543 .arg(ad)
11544 .arg(&mut y0)
11545 .arg(&mut y1)
11546 .arg(&mut y2)
11547 .arg(&inf)
11548 .arg(&o0)
11549 .arg(&o1)
11550 .arg(&o2)
11551 .arg(&rbl);
11552 unsafe {
11553 b.launch(cfg)?;
11554 }
11555 Ok((y0, y1, y2))
11556 }
11557
11558 #[allow(clippy::too_many_arguments)]
11560 pub fn qmatvec_q8_fused3_raw(
11561 &self,
11562 b0: &CudaSlice<u8>,
11563 b1: &CudaSlice<u8>,
11564 b2: &CudaSlice<u8>,
11565 x: &CudaSlice<f32>,
11566 in_f: usize,
11567 out0: usize,
11568 out1: usize,
11569 out2: usize,
11570 row_bytes: usize,
11571 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11572 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
11573 self.q8_fused3_core(b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes)
11574 }
11575
11576 pub fn matmul_q8_fused2_t(
11587 &self,
11588 w0: &crate::model::GpuTensor,
11589 w1: &crate::model::GpuTensor,
11590 aq: &CudaSlice<i8>,
11591 ad: &CudaSlice<f32>,
11592 m: usize,
11593 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11594 if !(2..=8).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
11598 return Ok(None);
11599 }
11600 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
11603 if m > 4 && !Self::b8_enabled() {
11604 return Ok(None);
11605 }
11606 return Ok(Some(self.e4m3_fused2_t_core(
11607 p0.0,
11608 p1.0,
11609 aq,
11610 ad,
11611 m,
11612 w0.in_features(),
11613 p0.1,
11614 p1.1,
11615 p0.2,
11616 p0.3,
11617 p1.3,
11618 )?));
11619 }
11620 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
11621 return Ok(None);
11622 };
11623 Ok(Some(self.q8_fused2_t_core(
11624 p0.0,
11625 p1.0,
11626 aq,
11627 ad,
11628 m,
11629 w0.in_features(),
11630 p0.1,
11631 p1.1,
11632 p0.2,
11633 )?))
11634 }
11635
11636 #[allow(clippy::too_many_arguments)]
11637 fn q8_fused2_t_core(
11638 &self,
11639 b0: &CudaSlice<u8>,
11640 b1: &CudaSlice<u8>,
11641 aq: &CudaSlice<i8>,
11642 ad: &CudaSlice<f32>,
11643 m: usize,
11644 in_f: usize,
11645 out0: usize,
11646 out1: usize,
11647 row_bytes: usize,
11648 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11649 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11651 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11652 let f = self.func(match Self::batched_mcols(m) {
11653 2 => "qmatvec_q8_0_mmvq_fused2_b2",
11654 4 => "qmatvec_q8_0_mmvq_fused2_b4",
11655 _ => "qmatvec_q8_0_mmvq_fused2_b8",
11657 });
11658 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
11659 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
11660 let cfg = LaunchConfig {
11661 grid_dim: (nb0 + nb1, 1, 1),
11662 block_dim: (32, ROWS_PER_BLOCK, 1),
11663 shared_mem_bytes: 0,
11664 };
11665 let (inf, o0, o1, mi, rbl) = (
11666 in_f as i32,
11667 out0 as i32,
11668 out1 as i32,
11669 m as i32,
11670 row_bytes as i64,
11671 );
11672 let __s_b = self.gpu.stream();
11673 let mut b = __s_b.launch_builder(&f);
11674 b.arg(b0)
11675 .arg(b1)
11676 .arg(aq)
11677 .arg(ad)
11678 .arg(&mut y0)
11679 .arg(&mut y1)
11680 .arg(&inf)
11681 .arg(&o0)
11682 .arg(&o1)
11683 .arg(&mi)
11684 .arg(&rbl);
11685 unsafe {
11686 b.launch(cfg)?;
11687 }
11688 Ok((y0, y1))
11689 }
11690
11691 #[allow(clippy::too_many_arguments)]
11694 pub fn qmatvec_q8_fused2_t_raw(
11695 &self,
11696 b0: &CudaSlice<u8>,
11697 b1: &CudaSlice<u8>,
11698 x: &CudaSlice<f32>,
11699 m: usize,
11700 in_f: usize,
11701 out0: usize,
11702 out1: usize,
11703 row_bytes: usize,
11704 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11705 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
11706 self.q8_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes)
11707 }
11708
11709 #[allow(clippy::too_many_arguments)]
11712 pub fn matmul_q8_fused3_t(
11713 &self,
11714 w0: &crate::model::GpuTensor,
11715 w1: &crate::model::GpuTensor,
11716 w2: &crate::model::GpuTensor,
11717 aq: &CudaSlice<i8>,
11718 ad: &CudaSlice<f32>,
11719 m: usize,
11720 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11721 {
11722 if !(2..=4).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
11723 return Ok(None);
11724 }
11725 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
11726 return Ok(Some(self.e4m3_fused3_t_core(
11727 p0.0,
11728 p1.0,
11729 p2.0,
11730 aq,
11731 ad,
11732 m,
11733 w0.in_features(),
11734 p0.1,
11735 p1.1,
11736 p2.1,
11737 p0.2,
11738 p0.3,
11739 p1.3,
11740 p2.3,
11741 )?));
11742 }
11743 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
11744 return Ok(None);
11745 };
11746 Ok(Some(self.q8_fused3_t_core(
11747 p0.0,
11748 p1.0,
11749 p2.0,
11750 aq,
11751 ad,
11752 m,
11753 w0.in_features(),
11754 p0.1,
11755 p1.1,
11756 p2.1,
11757 p0.2,
11758 )?))
11759 }
11760
11761 #[allow(clippy::too_many_arguments)]
11762 fn q8_fused3_t_core(
11763 &self,
11764 b0: &CudaSlice<u8>,
11765 b1: &CudaSlice<u8>,
11766 b2: &CudaSlice<u8>,
11767 aq: &CudaSlice<i8>,
11768 ad: &CudaSlice<f32>,
11769 m: usize,
11770 in_f: usize,
11771 out0: usize,
11772 out1: usize,
11773 out2: usize,
11774 row_bytes: usize,
11775 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11776 const ROWS_PER_BLOCK: u32 = 4;
11777 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11778 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11779 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
11780 let f = self.func(if Self::batched_mcols(m) == 2 {
11781 "qmatvec_q8_0_mmvq_fused3_b2"
11782 } else {
11783 "qmatvec_q8_0_mmvq_fused3_b4"
11784 });
11785 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
11786 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
11787 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
11788 let cfg = LaunchConfig {
11789 grid_dim: (nb0 + nb1 + nb2, 1, 1),
11790 block_dim: (32, ROWS_PER_BLOCK, 1),
11791 shared_mem_bytes: 0,
11792 };
11793 let (inf, o0, o1, o2, mi, rbl) = (
11794 in_f as i32,
11795 out0 as i32,
11796 out1 as i32,
11797 out2 as i32,
11798 m as i32,
11799 row_bytes as i64,
11800 );
11801 let __s_b = self.gpu.stream();
11802 let mut b = __s_b.launch_builder(&f);
11803 b.arg(b0)
11804 .arg(b1)
11805 .arg(b2)
11806 .arg(aq)
11807 .arg(ad)
11808 .arg(&mut y0)
11809 .arg(&mut y1)
11810 .arg(&mut y2)
11811 .arg(&inf)
11812 .arg(&o0)
11813 .arg(&o1)
11814 .arg(&o2)
11815 .arg(&mi)
11816 .arg(&rbl);
11817 unsafe {
11818 b.launch(cfg)?;
11819 }
11820 Ok((y0, y1, y2))
11821 }
11822
11823 #[allow(clippy::too_many_arguments)]
11825 pub fn qmatvec_q8_fused3_t_raw(
11826 &self,
11827 b0: &CudaSlice<u8>,
11828 b1: &CudaSlice<u8>,
11829 b2: &CudaSlice<u8>,
11830 x: &CudaSlice<f32>,
11831 m: usize,
11832 in_f: usize,
11833 out0: usize,
11834 out1: usize,
11835 out2: usize,
11836 row_bytes: usize,
11837 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11838 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
11839 self.q8_fused3_t_core(b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes)
11840 }
11841
11842 pub fn q8_ffn_fuse2_on(&self) -> bool {
11846 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11847 *ON.get_or_init(|| std::env::var("MEMRA_Q8_FFN_FUSE2").as_deref() != Ok("0"))
11848 }
11849
11850 #[allow(clippy::type_complexity)]
11856 fn q8_fused_params<'w, const N: usize>(
11857 &self,
11858 ws: &[&'w crate::model::GpuTensor; N],
11859 ) -> Option<[(&'w CudaSlice<u8>, usize, usize); N]> {
11860 use crate::model::GpuTensor;
11861 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
11862 return None;
11863 }
11864 if std::env::var("MEMRA_Q8_DUAL").is_ok_and(|v| v == "0") {
11865 return None;
11866 }
11867 let in_f = ws[0].in_features();
11868 let mut out: [Option<(&CudaSlice<u8>, usize, usize)>; N] = [None; N];
11869 for (i, w) in ws.iter().enumerate() {
11870 match w {
11871 GpuTensor::Quant {
11872 bytes,
11873 qtype,
11874 row_bytes,
11875 scale,
11876 ..
11877 } if *qtype == QT_Q8_0 && *scale == 1.0 && w.in_features() == in_f => {
11878 out[i] = Some((bytes, w.out_features(), *row_bytes))
11879 }
11880 _ => return None,
11881 }
11882 }
11883 Some(out.map(|o| o.unwrap()))
11884 }
11885
11886 pub fn e4m3_dual_on(&self) -> bool {
11889 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11890 *ON.get_or_init(|| std::env::var("MEMRA_E4M3_DUAL").as_deref() != Ok("0"))
11891 }
11892
11893 #[allow(clippy::type_complexity)]
11905 fn e4m3_fused_params<'w, const N: usize>(
11906 &self,
11907 ws: &[&'w crate::model::GpuTensor; N],
11908 ) -> Option<[(&'w CudaSlice<u8>, usize, usize, f32); N]> {
11909 use crate::model::GpuTensor;
11910 if !self.e4m3_dual_on() {
11911 return None;
11912 }
11913 let in_f = ws[0].in_features();
11914 let mut out: [Option<(&CudaSlice<u8>, usize, usize, f32)>; N] = [None; N];
11915 for (i, w) in ws.iter().enumerate() {
11916 match w {
11917 GpuTensor::Quant {
11918 bytes,
11919 qtype,
11920 row_bytes,
11921 scale,
11922 rp,
11923 rp4,
11924 ..
11925 } if *qtype == QT_F8_E4M3
11926 && w.in_features() == in_f
11927 && *row_bytes == in_f
11928 && !*rp
11929 && rp4.is_none() =>
11930 {
11931 out[i] = Some((bytes, w.out_features(), *row_bytes, *scale))
11932 }
11933 _ => return None,
11934 }
11935 }
11936 Some(out.map(|o| o.unwrap()))
11937 }
11938
11939 #[allow(clippy::too_many_arguments)]
11943 fn e4m3_fused2_core(
11944 &self,
11945 b0: &CudaSlice<u8>,
11946 b1: &CudaSlice<u8>,
11947 aq: &CudaSlice<i8>,
11948 ad: &CudaSlice<f32>,
11949 in_f: usize,
11950 out0: usize,
11951 out1: usize,
11952 row_bytes: usize,
11953 ws0: f32,
11954 ws1: f32,
11955 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11956 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11958 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11959 let f = self.func("qmatvec_e4m3_mmvq_fused2");
11960 let mut y0 = self.alloc_uninit::<f32>(out0)?;
11961 let mut y1 = self.alloc_uninit::<f32>(out1)?;
11962 let cfg = LaunchConfig {
11963 grid_dim: (nb0 + nb1, 1, 1),
11964 block_dim: (32, ROWS_PER_BLOCK, 1),
11965 shared_mem_bytes: 0,
11966 };
11967 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
11968 let __s_b = self.gpu.stream();
11969 let mut b = __s_b.launch_builder(&f);
11970 b.arg(b0)
11971 .arg(b1)
11972 .arg(aq)
11973 .arg(ad)
11974 .arg(&mut y0)
11975 .arg(&mut y1)
11976 .arg(&inf)
11977 .arg(&o0)
11978 .arg(&o1)
11979 .arg(&rbl)
11980 .arg(&ws0)
11981 .arg(&ws1);
11982 unsafe {
11983 b.launch(cfg)?;
11984 }
11985 Ok((y0, y1))
11986 }
11987
11988 #[allow(clippy::too_many_arguments)]
11990 fn e4m3_fused3_core(
11991 &self,
11992 b0: &CudaSlice<u8>,
11993 b1: &CudaSlice<u8>,
11994 b2: &CudaSlice<u8>,
11995 aq: &CudaSlice<i8>,
11996 ad: &CudaSlice<f32>,
11997 in_f: usize,
11998 out0: usize,
11999 out1: usize,
12000 out2: usize,
12001 row_bytes: usize,
12002 ws0: f32,
12003 ws1: f32,
12004 ws2: f32,
12005 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12006 const ROWS_PER_BLOCK: u32 = 4;
12007 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12008 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12009 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12010 let f = self.func("qmatvec_e4m3_mmvq_fused3");
12011 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12012 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12013 let mut y2 = self.alloc_uninit::<f32>(out2)?;
12014 let cfg = LaunchConfig {
12015 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12016 block_dim: (32, ROWS_PER_BLOCK, 1),
12017 shared_mem_bytes: 0,
12018 };
12019 let (inf, o0, o1, o2, rbl) = (
12020 in_f as i32,
12021 out0 as i32,
12022 out1 as i32,
12023 out2 as i32,
12024 row_bytes as i64,
12025 );
12026 let __s_b = self.gpu.stream();
12027 let mut b = __s_b.launch_builder(&f);
12028 b.arg(b0)
12029 .arg(b1)
12030 .arg(b2)
12031 .arg(aq)
12032 .arg(ad)
12033 .arg(&mut y0)
12034 .arg(&mut y1)
12035 .arg(&mut y2)
12036 .arg(&inf)
12037 .arg(&o0)
12038 .arg(&o1)
12039 .arg(&o2)
12040 .arg(&rbl)
12041 .arg(&ws0)
12042 .arg(&ws1)
12043 .arg(&ws2);
12044 unsafe {
12045 b.launch(cfg)?;
12046 }
12047 Ok((y0, y1, y2))
12048 }
12049
12050 #[allow(clippy::too_many_arguments)]
12054 fn e4m3_fused2_t_core(
12055 &self,
12056 b0: &CudaSlice<u8>,
12057 b1: &CudaSlice<u8>,
12058 aq: &CudaSlice<i8>,
12059 ad: &CudaSlice<f32>,
12060 m: usize,
12061 in_f: usize,
12062 out0: usize,
12063 out1: usize,
12064 row_bytes: usize,
12065 ws0: f32,
12066 ws1: f32,
12067 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12068 const ROWS_PER_BLOCK: u32 = 4;
12069 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12070 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12071 let f = self.func(match Self::batched_mcols(m) {
12072 2 => "qmatvec_e4m3_mmvq_fused2_b2",
12073 4 => "qmatvec_e4m3_mmvq_fused2_b4",
12074 _ => "qmatvec_e4m3_mmvq_fused2_b8",
12075 });
12076 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12077 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12078 let cfg = LaunchConfig {
12079 grid_dim: (nb0 + nb1, 1, 1),
12080 block_dim: (32, ROWS_PER_BLOCK, 1),
12081 shared_mem_bytes: 0,
12082 };
12083 let (inf, o0, o1, mi, rbl) = (
12084 in_f as i32,
12085 out0 as i32,
12086 out1 as i32,
12087 m as i32,
12088 row_bytes as i64,
12089 );
12090 let __s_b = self.gpu.stream();
12091 let mut b = __s_b.launch_builder(&f);
12092 b.arg(b0)
12093 .arg(b1)
12094 .arg(aq)
12095 .arg(ad)
12096 .arg(&mut y0)
12097 .arg(&mut y1)
12098 .arg(&inf)
12099 .arg(&o0)
12100 .arg(&o1)
12101 .arg(&mi)
12102 .arg(&rbl);
12103 unsafe {
12104 b.launch(cfg)?;
12105 }
12106 if ws0 != 1.0 {
12107 self.scale_inplace(&mut y0, ws0, m * out0)?;
12108 }
12109 if ws1 != 1.0 {
12110 self.scale_inplace(&mut y1, ws1, m * out1)?;
12111 }
12112 Ok((y0, y1))
12113 }
12114
12115 #[allow(clippy::too_many_arguments)]
12117 fn e4m3_fused3_t_core(
12118 &self,
12119 b0: &CudaSlice<u8>,
12120 b1: &CudaSlice<u8>,
12121 b2: &CudaSlice<u8>,
12122 aq: &CudaSlice<i8>,
12123 ad: &CudaSlice<f32>,
12124 m: usize,
12125 in_f: usize,
12126 out0: usize,
12127 out1: usize,
12128 out2: usize,
12129 row_bytes: usize,
12130 ws0: f32,
12131 ws1: f32,
12132 ws2: f32,
12133 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12134 const ROWS_PER_BLOCK: u32 = 4;
12135 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12136 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12137 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12138 let f = self.func(if Self::batched_mcols(m) == 2 {
12139 "qmatvec_e4m3_mmvq_fused3_b2"
12140 } else {
12141 "qmatvec_e4m3_mmvq_fused3_b4"
12142 });
12143 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12144 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12145 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
12146 let cfg = LaunchConfig {
12147 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12148 block_dim: (32, ROWS_PER_BLOCK, 1),
12149 shared_mem_bytes: 0,
12150 };
12151 let (inf, o0, o1, o2, mi, rbl) = (
12152 in_f as i32,
12153 out0 as i32,
12154 out1 as i32,
12155 out2 as i32,
12156 m as i32,
12157 row_bytes as i64,
12158 );
12159 let __s_b = self.gpu.stream();
12160 let mut b = __s_b.launch_builder(&f);
12161 b.arg(b0)
12162 .arg(b1)
12163 .arg(b2)
12164 .arg(aq)
12165 .arg(ad)
12166 .arg(&mut y0)
12167 .arg(&mut y1)
12168 .arg(&mut y2)
12169 .arg(&inf)
12170 .arg(&o0)
12171 .arg(&o1)
12172 .arg(&o2)
12173 .arg(&mi)
12174 .arg(&rbl);
12175 unsafe {
12176 b.launch(cfg)?;
12177 }
12178 if ws0 != 1.0 {
12179 self.scale_inplace(&mut y0, ws0, m * out0)?;
12180 }
12181 if ws1 != 1.0 {
12182 self.scale_inplace(&mut y1, ws1, m * out1)?;
12183 }
12184 if ws2 != 1.0 {
12185 self.scale_inplace(&mut y2, ws2, m * out2)?;
12186 }
12187 Ok((y0, y1, y2))
12188 }
12189
12190 pub fn qmatvec_e4m3_blk_mmvq(
12200 &self,
12201 bytes: &CudaSlice<u8>,
12202 aq: &CudaSlice<i8>,
12203 ad: &CudaSlice<f32>,
12204 scales: &CudaSlice<f32>,
12205 m: usize,
12206 in_f: usize,
12207 out_f: usize,
12208 row_bytes: usize,
12209 scale_cols: usize,
12210 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12211 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_e4m3_blk_mmvq_into(
12213 bytes, aq, ad, scales, m, in_f, out_f, row_bytes, scale_cols, &mut y,
12214 )?;
12215 Ok(y)
12216 }
12217
12218 #[allow(clippy::too_many_arguments)]
12220 pub fn qmatvec_e4m3_blk_mmvq_into(
12221 &self,
12222 bytes: &CudaSlice<u8>,
12223 aq: &CudaSlice<i8>,
12224 ad: &CudaSlice<f32>,
12225 scales: &CudaSlice<f32>,
12226 m: usize,
12227 in_f: usize,
12228 out_f: usize,
12229 row_bytes: usize,
12230 scale_cols: usize,
12231 y: &mut CudaSlice<f32>,
12232 ) -> Result<(), Box<dyn std::error::Error>> {
12233 const ROWS_PER_BLOCK: u32 = 4; let f = self.func("qmatvec_e4m3_blk_mmvq");
12235 let cfg = LaunchConfig {
12236 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), m as u32, 1),
12237 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
12240 let (inf, outf, mi, rb, sc) = (
12241 in_f as i32,
12242 out_f as i32,
12243 m as i32,
12244 row_bytes as i64,
12245 scale_cols as i32,
12246 );
12247 let __s_b = self.gpu.stream();
12248 let mut b = __s_b.launch_builder(&f);
12249 b.arg(bytes)
12250 .arg(aq)
12251 .arg(ad)
12252 .arg(scales)
12253 .arg(&mut *y)
12254 .arg(&inf)
12255 .arg(&outf)
12256 .arg(&mi)
12257 .arg(&rb)
12258 .arg(&sc);
12259 unsafe {
12260 b.launch(cfg)?;
12261 }
12262 Ok(())
12263 }
12264
12265 #[allow(clippy::too_many_arguments)]
12271 pub fn qmatvec_e4m3_blk_mmvq_batched(
12272 &self,
12273 bytes: &CudaSlice<u8>,
12274 aq: &CudaSlice<i8>,
12275 ad: &CudaSlice<f32>,
12276 scales: &CudaSlice<f32>,
12277 m: usize,
12278 in_f: usize,
12279 out_f: usize,
12280 row_bytes: usize,
12281 scale_cols: usize,
12282 mcols: usize,
12283 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12284 const ROWS_PER_BLOCK: u32 = 4; debug_assert!(mcols >= m, "blk batched: mcols {mcols} < m {m}");
12286 let name = match mcols {
12287 2 => "qmatvec_e4m3_blk_mmvq_b2",
12288 4 => "qmatvec_e4m3_blk_mmvq_b4",
12289 8 => "qmatvec_e4m3_blk_mmvq_b8",
12290 16 => "qmatvec_e4m3_blk_mmvq_b16",
12291 _ => {
12292 return Err(
12293 format!("qmatvec_e4m3_blk_mmvq_batched: no kernel for mcols {mcols}").into(),
12294 );
12295 }
12296 };
12297 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
12298 let f = self.func(name);
12299 let cfg = LaunchConfig {
12300 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), 1, 1),
12301 block_dim: (32, ROWS_PER_BLOCK, 1),
12302 shared_mem_bytes: 0,
12303 };
12304 let (inf, outf, mi, rb, sc) = (
12305 in_f as i32,
12306 out_f as i32,
12307 m as i32,
12308 row_bytes as i64,
12309 scale_cols as i32,
12310 );
12311 let __s_b = self.gpu.stream();
12312 let mut b = __s_b.launch_builder(&f);
12313 b.arg(bytes)
12314 .arg(aq)
12315 .arg(ad)
12316 .arg(scales)
12317 .arg(&mut y)
12318 .arg(&inf)
12319 .arg(&outf)
12320 .arg(&mi)
12321 .arg(&rb)
12322 .arg(&sc);
12323 unsafe {
12324 b.launch(cfg)?;
12325 }
12326 Ok(y)
12327 }
12328
12329 #[allow(clippy::too_many_arguments)]
12332 pub fn qmatvec_e4m3_blk_batched_raw(
12333 &self,
12334 bytes: &CudaSlice<u8>,
12335 x: &CudaSlice<f32>,
12336 scales: &CudaSlice<f32>,
12337 m: usize,
12338 in_f: usize,
12339 out_f: usize,
12340 row_bytes: usize,
12341 scale_cols: usize,
12342 mcols: usize,
12343 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12344 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12345 self.qmatvec_e4m3_blk_mmvq_batched(
12346 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols, mcols,
12347 )
12348 }
12349
12350 #[allow(clippy::too_many_arguments)]
12353 pub fn qmatvec_e4m3_blk_mmvq_raw(
12354 &self,
12355 bytes: &CudaSlice<u8>,
12356 x: &CudaSlice<f32>,
12357 scales: &CudaSlice<f32>,
12358 m: usize,
12359 in_f: usize,
12360 out_f: usize,
12361 row_bytes: usize,
12362 scale_cols: usize,
12363 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12364 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12365 self.qmatvec_e4m3_blk_mmvq(
12366 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols,
12367 )
12368 }
12369
12370 #[allow(clippy::too_many_arguments)]
12373 pub fn qmatvec_e4m3_fused2_raw(
12374 &self,
12375 b0: &CudaSlice<u8>,
12376 b1: &CudaSlice<u8>,
12377 x: &CudaSlice<f32>,
12378 in_f: usize,
12379 out0: usize,
12380 out1: usize,
12381 row_bytes: usize,
12382 ws0: f32,
12383 ws1: f32,
12384 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12385 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
12386 self.e4m3_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes, ws0, ws1)
12387 }
12388
12389 #[allow(clippy::too_many_arguments)]
12390 pub fn qmatvec_e4m3_fused3_raw(
12391 &self,
12392 b0: &CudaSlice<u8>,
12393 b1: &CudaSlice<u8>,
12394 b2: &CudaSlice<u8>,
12395 x: &CudaSlice<f32>,
12396 in_f: usize,
12397 out0: usize,
12398 out1: usize,
12399 out2: usize,
12400 row_bytes: usize,
12401 ws0: f32,
12402 ws1: f32,
12403 ws2: f32,
12404 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12405 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
12406 self.e4m3_fused3_core(
12407 b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
12408 )
12409 }
12410
12411 #[allow(clippy::too_many_arguments)]
12412 pub fn qmatvec_e4m3_fused2_t_raw(
12413 &self,
12414 b0: &CudaSlice<u8>,
12415 b1: &CudaSlice<u8>,
12416 x: &CudaSlice<f32>,
12417 m: usize,
12418 in_f: usize,
12419 out0: usize,
12420 out1: usize,
12421 row_bytes: usize,
12422 ws0: f32,
12423 ws1: f32,
12424 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12425 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12426 self.e4m3_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes, ws0, ws1)
12427 }
12428
12429 #[allow(clippy::too_many_arguments)]
12430 pub fn qmatvec_e4m3_fused3_t_raw(
12431 &self,
12432 b0: &CudaSlice<u8>,
12433 b1: &CudaSlice<u8>,
12434 b2: &CudaSlice<u8>,
12435 x: &CudaSlice<f32>,
12436 m: usize,
12437 in_f: usize,
12438 out0: usize,
12439 out1: usize,
12440 out2: usize,
12441 row_bytes: usize,
12442 ws0: f32,
12443 ws1: f32,
12444 ws2: f32,
12445 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12446 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12447 self.e4m3_fused3_t_core(
12448 b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
12449 )
12450 }
12451
12452 fn try_e4m3_blk_pre(
12463 &self,
12464 w: &crate::model::GpuTensor,
12465 aq: &CudaSlice<i8>,
12466 ad: &CudaSlice<f32>,
12467 m: usize,
12468 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
12469 use crate::model::GpuTensor;
12470 if let GpuTensor::Quant {
12471 bytes,
12472 qtype,
12473 row_bytes,
12474 blk: Some(g),
12475 ..
12476 } = w
12477 {
12478 if *qtype == QT_F8_E4M3_BLK {
12479 if (2..=16).contains(&m)
12485 && std::env::var("MEMRA_NO_BATCHED").is_err()
12486 && (m <= 4 || Self::b8_enabled())
12487 {
12488 let mcols = Self::batched_mcols(m);
12489 return Ok(Some(self.qmatvec_e4m3_blk_mmvq_batched(
12490 bytes,
12491 aq,
12492 ad,
12493 &g.scales,
12494 m,
12495 w.in_features(),
12496 w.out_features(),
12497 *row_bytes,
12498 g.cols,
12499 mcols,
12500 )?));
12501 }
12502 return Ok(Some(self.qmatvec_e4m3_blk_mmvq(
12503 bytes,
12504 aq,
12505 ad,
12506 &g.scales,
12507 m,
12508 w.in_features(),
12509 w.out_features(),
12510 *row_bytes,
12511 g.cols,
12512 )?));
12513 }
12514 }
12515 Ok(None)
12516 }
12517
12518 fn try_e4m3_blk_prefill(
12565 &self,
12566 w: &crate::model::GpuTensor,
12567 x: &CudaSlice<f32>,
12568 m: usize,
12569 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
12570 use crate::model::GpuTensor;
12571 let GpuTensor::Quant {
12572 bytes,
12573 qtype,
12574 blk: Some(g),
12575 ..
12576 } = w
12577 else {
12578 return Ok(None);
12579 };
12580 if *qtype != QT_F8_E4M3_BLK {
12581 return Ok(None);
12582 }
12583 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
12588 return Ok(Some(y));
12589 }
12590 let (in_f, out_f) = (w.in_features(), w.out_features());
12591 let slab = self.fp8_blk_dequant_q8_0_dev(bytes, &g.scales, out_f, in_f)?;
12592 let tmp = GpuTensor::Quant {
12593 bytes: slab,
12594 qtype: QT_Q8_0,
12595 row_bytes: in_f / 32 * 34,
12596 ne: vec![in_f as u64, out_f as u64],
12597 scale: 1.0,
12598 rp: false,
12599 #[cfg(memra_cutlass)]
12600 cutlass: None,
12601 fp8: None,
12602 blk: None,
12603 f16: None,
12604 rp4: None,
12605 };
12606 Ok(Some(self.matmul(&tmp, x, m)?))
12608 }
12609
12610 pub fn matmul_pre_noscale(
12611 &self,
12612 w: &crate::model::GpuTensor,
12613 aq: &CudaSlice<i8>,
12614 ad: &CudaSlice<f32>,
12615 m: usize,
12616 ) -> Result<Option<(CudaSlice<f32>, f32)>, Box<dyn std::error::Error>> {
12617 use crate::model::GpuTensor;
12618 if m == 1 {
12622 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
12623 return Ok(Some((y, 1.0)));
12624 }
12625 }
12626 if m != 1 || !self.uses_q8_1_fast(w) {
12628 return Ok(None);
12629 }
12630 let in_f = w.in_features();
12631 let out_f = w.out_features();
12632 let (bytes, qtype, row_bytes, scale, rp) = match w {
12633 GpuTensor::Quant {
12634 bytes,
12635 qtype,
12636 row_bytes,
12637 scale,
12638 rp,
12639 ..
12640 } => (bytes, *qtype, *row_bytes, *scale, *rp),
12641 _ => return Ok(None),
12642 };
12643 if self.mmvq_supports(qtype) {
12645 let (mbytes, mrp) = match w {
12647 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
12648 _ => (bytes, rp),
12649 };
12650 let y = self.qmatvec_mmvq(
12651 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, 1.0, mrp,
12652 )?;
12653 return Ok(Some((y, scale)));
12654 }
12655 let name = match qtype {
12657 QT_Q8_0 => "qmatvec_q8_0_dp4a",
12658 QT_Q4_K => "qmatvec_q4_K_dp4a",
12659 QT_Q6_K => "qmatvec_q6_K_dp4a",
12660 QT_Q5_K => "qmatvec_q5_K_dp4a",
12661 QT_Q3_K => "qmatvec_q3_K_dp4a",
12662 QT_NVFP4 => {
12663 if rp {
12664 "qmatvec_nvfp4_dp4a_rp"
12665 } else {
12666 "qmatvec_nvfp4_dp4a"
12667 }
12668 }
12669 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
12670 _ => return Ok(None),
12671 };
12672 let f = self.func(name);
12673 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
12674 let cfg = LaunchConfig {
12675 grid_dim: (out_f as u32, m as u32, 1),
12676 block_dim: (128, 1, 1),
12677 shared_mem_bytes: 0,
12678 };
12679 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
12680 let __s_b = self.gpu.stream();
12681 let mut b = __s_b.launch_builder(&f);
12682 b.arg(bytes)
12683 .arg(aq)
12684 .arg(ad)
12685 .arg(&mut y)
12686 .arg(&inf)
12687 .arg(&outf)
12688 .arg(&mi)
12689 .arg(&rb);
12690 unsafe {
12691 b.launch(cfg)?;
12692 }
12693 Ok(Some((y, scale)))
12694 }
12695
12696 pub fn mmvq_supports(&self, qtype: i32) -> bool {
12699 if qtype == QT_F8_E4M3 {
12704 return true;
12705 }
12706 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
12707 return false;
12708 }
12709 matches!(
12710 qtype,
12711 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_Q4_0
12712 )
12713 }
12714
12715 pub fn qmatvec_mmvq(
12720 &self,
12721 bytes: &CudaSlice<u8>,
12722 aq: &CudaSlice<i8>,
12723 ad: &CudaSlice<f32>,
12724 m: usize,
12725 in_f: usize,
12726 out_f: usize,
12727 qtype: i32,
12728 row_bytes: usize,
12729 scale: f32,
12730 rp: bool,
12731 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12732 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_mmvq_into(
12734 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp, &mut y,
12735 )?;
12736 Ok(y)
12737 }
12738
12739 #[allow(clippy::too_many_arguments)]
12741 pub fn qmatvec_mmvq_into(
12742 &self,
12743 bytes: &CudaSlice<u8>,
12744 aq: &CudaSlice<i8>,
12745 ad: &CudaSlice<f32>,
12746 m: usize,
12747 in_f: usize,
12748 out_f: usize,
12749 qtype: i32,
12750 row_bytes: usize,
12751 scale: f32,
12752 rp: bool,
12753 y: &mut CudaSlice<f32>,
12754 ) -> Result<(), Box<dyn std::error::Error>> {
12755 debug_assert!(y.len() >= m * out_f);
12756 const ROWS_PER_BLOCK: u32 = 4; if qtype == QT_Q8_0
12762 && rp
12763 && m == 1
12764 && out_f >= 64
12765 && (out_f as u32).div_ceil(ROWS_PER_BLOCK) < 4 * self.sm_count() as u32
12766 && {
12767 static G2: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12768 *G2.get_or_init(|| std::env::var("MEMRA_Q80_G2").as_deref() != Ok("0"))
12769 }
12770 {
12771 let f = self.func("qmatvec_q8_0_mmvq_rp_g2");
12772 let cfg = LaunchConfig {
12773 grid_dim: ((out_f as u32).div_ceil(2), 1, 1),
12774 block_dim: (32, 2, 1),
12775 shared_mem_bytes: 0,
12776 };
12777 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, row_bytes as i64);
12778 let __s_b = self.gpu.stream();
12779 let mut b = __s_b.launch_builder(&f);
12780 b.arg(bytes)
12781 .arg(aq)
12782 .arg(ad)
12783 .arg(&mut *y)
12784 .arg(&inf)
12785 .arg(&outf)
12786 .arg(&mi)
12787 .arg(&rb);
12788 unsafe {
12789 b.launch(cfg)?;
12790 }
12791 if scale != 1.0 {
12792 self.scale_inplace(y, scale, out_f)?;
12793 }
12794 return Ok(());
12795 }
12796 let mut mr: u32 = if m == 1 && (qtype == QT_NVFP4 || qtype == QT_Q5_K) {
12805 2
12806 } else {
12807 1
12808 };
12809 if m == 1 && qtype == QT_Q4_0 {
12814 static Q40MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
12815 mr = *Q40MR.get_or_init(|| {
12818 std::env::var("MEMRA_Q40_MR")
12819 .ok()
12820 .and_then(|v| v.parse().ok())
12821 .unwrap_or(1)
12822 });
12823 }
12824 let q5_mode = std::env::var("MEMRA_Q5K_ISSUE").ok();
12835 let q5_force = q5_mode.as_deref() == Some("2");
12836 let q5_il = qtype == QT_Q5_K
12839 && m == 1
12840 && (q5_force || q5_mode.as_deref().map(|v| v != "0").unwrap_or(true));
12841 if q5_il && !q5_force && out_f > 65536 {
12842 mr = 1;
12843 }
12844 if qtype == QT_Q4_0 && rp && mr != 1 {
12847 mr = 2;
12848 }
12849 if qtype == QT_Q8_0 && rp {
12853 static Q80MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
12854 mr = *Q80MR.get_or_init(|| {
12855 std::env::var("MEMRA_Q80_MR")
12856 .ok()
12857 .and_then(|v| v.parse().ok())
12858 .unwrap_or(1)
12859 });
12860 }
12861 let name = match (qtype, mr, rp) {
12862 (QT_NVFP4, 2, false) => "qmatvec_nvfp4_mmvq_mr2",
12863 (QT_NVFP4, 2, true) => "qmatvec_nvfp4_mmvq_mr2_rp",
12864 (QT_NVFP4, _, true) => "qmatvec_nvfp4_mmvq_rp",
12865 (QT_Q4_0, 1, true) => "qmatvec_q4_0_mmvq_rp",
12866 (QT_Q4_0, _, true) => "qmatvec_q4_0_mmvq_mr2_rp",
12867 (QT_Q5_K, 2, _) => {
12868 if q5_il {
12869 "qmatvec_q5_K_mmvq_mr2_il"
12870 } else {
12871 "qmatvec_q5_K_mmvq_mr2"
12872 }
12873 }
12874 (QT_Q8_0, 2, true) => "qmatvec_q8_0_mmvq_mr2_rp",
12875 (QT_Q8_0, _, true)
12880 if in_f % 1024 == 0 && {
12881 static CA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12882 *CA.get_or_init(|| std::env::var("MEMRA_Q80_CA").as_deref() == Ok("1"))
12883 } =>
12884 {
12885 "qmatvec_q8_0_mmvq_rpca"
12886 }
12887 (QT_Q8_0, _, true) => "qmatvec_q8_0_mmvq_rp",
12888 (QT_Q8_0, _, _) => "qmatvec_q8_0_mmvq",
12889 (QT_Q4_K, _, true) => "qmatvec_q4_K_mmvq_rp",
12893 (QT_Q6_K, _, true) => "qmatvec_q6_K_mmvq_rp",
12894 (QT_Q4_K, _, _) => "qmatvec_q4_K_mmvq",
12895 (QT_Q4_0, 2, false) => "qmatvec_q4_0_mmvq_mr2",
12896 (QT_Q4_0, _, false) => "qmatvec_q4_0_mmvq",
12897 (QT_Q5_K, _, _) => {
12898 if q5_il {
12899 "qmatvec_q5_K_mmvq_il"
12900 } else {
12901 "qmatvec_q5_K_mmvq"
12902 }
12903 }
12904 (QT_Q6_K, _, _) => "qmatvec_q6_K_mmvq",
12905 (QT_NVFP4, _, false) => "qmatvec_nvfp4_mmvq",
12906 (QT_F8_E4M3, _, _) => "qmatvec_e4m3_mmvq",
12907 _ => panic!("qmatvec_mmvq: qtype {qtype} has no MMVQ kernel"),
12908 };
12909 let f = self.func(name);
12910 let rows_per_block = ROWS_PER_BLOCK * mr;
12912 let cfg = LaunchConfig {
12913 grid_dim: (
12914 (out_f as u32 + rows_per_block - 1) / rows_per_block,
12915 m as u32,
12916 1,
12917 ),
12918 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
12921 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
12922 let __s_b = self.gpu.stream();
12923 let mut b = __s_b.launch_builder(&f);
12924 if qtype == QT_NVFP4 || qtype == QT_F8_E4M3 {
12929 b.arg(bytes)
12930 .arg(aq)
12931 .arg(ad)
12932 .arg(&mut *y)
12933 .arg(&inf)
12934 .arg(&outf)
12935 .arg(&mi)
12936 .arg(&rb)
12937 .arg(&scale);
12938 unsafe {
12939 b.launch(cfg)?;
12940 }
12941 } else if Self::pdl_on()
12942 && Self::pdl_mmvq_on()
12943 && matches!(
12944 name,
12945 "qmatvec_q4_0_mmvq_rp" | "qmatvec_q6_K_mmvq" | "qmatvec_q6_K_mmvq_rp"
12946 )
12947 {
12948 {
12952 use cudarc::driver::{DevicePtr, DevicePtrMut};
12953 let s = &self.gpu.stream();
12954 let (pw, _g0) = bytes.device_ptr(s);
12955 let (paq, _g1) = aq.device_ptr(s);
12956 let (pad, _g2) = ad.device_ptr(s);
12957 let (py, _g3) = y.device_ptr_mut(s);
12958 let mut ps = [
12959 &pw as *const _ as *mut std::ffi::c_void,
12960 &paq as *const _ as *mut _,
12961 &pad as *const _ as *mut _,
12962 &py as *const _ as *mut _,
12963 &inf as *const _ as *mut _,
12964 &outf as *const _ as *mut _,
12965 &mi as *const _ as *mut _,
12966 &rb as *const _ as *mut _,
12967 ];
12968 unsafe {
12969 self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
12970 }
12971 }
12972 if scale != 1.0 {
12973 self.scale_inplace(y, scale, m * out_f)?;
12974 }
12975 } else {
12976 b.arg(bytes)
12977 .arg(aq)
12978 .arg(ad)
12979 .arg(&mut *y)
12980 .arg(&inf)
12981 .arg(&outf)
12982 .arg(&mi)
12983 .arg(&rb);
12984 unsafe {
12985 b.launch(cfg)?;
12986 }
12987 if scale != 1.0 {
12988 self.scale_inplace(y, scale, m * out_f)?;
12989 }
12990 }
12991 Ok(())
12992 }
12993
12994 pub fn qmatvec_mmvq_raw(
12998 &self,
12999 bytes: &CudaSlice<u8>,
13000 x: &CudaSlice<f32>,
13001 m: usize,
13002 in_f: usize,
13003 out_f: usize,
13004 qtype: i32,
13005 row_bytes: usize,
13006 rp: bool,
13007 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13008 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13009 self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, 1.0, rp)
13010 }
13011
13012 pub fn batched_supports(&self, qtype: i32) -> bool {
13016 matches!(
13017 qtype,
13018 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_F8_E4M3 | QT_Q4_0
13019 )
13020 }
13021
13022 pub fn iq_fast_enabled() -> bool {
13030 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13031 *ON.get_or_init(|| {
13032 std::env::var("MEMRA_IQ_FAST")
13033 .map(|v| v != "0")
13034 .unwrap_or(true)
13035 })
13036 }
13037
13038 pub fn b8_enabled() -> bool {
13041 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13042 *ON.get_or_init(|| std::env::var("MEMRA_B8").map(|v| v != "0").unwrap_or(true))
13043 }
13044
13045 pub fn batched_mcols(m: usize) -> usize {
13047 if m == 2 {
13048 2
13049 } else if m <= 4 {
13050 4
13051 } else if m <= 8 {
13052 8
13053 } else {
13054 16
13055 }
13056 }
13057
13058 fn batched_kernel_name(qtype: i32, mcols: usize) -> Option<&'static str> {
13063 Some(match (qtype, mcols) {
13064 (QT_Q8_0, 2) => "qmatvec_q8_0_mmvq_b2",
13065 (QT_Q8_0, 4) => "qmatvec_q8_0_mmvq_b4",
13066 (QT_Q8_0, 8) => "qmatvec_q8_0_mmvq_b8",
13067 (QT_Q8_0, 16) => "qmatvec_q8_0_mmvq_b16",
13073 (QT_Q4_K, 2) => "qmatvec_q4_K_mmvq_b2",
13074 (QT_Q4_K, 4) => "qmatvec_q4_K_mmvq_b4",
13075 (QT_Q4_K, 8) => "qmatvec_q4_K_mmvq_b8",
13076 (QT_Q4_K, 16) => "qmatvec_q4_K_mmvq_b16",
13079 (QT_Q5_K, 2) => "qmatvec_q5_K_mmvq_b2",
13080 (QT_Q5_K, 4) => "qmatvec_q5_K_mmvq_b4",
13081 (QT_Q5_K, 8) => "qmatvec_q5_K_mmvq_b8",
13082 (QT_Q5_K, 16) => "qmatvec_q5_K_mmvq_b16",
13085 (QT_Q6_K, 2) => "qmatvec_q6_K_mmvq_b2",
13086 (QT_Q6_K, 4) => "qmatvec_q6_K_mmvq_b4",
13087 (QT_Q6_K, 8) => "qmatvec_q6_K_mmvq_b8",
13088 (QT_Q6_K, 16) => "qmatvec_q6_K_mmvq_b16",
13089 (QT_NVFP4, 2) => "qmatvec_nvfp4_mmvq_b2",
13090 (QT_NVFP4, 4) => "qmatvec_nvfp4_mmvq_b4",
13091 (QT_NVFP4, 8) => "qmatvec_nvfp4_mmvq_b8",
13092 (QT_NVFP4, 16) => "qmatvec_nvfp4_mmvq_b16",
13096 (QT_F8_E4M3, 2) => "qmatvec_e4m3_mmvq_b2",
13097 (QT_F8_E4M3, 4) => "qmatvec_e4m3_mmvq_b4",
13098 (QT_F8_E4M3, 8) => "qmatvec_e4m3_mmvq_b8",
13099 (QT_F8_E4M3, 16) => "qmatvec_e4m3_mmvq_b16",
13103 (QT_Q4_0, 2) => "qmatvec_q4_0_mmvq_b2",
13104 (QT_Q4_0, 4) => "qmatvec_q4_0_mmvq_b4",
13105 (QT_Q4_0, 8) => "qmatvec_q4_0_mmvq_b8",
13106 (QT_Q4_0, 16) => "qmatvec_q4_0_mmvq_b16",
13107 _ => return None,
13108 })
13109 }
13110
13111 pub fn sm_count(&self) -> i32 {
13146 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
13147 *SMS.get_or_init(|| {
13148 use cudarc::driver::sys::CUdevice_attribute_enum as A;
13149 self.gpu
13150 .ctx
13151 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
13152 .unwrap_or(82)
13153 })
13154 }
13155
13156 pub fn batched_variant(
13157 &self,
13158 _m: usize,
13159 in_f: usize,
13160 out_f: usize,
13161 qtype: i32,
13162 row_bytes: usize,
13163 mcols: usize,
13164 rp: bool,
13165 ) -> &'static str {
13166 if qtype == QT_Q8_0 {
13171 return if rp { "rp" } else { "base" };
13172 }
13173 static BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13174 let bv = *BV.get_or_init(|| match std::env::var("MEMRA_MMVQ_BV").as_deref() {
13175 Ok("base") => "base",
13176 Ok("pf") => "pf",
13177 Ok("r2") => "r2",
13178 Ok("r2w8") => "r2w8",
13179 Ok("pfr2") => "pfr2",
13180 Ok("ca") => "ca",
13181 Ok("car2") => "car2",
13182 Ok("rp") => "rp",
13185 Ok("rpr2") => "rpr2",
13186 Ok("rpr2w8") => "rpr2w8",
13187 Ok("rpca") => "rpca",
13190 Ok("rpcar2") => "rpcar2",
13191 Ok("rpsc") => "rpsc",
13198 Ok("rpms") => "rpms",
13199 Ok("rpmsc") => "rpmsc",
13200 Ok("rpks") => "rpks",
13201 Ok("rpksc") => "rpksc",
13202 _ => "auto",
13203 });
13204 let ca_ok = qtype == QT_NVFP4 && (row_bytes % 16 == 0) && (in_f % 1024 == 0);
13208 static KS_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13213 let ks_on = *KS_ON.get_or_init(|| std::env::var("MEMRA_KS").as_deref() != Ok("0"));
13214 let sc_ok = ks_on && qtype == QT_NVFP4 && (in_f % 256 == 0) && (in_f / 64 <= 272);
13215 let ks_ok = ks_on && qtype == QT_NVFP4 && (in_f % 512 == 0) && (in_f / 64 <= 272);
13216 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
13217 let sms = *SMS.get_or_init(|| {
13218 use cudarc::driver::sys::CUdevice_attribute_enum as A;
13219 self.gpu
13220 .ctx
13221 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
13222 .unwrap_or(82)
13223 });
13224 let kq_r2 = matches!(qtype, QT_Q4_K | QT_Q5_K | QT_Q6_K);
13244 static KQBV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13247 let kq_bv = *KQBV.get_or_init(|| match std::env::var("MEMRA_KQ_BV").as_deref() {
13248 Ok("base") => "base",
13249 Ok("r2") => "r2",
13250 Ok("r2w8") => "r2w8",
13251 _ => "auto",
13252 });
13253 let variant: &'static str = if qtype == QT_Q4_0 {
13254 static Q40BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13258 let q40 = *Q40BV.get_or_init(|| match std::env::var("MEMRA_Q40_BV").as_deref() {
13259 Ok("base") => "base",
13265 Ok("r2") => "r2",
13266 Ok("ms") => "ms",
13267 Ok("sm") => "sm",
13268 Ok("la") => "la",
13269 _ => "auto",
13270 });
13271 let v = if q40 != "auto" {
13272 q40
13273 } else if (out_f as u32).div_ceil(8) >= 4 * sms as u32 {
13274 "r2"
13275 } else {
13276 "base"
13277 };
13278 if rp {
13283 match v {
13284 "ms" => "r2ms_rp",
13285 "sm" => "r2sm_rp",
13286 "la" => "r2la_rp",
13287 "r2" => "r2_rp",
13288 _ => "rp",
13289 }
13290 } else if matches!(v, "ms" | "sm" | "la") {
13291 "r2"
13292 } else {
13293 v
13294 }
13295 } else if qtype != QT_NVFP4 && !kq_r2 {
13296 "base"
13297 } else if kq_r2 && rp {
13298 "rp"
13302 } else if kq_r2 {
13303 if kq_bv != "auto" {
13306 if kq_bv == "r2w8" && mcols != 4 {
13307 "r2"
13308 } else {
13309 kq_bv
13310 }
13311 } else if bv != "auto" {
13312 match bv {
13313 "r2" | "pfr2" | "rpr2" | "car2" => "r2",
13314 "r2w8" | "rpr2w8" => {
13315 if mcols != 4 {
13316 "r2"
13317 } else {
13318 "r2w8"
13319 }
13320 }
13321 _ => "base", }
13323 } else {
13324 let blocks = (out_f + 7) / 8;
13325 let waves = blocks as f64 / (7 * sms as usize) as f64;
13326 let filled = blocks >= 4 * sms as usize;
13327 let use_r2 = if qtype == QT_Q4_K {
13328 filled
13329 } else {
13330 waves >= 2.0
13331 };
13332 if use_r2 { "r2" } else { "base" }
13333 }
13334 } else if bv != "auto" {
13335 let v = if bv == "r2w8" && mcols == 2 {
13340 "r2"
13341 } else if bv == "ca" && (!ca_ok || mcols == 8) {
13342 "pf"
13343 } else if bv == "car2" && (!ca_ok || mcols == 8) {
13344 "r2"
13345 } else if bv == "pfr2" && mcols == 8 {
13346 "r2"
13347 } else if (bv == "rpr2w8" || bv == "rpr2") && mcols == 2 {
13348 "rpr2"
13349 }
13350 else if (bv == "rpca" || bv == "rpcar2") && (!ca_ok || mcols == 8) {
13352 if mcols == 8 { "rpr2w8" } else { "rpr2" }
13353 } else if bv == "rpcar2" && mcols == 2 {
13354 "rpca"
13355 }
13356 else if (bv == "rpsc" || bv == "rpmsc") && !sc_ok {
13359 "rpr2"
13360 } else if (bv == "rpks" || bv == "rpksc") && !ks_ok {
13361 "rpr2"
13362 } else {
13363 bv
13364 };
13365 if rp {
13366 match v {
13367 "base" | "pf" | "ca" | "rp" => "rp",
13368 "r2" | "pfr2" | "car2" | "rpr2" => "rpr2",
13369 "r2w8" | "rpr2w8" => {
13370 if mcols == 2 {
13371 "rpr2"
13372 } else {
13373 "rpr2w8"
13374 }
13375 }
13376 other => other, }
13378 } else {
13379 v
13380 }
13381 } else if mcols == 8 {
13382 if rp {
13393 if sc_ok { "rpsc" } else { "rpr2w8" }
13394 } else {
13395 "r2w8"
13396 }
13397 } else if mcols >= 4 {
13398 let blocks = (out_f + 7) / 8;
13402 let r7 = 7 * sms as usize;
13403 let r8 = 8 * sms as usize;
13404 let waves = blocks as f64 / r7 as f64;
13405 let filled = blocks >= 4 * sms as usize;
13406 if filled && blocks.div_ceil(r8) < blocks.div_ceil(r7) {
13410 if rp { "rpr2w8" } else { "r2w8" }
13414 } else if waves >= 2.0 || (waves <= 1.0 && filled) {
13415 if rp { "rpr2" } else { "r2" }
13418 } else {
13419 if rp { "rp" } else { "pf" }
13423 }
13424 } else if in_f >= 6144 {
13425 if rp { "rpr2" } else { "r2" }
13429 } else if rp {
13430 let waves = ((out_f + 7) / 8) as f64 / (7 * sms as usize) as f64;
13435 if sc_ok && waves >= 0.9 && waves <= 1.1 {
13436 "rpsc"
13437 } else {
13438 "rp"
13439 }
13440 } else {
13441 "base"
13442 };
13443 variant
13444 }
13445
13446 pub fn qmatvec_mmvq_batched(
13447 &self,
13448 bytes: &CudaSlice<u8>,
13449 aq: &CudaSlice<i8>,
13450 ad: &CudaSlice<f32>,
13451 m: usize,
13452 in_f: usize,
13453 out_f: usize,
13454 qtype: i32,
13455 row_bytes: usize,
13456 mcols: usize,
13457 scale: f32,
13458 rp: bool,
13459 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13460 const ROWS_PER_BLOCK: u32 = 4;
13461 let forced: Option<&'static str> = {
13466 static V: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
13467 V.get_or_init(|| std::env::var("MEMRA_BVAR").ok())
13468 .as_deref()
13469 .map(|s| Box::leak(s.to_string().into_boxed_str()) as &'static str)
13470 };
13471 let variant = match forced {
13472 Some(v) if !rp || v.contains("rp") => v,
13473 _ => self.batched_variant(m, in_f, out_f, qtype, row_bytes, mcols, rp),
13474 };
13475 let base_name = Self::batched_kernel_name(qtype, mcols).ok_or_else(|| {
13476 format!("qmatvec_mmvq_batched: no kernel for qtype {qtype} mcols {mcols}")
13477 })?;
13478 let variant = if mcols == 16 {
13482 if rp { "rp" } else { "base" }
13483 } else {
13484 variant
13485 };
13486 static B567: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13493 let b567 = *B567.get_or_init(|| std::env::var("MEMRA_B567").as_deref() != Ok("0"));
13494 if b567
13495 && qtype == QT_NVFP4
13496 && rp
13497 && mcols == 8
13498 && (5..=7).contains(&m)
13499 && matches!(variant, "rpsc" | "rpr2w8")
13500 {
13501 let f = self.func(&format!("qmatvec_nvfp4_mmvq_b{m}_{variant}"));
13502 let rows_per_block = ROWS_PER_BLOCK * 2; let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13504 let cfg = LaunchConfig {
13505 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
13506 block_dim: (32, ROWS_PER_BLOCK, 1),
13507 shared_mem_bytes: 0,
13508 };
13509 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13510 let __s_b = self.gpu.stream();
13511 let mut b = __s_b.launch_builder(&f);
13512 b.arg(bytes)
13513 .arg(aq)
13514 .arg(ad)
13515 .arg(&mut y)
13516 .arg(&inf)
13517 .arg(&outf)
13518 .arg(&mi)
13519 .arg(&rb);
13520 unsafe {
13521 b.launch(cfg)?;
13522 }
13523 if scale != 1.0 {
13524 self.scale_inplace(&mut y, scale, m * out_f)?;
13525 }
13526 return Ok(y);
13527 }
13528 let (name, rows_per_block): (std::borrow::Cow<'static, str>, u32) = match variant {
13529 "base" => (base_name.into(), ROWS_PER_BLOCK),
13530 "pf" => (format!("{base_name}_pf").into(), ROWS_PER_BLOCK),
13531 "ca" => (format!("{base_name}_ca").into(), ROWS_PER_BLOCK),
13532 "rp" => (format!("{base_name}_rp").into(), ROWS_PER_BLOCK),
13533 "rpca" => (format!("{base_name}_rpca").into(), ROWS_PER_BLOCK), "rpks" => (format!("{base_name}_rpks").into(), ROWS_PER_BLOCK),
13537 "rpksc" => (format!("{base_name}_rpksc").into(), ROWS_PER_BLOCK),
13538 "rpms" => (format!("{base_name}_rpms").into(), ROWS_PER_BLOCK),
13539 "rpmsc" => (format!("{base_name}_rpmsc").into(), ROWS_PER_BLOCK),
13540 "r2ms_rp" => (format!("{base_name}_r2ms_rp").into(), ROWS_PER_BLOCK),
13541 "r2sm_rp" => (format!("{base_name}_r2sm_rp").into(), ROWS_PER_BLOCK * 2),
13542 "r2la_rp" => (format!("{base_name}_r2la_rp").into(), ROWS_PER_BLOCK * 2),
13543 v => (format!("{base_name}_{v}").into(), ROWS_PER_BLOCK * 2), };
13545 debug_assert!(
13546 !rp || name.contains("_rp"),
13547 "rp weight dispatched to a GGUF-layout kernel"
13548 );
13549 let f = self.func(&name);
13550 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13551 let smem = if name.contains("_r2sm_rp") {
13553 (mcols * 32 * 9 * 4 + mcols * 32 * 4) as u32
13554 } else {
13555 0
13556 };
13557 let cfg = LaunchConfig {
13558 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
13559 block_dim: (32, ROWS_PER_BLOCK, 1),
13560 shared_mem_bytes: smem,
13561 };
13562 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13563 let __s_b = self.gpu.stream();
13564 let mut b = __s_b.launch_builder(&f);
13565 b.arg(bytes)
13566 .arg(aq)
13567 .arg(ad)
13568 .arg(&mut y)
13569 .arg(&inf)
13570 .arg(&outf)
13571 .arg(&mi)
13572 .arg(&rb);
13573 unsafe {
13574 b.launch(cfg)?;
13575 }
13576 if scale != 1.0 {
13577 self.scale_inplace(&mut y, scale, m * out_f)?;
13578 }
13579 Ok(y)
13580 }
13581
13582 pub fn qmatvec_batched_raw(
13586 &self,
13587 bytes: &CudaSlice<u8>,
13588 x: &CudaSlice<f32>,
13589 m: usize,
13590 in_f: usize,
13591 out_f: usize,
13592 qtype: i32,
13593 row_bytes: usize,
13594 mcols: usize,
13595 rp: bool,
13596 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13597 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13598 self.qmatvec_mmvq_batched(
13599 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, 1.0, rp,
13600 )
13601 }
13602
13603 pub fn qmatvec_nvfp4_batched_raw(
13605 &self,
13606 bytes: &CudaSlice<u8>,
13607 x: &CudaSlice<f32>,
13608 m: usize,
13609 in_f: usize,
13610 out_f: usize,
13611 row_bytes: usize,
13612 mcols: usize,
13613 rp: bool,
13614 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13615 self.qmatvec_batched_raw(bytes, x, m, in_f, out_f, QT_NVFP4, row_bytes, mcols, rp)
13616 }
13617
13618 fn try_fp4_gemm(
13622 &self,
13623 w: &crate::model::GpuTensor,
13624 x: &CudaSlice<f32>,
13625 m: usize,
13626 in_f: usize,
13627 out_f: usize,
13628 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13629 use crate::model::GpuTensor;
13630 if cfg!(memra_portable_cuda) {
13631 return Ok(None);
13632 }
13633 if std::env::var("MEMRA_FP4").is_err() {
13634 return Ok(None);
13635 }
13636 #[cfg(memra_cutlass)]
13645 if m >= 128 && std::env::var("MEMRA_FP4_CUTLASS").is_ok() {
13646 if let GpuTensor::Quant {
13647 bytes,
13648 qtype,
13649 scale,
13650 row_bytes,
13651 cutlass,
13652 ..
13653 } = w
13654 {
13655 if *qtype == QT_NVFP4 && in_f % 64 == 0 {
13656 if let Some(cw) = cutlass {
13657 let y = self.cutlass_fp4_gemm(
13659 &cw.b_packed,
13660 &cw.sfb_swizzled,
13661 x,
13662 *scale,
13663 m,
13664 out_f,
13665 in_f,
13666 )?;
13667 return Ok(Some(y));
13668 } else if std::env::var("MEMRA_FP4_CUTLASS_OTF").is_ok() {
13669 let (b_packed, sfb_sw) =
13674 self.build_cutlass_weight(bytes, out_f, in_f, *row_bytes)?;
13675 let y =
13676 self.cutlass_fp4_gemm(&b_packed, &sfb_sw, x, *scale, m, out_f, in_f)?;
13677 return Ok(Some(y));
13678 }
13679 }
13680 }
13681 }
13682 if let GpuTensor::Quant {
13683 bytes,
13684 qtype,
13685 row_bytes,
13686 scale,
13687 rp,
13688 ..
13689 } = w
13690 {
13691 if *qtype == QT_NVFP4 && in_f % 64 == 0 && !*rp {
13694 let y =
13695 self.qmatvec_gemm_nvfp4_fp4(bytes, x, m, in_f, out_f, *row_bytes, *scale)?;
13696 return Ok(Some(y));
13697 }
13698 }
13699 Ok(None)
13700 }
13701
13702 pub fn rms_norm_f16out(
13706 &self,
13707 x: &CudaSlice<f32>,
13708 w: &CudaSlice<f32>,
13709 dst: &mut CudaSlice<f32>,
13710 dst16: &mut CudaSlice<u8>,
13711 ncols: usize,
13712 nrows: usize,
13713 eps: f32,
13714 ) -> Result<(), Box<dyn std::error::Error>> {
13715 let f = self.func("rms_norm_f16out_f32");
13716 let cfg = LaunchConfig {
13717 grid_dim: (nrows as u32, 1, 1),
13718 block_dim: (rms_block(), 1, 1),
13719 shared_mem_bytes: 0,
13720 };
13721 let (nc, e) = (ncols as i32, eps);
13722 let __s_b = self.gpu.stream();
13723 let mut b = __s_b.launch_builder(&f);
13724 b.arg(x).arg(w).arg(dst).arg(dst16).arg(&nc).arg(&e);
13725 unsafe {
13726 b.launch(cfg)?;
13727 }
13728 Ok(())
13729 }
13730
13731 #[allow(clippy::too_many_arguments)]
13734 pub fn add_rms_norm_f16out(
13735 &self,
13736 a: &CudaSlice<f32>,
13737 b: &CudaSlice<f32>,
13738 w: &CudaSlice<f32>,
13739 res: &mut CudaSlice<f32>,
13740 dst: &mut CudaSlice<f32>,
13741 dst16: &mut CudaSlice<u8>,
13742 ncols: usize,
13743 nrows: usize,
13744 eps: f32,
13745 ) -> Result<(), Box<dyn std::error::Error>> {
13746 let f = self.func("add_rms_norm_f16out_f32");
13747 let cfg = LaunchConfig {
13748 grid_dim: (nrows as u32, 1, 1),
13749 block_dim: (rms_block(), 1, 1),
13750 shared_mem_bytes: 0,
13751 };
13752 let (nc, e) = (ncols as i32, eps);
13753 let __s_lb = self.gpu.stream();
13754 let mut lb = __s_lb.launch_builder(&f);
13755 lb.arg(a)
13756 .arg(b)
13757 .arg(w)
13758 .arg(res)
13759 .arg(dst)
13760 .arg(dst16)
13761 .arg(&nc)
13762 .arg(&e);
13763 unsafe {
13764 lb.launch(cfg)?;
13765 }
13766 Ok(())
13767 }
13768
13769 pub fn matmul_group_xh(
13772 &self,
13773 ws: &[&crate::model::GpuTensor],
13774 x: &CudaSlice<f32>,
13775 xh: &CudaSlice<u8>,
13776 m: usize,
13777 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13778 let mut out = Vec::with_capacity(ws.len());
13779 let in_f = ws[0].in_features();
13780 for w in ws {
13781 if w.in_features() == in_f && m >= 16 && !self.verify_exact_on() {
13782 if let Some(y) = self.try_f16_gemm_pre(w, xh, m)? {
13783 out.push(y);
13784 continue;
13785 }
13786 }
13787 out.push(self.matmul(w, x, m)?);
13788 }
13789 Ok(out)
13790 }
13791
13792 pub fn gdn_pad_mask(
13795 &self,
13796 beta: &mut CudaSlice<f32>,
13797 g_log: &mut CudaSlice<f32>,
13798 len_d: &CudaSlice<i32>,
13799 h: usize,
13800 t: usize,
13801 ) -> Result<(), Box<dyn std::error::Error>> {
13802 let f = self.func("gdn_pad_mask_f32");
13803 let cfg = LaunchConfig::for_num_elems((t * h) as u32);
13804 let (hi, ti) = (h as i32, t as i32);
13805 let __s_b = self.gpu.stream();
13806 let mut b = __s_b.launch_builder(&f);
13807 b.arg(beta).arg(g_log).arg(len_d).arg(&hi).arg(&ti);
13808 unsafe {
13809 b.launch(cfg)?;
13810 }
13811 Ok(())
13812 }
13813
13814 pub fn row_gather_dev(
13817 &self,
13818 src: &CudaSlice<f32>,
13819 dst: &mut CudaSlice<f32>,
13820 len_d: &CudaSlice<i32>,
13821 ncols: usize,
13822 ) -> Result<(), Box<dyn std::error::Error>> {
13823 let f = self.func("row_gather_dev_f32");
13824 let cfg = LaunchConfig::for_num_elems(ncols as u32);
13825 let nc = ncols as i32;
13826 let __s_b = self.gpu.stream();
13827 let mut b = __s_b.launch_builder(&f);
13828 b.arg(src).arg(dst).arg(len_d).arg(&nc);
13829 unsafe {
13830 b.launch(cfg)?;
13831 }
13832 Ok(())
13833 }
13834
13835 pub fn matmul_group(
13842 &self,
13843 ws: &[&crate::model::GpuTensor],
13844 x: &CudaSlice<f32>,
13845 m: usize,
13846 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13847 use crate::model::GpuTensor;
13848 let mut out = Vec::with_capacity(ws.len());
13849 let any_mirror = ws
13850 .iter()
13851 .any(|w| matches!(w, GpuTensor::Quant { f16: Some(_), .. }));
13852 if m >= 16 && any_mirror && !self.verify_exact_on() {
13853 let in_f = ws[0].in_features();
13854 let xh = self.f16_act(x, m * in_f, in_f)?;
13855 for w in ws {
13856 if w.in_features() == in_f {
13857 if let Some(y) = self.try_f16_gemm_pre(w, &xh, m)? {
13858 out.push(y);
13859 continue;
13860 }
13861 }
13862 out.push(self.matmul(w, x, m)?);
13863 }
13864 return Ok(out);
13865 }
13866 for w in ws {
13867 out.push(self.matmul(w, x, m)?);
13868 }
13869 Ok(out)
13870 }
13871
13872 pub fn matmul_group_multi(
13879 &self,
13880 ws: &[&crate::model::GpuTensor],
13881 xs: &[&CudaSlice<f32>],
13882 ms: &[usize],
13883 ) -> Result<Vec<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
13884 assert_eq!(xs.len(), ms.len());
13885 let in_f = ws[0].in_features();
13886 let total: usize = ms.iter().sum();
13887 let mut xcat = self.uninit(total * in_f)?;
13888 let mut off = 0usize;
13889 for (x, &m) in xs.iter().zip(ms) {
13890 self.copy_into(&mut xcat, off * in_f, x, m * in_f)?;
13891 off += m;
13892 }
13893 let ys = self.matmul_group(ws, &xcat, total)?;
13894 let mut out: Vec<Vec<CudaSlice<f32>>> = (0..xs.len()).map(|_| Vec::new()).collect();
13895 for (w, y) in ws.iter().zip(ys) {
13896 let out_f = w.out_features();
13897 let mut off = 0usize;
13898 for (s, &m) in ms.iter().enumerate() {
13899 let mut ys_s = self.uninit(m * out_f)?;
13900 let src = y.slice(off * out_f..(off + m) * out_f);
13901 self.gpu.stream().memcpy_dtod(&src, &mut ys_s)?;
13902 out[s].push(ys_s);
13903 off += m;
13904 }
13905 }
13906 Ok(out)
13907 }
13908
13909 pub fn gemm_supports(&self, w: &crate::model::GpuTensor) -> bool {
13919 use crate::model::GpuTensor;
13920 if !legacy_quant_gemm_allowed(
13921 cfg!(memra_portable_cuda),
13922 cfg!(memra_hopper_mma),
13923 std::env::var_os("MEMRA_NO_GEMM").is_some(),
13924 ) {
13925 return false;
13926 }
13927 match w {
13928 GpuTensor::Quant { qtype, .. } => {
13929 matches!(*qtype, QT_Q8_0 | QT_Q4_K | QT_Q6_K | QT_Q5_K | QT_Q4_0)
13930 || (*qtype == QT_NVFP4 && w.in_features() % 64 == 0)
13931 }
13932 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
13933 }
13934 }
13935
13936 pub fn qmatvec_gemm(
13943 &self,
13944 w: &crate::model::GpuTensor,
13945 aq: &CudaSlice<i8>,
13946 ad: &CudaSlice<f32>,
13947 m: usize,
13948 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13949 use crate::model::GpuTensor;
13950 let in_f = w.in_features();
13951 let out_f = w.out_features();
13952 let (bytes, qtype, row_bytes, scale, rp) = match w {
13953 GpuTensor::Quant {
13954 bytes,
13955 qtype,
13956 row_bytes,
13957 scale,
13958 rp,
13959 ..
13960 } => (bytes, *qtype, *row_bytes, *scale, *rp),
13961 _ => unreachable!("gemm_supports guaranteed Quant"),
13962 };
13963 if cfg!(memra_hopper_mma) && qtype == QT_Q8_0 && out_f % 64 == 0 && wgmma_gemm_enabled() {
13969 if let GpuTensor::Quant { rp4: Some(m4), .. } = w {
13970 let mut y = self.qmatvec_gemm_q8_0_wgmma_raw(m4, aq, ad, m, in_f, out_f)?;
13971 if scale != 1.0 {
13972 self.scale_inplace(&mut y, scale, m * out_f)?;
13973 }
13974 return Ok(y);
13975 }
13976 }
13977 let name = match qtype {
13978 QT_Q8_0 => "qmatvec_gemm_q8_0",
13979 QT_Q4_K => "qmatvec_gemm_q4_K",
13980 QT_Q4_0 => {
13981 if rp {
13982 "qmatvec_gemm_q4_0_rp"
13983 } else {
13984 "qmatvec_gemm_q4_0"
13985 }
13986 }
13987 QT_Q5_K => "qmatvec_gemm_q5_K",
13988 QT_Q6_K => "qmatvec_gemm_q6_K",
13989 QT_NVFP4 => {
13990 if rp {
13991 "qmatvec_gemm_nvfp4_rp"
13992 } else {
13993 "qmatvec_gemm_nvfp4"
13994 }
13995 }
13996 _ => unreachable!(),
13997 };
13998 let f = self.func(name);
13999 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);
14004 let k1_tile = if is_k1 {
14006 k1_launch_override().unwrap_or((128, 128, 8))
14007 } else {
14008 (128, 128, 8)
14009 };
14010 let (bm, bn): (u32, u32) = if is_k1 {
14011 (k1_tile.0, k1_tile.1)
14012 } else {
14013 (64, 256)
14014 };
14015 let warps: u32 = if is_k1 {
14016 k1_tile.2
14017 } else {
14018 match qtype {
14019 QT_NVFP4 => 8,
14020 _ => 4,
14021 }
14022 };
14023 let cfg = LaunchConfig {
14024 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
14025 block_dim: (32, warps, 1),
14026 shared_mem_bytes: 0,
14027 };
14028 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14029 let __s_b = self.gpu.stream();
14030 let mut b = __s_b.launch_builder(&f);
14031 b.arg(bytes)
14032 .arg(aq)
14033 .arg(ad)
14034 .arg(&mut y)
14035 .arg(&inf)
14036 .arg(&outf)
14037 .arg(&mi)
14038 .arg(&rb);
14039 unsafe {
14040 b.launch(cfg)?;
14041 }
14042 if scale != 1.0 {
14043 self.scale_inplace(&mut y, scale, m * out_f)?;
14044 }
14045 Ok(y)
14046 }
14047
14048 pub fn qmatvec_gemm_raw(
14053 &self,
14054 bytes: &CudaSlice<u8>,
14055 x: &CudaSlice<f32>,
14056 m: usize,
14057 in_f: usize,
14058 out_f: usize,
14059 qtype: i32,
14060 row_bytes: usize,
14061 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14062 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
14063 let name = match qtype {
14064 QT_Q8_0 => "qmatvec_gemm_q8_0",
14065 QT_Q4_K => "qmatvec_gemm_q4_K",
14066 QT_Q4_0 => "qmatvec_gemm_q4_0",
14067 QT_Q5_K => "qmatvec_gemm_q5_K",
14068 QT_Q6_K => "qmatvec_gemm_q6_K",
14069 QT_NVFP4 => "qmatvec_gemm_nvfp4",
14070 QT_NVFP4_RP => "qmatvec_gemm_nvfp4_rp",
14071 _ => panic!("qmatvec_gemm_raw: qtype {qtype} has no GEMM kernel"),
14072 };
14073 let f = self.func(name);
14074 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);
14078 let k1_tile = if is_k1 {
14080 k1_launch_override().unwrap_or((128, 128, 8))
14081 } else {
14082 (128, 128, 8)
14083 };
14084 let (bm, bn): (u32, u32) = if is_k1 {
14085 (k1_tile.0, k1_tile.1)
14086 } else {
14087 (64, 256)
14088 };
14089 let warps: u32 = if is_k1 {
14090 k1_tile.2
14091 } else {
14092 match qtype {
14093 QT_NVFP4 | QT_NVFP4_RP => 8,
14094 _ => 4,
14095 }
14096 };
14097 let cfg = LaunchConfig {
14098 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
14099 block_dim: (32, warps, 1),
14100 shared_mem_bytes: 0,
14101 };
14102 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14103 let __s_b = self.gpu.stream();
14104 let mut b = __s_b.launch_builder(&f);
14105 b.arg(bytes)
14106 .arg(&aq)
14107 .arg(&ad)
14108 .arg(&mut y)
14109 .arg(&inf)
14110 .arg(&outf)
14111 .arg(&mi)
14112 .arg(&rb);
14113 unsafe {
14114 b.launch(cfg)?;
14115 }
14116 Ok(y)
14117 }
14118
14119 pub fn qmatvec_gemm_q8_0_wgmma_raw(
14126 &self,
14127 rp4: &CudaSlice<u8>,
14128 aq: &CudaSlice<i8>,
14129 ad: &CudaSlice<f32>,
14130 m: usize,
14131 in_f: usize,
14132 out_f: usize,
14133 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14134 assert!(
14135 out_f % 64 == 0 && in_f % 32 == 0,
14136 "wgmma GEMM needs out_f%64==0, in_f%32==0"
14137 );
14138 let f = self.func("qmatvec_gemm_q8_0_wgmma");
14139 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
14141 grid_dim: ((out_f / 64) as u32, (m as u32).div_ceil(64), 1),
14142 block_dim: (128, 1, 1),
14143 shared_mem_bytes: 0,
14144 };
14145 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
14146 let __s_b = self.gpu.stream();
14147 let mut b = __s_b.launch_builder(&f);
14148 b.arg(rp4)
14149 .arg(aq)
14150 .arg(ad)
14151 .arg(&mut y)
14152 .arg(&inf)
14153 .arg(&outf)
14154 .arg(&mi);
14155 unsafe {
14156 b.launch(cfg)?;
14157 }
14158 Ok(y)
14159 }
14160
14161 pub fn scale_inplace(
14163 &self,
14164 y: &mut CudaSlice<f32>,
14165 s: f32,
14166 n: usize,
14167 ) -> Result<(), Box<dyn std::error::Error>> {
14168 let f = self.func("scale_f32");
14169 let cfg = LaunchConfig::for_num_elems(n as u32);
14170 let (sf, ni) = (s, n as i32);
14171 let __s_b = self.gpu.stream();
14172 let mut b = __s_b.launch_builder(&f);
14173 b.arg(y).arg(&sf).arg(&ni);
14174 unsafe {
14175 b.launch(cfg)?;
14176 }
14177 Ok(())
14178 }
14179
14180 pub fn bf16_to_f32(
14185 &self,
14186 data: &cudarc::driver::CudaView<'_, u8>,
14187 n: usize,
14188 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14189 let mut out = self.alloc_uninit::<f32>(n)?;
14190 let f = self.func("bf16_to_f32");
14191 let cfg = LaunchConfig::for_num_elems(n as u32);
14192 let ni = n as i32;
14193 let __s_b = self.gpu.stream();
14194 let mut b = __s_b.launch_builder(&f);
14195 b.arg(data).arg(&mut out).arg(&ni);
14196 unsafe {
14197 b.launch(cfg)?;
14198 }
14199 Ok(out)
14200 }
14201
14202 fn linear_bf16_chunked(
14209 &self,
14210 x: &CudaSlice<f32>,
14211 data: &CudaSlice<u8>,
14212 m: usize,
14213 in_f: usize,
14214 out_f: usize,
14215 exact: bool,
14216 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14217 const CHUNK_BYTES: usize = 256 << 20;
14218 let chunk_rows = (CHUNK_BYTES / (in_f * 4)).max(1).min(out_f);
14219 if chunk_rows >= out_f {
14220 let wf32 = self.bf16_to_f32(&data.slice(0..in_f * out_f * 2), in_f * out_f)?;
14221 return if exact {
14222 self.linear_decode_exact(x, &wf32, m, in_f, out_f)
14223 } else {
14224 self.linear(x, &wf32, m, in_f, out_f)
14225 };
14226 }
14227 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
14228 let mut r0 = 0usize;
14229 while r0 < out_f {
14230 let rows = chunk_rows.min(out_f - r0);
14231 let wslice = data.slice(r0 * in_f * 2..(r0 + rows) * in_f * 2);
14232 let wf32 = self.bf16_to_f32(&wslice, in_f * rows)?;
14233 let yc = if exact {
14234 self.linear_decode_exact(x, &wf32, m, in_f, rows)?
14235 } else {
14236 self.linear(x, &wf32, m, in_f, rows)?
14237 };
14238 for mi in 0..m {
14240 let src = yc.slice(mi * rows..(mi + 1) * rows);
14241 let mut dst = y.slice_mut(mi * out_f + r0..mi * out_f + r0 + rows);
14242 self.gpu.stream().memcpy_dtod(&src, &mut dst)?;
14243 }
14244 r0 += rows;
14245 }
14246 Ok(y)
14247 }
14248
14249 pub fn linear_decode_exact(
14256 &self,
14257 x: &CudaSlice<f32>,
14258 w: &CudaSlice<f32>,
14259 m_tokens: usize,
14260 in_f: usize,
14261 out_f: usize,
14262 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14263 if m_tokens == 1 {
14264 return self.linear(x, w, 1, in_f, out_f);
14265 }
14266 let xv = self.view(x, m_tokens * in_f);
14267 let mut y = self.alloc_uninit::<f32>(m_tokens * out_f)?;
14268 for t in 0..m_tokens {
14269 let row = xv.slice(t * in_f..(t + 1) * in_f);
14270 let mut xr = self.alloc_uninit::<f32>(in_f)?;
14271 self.copy_view_into(&mut xr, 0, &row, in_f)?;
14272 let yr = self.linear(&xr, w, 1, in_f, out_f)?;
14273 self.copy_into(&mut y, t * out_f, &yr, out_f)?;
14274 }
14275 Ok(y)
14276 }
14277
14278 pub fn linear(
14279 &self,
14280 x: &CudaSlice<f32>,
14281 w: &CudaSlice<f32>,
14282 m_tokens: usize,
14283 in_f: usize,
14284 out_f: usize,
14285 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14286 use cudarc::cublaslt::{Matmul, MatmulConfig};
14287 let mut c = self.alloc_uninit::<f32>(m_tokens * out_f)?; let cfg = MatmulConfig {
14289 transa: true,
14290 transb: false,
14291 transc: false,
14292 m: out_f as u64,
14293 n: m_tokens as u64,
14294 k: in_f as u64,
14295 alpha: 1.0,
14296 lda: in_f as i64,
14297 ldb: in_f as i64,
14298 beta: 0.0,
14299 ldc: out_f as i64,
14300 stride_a: None,
14301 stride_b: None,
14302 stride_c: None,
14303 stride_bias: None,
14304 batch_size: None,
14305 };
14306 unsafe {
14307 self.gpu.blas.matmul(cfg, w, x, &mut c, None, None)?;
14308 }
14309 Ok(c)
14310 }
14311
14312 pub fn sdpa_naive(
14314 &self,
14315 q: &CudaSlice<f32>,
14316 k: &CudaSlice<f32>,
14317 v: &CudaSlice<f32>,
14318 o: &mut CudaSlice<f32>,
14319 head_dim: usize,
14320 n_head: usize,
14321 n_head_kv: usize,
14322 t: usize,
14323 t_kv: usize,
14324 scale: f32,
14325 causal: bool,
14326 ) -> Result<(), Box<dyn std::error::Error>> {
14327 let f = self.func("sdpa_naive_f32");
14328 let cfg = LaunchConfig {
14329 grid_dim: (n_head as u32, t as u32, 1),
14330 block_dim: (128, 1, 1),
14331 shared_mem_bytes: (t_kv * 4) as u32,
14332 };
14333 let (hd, nh, nhkv, ti, tkvi, cz) = (
14334 head_dim as i32,
14335 n_head as i32,
14336 n_head_kv as i32,
14337 t as i32,
14338 t_kv as i32,
14339 causal as i32,
14340 );
14341 let __s_b = self.gpu.stream();
14342 let mut b = __s_b.launch_builder(&f);
14343 b.arg(q)
14344 .arg(k)
14345 .arg(v)
14346 .arg(o)
14347 .arg(&hd)
14348 .arg(&nh)
14349 .arg(&nhkv)
14350 .arg(&ti)
14351 .arg(&tkvi)
14352 .arg(&scale)
14353 .arg(&cz);
14354 unsafe {
14355 b.launch(cfg)?;
14356 }
14357 Ok(())
14358 }
14359
14360 #[allow(clippy::too_many_arguments)]
14362 pub fn sdpa_naive_w(
14363 &self,
14364 q: &CudaSlice<f32>,
14365 k: &CudaSlice<f32>,
14366 v: &CudaSlice<f32>,
14367 o: &mut CudaSlice<f32>,
14368 head_dim: usize,
14369 n_head: usize,
14370 n_head_kv: usize,
14371 t: usize,
14372 t_kv: usize,
14373 scale: f32,
14374 causal: bool,
14375 window: usize,
14376 ) -> Result<(), Box<dyn std::error::Error>> {
14377 let f = self.func("sdpa_naive_w_f32");
14378 let cfg = LaunchConfig {
14379 grid_dim: (n_head as u32, t as u32, 1),
14380 block_dim: (128, 1, 1),
14381 shared_mem_bytes: (t_kv * 4) as u32,
14382 };
14383 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
14384 head_dim as i32,
14385 n_head as i32,
14386 n_head_kv as i32,
14387 t as i32,
14388 t_kv as i32,
14389 causal as i32,
14390 window as i32,
14391 );
14392 let __s_b = self.gpu.stream();
14393 let mut b = __s_b.launch_builder(&f);
14394 b.arg(q)
14395 .arg(k)
14396 .arg(v)
14397 .arg(o)
14398 .arg(&hd)
14399 .arg(&nh)
14400 .arg(&nhkv)
14401 .arg(&ti)
14402 .arg(&tkvi)
14403 .arg(&scale)
14404 .arg(&cz)
14405 .arg(&wi);
14406 unsafe {
14407 b.launch(cfg)?;
14408 }
14409 Ok(())
14410 }
14411
14412 pub fn sdpa_naive_view(
14414 &self,
14415 q: &CudaSlice<f32>,
14416 k: &cudarc::driver::CudaView<f32>,
14417 v: &cudarc::driver::CudaView<f32>,
14418 o: &mut CudaSlice<f32>,
14419 head_dim: usize,
14420 n_head: usize,
14421 n_head_kv: usize,
14422 t: usize,
14423 t_kv: usize,
14424 scale: f32,
14425 causal: bool,
14426 ) -> Result<(), Box<dyn std::error::Error>> {
14427 let f = self.func("sdpa_naive_f32");
14428 let cfg = LaunchConfig {
14429 grid_dim: (n_head as u32, t as u32, 1),
14430 block_dim: (128, 1, 1),
14431 shared_mem_bytes: (t_kv * 4) as u32,
14432 };
14433 let (hd, nh, nhkv, ti, tkvi, cz) = (
14434 head_dim as i32,
14435 n_head as i32,
14436 n_head_kv as i32,
14437 t as i32,
14438 t_kv as i32,
14439 causal as i32,
14440 );
14441 let __s_b = self.gpu.stream();
14442 let mut b = __s_b.launch_builder(&f);
14443 b.arg(q)
14444 .arg(k)
14445 .arg(v)
14446 .arg(o)
14447 .arg(&hd)
14448 .arg(&nh)
14449 .arg(&nhkv)
14450 .arg(&ti)
14451 .arg(&tkvi)
14452 .arg(&scale)
14453 .arg(&cz);
14454 unsafe {
14455 b.launch(cfg)?;
14456 }
14457 Ok(())
14458 }
14459
14460 #[allow(clippy::too_many_arguments)]
14468 pub fn fa_dequant_kv_view_f32(
14469 &self,
14470 k: &cudarc::driver::CudaView<u8>,
14471 v: &cudarc::driver::CudaView<u8>,
14472 kf: &mut CudaSlice<f32>,
14473 vf: &mut CudaSlice<f32>,
14474 kv_dim_k: usize,
14475 kv_dim_v: usize,
14476 t_kv: usize,
14477 k_tok_bytes: usize,
14478 v_tok_bytes: usize,
14479 g: bool,
14480 ) -> Result<(), Box<dyn std::error::Error>> {
14481 let f = if g {
14482 self.func_g("fa_dequant_kv_ws_f32")
14483 } else {
14484 self.func("fa_dequant_kv_ws_f32")
14485 };
14486 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
14487 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
14488 let cfg = LaunchConfig {
14489 grid_dim: (nblk.max(1), 1, 1),
14490 block_dim: (256, 1, 1),
14491 shared_mem_bytes: 0,
14492 };
14493 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
14494 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
14495 let __s_b = self.gpu.stream();
14496 let mut b = __s_b.launch_builder(&f);
14497 b.arg(k)
14498 .arg(v)
14499 .arg(&mut *kf)
14500 .arg(&mut *vf)
14501 .arg(&kdk)
14502 .arg(&kdv)
14503 .arg(&tkvi)
14504 .arg(&ktb)
14505 .arg(&vtb);
14506 unsafe {
14507 b.launch(cfg)?;
14508 }
14509 Ok(())
14510 }
14511
14512 #[allow(clippy::too_many_arguments)]
14513 pub fn sdpa_naive_quantized_view(
14514 &self,
14515 q: &CudaSlice<f32>,
14516 k: &cudarc::driver::CudaView<u8>,
14517 v: &cudarc::driver::CudaView<u8>,
14518 o: &mut CudaSlice<f32>,
14519 head_dim: usize,
14520 n_head: usize,
14521 n_head_kv: usize,
14522 t: usize,
14523 t_kv: usize,
14524 scale: f32,
14525 causal: bool,
14526 k_tok_bytes: usize,
14527 v_tok_bytes: usize,
14528 ) -> Result<(), Box<dyn std::error::Error>> {
14529 let kv_dim = n_head_kv * head_dim;
14530 let mut kf = self.uninit(t_kv * kv_dim)?;
14531 let mut vf = self.uninit(t_kv * kv_dim)?;
14532 let f = self.func("fa_dequant_kv_ws_f32");
14533 let total = (2 * t_kv * kv_dim) as u64;
14534 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
14535 let cfg = LaunchConfig {
14536 grid_dim: (nblk.max(1), 1, 1),
14537 block_dim: (256, 1, 1),
14538 shared_mem_bytes: 0,
14539 };
14540 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
14541 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
14542 let __s_b = self.gpu.stream();
14543 let mut b = __s_b.launch_builder(&f);
14544 b.arg(k)
14545 .arg(v)
14546 .arg(&mut kf)
14547 .arg(&mut vf)
14548 .arg(&kv_dim_i)
14549 .arg(&kv_dim_i)
14550 .arg(&t_kv_i)
14551 .arg(&k_tok_bytes_i)
14552 .arg(&v_tok_bytes_i);
14553 unsafe { b.launch(cfg)? };
14554 self.sdpa_naive(
14555 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
14556 )
14557 }
14558
14559 #[allow(clippy::too_many_arguments)]
14571 pub fn sdpa_naive_w_quantized_view(
14572 &self,
14573 q: &CudaSlice<f32>,
14574 k: &cudarc::driver::CudaView<u8>,
14575 v: &cudarc::driver::CudaView<u8>,
14576 o: &mut CudaSlice<f32>,
14577 head_dim: usize,
14578 n_head: usize,
14579 n_head_kv: usize,
14580 t: usize,
14581 t_kv: usize,
14582 scale: f32,
14583 causal: bool,
14584 window: usize,
14585 k_tok_bytes: usize,
14586 v_tok_bytes: usize,
14587 ) -> Result<(), Box<dyn std::error::Error>> {
14588 let kv_dim = n_head_kv * head_dim;
14589 let mut kf = self.uninit(t_kv * kv_dim)?;
14590 let mut vf = self.uninit(t_kv * kv_dim)?;
14591 let f = self.func("fa_dequant_kv_ws_f32");
14592 let total = (2 * t_kv * kv_dim) as u64;
14593 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
14594 let cfg = LaunchConfig {
14595 grid_dim: (nblk.max(1), 1, 1),
14596 block_dim: (256, 1, 1),
14597 shared_mem_bytes: 0,
14598 };
14599 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
14600 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
14601 let __s_b = self.gpu.stream();
14602 let mut b = __s_b.launch_builder(&f);
14603 b.arg(k)
14604 .arg(v)
14605 .arg(&mut kf)
14606 .arg(&mut vf)
14607 .arg(&kv_dim_i)
14608 .arg(&kv_dim_i)
14609 .arg(&t_kv_i)
14610 .arg(&k_tok_bytes_i)
14611 .arg(&v_tok_bytes_i);
14612 unsafe { b.launch(cfg)? };
14613 self.sdpa_naive_w(
14614 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
14615 )
14616 }
14617
14618 pub fn fa_prefill(
14622 &self,
14623 q: &CudaSlice<f32>,
14624 k: &CudaSlice<f32>,
14625 v: &CudaSlice<f32>,
14626 o: &mut CudaSlice<f32>,
14627 head_dim: usize,
14628 n_head: usize,
14629 n_head_kv: usize,
14630 t: usize,
14631 t_kv: usize,
14632 scale: f32,
14633 causal: bool,
14634 ) -> Result<(), Box<dyn std::error::Error>> {
14635 if portable_mma_gated() {
14636 return self.sdpa_naive(
14637 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
14638 );
14639 }
14640 let fa3_on = head_dim == 256
14648 && causal
14649 && t == t_kv
14650 && match std::env::var("MEMRA_FA3").as_deref() {
14651 Ok("0") => false,
14652 Ok("1") => true,
14653 _ => cfg!(memra_hopper_mma),
14654 };
14655 if fa3_on {
14656 let n = t * n_head * head_dim;
14657 let nkv = t * n_head_kv * head_dim;
14658 let mut q16 = self.alloc_u8_uninit(n * 2)?;
14659 let mut k16 = self.alloc_u8_uninit(nkv * 2)?;
14660 let mut v16 = self.alloc_u8_uninit(nkv * 2)?;
14661 self.f32_to_bf16_into(q, &mut q16, n)?;
14662 self.f32_to_bf16_into(k, &mut k16, nkv)?;
14663 self.f32_to_bf16_into(v, &mut v16, nkv)?;
14664 let rc = {
14665 use cudarc::driver::{DevicePtr, DevicePtrMut};
14666 let stream = self.gpu.stream();
14667 let (qp, _g1) = q16.device_ptr(&stream);
14668 let (kp, _g2) = k16.device_ptr(&stream);
14669 let (vp, _g3) = v16.device_ptr(&stream);
14670 let (op, _g4) = o.device_ptr_mut(&stream);
14671 unsafe {
14672 memra_fa3_prefill(
14673 qp as *const core::ffi::c_void,
14674 kp as *const core::ffi::c_void,
14675 vp as *const core::ffi::c_void,
14676 op as *mut f32,
14677 t as i32,
14678 n_head as i32,
14679 n_head_kv as i32,
14680 head_dim as i32,
14681 scale,
14682 stream.cu_stream() as *mut core::ffi::c_void,
14683 )
14684 }
14685 };
14686 if rc != 0 {
14687 return Err(format!("memra_fa3_prefill rc={rc}").into());
14688 }
14689 return Ok(());
14690 }
14691 static FA_P1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14696 let fa_p1 = *FA_P1.get_or_init(|| std::env::var("MEMRA_FA_P1").as_deref() == Ok("1"));
14697 if fa_p1 && head_dim == 256 && !std::env::var("MEMRA_FA_FLOOR").is_ok() {
14698 const BLOCK_Q: usize = 64;
14699 const BKX: usize = 32;
14700 let f = self.func("fa_prefill_bf16_p1");
14701 let shmem = (2 * (2 * BKX * head_dim + BLOCK_Q * BKX)
14702 + 4 * (BLOCK_Q * BKX + 2 * BLOCK_Q)) as u32;
14703 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14704 f.set_attribute(
14705 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14706 shmem as i32,
14707 )?;
14708 let cfg = LaunchConfig {
14709 grid_dim: (
14710 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
14711 n_head as u32,
14712 1,
14713 ),
14714 block_dim: (32, 4, 1),
14715 shared_mem_bytes: shmem,
14716 };
14717 let (hd, nh, nhkv, ti, tkvi, cz) = (
14718 head_dim as i32,
14719 n_head as i32,
14720 n_head_kv as i32,
14721 t as i32,
14722 t_kv as i32,
14723 causal as i32,
14724 );
14725 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
14726 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
14727 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
14728 let __s_b = self.gpu.stream();
14729 let mut b = __s_b.launch_builder(&f);
14730 b.arg(&qb)
14731 .arg(&kb)
14732 .arg(&vb)
14733 .arg(o)
14734 .arg(&hd)
14735 .arg(&nh)
14736 .arg(&nhkv)
14737 .arg(&ti)
14738 .arg(&tkvi)
14739 .arg(&scale)
14740 .arg(&cz);
14741 unsafe {
14742 b.launch(cfg)?;
14743 }
14744 return Ok(());
14745 }
14746 const BK: usize = 32;
14752 let w2 = std::env::var("MEMRA_FA_PP_W2").as_deref() == Ok("1");
14755 let (block_q, warps, w2_sfx): (usize, u32, &str) =
14756 if w2 { (32, 2, "_w2") } else { (64, 4, "") };
14757 let hd_sfx = fa_hd_suffix(head_dim)?;
14761 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
14762 let bf16kv = !floor && !w2 && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0");
14767 let (kb16, vb16) = if bf16kv {
14768 let n = t_kv * n_head_kv * head_dim;
14769 let mut kb = self.alloc_u8_uninit(n * 2)?;
14770 let mut vb = self.alloc_u8_uninit(n * 2)?;
14771 let fcv = self.func("f32_to_bf16_bulk");
14772 let ni = n as i64;
14773 let cfgc = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
14774 let __s_b = self.gpu.stream();
14775 let mut b = __s_b.launch_builder(&fcv);
14776 b.arg(k).arg(&mut kb).arg(&ni);
14777 unsafe {
14778 b.launch(cfgc)?;
14779 }
14780 let __s_b = self.gpu.stream();
14781 let mut b = __s_b.launch_builder(&fcv);
14782 b.arg(v).arg(&mut vb).arg(&ni);
14783 unsafe {
14784 b.launch(cfgc)?;
14785 }
14786 (Some(kb), Some(vb))
14787 } else {
14788 (None, None)
14789 };
14790 let f = self.func(&if bf16kv {
14791 format!("fa_prefill_bf16kv_pp{hd_sfx}")
14792 } else {
14793 format!(
14794 "fa_prefill_f32{}{}{hd_sfx}",
14795 if floor { "" } else { "_pp" },
14796 if floor { "" } else { w2_sfx }
14797 )
14798 });
14799 let kv_stages = if bf16kv { 2 } else { 1 };
14802 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
14803 + 4 * (block_q * BK + 2 * block_q)) as u32;
14804 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14805 f.set_attribute(
14806 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14807 shmem as i32,
14808 )?;
14809 let cfg = LaunchConfig {
14810 grid_dim: (
14811 (t as u32 + block_q as u32 - 1) / block_q as u32,
14812 n_head as u32,
14813 1,
14814 ),
14815 block_dim: (32, warps, 1),
14816 shared_mem_bytes: shmem,
14817 };
14818 let (hd, nh, nhkv, ti, tkvi, cz) = (
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 );
14826 let __s_b = self.gpu.stream();
14827 let mut b = __s_b.launch_builder(&f);
14828 b.arg(q);
14829 match (&kb16, &vb16) {
14830 (Some(kb), Some(vb)) => {
14831 b.arg(kb).arg(vb);
14832 }
14833 _ => {
14834 b.arg(k).arg(v);
14835 }
14836 }
14837 b.arg(o)
14838 .arg(&hd)
14839 .arg(&nh)
14840 .arg(&nhkv)
14841 .arg(&ti)
14842 .arg(&tkvi)
14843 .arg(&scale)
14844 .arg(&cz);
14845 unsafe {
14846 b.launch(cfg)?;
14847 }
14848 Ok(())
14849 }
14850
14851 #[allow(clippy::too_many_arguments)]
14855 pub fn fa_prefill_w(
14856 &self,
14857 q: &CudaSlice<f32>,
14858 k: &CudaSlice<f32>,
14859 v: &CudaSlice<f32>,
14860 o: &mut CudaSlice<f32>,
14861 head_dim: usize,
14862 n_head: usize,
14863 n_head_kv: usize,
14864 t: usize,
14865 t_kv: usize,
14866 scale: f32,
14867 causal: bool,
14868 window: usize,
14869 ) -> Result<(), Box<dyn std::error::Error>> {
14870 if portable_mma_gated() {
14873 return self.sdpa_naive_w(
14874 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
14875 );
14876 }
14877 static FAW_F32: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14881 let faw_f32 =
14882 *FAW_F32.get_or_init(|| std::env::var("MEMRA_FAW_STAGE").as_deref() == Ok("f32"));
14883 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
14884 self.fa_prefill_w_arm(
14885 q,
14886 k,
14887 v,
14888 o,
14889 head_dim,
14890 n_head,
14891 n_head_kv,
14892 t,
14893 t_kv,
14894 scale,
14895 causal,
14896 window,
14897 floor || faw_f32,
14898 floor,
14899 )
14900 }
14901
14902 #[allow(clippy::too_many_arguments)]
14905 pub fn fa_prefill_w_pre(
14906 &self,
14907 qb: &CudaSlice<u8>,
14908 kb: &CudaSlice<u8>,
14909 vb: &CudaSlice<u8>,
14910 o: &mut CudaSlice<f32>,
14911 head_dim: usize,
14912 n_head: usize,
14913 n_head_kv: usize,
14914 t: usize,
14915 t_kv: usize,
14916 scale: f32,
14917 causal: bool,
14918 window: usize,
14919 v_f16: bool,
14920 ) -> Result<(), Box<dyn std::error::Error>> {
14921 const BLOCK_Q: usize = 64;
14922 const BK: usize = 32;
14923 debug_assert_eq!(head_dim, 256);
14924 let hp = fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
14925 debug_assert!(!v_f16 || hp, "f16 V emitted but the SWA hp arm is off");
14926 if hp {
14927 const BLOCK_QH: usize = 32;
14928 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
14931 let vh: &CudaSlice<u8> = if v_f16 {
14932 vb
14933 } else {
14934 let n = t_kv * n_head_kv * head_dim;
14935 if vguard.as_ref().map(|b| b.len() < n * 2).unwrap_or(true) {
14936 *vguard = Some(self.alloc_uninit::<u8>(n * 2)?);
14937 }
14938 self.bf16_to_f16_into(vb, n, vguard.as_mut().unwrap())?;
14939 vguard.as_ref().unwrap()
14940 };
14941 let f = self.func("fa_prefill_w_bf16_p1h2");
14942 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
14943 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14944 f.set_attribute(
14945 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14946 shmem as i32,
14947 )?;
14948 let cfg = LaunchConfig {
14949 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
14950 block_dim: (32, 4, 1),
14951 shared_mem_bytes: shmem,
14952 };
14953 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
14954 head_dim as i32,
14955 n_head as i32,
14956 n_head_kv as i32,
14957 t as i32,
14958 t_kv as i32,
14959 causal as i32,
14960 window as i32,
14961 );
14962 let __s_b = self.gpu.stream();
14963 let mut b = __s_b.launch_builder(&f);
14964 b.arg(qb)
14965 .arg(kb)
14966 .arg(vh)
14967 .arg(o)
14968 .arg(&hd)
14969 .arg(&nh)
14970 .arg(&nhkv)
14971 .arg(&ti)
14972 .arg(&tkvi)
14973 .arg(&scale)
14974 .arg(&cz)
14975 .arg(&wi);
14976 unsafe {
14977 b.launch(cfg)?;
14978 }
14979 return Ok(());
14980 }
14981 let f = self.func("fa_prefill_w_bf16_p1");
14982 let shmem =
14983 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
14984 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14985 f.set_attribute(
14986 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14987 shmem as i32,
14988 )?;
14989 let cfg = LaunchConfig {
14990 grid_dim: (
14991 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
14992 n_head as u32,
14993 1,
14994 ),
14995 block_dim: (32, 4, 1),
14996 shared_mem_bytes: shmem,
14997 };
14998 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
14999 head_dim as i32,
15000 n_head as i32,
15001 n_head_kv as i32,
15002 t as i32,
15003 t_kv as i32,
15004 causal as i32,
15005 window as i32,
15006 );
15007 let __s_b = self.gpu.stream();
15008 let mut b = __s_b.launch_builder(&f);
15009 b.arg(qb)
15010 .arg(kb)
15011 .arg(vb)
15012 .arg(o)
15013 .arg(&hd)
15014 .arg(&nh)
15015 .arg(&nhkv)
15016 .arg(&ti)
15017 .arg(&tkvi)
15018 .arg(&scale)
15019 .arg(&cz)
15020 .arg(&wi);
15021 unsafe {
15022 b.launch(cfg)?;
15023 }
15024 Ok(())
15025 }
15026
15027 #[allow(clippy::too_many_arguments)]
15029 pub fn fa_prefill_w_arm(
15030 &self,
15031 q: &CudaSlice<f32>,
15032 k: &CudaSlice<f32>,
15033 v: &CudaSlice<f32>,
15034 o: &mut CudaSlice<f32>,
15035 head_dim: usize,
15036 n_head: usize,
15037 n_head_kv: usize,
15038 t: usize,
15039 t_kv: usize,
15040 scale: f32,
15041 causal: bool,
15042 window: usize,
15043 f32_stage: bool,
15044 floor: bool,
15045 ) -> Result<(), Box<dyn std::error::Error>> {
15046 const BLOCK_Q: usize = 64;
15047 const BK: usize = 32;
15048 debug_assert_eq!(head_dim, 256, "fa_prefill_w is stamped hd256 only");
15049 static P1_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15053 let p1 = !floor
15054 && !f32_stage
15055 && *P1_ON.get_or_init(|| {
15056 std::env::var("MEMRA_FAW_P1")
15057 .map(|v| v != "0")
15058 .unwrap_or(true)
15059 });
15060 let hp =
15061 p1 && fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15062 if hp {
15063 const BLOCK_QH: usize = 32;
15064 let f = self.func("fa_prefill_w_bf16_p1h2");
15065 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
15066 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15067 f.set_attribute(
15068 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15069 shmem as i32,
15070 )?;
15071 let cfg = LaunchConfig {
15072 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
15073 block_dim: (32, 4, 1),
15074 shared_mem_bytes: shmem,
15075 };
15076 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15077 head_dim as i32,
15078 n_head as i32,
15079 n_head_kv as i32,
15080 t as i32,
15081 t_kv as i32,
15082 causal as i32,
15083 window as i32,
15084 );
15085 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15086 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15087 let vh = self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?;
15088 let __s_b = self.gpu.stream();
15089 let mut b = __s_b.launch_builder(&f);
15090 b.arg(&qb)
15091 .arg(&kb)
15092 .arg(&vh)
15093 .arg(o)
15094 .arg(&hd)
15095 .arg(&nh)
15096 .arg(&nhkv)
15097 .arg(&ti)
15098 .arg(&tkvi)
15099 .arg(&scale)
15100 .arg(&cz)
15101 .arg(&wi);
15102 unsafe {
15103 b.launch(cfg)?;
15104 }
15105 return Ok(());
15106 }
15107 if p1 {
15108 let f = self.func("fa_prefill_w_bf16_p1");
15109 let shmem =
15110 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15111 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15112 f.set_attribute(
15113 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15114 shmem as i32,
15115 )?;
15116 let cfg = LaunchConfig {
15117 grid_dim: (
15118 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15119 n_head as u32,
15120 1,
15121 ),
15122 block_dim: (32, 4, 1),
15123 shared_mem_bytes: shmem,
15124 };
15125 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15126 head_dim as i32,
15127 n_head as i32,
15128 n_head_kv as i32,
15129 t as i32,
15130 t_kv as i32,
15131 causal as i32,
15132 window as i32,
15133 );
15134 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15135 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15136 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15137 let __s_b = self.gpu.stream();
15138 let mut b = __s_b.launch_builder(&f);
15139 b.arg(&qb)
15140 .arg(&kb)
15141 .arg(&vb)
15142 .arg(o)
15143 .arg(&hd)
15144 .arg(&nh)
15145 .arg(&nhkv)
15146 .arg(&ti)
15147 .arg(&tkvi)
15148 .arg(&scale)
15149 .arg(&cz)
15150 .arg(&wi);
15151 unsafe {
15152 b.launch(cfg)?;
15153 }
15154 return Ok(());
15155 }
15156 static G4_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15159 let g4 = !floor
15160 && !f32_stage
15161 && n_head_kv == 1
15162 && n_head % 4 == 0
15163 && *G4_ON.get_or_init(|| {
15164 std::env::var("MEMRA_FAW_G4")
15165 .map(|v| v != "0")
15166 .unwrap_or(true)
15167 });
15168 if g4 {
15169 const SP_M: usize = 16;
15170 static O2_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15173 let o2 = *O2_ON.get_or_init(|| {
15174 std::env::var("MEMRA_FAW_O2")
15175 .map(|v| v != "0")
15176 .unwrap_or(true)
15177 });
15178 let f = self.func(if o2 {
15179 "fa_prefill_w_bf16_g4o2"
15180 } else {
15181 "fa_prefill_w_bf16_g4"
15182 });
15183 let shmem = if o2 {
15184 (2 * (4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M)) as u32
15185 } else {
15186 (2 * (2 * BK * head_dim + 4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M))
15187 as u32
15188 };
15189 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15190 f.set_attribute(
15191 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15192 shmem as i32,
15193 )?;
15194 let cfg = LaunchConfig {
15195 grid_dim: ((t as u32).div_ceil(SP_M as u32), (n_head / 4) as u32, 1),
15196 block_dim: (32, 4, 1),
15197 shared_mem_bytes: shmem,
15198 };
15199 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15200 head_dim as i32,
15201 n_head as i32,
15202 n_head_kv as i32,
15203 t as i32,
15204 t_kv as i32,
15205 causal as i32,
15206 window as i32,
15207 );
15208 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15209 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15210 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15211 let __s_b = self.gpu.stream();
15212 let mut b = __s_b.launch_builder(&f);
15213 b.arg(&qb)
15214 .arg(&kb)
15215 .arg(&vb)
15216 .arg(o)
15217 .arg(&hd)
15218 .arg(&nh)
15219 .arg(&nhkv)
15220 .arg(&ti)
15221 .arg(&tkvi)
15222 .arg(&scale)
15223 .arg(&cz)
15224 .arg(&wi);
15225 unsafe {
15226 b.launch(cfg)?;
15227 }
15228 return Ok(());
15229 }
15230 let f = self.func(if floor {
15231 "fa_prefill_w_f32"
15232 } else if f32_stage {
15233 "fa_prefill_w_f32_pp"
15234 } else {
15235 "fa_prefill_w_bf16_pp"
15236 });
15237 let shmem =
15238 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15239 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15240 f.set_attribute(
15241 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15242 shmem as i32,
15243 )?;
15244 let cfg = LaunchConfig {
15245 grid_dim: (
15246 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15247 n_head as u32,
15248 1,
15249 ),
15250 block_dim: (32, 4, 1),
15251 shared_mem_bytes: shmem,
15252 };
15253 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15254 head_dim as i32,
15255 n_head as i32,
15256 n_head_kv as i32,
15257 t as i32,
15258 t_kv as i32,
15259 causal as i32,
15260 window as i32,
15261 );
15262 if f32_stage {
15263 let __s_b = self.gpu.stream();
15264 let mut b = __s_b.launch_builder(&f);
15265 b.arg(q)
15266 .arg(k)
15267 .arg(v)
15268 .arg(o)
15269 .arg(&hd)
15270 .arg(&nh)
15271 .arg(&nhkv)
15272 .arg(&ti)
15273 .arg(&tkvi)
15274 .arg(&scale)
15275 .arg(&cz)
15276 .arg(&wi);
15277 unsafe {
15278 b.launch(cfg)?;
15279 }
15280 } else {
15281 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15282 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15283 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15284 let __s_b = self.gpu.stream();
15285 let mut b = __s_b.launch_builder(&f);
15286 b.arg(&qb)
15287 .arg(&kb)
15288 .arg(&vb)
15289 .arg(o)
15290 .arg(&hd)
15291 .arg(&nh)
15292 .arg(&nhkv)
15293 .arg(&ti)
15294 .arg(&tkvi)
15295 .arg(&scale)
15296 .arg(&cz)
15297 .arg(&wi);
15298 unsafe {
15299 b.launch(cfg)?;
15300 }
15301 }
15302 Ok(())
15303 }
15304
15305 #[allow(clippy::too_many_arguments)]
15309 pub fn fa_prefill_hd512(
15310 &self,
15311 q: &CudaSlice<f32>,
15312 k: &CudaSlice<f32>,
15313 v: &CudaSlice<f32>,
15314 o: &mut CudaSlice<f32>,
15315 head_dim: usize,
15316 n_head: usize,
15317 n_head_kv: usize,
15318 t: usize,
15319 t_kv: usize,
15320 scale: f32,
15321 causal: bool,
15322 ) -> Result<(), Box<dyn std::error::Error>> {
15323 if portable_mma_gated() {
15325 return self.sdpa_naive(
15326 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
15327 );
15328 }
15329 static F32_STAGE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15335 let f32_stage =
15336 *F32_STAGE.get_or_init(|| std::env::var("MEMRA_FA512_STAGE").as_deref() == Ok("f32"));
15337 static SP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15341 let sp = !f32_stage
15342 && *SP_ON.get_or_init(|| {
15343 std::env::var("MEMRA_FA512_SP")
15344 .map(|v| v != "0")
15345 .unwrap_or(true)
15346 });
15347 self.fa_prefill_hd512_arm(
15348 q,
15349 k,
15350 v,
15351 o,
15352 head_dim,
15353 n_head,
15354 n_head_kv,
15355 t,
15356 t_kv,
15357 scale,
15358 causal,
15359 f32_stage,
15360 sp,
15361 sp && fa_f16pv_on(),
15362 )
15363 }
15364
15365 #[allow(clippy::too_many_arguments)]
15367 pub fn fa_prefill_hd512_pre(
15368 &self,
15369 qb: &CudaSlice<u8>,
15370 kb: &CudaSlice<u8>,
15371 vb: &CudaSlice<u8>,
15372 o: &mut CudaSlice<f32>,
15373 head_dim: usize,
15374 n_head: usize,
15375 n_head_kv: usize,
15376 t: usize,
15377 t_kv: usize,
15378 scale: f32,
15379 causal: bool,
15380 v_f16: bool,
15381 ) -> Result<(), Box<dyn std::error::Error>> {
15382 debug_assert_eq!(head_dim, 512);
15383 const SP_M: usize = 16;
15384 const BKS: usize = 32;
15385 let f16pv = fa_f16pv_on();
15389 let nw = if f16pv { fa512_wide_warps() } else { 2 };
15390 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15391 debug_assert!(!v_f16 || f16pv, "f16 V emitted without the door on");
15392 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
15393 let vref: &CudaSlice<u8> = if f16pv && !v_f16 {
15394 let n = t_kv * n_head_kv * head_dim;
15396 let need = n * 2;
15397 if vguard.as_ref().map(|b| b.len() < need).unwrap_or(true) {
15398 *vguard = Some(self.alloc_uninit::<u8>(need)?);
15399 }
15400 let dst = vguard.as_mut().unwrap();
15401 self.bf16_to_f16_into(vb, n, dst)?;
15402 vguard.as_ref().unwrap()
15403 } else {
15404 vb
15405 };
15406 let f = self.func(if hp {
15407 "fa_prefill_bf16_hd512_sp16h2"
15408 } else {
15409 match (f16pv, nw) {
15410 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
15411 (true, _) => "fa_prefill_bf16_hd512_sp16",
15412 _ => "fa_prefill_bf16_hd512_sp",
15413 }
15414 });
15415 let (nwarp, npart) = if hp {
15416 (4usize, 4usize)
15417 } else if nw > 2 {
15418 (nw, nw)
15419 } else {
15420 (2, 1)
15421 };
15422 let shmem = if hp {
15424 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS) + 4 * (2 * npart * SP_M * BKS + 2 * SP_M))
15425 as u32
15426 } else {
15427 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
15428 + 4 * (npart * SP_M * BKS + SP_M)) as u32
15429 };
15430 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15431 f.set_attribute(
15432 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15433 shmem as i32,
15434 )?;
15435 let grid_y = if hp {
15436 (n_head / 2) as u32
15437 } else {
15438 n_head as u32
15439 };
15440 let cfg = LaunchConfig {
15441 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
15442 block_dim: (32, nwarp as u32, 1),
15443 shared_mem_bytes: shmem,
15444 };
15445 let (hd, nh, nhkv, ti, tkvi, cz) = (
15446 head_dim as i32,
15447 n_head as i32,
15448 n_head_kv as i32,
15449 t as i32,
15450 t_kv as i32,
15451 causal as i32,
15452 );
15453 let __s_b = self.gpu.stream();
15454 let mut b = __s_b.launch_builder(&f);
15455 b.arg(qb)
15456 .arg(kb)
15457 .arg(vref)
15458 .arg(o)
15459 .arg(&hd)
15460 .arg(&nh)
15461 .arg(&nhkv)
15462 .arg(&ti)
15463 .arg(&tkvi)
15464 .arg(&scale)
15465 .arg(&cz);
15466 unsafe {
15467 b.launch(cfg)?;
15468 }
15469 Ok(())
15470 }
15471
15472 #[allow(clippy::too_many_arguments)]
15475 pub fn fa_prefill_hd512_arm(
15476 &self,
15477 q: &CudaSlice<f32>,
15478 k: &CudaSlice<f32>,
15479 v: &CudaSlice<f32>,
15480 o: &mut CudaSlice<f32>,
15481 head_dim: usize,
15482 n_head: usize,
15483 n_head_kv: usize,
15484 t: usize,
15485 t_kv: usize,
15486 scale: f32,
15487 causal: bool,
15488 f32_stage: bool,
15489 sp: bool,
15490 f16pv: bool,
15491 ) -> Result<(), Box<dyn std::error::Error>> {
15492 debug_assert_eq!(head_dim, 512, "fa_prefill_hd512 is hd512 only");
15493 if sp && !f32_stage {
15494 const SP_M: usize = 16;
15498 const BKS: usize = 32;
15499 let nw = if f16pv { fa512_wide_warps() } else { 2 };
15500 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15501 let f = self.func(if hp {
15502 "fa_prefill_bf16_hd512_sp16h2"
15503 } else {
15504 match (f16pv, nw) {
15505 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
15506 (true, _) => "fa_prefill_bf16_hd512_sp16",
15507 _ => "fa_prefill_bf16_hd512_sp",
15508 }
15509 });
15510 let (nwarp, npart) = if hp {
15511 (4usize, 4usize)
15512 } else if nw > 2 {
15513 (nw, nw)
15514 } else {
15515 (2, 1)
15516 };
15517 let shmem = if hp {
15518 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS)
15519 + 4 * (2 * npart * SP_M * BKS + 2 * SP_M)) as u32
15520 } else {
15521 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
15522 + 4 * (npart * SP_M * BKS + SP_M)) as u32
15523 };
15524 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15525 f.set_attribute(
15526 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15527 shmem as i32,
15528 )?;
15529 let grid_y = if hp {
15530 (n_head / 2) as u32
15531 } else {
15532 n_head as u32
15533 };
15534 let cfg = LaunchConfig {
15535 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
15536 block_dim: (32, nwarp as u32, 1),
15537 shared_mem_bytes: shmem,
15538 };
15539 let (hd, nh, nhkv, ti, tkvi, cz) = (
15540 head_dim as i32,
15541 n_head as i32,
15542 n_head_kv as i32,
15543 t as i32,
15544 t_kv as i32,
15545 causal as i32,
15546 );
15547 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15548 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15549 let vb = if f16pv {
15550 self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?
15551 } else {
15552 self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?
15553 };
15554 let __s_b = self.gpu.stream();
15555 let mut b = __s_b.launch_builder(&f);
15556 b.arg(&qb)
15557 .arg(&kb)
15558 .arg(&vb)
15559 .arg(o)
15560 .arg(&hd)
15561 .arg(&nh)
15562 .arg(&nhkv)
15563 .arg(&ti)
15564 .arg(&tkvi)
15565 .arg(&scale)
15566 .arg(&cz);
15567 unsafe {
15568 b.launch(cfg)?;
15569 }
15570 return Ok(());
15571 }
15572 const BLOCK_Q: usize = 32;
15573 const BK: usize = 32;
15574 const HALF: usize = 256;
15575 let f = self.func(if f32_stage {
15576 "fa_prefill_f32_hd512"
15577 } else {
15578 "fa_prefill_bf16_hd512"
15579 });
15580 let shmem = (2 * (BLOCK_Q * head_dim + BK * head_dim + BK * HALF + BLOCK_Q * BK)
15582 + 4 * BLOCK_Q) as u32;
15583 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15584 f.set_attribute(
15585 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15586 shmem as i32,
15587 )?;
15588 let cfg = LaunchConfig {
15589 grid_dim: (
15590 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15591 n_head as u32,
15592 2,
15593 ),
15594 block_dim: (32, 2, 1),
15595 shared_mem_bytes: shmem,
15596 };
15597 let (hd, nh, nhkv, ti, tkvi, cz) = (
15598 head_dim as i32,
15599 n_head as i32,
15600 n_head_kv as i32,
15601 t as i32,
15602 t_kv as i32,
15603 causal as i32,
15604 );
15605 if f32_stage {
15606 let __s_b = self.gpu.stream();
15607 let mut b = __s_b.launch_builder(&f);
15608 b.arg(q)
15609 .arg(k)
15610 .arg(v)
15611 .arg(o)
15612 .arg(&hd)
15613 .arg(&nh)
15614 .arg(&nhkv)
15615 .arg(&ti)
15616 .arg(&tkvi)
15617 .arg(&scale)
15618 .arg(&cz);
15619 unsafe {
15620 b.launch(cfg)?;
15621 }
15622 } else {
15623 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15624 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15625 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15626 let __s_b = self.gpu.stream();
15627 let mut b = __s_b.launch_builder(&f);
15628 b.arg(&qb)
15629 .arg(&kb)
15630 .arg(&vb)
15631 .arg(o)
15632 .arg(&hd)
15633 .arg(&nh)
15634 .arg(&nhkv)
15635 .arg(&ti)
15636 .arg(&tkvi)
15637 .arg(&scale)
15638 .arg(&cz);
15639 unsafe {
15640 b.launch(cfg)?;
15641 }
15642 }
15643 Ok(())
15644 }
15645
15646 #[allow(clippy::too_many_arguments)]
15650 pub fn rope_neox2_bf16e(
15651 &self,
15652 q: &mut CudaSlice<f32>,
15653 k: &mut CudaSlice<f32>,
15654 qb: &mut CudaSlice<u8>,
15655 kb: &mut CudaSlice<u8>,
15656 pos: &CudaSlice<i32>,
15657 head_dim: usize,
15658 n_dims: usize,
15659 nh_q: usize,
15660 nh_k: usize,
15661 n_tokens: usize,
15662 base: f32,
15663 freq_scale: f32,
15664 ff: Option<&CudaSlice<f32>>,
15665 ) -> Result<(), Box<dyn std::error::Error>> {
15666 let f = self.func("rope_neox2_bf16e_f32");
15667 let rows = ((nh_q + nh_k) * n_tokens) as u32;
15668 let cfg = LaunchConfig {
15669 grid_dim: (rows, 1, 1),
15670 block_dim: ((head_dim / 2) as u32, 1, 1),
15671 shared_mem_bytes: 0,
15672 };
15673 let theta_scale = base.powf(-2.0 / n_dims as f32);
15674 let (hd, nd, nhq, nhk, nt) = (
15675 head_dim as i32,
15676 n_dims as i32,
15677 nh_q as i32,
15678 nh_k as i32,
15679 n_tokens as i32,
15680 );
15681 let __s_b = self.gpu.stream();
15682 let mut b = __s_b.launch_builder(&f);
15683 match ff {
15684 Some(t) => {
15685 b.arg(&mut *q)
15686 .arg(&mut *k)
15687 .arg(&mut *qb)
15688 .arg(&mut *kb)
15689 .arg(pos)
15690 .arg(&hd)
15691 .arg(&nd)
15692 .arg(&nhq)
15693 .arg(&nhk)
15694 .arg(&nt)
15695 .arg(&theta_scale)
15696 .arg(&freq_scale)
15697 .arg(t);
15698 unsafe {
15699 b.launch(cfg)?;
15700 }
15701 }
15702 None => {
15703 let null: u64 = 0;
15704 b.arg(&mut *q)
15705 .arg(&mut *k)
15706 .arg(&mut *qb)
15707 .arg(&mut *kb)
15708 .arg(pos)
15709 .arg(&hd)
15710 .arg(&nd)
15711 .arg(&nhq)
15712 .arg(&nhk)
15713 .arg(&nt)
15714 .arg(&theta_scale)
15715 .arg(&freq_scale)
15716 .arg(&null);
15717 unsafe {
15718 b.launch(cfg)?;
15719 }
15720 }
15721 }
15722 Ok(())
15723 }
15724
15725 pub fn f32_to_bf16(
15728 &self,
15729 x: &CudaSlice<f32>,
15730 n: usize,
15731 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
15732 assert!(n % 4 == 0, "f32_to_bf16 requires n % 4 == 0, got {n}");
15733 let mut y = self.alloc_uninit::<u8>(n * 2)?;
15734 let f = self.func("f32_to_bf16_flat");
15735 let n_i = n as i64;
15736 let cfg = LaunchConfig {
15737 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
15738 block_dim: (256, 1, 1),
15739 shared_mem_bytes: 0,
15740 };
15741 let __s_b = self.gpu.stream();
15742 let mut b = __s_b.launch_builder(&f);
15743 b.arg(x).arg(&mut y).arg(&n_i);
15744 unsafe {
15745 b.launch(cfg)?;
15746 }
15747 Ok(y)
15748 }
15749
15750 pub fn f32_to_f16(
15751 &self,
15752 x: &CudaSlice<f32>,
15753 n: usize,
15754 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
15755 assert!(n % 4 == 0, "f32_to_f16 requires n % 4 == 0, got {n}");
15756 let mut y = self.alloc_uninit::<u8>(n * 2)?;
15757 let f = self.func("f32_to_f16_flat");
15758 let n_i = n as i64;
15759 let cfg = LaunchConfig {
15760 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
15761 block_dim: (256, 1, 1),
15762 shared_mem_bytes: 0,
15763 };
15764 let __s_b = self.gpu.stream();
15765 let mut b = __s_b.launch_builder(&f);
15766 b.arg(x).arg(&mut y).arg(&n_i);
15767 unsafe {
15768 b.launch(cfg)?;
15769 }
15770 Ok(y)
15771 }
15772
15773 pub fn bf16_to_f16(
15775 &self,
15776 xb: &CudaSlice<u8>,
15777 n: usize,
15778 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
15779 let mut y = self.alloc_uninit::<u8>(n * 2)?;
15780 self.bf16_to_f16_into(xb, n, &mut y)?;
15781 Ok(y)
15782 }
15783
15784 pub fn bf16_to_f16_into(
15786 &self,
15787 xb: &CudaSlice<u8>,
15788 n: usize,
15789 y: &mut CudaSlice<u8>,
15790 ) -> Result<(), Box<dyn std::error::Error>> {
15791 assert!(n % 2 == 0, "bf16_to_f16 requires n % 2 == 0, got {n}");
15792 assert!(y.len() >= n * 2);
15793 let f = self.func("bf16_to_f16_flat");
15794 let n2 = (n / 2) as i64;
15795 let cfg = LaunchConfig {
15796 grid_dim: (((n / 2) as u32).div_ceil(256), 1, 1),
15797 block_dim: (256, 1, 1),
15798 shared_mem_bytes: 0,
15799 };
15800 let __s_b = self.gpu.stream();
15801 let mut b = __s_b.launch_builder(&f);
15802 b.arg(xb).arg(y).arg(&n2);
15803 unsafe {
15804 b.launch(cfg)?;
15805 }
15806 Ok(())
15807 }
15808
15809 #[allow(clippy::too_many_arguments)]
15814 pub fn fa_prefill_vl8(
15815 &self,
15816 seqs: &[FaSeqVl],
15817 head_dim: usize,
15818 n_head: usize,
15819 n_head_kv: usize,
15820 scale: f32,
15821 ) -> Result<(), Box<dyn std::error::Error>> {
15822 const BK: usize = 32;
15823 let b = seqs.len();
15824 assert!(b >= 1 && b <= 8);
15825 let mut packed = [FaSeqVl::default(); 8];
15826 packed[..b].copy_from_slice(seqs);
15827 let v = FaVl8(packed);
15828 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
15829 let ept = (n_head_kv * head_dim) as i32;
15830 {
15831 let f = self.func("fa_mirror_vl");
15832 let max_n = (max_t as i64) * ept as i64;
15833 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
15834 for which in 0..2i32 {
15835 let cfg = LaunchConfig {
15836 grid_dim: (blocks, 1, b as u32),
15837 block_dim: (256, 1, 1),
15838 shared_mem_bytes: 0,
15839 };
15840 let __s_lb = self.gpu.stream();
15841 let mut lb = __s_lb.launch_builder(&f);
15842 lb.arg(&v).arg(&ept).arg(&which);
15843 unsafe {
15844 lb.launch(cfg)?;
15845 }
15846 }
15847 }
15848 let hd_sfx = fa_hd_suffix(head_dim)?;
15849 let f = self.func(&format!("fa_prefill_bf16kv_vl{hd_sfx}"));
15850 let block_q = 64usize;
15851 let kv_stages = 2usize;
15852 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
15853 + 4 * (block_q * BK + 2 * block_q)) as u32;
15854 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15855 f.set_attribute(
15856 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15857 shmem as i32,
15858 )?;
15859 let cfg = LaunchConfig {
15860 grid_dim: (max_t.div_ceil(block_q as u32), n_head as u32, b as u32),
15861 block_dim: (32, 4, 1),
15862 shared_mem_bytes: shmem,
15863 };
15864 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
15865 let __s_lb = self.gpu.stream();
15866 let mut lb = __s_lb.launch_builder(&f);
15867 lb.arg(&v).arg(&hd).arg(&nh).arg(&nhkv).arg(&scale);
15868 unsafe {
15869 lb.launch(cfg)?;
15870 }
15871 Ok(())
15872 }
15873
15874 #[allow(clippy::too_many_arguments)]
15878 pub fn attn_pre_vl8(
15879 &self,
15880 seqs: &[AttnPreVl],
15881 wq: &CudaSlice<f32>,
15882 wk: &CudaSlice<f32>,
15883 head_dim: usize,
15884 rope_dims: usize,
15885 n_head: usize,
15886 n_head_kv: usize,
15887 eps: f32,
15888 freq_base: f32,
15889 freq_scale: f32,
15890 kv_dim_k: usize,
15891 kv_dim_v: usize,
15892 k_tok_bytes: usize,
15893 v_tok_bytes: usize,
15894 ) -> Result<(), Box<dyn std::error::Error>> {
15895 let b = seqs.len();
15896 assert!(b >= 1 && b <= 8);
15897 let mut packed = [AttnPreVl::default(); 8];
15898 packed[..b].copy_from_slice(seqs);
15899 let v = AttnPreVl8(packed);
15900 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
15901 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
15902 {
15903 let f = self.func("q_gate_split_vl");
15904 let n = max_t * (n_head * head_dim) as u32;
15905 let cfg = LaunchConfig {
15906 grid_dim: (n.div_ceil(256), 1, b as u32),
15907 block_dim: (256, 1, 1),
15908 shared_mem_bytes: 0,
15909 };
15910 let __s_lb = self.gpu.stream();
15911 let mut lb = __s_lb.launch_builder(&f);
15912 lb.arg(&v).arg(&hd).arg(&nh);
15913 unsafe {
15914 lb.launch(cfg)?;
15915 }
15916 }
15917 {
15918 let f = self.func("attn_rms_vl");
15919 let cfg = LaunchConfig {
15920 grid_dim: (max_t * n_head as u32, 2, b as u32),
15921 block_dim: (rms_block(), 1, 1),
15922 shared_mem_bytes: 0,
15923 };
15924 let __s_lb = self.gpu.stream();
15925 let mut lb = __s_lb.launch_builder(&f);
15926 lb.arg(&v)
15927 .arg(wq)
15928 .arg(wk)
15929 .arg(&hd)
15930 .arg(&nh)
15931 .arg(&nhkv)
15932 .arg(&eps);
15933 unsafe {
15934 lb.launch(cfg)?;
15935 }
15936 }
15937 {
15938 let f = self.func("attn_rope_vl");
15939 let theta_scale = freq_base.powf(-2.0 / rope_dims as f32);
15940 let nd = rope_dims as i32;
15941 let cfg = LaunchConfig {
15942 grid_dim: (max_t * n_head as u32, 2, b as u32),
15943 block_dim: ((head_dim / 2) as u32, 1, 1),
15944 shared_mem_bytes: 0,
15945 };
15946 let __s_lb = self.gpu.stream();
15947 let mut lb = __s_lb.launch_builder(&f);
15948 lb.arg(&v)
15949 .arg(&hd)
15950 .arg(&nd)
15951 .arg(&nh)
15952 .arg(&nhkv)
15953 .arg(&theta_scale)
15954 .arg(&freq_scale);
15955 unsafe {
15956 lb.launch(cfg)?;
15957 }
15958 }
15959 {
15960 let f = self.func("append_kv_vl");
15961 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
15962 let cfg = LaunchConfig {
15963 grid_dim: (nblk, max_t, b as u32),
15964 block_dim: (32, 1, 1),
15965 shared_mem_bytes: 0,
15966 };
15967 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
15968 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
15969 let __s_lb = self.gpu.stream();
15970 let mut lb = __s_lb.launch_builder(&f);
15971 lb.arg(&v).arg(&kdk).arg(&kdv).arg(&ktb).arg(&vtb);
15972 unsafe {
15973 lb.launch(cfg)?;
15974 }
15975 }
15976 Ok(())
15977 }
15978
15979 pub fn fa_prefill_view(
15984 &self,
15985 q: &CudaSlice<f32>,
15986 k: &cudarc::driver::CudaView<u8>,
15987 v: &cudarc::driver::CudaView<u8>,
15988 o: &mut CudaSlice<f32>,
15989 head_dim: usize,
15990 n_head: usize,
15991 n_head_kv: usize,
15992 t: usize,
15993 t_kv: usize,
15994 scale: f32,
15995 causal: bool,
15996 k_tok_bytes: usize,
15997 v_tok_bytes: usize,
15998 g: bool,
15999 ) -> Result<(), Box<dyn std::error::Error>> {
16000 if portable_mma_gated() {
16001 return self.sdpa_naive_quantized_view(
16002 q,
16003 k,
16004 v,
16005 o,
16006 head_dim,
16007 n_head,
16008 n_head_kv,
16009 t,
16010 t_kv,
16011 scale,
16012 causal,
16013 k_tok_bytes,
16014 v_tok_bytes,
16015 );
16016 }
16017 const BLOCK_Q: usize = 64;
16018 const BK: usize = 32;
16019 let name = format!("fa_prefill_q{}", fa_hd_suffix(head_dim)?);
16022 let f = if g {
16023 self.func_g(&name)
16024 } else {
16025 self.func(&name)
16026 };
16027 let shmem =
16028 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
16029 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16030 f.set_attribute(
16031 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16032 shmem as i32,
16033 )?;
16034 let cfg = LaunchConfig {
16035 grid_dim: (
16036 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16037 n_head as u32,
16038 1,
16039 ),
16040 block_dim: (32, 4, 1),
16041 shared_mem_bytes: shmem,
16042 };
16043 let (hd, nh, nhkv, ti, tkvi, cz) = (
16044 head_dim as i32,
16045 n_head as i32,
16046 n_head_kv as i32,
16047 t as i32,
16048 t_kv as i32,
16049 causal as i32,
16050 );
16051 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16052 let __s_b = self.gpu.stream();
16053 let mut b = __s_b.launch_builder(&f);
16054 b.arg(q)
16055 .arg(k)
16056 .arg(v)
16057 .arg(o)
16058 .arg(&hd)
16059 .arg(&nh)
16060 .arg(&nhkv)
16061 .arg(&ti)
16062 .arg(&tkvi)
16063 .arg(&scale)
16064 .arg(&cz)
16065 .arg(&ktb)
16066 .arg(&vtb);
16067 unsafe {
16068 b.launch(cfg)?;
16069 }
16070 Ok(())
16071 }
16072
16073 #[allow(clippy::too_many_arguments)]
16083 pub fn fa_prefill_view_ws(
16084 &self,
16085 q: &CudaSlice<f32>,
16086 k: &cudarc::driver::CudaView<u8>,
16087 v: &cudarc::driver::CudaView<u8>,
16088 o: &mut CudaSlice<f32>,
16089 head_dim: usize,
16090 n_head: usize,
16091 n_head_kv: usize,
16092 t: usize,
16093 t_kv: usize,
16094 scale: f32,
16095 causal: bool,
16096 k_tok_bytes: usize,
16097 v_tok_bytes: usize,
16098 g: bool,
16099 ) -> Result<(), Box<dyn std::error::Error>> {
16100 if portable_mma_gated() {
16101 return self.sdpa_naive_quantized_view(
16102 q,
16103 k,
16104 v,
16105 o,
16106 head_dim,
16107 n_head,
16108 n_head_kv,
16109 t,
16110 t_kv,
16111 scale,
16112 causal,
16113 k_tok_bytes,
16114 v_tok_bytes,
16115 );
16116 }
16117 const BLOCK_Q: usize = 64;
16118 const BK: usize = 32;
16119 let kv_dim_k = n_head_kv * head_dim;
16120 let kv_dim_v = n_head_kv * head_dim;
16121 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
16123 let mut guard = self.prime_deqw_ws.lock().unwrap();
16125 let need_grow = match guard.as_ref() {
16126 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
16127 None => true,
16128 };
16129 if need_grow {
16130 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
16131 let (ck, cv) = guard
16132 .as_ref()
16133 .map(|(a, b)| (a.len(), b.len()))
16134 .unwrap_or((0, 0));
16135 *guard = Some((
16136 self.alloc_u8(grow(ck, k_ws_bytes))?,
16137 self.alloc_u8(grow(cv, v_ws_bytes))?,
16138 ));
16139 }
16140 let (kw, vw) = guard.as_mut().unwrap();
16141 {
16143 let f = if g {
16145 self.func_g("fa_dequant_kv_ws_bf16")
16146 } else {
16147 self.func("fa_dequant_kv_ws_bf16")
16148 };
16149 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
16150 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
16151 let cfg = LaunchConfig {
16152 grid_dim: (nblk.max(1), 1, 1),
16153 block_dim: (256, 1, 1),
16154 shared_mem_bytes: 0,
16155 };
16156 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
16157 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16158 let __s_b = self.gpu.stream();
16159 let mut b = __s_b.launch_builder(&f);
16160 b.arg(k)
16161 .arg(v)
16162 .arg(&mut *kw)
16163 .arg(&mut *vw)
16164 .arg(&kdk)
16165 .arg(&kdv)
16166 .arg(&tkvi)
16167 .arg(&ktb)
16168 .arg(&vtb);
16169 unsafe {
16170 b.launch(cfg)?;
16171 }
16172 }
16173 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
16181 .map(|v| v != "0")
16182 .unwrap_or(true);
16183 {
16184 let hd_sfx = fa_hd_suffix(head_dim)?;
16185 let f = self.func(&format!(
16186 "fa_prefill_qw{}{hd_sfx}",
16187 if db { "_db" } else { "" }
16188 ));
16189 let shmem = if db {
16190 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
16192 } else {
16193 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
16194 };
16195 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16196 f.set_attribute(
16197 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16198 shmem as i32,
16199 )?;
16200 let cfg = LaunchConfig {
16201 grid_dim: (
16202 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16203 n_head as u32,
16204 1,
16205 ),
16206 block_dim: (32, 4, 1),
16207 shared_mem_bytes: shmem,
16208 };
16209 let (hd, nh, nhkv, ti, tkvi, cz) = (
16210 head_dim as i32,
16211 n_head as i32,
16212 n_head_kv as i32,
16213 t as i32,
16214 t_kv as i32,
16215 causal as i32,
16216 );
16217 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
16218 let __s_b = self.gpu.stream();
16219 let mut b = __s_b.launch_builder(&f);
16220 b.arg(q)
16221 .arg(&*kw)
16222 .arg(&*vw)
16223 .arg(o)
16224 .arg(&hd)
16225 .arg(&nh)
16226 .arg(&nhkv)
16227 .arg(&ti)
16228 .arg(&tkvi)
16229 .arg(&scale)
16230 .arg(&cz)
16231 .arg(&kdk)
16232 .arg(&kdv);
16233 unsafe {
16234 b.launch(cfg)?;
16235 }
16236 }
16237 Ok(())
16238 }
16239
16240 #[allow(clippy::too_many_arguments)]
16256 pub fn fa_prefill_view_ws_w_hd128(
16257 &self,
16258 q: &CudaSlice<f32>,
16259 k: &cudarc::driver::CudaView<u8>,
16260 v: &cudarc::driver::CudaView<u8>,
16261 o: &mut CudaSlice<f32>,
16262 head_dim: usize,
16263 n_head: usize,
16264 n_head_kv: usize,
16265 t: usize,
16266 t_kv: usize,
16267 scale: f32,
16268 causal: bool,
16269 window: usize,
16270 k_tok_bytes: usize,
16271 v_tok_bytes: usize,
16272 ) -> Result<(), Box<dyn std::error::Error>> {
16273 assert_eq!(
16274 head_dim, 128,
16275 "fa_prefill_view_ws_w_hd128: only the hd128 twin is stamped"
16276 );
16277 if portable_mma_gated() {
16278 return self.sdpa_naive_w_quantized_view(
16279 q,
16280 k,
16281 v,
16282 o,
16283 head_dim,
16284 n_head,
16285 n_head_kv,
16286 t,
16287 t_kv,
16288 scale,
16289 causal,
16290 window,
16291 k_tok_bytes,
16292 v_tok_bytes,
16293 );
16294 }
16295 const BLOCK_Q: usize = 64;
16296 const BK: usize = 32;
16297 let kv_dim_k = n_head_kv * head_dim;
16298 let kv_dim_v = n_head_kv * head_dim;
16299 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
16301 let mut guard = self.prime_deqw_ws.lock().unwrap();
16302 let need_grow = match guard.as_ref() {
16303 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
16304 None => true,
16305 };
16306 if need_grow {
16307 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
16308 let (ck, cv) = guard
16309 .as_ref()
16310 .map(|(a, b)| (a.len(), b.len()))
16311 .unwrap_or((0, 0));
16312 *guard = Some((
16313 self.alloc_u8(grow(ck, k_ws_bytes))?,
16314 self.alloc_u8(grow(cv, v_ws_bytes))?,
16315 ));
16316 }
16317 let (kw, vw) = guard.as_mut().unwrap();
16318 {
16321 let f = self.func("fa_dequant_kv_ws_bf16");
16322 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
16323 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
16324 let cfg = LaunchConfig {
16325 grid_dim: (nblk.max(1), 1, 1),
16326 block_dim: (256, 1, 1),
16327 shared_mem_bytes: 0,
16328 };
16329 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
16330 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16331 let __s_b = self.gpu.stream();
16332 let mut b = __s_b.launch_builder(&f);
16333 b.arg(k)
16334 .arg(v)
16335 .arg(&mut *kw)
16336 .arg(&mut *vw)
16337 .arg(&kdk)
16338 .arg(&kdv)
16339 .arg(&tkvi)
16340 .arg(&ktb)
16341 .arg(&vtb);
16342 unsafe {
16343 b.launch(cfg)?;
16344 }
16345 }
16346 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
16348 .map(|v| v != "0")
16349 .unwrap_or(true);
16350 {
16351 let f = self.func(if db {
16352 "fa_prefill_qw_db_w_hd128"
16353 } else {
16354 "fa_prefill_qw_w_hd128"
16355 });
16356 let shmem = if db {
16357 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
16358 } else {
16359 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
16360 };
16361 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16362 f.set_attribute(
16363 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16364 shmem as i32,
16365 )?;
16366 let cfg = LaunchConfig {
16367 grid_dim: (
16368 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16369 n_head as u32,
16370 1,
16371 ),
16372 block_dim: (32, 4, 1),
16373 shared_mem_bytes: shmem,
16374 };
16375 let (hd, nh, nhkv, ti, tkvi, cz) = (
16376 head_dim as i32,
16377 n_head as i32,
16378 n_head_kv as i32,
16379 t as i32,
16380 t_kv as i32,
16381 causal as i32,
16382 );
16383 let (kdk, kdv, wnd) = (kv_dim_k as i32, kv_dim_v as i32, window as i32);
16384 let __s_b = self.gpu.stream();
16385 let mut b = __s_b.launch_builder(&f);
16386 b.arg(q)
16387 .arg(&*kw)
16388 .arg(&*vw)
16389 .arg(o)
16390 .arg(&hd)
16391 .arg(&nh)
16392 .arg(&nhkv)
16393 .arg(&ti)
16394 .arg(&tkvi)
16395 .arg(&scale)
16396 .arg(&cz)
16397 .arg(&kdk)
16398 .arg(&kdv)
16399 .arg(&wnd);
16400 unsafe {
16401 b.launch(cfg)?;
16402 }
16403 }
16404 Ok(())
16405 }
16406
16407 pub fn fa_decode(
16411 &self,
16412 q: &CudaSlice<f32>,
16413 k: &cudarc::driver::CudaView<u8>,
16414 v: &cudarc::driver::CudaView<u8>,
16415 o: &mut CudaSlice<f32>,
16416 head_dim: usize,
16417 n_head: usize,
16418 n_head_kv: usize,
16419 t_kv: usize,
16420 scale: f32,
16421 k_tok_bytes: usize,
16422 v_tok_bytes: usize,
16423 ) -> Result<(), Box<dyn std::error::Error>> {
16424 self.fa_decode_kvmod(
16425 q,
16426 k,
16427 v,
16428 o,
16429 head_dim,
16430 n_head,
16431 n_head_kv,
16432 t_kv,
16433 scale,
16434 k_tok_bytes,
16435 v_tok_bytes,
16436 false,
16437 )
16438 }
16439
16440 #[allow(clippy::too_many_arguments)]
16444 #[allow(clippy::too_many_arguments)]
16448 #[allow(clippy::too_many_arguments)]
16449 fn fa_decode_scalar_unified(
16450 &self,
16451 q: &cudarc::driver::CudaView<f32>,
16452 k: &cudarc::driver::CudaView<u8>,
16453 v: &cudarc::driver::CudaView<u8>,
16454 o: &mut cudarc::driver::CudaViewMut<f32>,
16455 head_dim: usize,
16456 n_head: usize,
16457 n_head_kv: usize,
16458 t_kv_host: usize,
16459 t_kv_dev: Option<&CudaSlice<i32>>,
16460 scale: f32,
16461 n_splits: usize,
16462 split_keys: usize,
16463 k_tok_bytes: usize,
16464 v_tok_bytes: usize,
16465 g: bool,
16466 part_o: &mut CudaSlice<f32>,
16467 part_m: &mut CudaSlice<f32>,
16468 part_l: &mut CudaSlice<f32>,
16469 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
16470 ) -> Result<(), Box<dyn std::error::Error>> {
16471 let f = if g {
16472 self.func_g("fa_decode_f32")
16473 } else {
16474 self.fa_func("fa_decode_f32", head_dim)
16475 };
16476 let cfg = LaunchConfig {
16477 grid_dim: (n_head as u32, n_splits as u32, 1),
16478 block_dim: (head_dim as u32, 1, 1),
16479 shared_mem_bytes: (4 * (head_dim + 32)) as u32,
16480 };
16481 let (hd, nh, nhkv, nsp) = (
16482 head_dim as i32,
16483 n_head as i32,
16484 n_head_kv as i32,
16485 n_splits as i32,
16486 );
16487 let (ktb, vtb, tkvi, ski) = (
16488 k_tok_bytes as i64,
16489 v_tok_bytes as i64,
16490 t_kv_host as i32,
16491 split_keys as i32,
16492 );
16493 let __s_b = self.gpu.stream();
16494 let mut b = __s_b.launch_builder(&f);
16495 match t_kv_dev {
16496 Some(d) => {
16497 b.arg(q)
16498 .arg(k)
16499 .arg(v)
16500 .arg(&mut *part_o)
16501 .arg(&mut *part_m)
16502 .arg(&mut *part_l)
16503 .arg(&hd)
16504 .arg(&nh)
16505 .arg(&nhkv)
16506 .arg(&tkvi)
16507 .arg(d)
16508 .arg(&scale)
16509 .arg(&nsp)
16510 .arg(&ski)
16511 .arg(&ktb)
16512 .arg(&vtb);
16513 unsafe {
16514 b.launch(cfg)?;
16515 }
16516 }
16517 None => {
16518 let null: u64 = 0;
16519 b.arg(q)
16520 .arg(k)
16521 .arg(v)
16522 .arg(&mut *part_o)
16523 .arg(&mut *part_m)
16524 .arg(&mut *part_l)
16525 .arg(&hd)
16526 .arg(&nh)
16527 .arg(&nhkv)
16528 .arg(&tkvi)
16529 .arg(&null)
16530 .arg(&scale)
16531 .arg(&nsp)
16532 .arg(&ski)
16533 .arg(&ktb)
16534 .arg(&vtb);
16535 unsafe {
16536 b.launch(cfg)?;
16537 }
16538 }
16539 }
16540 let cfg2 = LaunchConfig {
16541 grid_dim: (n_head as u32, 1, 1),
16542 block_dim: (head_dim as u32, 1, 1),
16543 shared_mem_bytes: 0,
16544 };
16545 if let Some((oq, od)) = q8_out {
16546 let fc = if g {
16548 self.func_g("fa_decode_combine_q8_1")
16549 } else {
16550 self.fa_func("fa_decode_combine_q8_1", head_dim)
16551 };
16552 let __s_b2 = self.gpu.stream();
16553 let mut b2 = __s_b2.launch_builder(&fc);
16554 b2.arg(&*part_o)
16555 .arg(&*part_m)
16556 .arg(&*part_l)
16557 .arg(oq)
16558 .arg(od)
16559 .arg(&hd)
16560 .arg(&nh)
16561 .arg(&nsp);
16562 unsafe {
16563 b2.launch(cfg2)?;
16564 }
16565 return Ok(());
16566 }
16567 let fc = if g {
16568 self.func_g("fa_decode_combine_f32")
16569 } else {
16570 self.fa_func("fa_decode_combine_f32", head_dim)
16571 };
16572 let __s_b2 = self.gpu.stream();
16573 let mut b2 = __s_b2.launch_builder(&fc);
16574 b2.arg(&*part_o)
16575 .arg(&*part_m)
16576 .arg(&*part_l)
16577 .arg(o)
16578 .arg(&hd)
16579 .arg(&nh)
16580 .arg(&nsp);
16581 unsafe {
16582 b2.launch(cfg2)?;
16583 }
16584 Ok(())
16585 }
16586
16587 pub fn fa_decode_kvmod(
16588 &self,
16589 q: &CudaSlice<f32>,
16590 k: &cudarc::driver::CudaView<u8>,
16591 v: &cudarc::driver::CudaView<u8>,
16592 o: &mut CudaSlice<f32>,
16593 head_dim: usize,
16594 n_head: usize,
16595 n_head_kv: usize,
16596 t_kv: usize,
16597 scale: f32,
16598 k_tok_bytes: usize,
16599 v_tok_bytes: usize,
16600 g: bool,
16601 ) -> Result<(), Box<dyn std::error::Error>> {
16602 let q_view = q.as_view();
16603 let mut o_view = o.as_view_mut();
16604 self.fa_decode_kvmod_view(
16605 &q_view,
16606 k,
16607 v,
16608 &mut o_view,
16609 head_dim,
16610 n_head,
16611 n_head_kv,
16612 t_kv,
16613 scale,
16614 k_tok_bytes,
16615 v_tok_bytes,
16616 g,
16617 )
16618 }
16619
16620 #[allow(clippy::too_many_arguments)]
16625 pub fn fa_decode_kvmod_view(
16626 &self,
16627 q: &cudarc::driver::CudaView<f32>,
16628 k: &cudarc::driver::CudaView<u8>,
16629 v: &cudarc::driver::CudaView<u8>,
16630 o: &mut cudarc::driver::CudaViewMut<f32>,
16631 head_dim: usize,
16632 n_head: usize,
16633 n_head_kv: usize,
16634 t_kv: usize,
16635 scale: f32,
16636 k_tok_bytes: usize,
16637 v_tok_bytes: usize,
16638 g: bool,
16639 ) -> Result<(), Box<dyn std::error::Error>> {
16640 let mut fa_vec = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
16661 if g && head_dim == 256 && !fa_v4_at(t_kv) {
16665 fa_vec = false;
16666 }
16667 let sp = fa_split_keys(t_kv, n_head_kv);
16668 let n_splits = if fa_vec {
16669 ((t_kv + sp - 1) / sp).max(1)
16670 } else {
16671 ((t_kv + 255) / 256).max(1)
16672 };
16673 let o_len = n_head * n_splits * head_dim;
16674 let ml_len = n_head * n_splits;
16675 let mut part_guard = self.fa_part_pool.lock().unwrap();
16676 if part_guard
16677 .as_ref()
16678 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
16679 .unwrap_or(true)
16680 {
16681 let old = part_guard.take();
16692 let (co, cm) = old
16693 .as_ref()
16694 .map(|pp| (pp.0.len(), pp.1.len()))
16695 .unwrap_or((0, 0));
16696 if let Some(old) = old {
16697 self.fa_part_retired.lock().unwrap().push(old);
16698 }
16699 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
16700 eprintln!(
16701 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
16702 co, o_len, cm, ml_len
16703 );
16704 }
16705 *part_guard = Some((
16706 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
16707 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
16708 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
16709 ));
16710 }
16711 let pg = part_guard.as_mut().unwrap();
16712 self.gpu
16713 .stream()
16714 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
16715 self.gpu
16716 .stream()
16717 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
16718 self.gpu
16719 .stream()
16720 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
16721 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
16722 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
16723 let (hd, nh, nhkv, tkvi, nsp) = (
16724 head_dim as i32,
16725 n_head as i32,
16726 n_head_kv as i32,
16727 t_kv as i32,
16728 n_splits as i32,
16729 );
16730 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16731 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
16735 let fa512_min = fa512_min_tkv();
16740 let deep = fa_vec
16743 && head_dim == 256
16744 && fa_v4_at(t_kv)
16745 && !g
16746 && fa_deep_at(t_kv)
16747 && !matches!(fa_v4_mode(), "noB3" | "stage");
16748 let (f, cfg) = if fa_vec && head_dim == 512 && t_kv >= fa512_min {
16749 let gqa = (n_head / n_head_kv).max(1) as u32;
16752 let fv = self.fa_func("fa_decode_vec_q_dpl16", head_dim);
16753 (
16754 fv,
16755 LaunchConfig {
16756 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16757 block_dim: (32, gqa, 1),
16758 shared_mem_bytes: 0,
16759 },
16760 )
16761 } else if fa_vec && head_dim <= 256 {
16762 let gqa = (n_head / n_head_kv).max(1) as u32;
16763 static SMEM_TKV: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
16774 let smem_tkv = *SMEM_TKV.get_or_init(|| {
16775 std::env::var("MEMRA_FA_SMEM_TKV")
16776 .ok()
16777 .and_then(|v| v.parse().ok())
16778 .unwrap_or_else(|| {
16779 FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
16780 })
16781 });
16782 if fa_v4_at(t_kv) && head_dim == 256 {
16783 let v4name = match fa_v4_mode() {
16787 "noB3" => "fa_decode_vec_q_v4_noB3", "stage" => "fa_decode_vec_q_v4_stage", _ if deep => "fa_decode_vec_q_v4_deep",
16790 _ => "fa_decode_vec_q_v4",
16791 };
16792 let fv = if g {
16793 self.func_g(v4name)
16794 } else {
16795 self.func(v4name)
16796 };
16797 let shmem = (if deep { 12160 } else { 11520 }
16800 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
16801 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16802 fv.set_attribute(
16803 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16804 shmem as i32,
16805 )?;
16806 (
16807 fv,
16808 LaunchConfig {
16809 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16810 block_dim: (32, gqa, 1),
16811 shared_mem_bytes: shmem,
16812 },
16813 )
16814 } else if fa_v3_active(head_dim) {
16815 let fv = if g {
16818 self.func_g("fa_decode_vec_q_v3")
16819 } else {
16820 self.func("fa_decode_vec_q_v3")
16821 };
16822 let shmem = (32 * head_dim * 2) as u32; (
16824 fv,
16825 LaunchConfig {
16826 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16827 block_dim: (32, gqa, 1),
16828 shared_mem_bytes: shmem,
16829 },
16830 )
16831 } else if fa_v2_on() {
16832 let fv = if g {
16836 self.func_g("fa_decode_vec_q_v2")
16837 } else {
16838 self.func("fa_decode_vec_q_v2")
16839 };
16840 let shmem = (2 * 32 * head_dim * 2) as u32; (
16842 fv,
16843 LaunchConfig {
16844 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16845 block_dim: (32, gqa, 1),
16846 shared_mem_bytes: shmem,
16847 },
16848 )
16849 } else if smem_tkv > 0 && t_kv >= smem_tkv && !g && !(head_dim == 512 && Self::gkv_on())
16850 {
16851 let fv = if g {
16855 self.func_g("fa_decode_vec_q_smem")
16856 } else {
16857 self.func("fa_decode_vec_q_smem")
16858 };
16859 let shmem = (2 * 32 * head_dim * 2) as u32; use cudarc::driver::sys::CUfunction_attribute_enum as A;
16861 fv.set_attribute(
16862 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16863 shmem as i32,
16864 )?;
16865 (
16866 fv,
16867 LaunchConfig {
16868 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16869 block_dim: (32, gqa, 1),
16870 shared_mem_bytes: shmem,
16871 },
16872 )
16873 } else {
16874 let fv = if g {
16877 self.func_g("fa_decode_vec_q")
16878 } else {
16879 self.func("fa_decode_vec_q")
16880 };
16881 (
16882 fv,
16883 LaunchConfig {
16884 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16885 block_dim: (32, gqa, 1),
16886 shared_mem_bytes: 0,
16887 },
16888 )
16889 }
16890 } else {
16891 return self.fa_decode_scalar_unified(
16894 q,
16895 k,
16896 v,
16897 o,
16898 head_dim,
16899 n_head,
16900 n_head_kv,
16901 t_kv,
16902 None,
16903 scale,
16904 n_splits,
16905 if fa_vec { sp } else { 256 },
16906 k_tok_bytes,
16907 v_tok_bytes,
16908 g,
16909 part_o,
16910 part_m,
16911 part_l,
16912 None,
16913 );
16914 };
16915 let __s_b = self.gpu.stream();
16916 let mut b = __s_b.launch_builder(&f);
16917 b.arg(q)
16918 .arg(k)
16919 .arg(v)
16920 .arg(&mut *part_o)
16921 .arg(&mut *part_m)
16922 .arg(&mut *part_l)
16923 .arg(&hd)
16924 .arg(&nh)
16925 .arg(&nhkv)
16926 .arg(&tkvi)
16927 .arg(&scale)
16928 .arg(&nsp)
16929 .arg(&ktb)
16930 .arg(&vtb);
16931 unsafe {
16932 b.launch(cfg)?;
16933 }
16934 let (fc, cfg2) = (
16937 if g {
16938 self.func_g("fa_decode_combine_f32")
16939 } else {
16940 self.fa_func("fa_decode_combine_f32", head_dim)
16941 },
16942 LaunchConfig {
16943 grid_dim: (n_head as u32, 1, 1),
16944 block_dim: (head_dim as u32, 1, 1),
16945 shared_mem_bytes: 0,
16946 },
16947 );
16948 let __s_b2 = self.gpu.stream();
16949 let mut b2 = __s_b2.launch_builder(&fc);
16950 b2.arg(&*part_o)
16951 .arg(&*part_m)
16952 .arg(&*part_l)
16953 .arg(o)
16954 .arg(&hd)
16955 .arg(&nh)
16956 .arg(&nsp);
16957 unsafe {
16958 b2.launch(cfg2)?;
16959 }
16960 Ok(())
16961 }
16962
16963 #[allow(clippy::too_many_arguments)]
16974 pub fn fa_decode_batch_seqs_v4(
16975 &self,
16976 q: &CudaSlice<f32>,
16977 kv_ptrs: &cudarc::driver::CudaView<u64>,
16978 pos_seq: &CudaSlice<i32>,
16979 o: &mut CudaSlice<f32>,
16980 head_dim: usize,
16981 n_head: usize,
16982 n_head_kv: usize,
16983 b_n: usize,
16984 t_kv_max: usize,
16985 scale: f32,
16986 split_keys: usize,
16987 k_tok_bytes: usize,
16988 v_tok_bytes: usize,
16989 ) -> Result<(), Box<dyn std::error::Error>> {
16990 debug_assert!(head_dim == 256, "seqs twin is v4-stamped (hd256 only)");
16991 let n_splits_max = (t_kv_max + split_keys - 1) / split_keys;
16992 let o_len = b_n * n_head * n_splits_max * head_dim;
16993 let ml_len = b_n * n_head * n_splits_max;
16994 let mut part_guard = self.fa_part_pool.lock().unwrap();
16995 if part_guard
16996 .as_ref()
16997 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
16998 .unwrap_or(true)
16999 {
17000 let old = part_guard.take();
17011 let (co, cm) = old
17012 .as_ref()
17013 .map(|pp| (pp.0.len(), pp.1.len()))
17014 .unwrap_or((0, 0));
17015 if let Some(old) = old {
17016 self.fa_part_retired.lock().unwrap().push(old);
17017 }
17018 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17019 eprintln!(
17020 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17021 co, o_len, cm, ml_len
17022 );
17023 }
17024 *part_guard = Some((
17025 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17026 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17027 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17028 ));
17029 }
17030 let pg = part_guard.as_mut().unwrap();
17031 self.gpu
17032 .stream()
17033 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17034 self.gpu
17035 .stream()
17036 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17037 self.gpu
17038 .stream()
17039 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17040 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17041 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17042 let (nspm, spk) = (n_splits_max as i32, split_keys as i32);
17043 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17044 let gqa = (n_head / n_head_kv).max(1) as u32;
17045 let f = self.func("fa_decode_vec_q_seqs_v4");
17046 let shmem = (11520 + 32 * head_dim * 2) as u32;
17048 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17049 f.set_attribute(
17050 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17051 shmem as i32,
17052 )?;
17053 let cfg = LaunchConfig {
17054 grid_dim: (n_head_kv as u32, n_splits_max as u32, b_n as u32),
17055 block_dim: (32, gqa, 1),
17056 shared_mem_bytes: shmem,
17057 };
17058 {
17059 let __s_b = self.gpu.stream();
17060 let mut b = __s_b.launch_builder(&f);
17061 b.arg(q)
17062 .arg(kv_ptrs)
17063 .arg(pos_seq)
17064 .arg(&mut *part_o)
17065 .arg(&mut *part_m)
17066 .arg(&mut *part_l)
17067 .arg(&hd)
17068 .arg(&nh)
17069 .arg(&nhkv)
17070 .arg(&scale)
17071 .arg(&nspm)
17072 .arg(&spk)
17073 .arg(&ktb)
17074 .arg(&vtb);
17075 unsafe {
17076 b.launch(cfg)?;
17077 }
17078 }
17079 let fc = self.func("fa_decode_combine_seqs");
17080 let cfg2 = LaunchConfig {
17081 grid_dim: (n_head as u32, b_n as u32, 1),
17082 block_dim: (head_dim as u32, 1, 1),
17083 shared_mem_bytes: 0,
17084 };
17085 let __s_b2 = self.gpu.stream();
17086 let mut b2 = __s_b2.launch_builder(&fc);
17087 b2.arg(&*part_o)
17088 .arg(&*part_m)
17089 .arg(&*part_l)
17090 .arg(o)
17091 .arg(&hd)
17092 .arg(&nh)
17093 .arg(pos_seq)
17094 .arg(&nspm)
17095 .arg(&spk);
17096 unsafe {
17097 b2.launch(cfg2)?;
17098 }
17099 Ok(())
17100 }
17101
17102 #[allow(clippy::too_many_arguments)]
17109 pub fn append_kv_quantized_seqs(
17110 &self,
17111 k_rows: &CudaSlice<f32>,
17112 v_rows: &CudaSlice<f32>,
17113 kv_ptrs: &cudarc::driver::CudaView<u64>,
17114 pos_seq: &CudaSlice<i32>,
17115 b_n: usize,
17116 kv_dim_k: usize,
17117 kv_dim_v: usize,
17118 k_tok_bytes: usize,
17119 v_tok_bytes: usize,
17120 ) -> Result<(), Box<dyn std::error::Error>> {
17121 let f = self.func("append_quantize_kv_q8_0_q5_1_seqs");
17122 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
17123 let cfg = LaunchConfig {
17124 grid_dim: (nblk, b_n as u32, 1),
17125 block_dim: (32, 1, 1),
17126 shared_mem_bytes: 0,
17127 };
17128 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
17129 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17130 let __s_b = self.gpu.stream();
17131 let mut b = __s_b.launch_builder(&f);
17132 b.arg(k_rows)
17133 .arg(v_rows)
17134 .arg(kv_ptrs)
17135 .arg(pos_seq)
17136 .arg(&kdk)
17137 .arg(&kdv)
17138 .arg(&ktb)
17139 .arg(&vtb);
17140 unsafe {
17141 b.launch(cfg)?;
17142 }
17143 Ok(())
17144 }
17145
17146 pub fn fa_rows_eligible(&self, base_len: usize, head_dim: usize) -> bool {
17152 std::env::var("MEMRA_NO_FA_VEC").is_err()
17153 && std::env::var("MEMRA_FA_ROWS_OFF").is_err()
17154 && base_len + 1 >= fa_vec_min_tkv()
17155 && head_dim <= 256
17156 && head_dim % 32 == 0
17157 }
17158
17159 #[allow(clippy::too_many_arguments)]
17168 pub fn fa_decode_rows(
17169 &self,
17170 q: &CudaSlice<f32>,
17171 k: &cudarc::driver::CudaView<u8>,
17172 v: &cudarc::driver::CudaView<u8>,
17173 o: &mut CudaSlice<f32>,
17174 head_dim: usize,
17175 n_head: usize,
17176 n_head_kv: usize,
17177 base_len: usize,
17178 t: usize,
17179 scale: f32,
17180 k_tok_bytes: usize,
17181 v_tok_bytes: usize,
17182 base_dev: Option<(&CudaSlice<i32>, i32)>,
17186 kv_shared: bool,
17189 g: bool,
17193 mut q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
17196 ) -> Result<(), Box<dyn std::error::Error>> {
17197 debug_assert!(base_len + 1 >= fa_vec_min_tkv() && head_dim <= 512 && head_dim % 32 == 0);
17198 let t_kv_max = base_len + t; let mut sp = fa_split_keys(t_kv_max, n_head_kv); if head_dim == 512 {
17205 static SP512: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17206 let v = *SP512.get_or_init(|| {
17209 std::env::var("MEMRA_FA_SP512")
17210 .ok()
17211 .and_then(|x| x.parse().ok())
17212 .unwrap_or(0)
17213 });
17214 sp = if v >= 8 {
17215 v
17216 } else {
17217 FA_SP512_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
17218 };
17219 }
17220 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17221 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17222 let gqa = (n_head / n_head_kv).max(1) as u32;
17223 let mut groups: Vec<(usize, usize, usize)> = Vec::new(); if head_dim == 512 || fa_split_keys(base_len + 1, n_head_kv) == sp {
17234 groups.push((0, t, sp));
17235 } else {
17236 let mut r0 = 0usize;
17237 while r0 < t {
17238 let sp_g = fa_split_keys(base_len + r0 + 1, n_head_kv);
17239 let mut r1 = r0 + 1;
17240 while r1 < t && fa_split_keys(base_len + r1 + 1, n_head_kv) == sp_g {
17241 r1 += 1;
17242 }
17243 groups.push((r0, r1 - r0, sp_g));
17244 r0 = r1;
17245 }
17246 }
17247 static SMEM_TKV_R: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17251 let smem_tkv = *SMEM_TKV_R.get_or_init(|| {
17252 std::env::var("MEMRA_FA_SMEM_TKV")
17253 .ok()
17254 .and_then(|v| v.parse().ok())
17255 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
17256 });
17257 let v4 = fa_v4_at(base_len + t) && head_dim == 256;
17258 let v3 = fa_v3_active(head_dim);
17259 let smem_rows =
17260 head_dim <= 256 && !v3 && !fa_v2_on() && smem_tkv > 0 && t_kv_max >= smem_tkv;
17261 let _ = kv_shared;
17266 let i2 = head_dim == 512 && std::env::var("MEMRA_FA_I2").as_deref() != Ok("0");
17269 static TB512: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
17283 let tb512 = head_dim == 512
17285 && sp <= 32
17286 && n_head / n_head_kv.max(1) <= 16
17287 && *TB512.get_or_init(|| std::env::var("MEMRA_FA_TB512").as_deref() != Ok("0"));
17288 let fname = if tb512 {
17289 "fa_decode_vec_q_rows_v4_512_tb"
17290 } else if i2 {
17291 "fa_decode_vec_q_rows_dpl16_i2"
17292 } else if head_dim == 512 {
17293 "fa_decode_vec_q_rows_dpl16"
17294 }
17295 else if v4 {
17297 "fa_decode_vec_q_rows_v4"
17298 } else if v3 {
17299 "fa_decode_vec_q_rows_v3"
17300 } else if fa_v2_on() {
17301 "fa_decode_vec_q_rows_v2"
17302 } else if smem_rows {
17303 "fa_decode_vec_q_rows_smem"
17304 } else {
17305 "fa_decode_vec_q_rows"
17306 };
17307 let f = if head_dim == 512 {
17308 self.fa_func(fname, head_dim)
17309 } else if g {
17310 self.func_g(if smem_rows {
17318 "fa_decode_vec_q_rows"
17319 } else {
17320 fname
17321 })
17322 } else {
17323 self.func(fname)
17324 };
17325 let shmem = if tb512 {
17326 let gk = Self::gkv_on();
17328 let sh =
17329 (8192 + 1024 + 32 * 512 + 32 * 64 + 32 * head_dim * if gk { 1 } else { 2 }) as u32;
17330 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17331 f.set_attribute(
17332 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17333 sh as i32,
17334 )?;
17335 sh
17336 } else if v4 || v3 || smem_rows || fa_v2_on() {
17337 let sh = (if v4 {
17339 11520 + 32 * head_dim * if g { 1 } else { 2 }
17340 } else if v3 {
17341 32 * head_dim * 2
17342 } else {
17343 2 * 32 * head_dim * 2
17344 }) as u32;
17345 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17346 f.set_attribute(
17347 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17348 sh as i32,
17349 )?;
17350 sh
17351 } else {
17352 0
17353 };
17354 for &(r0, t_g, sp_g) in &groups {
17358 let n_splits_g = (base_len + r0 + t_g).div_ceil(sp_g);
17359 let (nspm, spk) = (n_splits_g as i32, sp_g as i32);
17360 let base_i = (base_len + r0) as i32;
17361 let o_len = t_g * n_head * n_splits_g * head_dim;
17362 let ml_len = t_g * n_head * n_splits_g;
17363 let mut part_guard = self.fa_part_pool.lock().unwrap();
17364 if part_guard
17365 .as_ref()
17366 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17367 .unwrap_or(true)
17368 {
17369 let old = part_guard.take();
17380 let (co, cm) = old
17381 .as_ref()
17382 .map(|pp| (pp.0.len(), pp.1.len()))
17383 .unwrap_or((0, 0));
17384 if let Some(old) = old {
17385 self.fa_part_retired.lock().unwrap().push(old);
17386 }
17387 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17388 eprintln!(
17389 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17390 co, o_len, cm, ml_len
17391 );
17392 }
17393 *part_guard = Some((
17394 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17395 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17396 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17397 ));
17398 }
17399 let pg = part_guard.as_mut().unwrap();
17400 self.gpu
17401 .stream()
17402 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17403 self.gpu
17404 .stream()
17405 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17406 self.gpu
17407 .stream()
17408 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17409 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17410 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
17411 let qv = self.view(q, t * n_head * head_dim);
17412 let q_g = qv.slice(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
17413 let cfg = LaunchConfig {
17414 grid_dim: (n_head_kv as u32, n_splits_g as u32, t_g as u32),
17415 block_dim: (32, gqa, 1),
17416 shared_mem_bytes: shmem,
17417 };
17418 {
17419 let __s_b = self.gpu.stream();
17420 let mut b = __s_b.launch_builder(&f);
17421 if tb512 {
17422 let (bd, plus) =
17424 base_dev.expect("hd512 rows twin requires a device base counter");
17425 let plus_g = plus + r0 as i32;
17426 let nr = t_g as i32;
17427 if Self::pdl_on() && Self::pdl_wb_on() {
17428 use cudarc::driver::{DevicePtr, DevicePtrMut};
17430 let s = &self.gpu.stream();
17431 let (pq, _b0) = q_g.device_ptr(s);
17432 let (pk, _b1) = k.device_ptr(s);
17433 let (pv, _b2) = v.device_ptr(s);
17434 let (po, _b3) = part_o.device_ptr_mut(s);
17435 let (pm, _b4) = part_m.device_ptr_mut(s);
17436 let (pl, _b5) = part_l.device_ptr_mut(s);
17437 let (pb, _b6) = bd.device_ptr(s);
17438 let mut ps = [
17439 &pq as *const _ as *mut std::ffi::c_void,
17440 &pk as *const _ as *mut _,
17441 &pv as *const _ as *mut _,
17442 &po as *const _ as *mut _,
17443 &pm as *const _ as *mut _,
17444 &pl as *const _ as *mut _,
17445 &hd as *const _ as *mut _,
17446 &nh as *const _ as *mut _,
17447 &nhkv as *const _ as *mut _,
17448 &pb as *const _ as *mut _,
17449 &plus_g as *const _ as *mut _,
17450 &scale as *const _ as *mut _,
17451 &nspm as *const _ as *mut _,
17452 &spk as *const _ as *mut _,
17453 &ktb as *const _ as *mut _,
17454 &vtb as *const _ as *mut _,
17455 &nr as *const _ as *mut _,
17456 ];
17457 unsafe {
17458 self.launch_pdl_flash(
17459 Self::gkv_on(),
17460 "fa_decode_vec_q_rows_v4_512_tb",
17461 (n_head_kv as u32, n_splits_g as u32, 1),
17462 (32, gqa, 1),
17463 shmem,
17464 &mut ps,
17465 )?;
17466 }
17467 } else {
17468 let cfg_tb = LaunchConfig {
17469 grid_dim: (n_head_kv as u32, n_splits_g as u32, 1),
17470 block_dim: (32, gqa, 1),
17471 shared_mem_bytes: shmem,
17472 };
17473 b.arg(&q_g)
17474 .arg(k)
17475 .arg(v)
17476 .arg(&mut *part_o)
17477 .arg(&mut *part_m)
17478 .arg(&mut *part_l)
17479 .arg(&hd)
17480 .arg(&nh)
17481 .arg(&nhkv)
17482 .arg(bd)
17483 .arg(&plus_g)
17484 .arg(&scale)
17485 .arg(&nspm)
17486 .arg(&spk)
17487 .arg(&ktb)
17488 .arg(&vtb)
17489 .arg(&nr);
17490 unsafe {
17491 b.launch(cfg_tb)?;
17492 }
17493 }
17494 } else if head_dim == 512 {
17495 let (bd, plus) =
17496 base_dev.expect("hd512 rows twin requires a device base counter");
17497 let plus_g = plus + r0 as i32;
17498 b.arg(&q_g)
17499 .arg(k)
17500 .arg(v)
17501 .arg(&mut *part_o)
17502 .arg(&mut *part_m)
17503 .arg(&mut *part_l)
17504 .arg(&hd)
17505 .arg(&nh)
17506 .arg(&nhkv)
17507 .arg(bd)
17508 .arg(&plus_g)
17509 .arg(&scale)
17510 .arg(&nspm)
17511 .arg(&spk)
17512 .arg(&ktb)
17513 .arg(&vtb);
17514 unsafe {
17515 b.launch(cfg)?;
17516 }
17517 } else {
17518 b.arg(&q_g)
17519 .arg(k)
17520 .arg(v)
17521 .arg(&mut *part_o)
17522 .arg(&mut *part_m)
17523 .arg(&mut *part_l)
17524 .arg(&hd)
17525 .arg(&nh)
17526 .arg(&nhkv)
17527 .arg(&base_i)
17528 .arg(&scale)
17529 .arg(&nspm)
17530 .arg(&spk)
17531 .arg(&ktb)
17532 .arg(&vtb);
17533 unsafe {
17534 b.launch(cfg)?;
17535 }
17536 }
17537 }
17538 let cfg2 = LaunchConfig {
17539 grid_dim: (n_head as u32, t_g as u32, 1),
17540 block_dim: (head_dim as u32, 1, 1),
17541 shared_mem_bytes: 0,
17542 };
17543 let mut o_g = o.slice_mut(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
17544 if head_dim == 512 {
17545 let (bd, plus) = base_dev.unwrap();
17548 let plus_g = plus + r0 as i32;
17549 if let Some((oq, od)) = q8_out.as_mut() {
17550 debug_assert!(t == 1, "rows q8 emit is a t=1 decode arm");
17552 if Self::pdl_on() && Self::pdl_wb_on() {
17553 use cudarc::driver::{DevicePtr, DevicePtrMut};
17555 let s = &self.gpu.stream();
17556 let (po, _g0) = part_o.device_ptr(s);
17557 let (pm, _g1) = part_m.device_ptr(s);
17558 let (pl, _g2) = part_l.device_ptr(s);
17559 let (pq, _g3) = oq.device_ptr_mut(s);
17560 let (pd, _g4) = od.device_ptr_mut(s);
17561 let (pb, _g5) = bd.device_ptr(s);
17562 let mut ps = [
17563 &po as *const _ as *mut std::ffi::c_void,
17564 &pm as *const _ as *mut _,
17565 &pl as *const _ as *mut _,
17566 &pq as *const _ as *mut _,
17567 &pd as *const _ as *mut _,
17568 &hd as *const _ as *mut _,
17569 &nh as *const _ as *mut _,
17570 &pb as *const _ as *mut _,
17571 &plus_g as *const _ as *mut _,
17572 &nspm as *const _ as *mut _,
17573 &spk as *const _ as *mut _,
17574 ];
17575 unsafe {
17576 self.launch_pdl_flash(
17577 Self::gkv_on(),
17578 "fa_decode_combine_rows_dc_q8_1",
17579 cfg2.grid_dim,
17580 cfg2.block_dim,
17581 0,
17582 &mut ps,
17583 )?;
17584 }
17585 continue;
17586 }
17587 let fc = self.fa_func("fa_decode_combine_rows_dc_q8_1", head_dim);
17588 let __s_b2 = self.gpu.stream();
17589 let mut b2 = __s_b2.launch_builder(&fc);
17590 b2.arg(&*part_o)
17591 .arg(&*part_m)
17592 .arg(&*part_l)
17593 .arg(&mut **oq)
17594 .arg(&mut **od)
17595 .arg(&hd)
17596 .arg(&nh)
17597 .arg(bd)
17598 .arg(&plus_g)
17599 .arg(&nspm)
17600 .arg(&spk);
17601 unsafe {
17602 b2.launch(cfg2)?;
17603 }
17604 continue;
17605 }
17606 let fc = self.fa_func("fa_decode_combine_rows_dc", head_dim);
17607 let __s_b2 = self.gpu.stream();
17608 let mut b2 = __s_b2.launch_builder(&fc);
17609 b2.arg(&*part_o)
17610 .arg(&*part_m)
17611 .arg(&*part_l)
17612 .arg(&mut o_g)
17613 .arg(&hd)
17614 .arg(&nh)
17615 .arg(bd)
17616 .arg(&plus_g)
17617 .arg(&nspm)
17618 .arg(&spk);
17619 unsafe {
17620 b2.launch(cfg2)?;
17621 }
17622 } else {
17623 assert!(
17626 q8_out.is_none(),
17627 "rows q8 emit requires the hd512 dc combine"
17628 );
17629 let fc = self.func("fa_decode_combine_rows");
17630 let __s_b2 = self.gpu.stream();
17631 let mut b2 = __s_b2.launch_builder(&fc);
17632 b2.arg(&*part_o)
17633 .arg(&*part_m)
17634 .arg(&*part_l)
17635 .arg(&mut o_g)
17636 .arg(&hd)
17637 .arg(&nh)
17638 .arg(&base_i)
17639 .arg(&nspm)
17640 .arg(&spk);
17641 unsafe {
17642 b2.launch(cfg2)?;
17643 }
17644 }
17645 }
17646 Ok(())
17647 }
17648
17649 #[allow(clippy::too_many_arguments)]
17653 pub fn fa_decode_rows_w(
17654 &self,
17655 q: &CudaSlice<f32>,
17656 k: &cudarc::driver::CudaView<u8>,
17657 v: &cudarc::driver::CudaView<u8>,
17658 o: &mut CudaSlice<f32>,
17659 head_dim: usize,
17660 n_head: usize,
17661 n_head_kv: usize,
17662 base_dev: &CudaSlice<i32>,
17663 base_plus: i32,
17664 t: usize,
17665 scale: f32,
17666 window: usize,
17667 k_tok_bytes: usize,
17668 v_tok_bytes: usize,
17669 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
17670 ) -> Result<(), Box<dyn std::error::Error>> {
17671 debug_assert!(head_dim == 256);
17676 let sp = {
17684 static SPW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17685 let v = *SPW.get_or_init(|| {
17686 std::env::var("MEMRA_FA_SPW")
17687 .ok()
17688 .and_then(|x| x.parse().ok())
17689 .unwrap_or(0)
17690 });
17691 if v >= 8 {
17692 v
17693 } else {
17694 FA_SPW_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
17695 }
17696 };
17697 let n_splits_max = (window + sp - 1) / sp;
17698 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17699 let (nspm, spk, wini) = (n_splits_max as i32, sp as i32, window as i32);
17700 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17701 let gqa = (n_head / n_head_kv).max(1) as u32;
17702 let o_len = t * n_head * n_splits_max * head_dim;
17703 let ml_len = t * n_head * n_splits_max;
17704 let mut part_guard = self.fa_part_pool.lock().unwrap();
17705 if part_guard
17706 .as_ref()
17707 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17708 .unwrap_or(true)
17709 {
17710 let old = part_guard.take();
17721 let (co, cm) = old
17722 .as_ref()
17723 .map(|pp| (pp.0.len(), pp.1.len()))
17724 .unwrap_or((0, 0));
17725 if let Some(old) = old {
17726 self.fa_part_retired.lock().unwrap().push(old);
17727 }
17728 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17729 eprintln!(
17730 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17731 co, o_len, cm, ml_len
17732 );
17733 }
17734 *part_guard = Some((
17735 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17736 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17737 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17738 ));
17739 }
17740 let pg = part_guard.as_mut().unwrap();
17741 self.gpu
17742 .stream()
17743 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17744 self.gpu
17745 .stream()
17746 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17747 self.gpu
17748 .stream()
17749 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17750 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17751 static SMEM_TKV_W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17757 let smem_tkv = *SMEM_TKV_W.get_or_init(|| {
17758 std::env::var("MEMRA_FA_SMEM_TKV")
17759 .ok()
17760 .and_then(|v| v.parse().ok())
17761 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
17762 });
17763 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17769 let wg = Self::wkv_on();
17774 let sp2 =
17777 gqa <= 4 && fa_v4_at(window) && std::env::var("MEMRA_FA_SPW2").as_deref() != Ok("0");
17778 if sp2 {
17779 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
17780 if Self::pdl_on() && Self::pdl_wb_on() {
17781 use cudarc::driver::{DevicePtr, DevicePtrMut};
17783 let s = &self.gpu.stream();
17784 let (pq, _b0) = q.device_ptr(s);
17785 let (pk, _b1) = k.device_ptr(s);
17786 let (pv, _b2) = v.device_ptr(s);
17787 let (po, _b3) = part_o.device_ptr_mut(s);
17788 let (pm, _b4) = part_m.device_ptr_mut(s);
17789 let (pl, _b5) = part_l.device_ptr_mut(s);
17790 let (pb, _b6) = base_dev.device_ptr(s);
17791 let mut ps = [
17792 &pq as *const _ as *mut std::ffi::c_void,
17793 &pk as *const _ as *mut _,
17794 &pv as *const _ as *mut _,
17795 &po as *const _ as *mut _,
17796 &pm as *const _ as *mut _,
17797 &pl as *const _ as *mut _,
17798 &hd as *const _ as *mut _,
17799 &nh as *const _ as *mut _,
17800 &nhkv as *const _ as *mut _,
17801 &pb as *const _ as *mut _,
17802 &base_plus as *const _ as *mut _,
17803 &scale as *const _ as *mut _,
17804 &nspm as *const _ as *mut _,
17805 &spk as *const _ as *mut _,
17806 &ktb as *const _ as *mut _,
17807 &vtb as *const _ as *mut _,
17808 &wini as *const _ as *mut _,
17809 ];
17810 unsafe {
17811 self.launch_pdl_flash(
17812 wg,
17813 "fa_decode_vec_q_rows_v4_w_sp",
17814 (n_head_kv as u32, n_splits_max as u32, t as u32),
17815 (32, gqa + 1, 1),
17816 sh,
17817 &mut ps,
17818 )?;
17819 }
17820 } else {
17821 let f = if wg {
17822 self.func_g("fa_decode_vec_q_rows_v4_w_sp")
17823 } else {
17824 self.func("fa_decode_vec_q_rows_v4_w_sp")
17825 };
17826 f.set_attribute(
17827 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17828 sh as i32,
17829 )?;
17830 let cfg = LaunchConfig {
17831 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
17832 block_dim: (32, gqa + 1, 1),
17833 shared_mem_bytes: sh,
17834 };
17835 let __s_b = self.gpu.stream();
17836 let mut b = __s_b.launch_builder(&f);
17837 b.arg(q)
17838 .arg(k)
17839 .arg(v)
17840 .arg(&mut *part_o)
17841 .arg(&mut *part_m)
17842 .arg(&mut *part_l)
17843 .arg(&hd)
17844 .arg(&nh)
17845 .arg(&nhkv)
17846 .arg(base_dev)
17847 .arg(&base_plus)
17848 .arg(&scale)
17849 .arg(&nspm)
17850 .arg(&spk)
17851 .arg(&ktb)
17852 .arg(&vtb)
17853 .arg(&wini);
17854 unsafe {
17855 b.launch(cfg)?;
17856 }
17857 }
17858 } else {
17859 if fa_v4_at(window) && Self::pdl_on() && Self::pdl_wb_on() {
17860 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
17862 use cudarc::driver::{DevicePtr, DevicePtrMut};
17863 let s = &self.gpu.stream();
17864 let (pq, _b0) = q.device_ptr(s);
17865 let (pk, _b1) = k.device_ptr(s);
17866 let (pv, _b2) = v.device_ptr(s);
17867 let (po, _b3) = part_o.device_ptr_mut(s);
17868 let (pm, _b4) = part_m.device_ptr_mut(s);
17869 let (pl, _b5) = part_l.device_ptr_mut(s);
17870 let (pb, _b6) = base_dev.device_ptr(s);
17871 let mut ps = [
17872 &pq as *const _ as *mut std::ffi::c_void,
17873 &pk as *const _ as *mut _,
17874 &pv as *const _ as *mut _,
17875 &po as *const _ as *mut _,
17876 &pm as *const _ as *mut _,
17877 &pl as *const _ as *mut _,
17878 &hd as *const _ as *mut _,
17879 &nh as *const _ as *mut _,
17880 &nhkv as *const _ as *mut _,
17881 &pb as *const _ as *mut _,
17882 &base_plus as *const _ as *mut _,
17883 &scale as *const _ as *mut _,
17884 &nspm as *const _ as *mut _,
17885 &spk as *const _ as *mut _,
17886 &ktb as *const _ as *mut _,
17887 &vtb as *const _ as *mut _,
17888 &wini as *const _ as *mut _,
17889 ];
17890 unsafe {
17891 self.launch_pdl_flash(
17892 wg,
17893 "fa_decode_vec_q_rows_v4_w",
17894 (n_head_kv as u32, n_splits_max as u32, t as u32),
17895 (32, gqa, 1),
17896 sh,
17897 &mut ps,
17898 )?;
17899 }
17900 } else {
17901 let pick = |name: &str| {
17902 if wg {
17903 self.func_g(name)
17904 } else {
17905 self.func(name)
17906 }
17907 };
17908 let (f, sh) = if fa_v4_at(window) {
17909 let f = pick("fa_decode_vec_q_rows_v4_w");
17910 (f, (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32)
17911 } else if smem_tkv > 0 && window >= smem_tkv {
17912 (
17915 pick("fa_decode_vec_q_rows_smem_w"),
17916 (2 * 32 * head_dim * 2) as u32,
17917 )
17918 } else {
17919 (pick("fa_decode_vec_q_rows_reg_w"), 0u32)
17920 };
17921 f.set_attribute(
17922 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17923 sh as i32,
17924 )?;
17925 let cfg = LaunchConfig {
17926 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
17927 block_dim: (32, gqa, 1),
17928 shared_mem_bytes: sh,
17929 };
17930 let __s_b = self.gpu.stream();
17931 let mut b = __s_b.launch_builder(&f);
17932 b.arg(q)
17933 .arg(k)
17934 .arg(v)
17935 .arg(&mut *part_o)
17936 .arg(&mut *part_m)
17937 .arg(&mut *part_l)
17938 .arg(&hd)
17939 .arg(&nh)
17940 .arg(&nhkv)
17941 .arg(base_dev)
17942 .arg(&base_plus)
17943 .arg(&scale)
17944 .arg(&nspm)
17945 .arg(&spk)
17946 .arg(&ktb)
17947 .arg(&vtb)
17948 .arg(&wini);
17949 unsafe {
17950 b.launch(cfg)?;
17951 }
17952 }
17953 }
17954 let cfg2 = LaunchConfig {
17955 grid_dim: (n_head as u32, t as u32, 1),
17956 block_dim: (head_dim as u32, 1, 1),
17957 shared_mem_bytes: 0,
17958 };
17959 if let Some((oq, od)) = q8_out {
17960 if Self::pdl_on() && Self::pdl_wb_on() {
17963 use cudarc::driver::{DevicePtr, DevicePtrMut};
17965 let s = &self.gpu.stream();
17966 let (po, _g0) = part_o.device_ptr(s);
17967 let (pm, _g1) = part_m.device_ptr(s);
17968 let (pl, _g2) = part_l.device_ptr(s);
17969 let (pq, _g3) = oq.device_ptr_mut(s);
17970 let (pd, _g4) = od.device_ptr_mut(s);
17971 let mut ps = [
17972 &po as *const _ as *mut std::ffi::c_void,
17973 &pm as *const _ as *mut _,
17974 &pl as *const _ as *mut _,
17975 &pq as *const _ as *mut _,
17976 &pd as *const _ as *mut _,
17977 &hd as *const _ as *mut _,
17978 &nh as *const _ as *mut _,
17979 &nspm as *const _ as *mut _,
17980 &spk as *const _ as *mut _,
17981 &wini as *const _ as *mut _,
17982 ];
17983 unsafe {
17984 self.launch_pdl_flash(
17985 wg,
17986 "fa_decode_combine_rows_w_q8_1",
17987 cfg2.grid_dim,
17988 cfg2.block_dim,
17989 0,
17990 &mut ps,
17991 )?;
17992 }
17993 return Ok(());
17994 }
17995 let fc = if wg {
17996 self.func_g("fa_decode_combine_rows_w_q8_1")
17997 } else {
17998 self.func("fa_decode_combine_rows_w_q8_1")
17999 };
18000 let __s_b2 = self.gpu.stream();
18001 let mut b2 = __s_b2.launch_builder(&fc);
18002 b2.arg(&*part_o)
18003 .arg(&*part_m)
18004 .arg(&*part_l)
18005 .arg(oq)
18006 .arg(od)
18007 .arg(&hd)
18008 .arg(&nh)
18009 .arg(&nspm)
18010 .arg(&spk)
18011 .arg(&wini);
18012 unsafe {
18013 b2.launch(cfg2)?;
18014 }
18015 return Ok(());
18016 }
18017 let fc = if wg {
18018 self.func_g("fa_decode_combine_rows_w")
18019 } else {
18020 self.func("fa_decode_combine_rows_w")
18021 };
18022 let __s_b2 = self.gpu.stream();
18023 let mut b2 = __s_b2.launch_builder(&fc);
18024 b2.arg(&*part_o)
18025 .arg(&*part_m)
18026 .arg(&*part_l)
18027 .arg(o)
18028 .arg(&hd)
18029 .arg(&nh)
18030 .arg(&nspm)
18031 .arg(&spk)
18032 .arg(&wini);
18033 unsafe {
18034 b2.launch(cfg2)?;
18035 }
18036 Ok(())
18037 }
18038
18039 #[allow(clippy::too_many_arguments)]
18045 pub fn fa_decode_rows_dc(
18046 &self,
18047 q: &CudaSlice<f32>,
18048 k: &cudarc::driver::CudaView<u8>,
18049 v: &cudarc::driver::CudaView<u8>,
18050 o: &mut CudaSlice<f32>,
18051 head_dim: usize,
18052 n_head: usize,
18053 n_head_kv: usize,
18054 base_dev: &CudaSlice<i32>,
18055 t_kv_upper: usize,
18056 t: usize,
18057 scale: f32,
18058 k_tok_bytes: usize,
18059 v_tok_bytes: usize,
18060 base_plus: i32,
18061 g: bool,
18062 ) -> Result<(), Box<dyn std::error::Error>> {
18063 let v4 = head_dim == 256 && fa_v4_at(t_kv_upper);
18064 assert!(
18065 v4 || fa_v3_active(head_dim),
18066 "stream fa rows requires the v3 or v4 lane"
18067 );
18068 assert!(v4 || base_plus == 0, "v3_dc kernel takes no plus arg");
18069 if v4 {
18070 let sp = fa_split_keys(t_kv_upper, n_head_kv);
18071 let n_splits_max = (t_kv_upper + sp - 1) / sp;
18072 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18073 let (nspm, spk) = (n_splits_max as i32, sp as i32);
18074 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18075 let gqa = (n_head / n_head_kv).max(1) as u32;
18076 let o_len = t * n_head * n_splits_max * head_dim;
18077 let ml_len = t * n_head * n_splits_max;
18078 let mut part_guard = self.fa_part_pool.lock().unwrap();
18079 if part_guard
18080 .as_ref()
18081 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18082 .unwrap_or(true)
18083 {
18084 let old = part_guard.take();
18095 let (co, cm) = old
18096 .as_ref()
18097 .map(|pp| (pp.0.len(), pp.1.len()))
18098 .unwrap_or((0, 0));
18099 if let Some(old) = old {
18100 self.fa_part_retired.lock().unwrap().push(old);
18101 }
18102 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18103 eprintln!(
18104 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18105 co, o_len, cm, ml_len
18106 );
18107 }
18108 *part_guard = Some((
18109 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18110 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18111 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18112 ));
18113 }
18114 let pg = part_guard.as_mut().unwrap();
18115 self.gpu
18116 .stream()
18117 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18118 self.gpu
18119 .stream()
18120 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18121 self.gpu
18122 .stream()
18123 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18124 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18125 let f = if g {
18126 self.func_g("fa_decode_vec_q_rows_v4_dc")
18127 } else {
18128 self.func("fa_decode_vec_q_rows_v4_dc")
18129 };
18130 let sh = (11520 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
18131 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18132 f.set_attribute(
18133 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18134 sh as i32,
18135 )?;
18136 let cfg = LaunchConfig {
18137 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18138 block_dim: (32, gqa, 1),
18139 shared_mem_bytes: sh,
18140 };
18141 let __s_b = self.gpu.stream();
18142 let mut b = __s_b.launch_builder(&f);
18143 b.arg(q)
18144 .arg(k)
18145 .arg(v)
18146 .arg(&mut *part_o)
18147 .arg(&mut *part_m)
18148 .arg(&mut *part_l)
18149 .arg(&hd)
18150 .arg(&nh)
18151 .arg(&nhkv)
18152 .arg(base_dev)
18153 .arg(&base_plus)
18154 .arg(&scale)
18155 .arg(&nspm)
18156 .arg(&spk)
18157 .arg(&ktb)
18158 .arg(&vtb);
18159 unsafe {
18160 b.launch(cfg)?;
18161 }
18162 let fc = self.func("fa_decode_combine_rows_dc");
18163 let cfg2 = LaunchConfig {
18164 grid_dim: (n_head as u32, t as u32, 1),
18165 block_dim: (head_dim as u32, 1, 1),
18166 shared_mem_bytes: 0,
18167 };
18168 let __s_b2 = self.gpu.stream();
18169 let mut b2 = __s_b2.launch_builder(&fc);
18170 b2.arg(&*part_o)
18171 .arg(&*part_m)
18172 .arg(&*part_l)
18173 .arg(o)
18174 .arg(&hd)
18175 .arg(&nh)
18176 .arg(base_dev)
18177 .arg(&base_plus)
18178 .arg(&nspm)
18179 .arg(&spk);
18180 unsafe {
18181 b2.launch(cfg2)?;
18182 }
18183 return Ok(());
18184 }
18185 let sp = fa_split_keys(t_kv_upper, n_head_kv);
18186 let n_splits_max = (t_kv_upper + sp - 1) / sp;
18187 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18188 let (nspm, spk) = (n_splits_max as i32, sp as i32);
18189 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18190 let gqa = (n_head / n_head_kv).max(1) as u32;
18191 let o_len = t * n_head * n_splits_max * head_dim;
18192 let ml_len = t * n_head * n_splits_max;
18193 let mut part_guard = self.fa_part_pool.lock().unwrap();
18194 if part_guard
18195 .as_ref()
18196 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18197 .unwrap_or(true)
18198 {
18199 let old = part_guard.take();
18210 let (co, cm) = old
18211 .as_ref()
18212 .map(|pp| (pp.0.len(), pp.1.len()))
18213 .unwrap_or((0, 0));
18214 if let Some(old) = old {
18215 self.fa_part_retired.lock().unwrap().push(old);
18216 }
18217 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18218 eprintln!(
18219 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18220 co, o_len, cm, ml_len
18221 );
18222 }
18223 *part_guard = Some((
18224 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18225 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18226 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18227 ));
18228 }
18229 let pg = part_guard.as_mut().unwrap();
18230 self.gpu
18231 .stream()
18232 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18233 self.gpu
18234 .stream()
18235 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18236 self.gpu
18237 .stream()
18238 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18239 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18240 let f = self.func("fa_decode_vec_q_rows_v3_dc");
18241 let sh = (32 * head_dim * 2) as u32;
18242 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18243 f.set_attribute(
18244 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18245 sh as i32,
18246 )?;
18247 let cfg = LaunchConfig {
18248 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18249 block_dim: (32, gqa, 1),
18250 shared_mem_bytes: sh,
18251 };
18252 let __s_b = self.gpu.stream();
18253 let mut b = __s_b.launch_builder(&f);
18254 b.arg(q)
18255 .arg(k)
18256 .arg(v)
18257 .arg(&mut *part_o)
18258 .arg(&mut *part_m)
18259 .arg(&mut *part_l)
18260 .arg(&hd)
18261 .arg(&nh)
18262 .arg(&nhkv)
18263 .arg(base_dev)
18264 .arg(&scale)
18265 .arg(&nspm)
18266 .arg(&spk)
18267 .arg(&ktb)
18268 .arg(&vtb);
18269 unsafe {
18270 b.launch(cfg)?;
18271 }
18272 let fc = self.func("fa_decode_combine_rows_dc");
18273 let cfg2 = LaunchConfig {
18274 grid_dim: (n_head as u32, t as u32, 1),
18275 block_dim: (head_dim as u32, 1, 1),
18276 shared_mem_bytes: 0,
18277 };
18278 let plus0 = 0i32;
18279 let __s_b2 = self.gpu.stream();
18280 let mut b2 = __s_b2.launch_builder(&fc);
18281 b2.arg(&*part_o)
18282 .arg(&*part_m)
18283 .arg(&*part_l)
18284 .arg(o)
18285 .arg(&hd)
18286 .arg(&nh)
18287 .arg(base_dev)
18288 .arg(&plus0)
18289 .arg(&nspm)
18290 .arg(&spk);
18291 unsafe {
18292 b2.launch(cfg2)?;
18293 }
18294 Ok(())
18295 }
18296
18297 pub fn fa_decode_dc(
18308 &self,
18309 q: &CudaSlice<f32>,
18310 k: &cudarc::driver::CudaView<u8>,
18311 v: &cudarc::driver::CudaView<u8>,
18312 o: &mut CudaSlice<f32>,
18313 head_dim: usize,
18314 n_head: usize,
18315 n_head_kv: usize,
18316 t_kv_dev: &CudaSlice<i32>,
18317 bucket_max: usize,
18318 scale: f32,
18319 k_tok_bytes: usize,
18320 v_tok_bytes: usize,
18321 g: bool,
18322 ) -> Result<(), Box<dyn std::error::Error>> {
18323 self.fa_decode_dc_q8(
18324 q,
18325 k,
18326 v,
18327 o,
18328 head_dim,
18329 n_head,
18330 n_head_kv,
18331 t_kv_dev,
18332 bucket_max,
18333 scale,
18334 k_tok_bytes,
18335 v_tok_bytes,
18336 g,
18337 None,
18338 )
18339 }
18340
18341 #[allow(clippy::too_many_arguments)]
18344 pub fn fa_decode_dc_q8(
18345 &self,
18346 q: &CudaSlice<f32>,
18347 k: &cudarc::driver::CudaView<u8>,
18348 v: &cudarc::driver::CudaView<u8>,
18349 o: &mut CudaSlice<f32>,
18350 head_dim: usize,
18351 n_head: usize,
18352 n_head_kv: usize,
18353 t_kv_dev: &CudaSlice<i32>,
18354 bucket_max: usize,
18355 scale: f32,
18356 k_tok_bytes: usize,
18357 v_tok_bytes: usize,
18358 g: bool,
18359 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
18360 ) -> Result<(), Box<dyn std::error::Error>> {
18361 let mut fa_vec =
18369 std::env::var("MEMRA_NO_FA_VEC").is_err() && bucket_max >= fa_vec_min_tkv();
18370 if g && head_dim == 256 && !fa_v4_at(bucket_max) {
18371 fa_vec = false;
18372 } let sp = fa_split_keys(bucket_max, n_head_kv);
18374 let n_splits = if fa_vec {
18375 ((bucket_max + sp - 1) / sp).max(1)
18376 } else {
18377 ((bucket_max + 255) / 256).max(1)
18378 };
18379 let o_len = n_head * n_splits * head_dim;
18380 let ml_len = n_head * n_splits;
18381 let mut part_guard = self.fa_part_pool.lock().unwrap();
18382 if part_guard
18383 .as_ref()
18384 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18385 .unwrap_or(true)
18386 {
18387 let old = part_guard.take();
18398 let (co, cm) = old
18399 .as_ref()
18400 .map(|pp| (pp.0.len(), pp.1.len()))
18401 .unwrap_or((0, 0));
18402 if let Some(old) = old {
18403 self.fa_part_retired.lock().unwrap().push(old);
18404 }
18405 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18406 eprintln!(
18407 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18408 co, o_len, cm, ml_len
18409 );
18410 }
18411 *part_guard = Some((
18412 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18413 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18414 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18415 ));
18416 }
18417 let pg = part_guard.as_mut().unwrap();
18418 self.gpu
18419 .stream()
18420 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18421 self.gpu
18422 .stream()
18423 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18424 self.gpu
18425 .stream()
18426 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18427 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18428 let (hd, nh, nhkv, nsp) = (
18429 head_dim as i32,
18430 n_head as i32,
18431 n_head_kv as i32,
18432 n_splits as i32,
18433 );
18434 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18435 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
18436 let deep = fa_vec
18439 && head_dim == 256
18440 && fa_v4_at(bucket_max)
18441 && !g
18442 && fa_deep_at(bucket_max)
18443 && !matches!(fa_v4_mode(), "noB3" | "stage");
18444 let (f, cfg) = if fa_vec
18445 && head_dim == 512
18446 && bucket_max >= {
18447 static FA512_MIN_DC: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18448 *FA512_MIN_DC.get_or_init(|| {
18449 std::env::var("MEMRA_FA512_MIN")
18450 .ok()
18451 .and_then(|v| v.parse().ok())
18452 .unwrap_or(512)
18453 })
18454 } {
18455 let gqa = (n_head / n_head_kv).max(1) as u32;
18457 (
18458 self.fa_func("fa_decode_vec_q_dpl16_dc", head_dim),
18459 LaunchConfig {
18460 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18461 block_dim: (32, gqa, 1),
18462 shared_mem_bytes: 0,
18463 },
18464 )
18465 } else if fa_vec && head_dim == 512 {
18466 let q_view = q.as_view();
18469 let mut o_view = o.as_view_mut();
18470 return self.fa_decode_scalar_unified(
18471 &q_view,
18472 k,
18473 v,
18474 &mut o_view,
18475 head_dim,
18476 n_head,
18477 n_head_kv,
18478 0,
18479 Some(t_kv_dev),
18480 scale,
18481 n_splits,
18482 sp,
18483 k_tok_bytes,
18484 v_tok_bytes,
18485 g,
18486 &mut *part_o,
18487 &mut *part_m,
18488 &mut *part_l,
18489 q8_out,
18490 );
18491 } else if fa_vec && head_dim == 256 && fa_v4_at(bucket_max) {
18492 let gqa = (n_head / n_head_kv).max(1) as u32;
18495 let fv = if g {
18496 self.func_g("fa_decode_vec_q_v4_dc")
18497 } else if deep {
18498 self.func("fa_decode_vec_q_v4_deep_dc")
18499 } else {
18500 self.func("fa_decode_vec_q_v4_dc")
18501 };
18502 let shmem =
18503 (if deep { 12160 } else { 11520 } + 32 * head_dim * if g { 1 } else { 2 }) as u32;
18504 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18505 fv.set_attribute(
18506 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18507 shmem as i32,
18508 )?;
18509 (
18510 fv,
18511 LaunchConfig {
18512 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18513 block_dim: (32, gqa, 1),
18514 shared_mem_bytes: shmem,
18515 },
18516 )
18517 } else if fa_vec && fa_v3_active(head_dim) {
18518 let gqa = (n_head / n_head_kv).max(1) as u32;
18521 let fv = if g {
18522 self.func_g("fa_decode_vec_q_v3_dc")
18523 } else {
18524 self.func("fa_decode_vec_q_v3_dc")
18525 };
18526 let shmem = (32 * head_dim * 2) as u32; (
18528 fv,
18529 LaunchConfig {
18530 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18531 block_dim: (32, gqa, 1),
18532 shared_mem_bytes: shmem,
18533 },
18534 )
18535 } else if fa_vec && fa_v2_on() {
18536 let gqa = (n_head / n_head_kv).max(1) as u32;
18540 let fv = if g {
18541 self.func_g("fa_decode_vec_q_v2_dc")
18542 } else {
18543 self.func("fa_decode_vec_q_v2_dc")
18544 };
18545 let shmem = (2 * 32 * head_dim * 2) as u32; (
18547 fv,
18548 LaunchConfig {
18549 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18550 block_dim: (32, gqa, 1),
18551 shared_mem_bytes: shmem,
18552 },
18553 )
18554 } else if fa_vec {
18555 let gqa = (n_head / n_head_kv).max(1) as u32;
18556 let fv = if g {
18558 self.func_g("fa_decode_vec_q_dc")
18559 } else {
18560 self.func("fa_decode_vec_q_dc")
18561 };
18562 (
18563 fv,
18564 LaunchConfig {
18565 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18566 block_dim: (32, gqa, 1),
18567 shared_mem_bytes: 0,
18568 },
18569 )
18570 } else {
18571 let q_view = q.as_view();
18572 let mut o_view = o.as_view_mut();
18573 return self.fa_decode_scalar_unified(
18574 &q_view,
18575 k,
18576 v,
18577 &mut o_view,
18578 head_dim,
18579 n_head,
18580 n_head_kv,
18581 0,
18582 Some(t_kv_dev),
18583 scale,
18584 n_splits,
18585 if fa_vec { sp } else { 256 },
18586 k_tok_bytes,
18587 v_tok_bytes,
18588 g,
18589 &mut *part_o,
18590 &mut *part_m,
18591 &mut *part_l,
18592 q8_out,
18593 );
18594 };
18595 let ski = sp as i32; let __s_b = self.gpu.stream();
18597 let mut b = __s_b.launch_builder(&f);
18598 b.arg(q)
18599 .arg(k)
18600 .arg(v)
18601 .arg(&mut *part_o)
18602 .arg(&mut *part_m)
18603 .arg(&mut *part_l)
18604 .arg(&hd)
18605 .arg(&nh)
18606 .arg(&nhkv)
18607 .arg(t_kv_dev)
18608 .arg(&scale)
18609 .arg(&nsp)
18610 .arg(&ski)
18611 .arg(&ktb)
18612 .arg(&vtb);
18613 unsafe {
18614 b.launch(cfg)?;
18615 }
18616 let cfg2 = LaunchConfig {
18617 grid_dim: (n_head as u32, 1, 1),
18618 block_dim: (head_dim as u32, 1, 1),
18619 shared_mem_bytes: 0,
18620 };
18621 if let Some((oq, od)) = q8_out {
18622 let fc = if g {
18623 self.func_g("fa_decode_combine_q8_1")
18624 } else {
18625 self.fa_func("fa_decode_combine_q8_1", head_dim)
18626 };
18627 let __s_b2 = self.gpu.stream();
18628 let mut b2 = __s_b2.launch_builder(&fc);
18629 b2.arg(&*part_o)
18630 .arg(&*part_m)
18631 .arg(&*part_l)
18632 .arg(oq)
18633 .arg(od)
18634 .arg(&hd)
18635 .arg(&nh)
18636 .arg(&nsp);
18637 unsafe {
18638 b2.launch(cfg2)?;
18639 }
18640 return Ok(());
18641 }
18642 let fc = if g {
18643 self.func_g("fa_decode_combine_f32")
18644 } else {
18645 self.fa_func("fa_decode_combine_f32", head_dim)
18646 };
18647 let __s_b2 = self.gpu.stream();
18648 let mut b2 = __s_b2.launch_builder(&fc);
18649 b2.arg(&*part_o)
18650 .arg(&*part_m)
18651 .arg(&*part_l)
18652 .arg(o)
18653 .arg(&hd)
18654 .arg(&nh)
18655 .arg(&nsp);
18656 unsafe {
18657 b2.launch(cfg2)?;
18658 }
18659 Ok(())
18660 }
18661
18662 pub fn fa_geom_eager(
18668 &self,
18669 t_kv: usize,
18670 head_dim: usize,
18671 n_head_kv: usize,
18672 g: bool,
18673 ) -> (bool, usize) {
18674 let fa_ok = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
18678 let vec512 = fa_ok && head_dim == 512 && t_kv >= fa512_min_tkv();
18684 let mut fa_vec = vec512 || (fa_ok && head_dim <= 256 && head_dim % 32 == 0);
18685 if g && head_dim == 256 && !fa_v4_at(t_kv) {
18691 fa_vec = false;
18692 }
18693 let sp = fa_split_keys(t_kv, n_head_kv);
18694 let n_splits = if fa_vec {
18695 ((t_kv + sp - 1) / sp).max(1)
18696 } else {
18697 ((t_kv + 255) / 256).max(1)
18698 };
18699 (fa_vec, n_splits)
18700 }
18701
18702 pub fn fa_bucket_key(
18708 &self,
18709 t_kv: usize,
18710 head_dim: usize,
18711 n_head_kv: usize,
18712 g: bool,
18713 ) -> (bool, usize) {
18714 self.fa_geom_eager(t_kv, head_dim, n_head_kv, g)
18715 }
18716
18717 pub fn capture_graph_retained<F>(
18729 &self,
18730 step: F,
18731 ) -> Result<
18732 (
18733 cudarc::driver::CudaGraph,
18734 Vec<Box<dyn std::any::Any + Send>>,
18735 ),
18736 Box<dyn std::error::Error>,
18737 >
18738 where
18739 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
18740 {
18741 use cudarc::driver::sys::CUgraphInstantiate_flags;
18742 self.capture_graph_retained_flags(
18743 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
18744 step,
18745 )
18746 }
18747
18748 pub fn capture_graph_retained_flags<F>(
18753 &self,
18754 flags: cudarc::driver::sys::CUgraphInstantiate_flags,
18755 mut step: F,
18756 ) -> Result<
18757 (
18758 cudarc::driver::CudaGraph,
18759 Vec<Box<dyn std::any::Any + Send>>,
18760 ),
18761 Box<dyn std::error::Error>,
18762 >
18763 where
18764 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
18765 {
18766 use cudarc::driver::sys::CUstreamCaptureMode;
18767 self.capture_keep.lock().unwrap().clear();
18775 let was_tracking = self.gpu.ctx.is_event_tracking();
18776 if was_tracking {
18777 unsafe {
18778 self.gpu.ctx.disable_event_tracking();
18779 }
18780 }
18781 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
18782 self.capture_keep_on
18783 .store(true, std::sync::atomic::Ordering::Relaxed);
18784 let w = (|| {
18785 step(self)?;
18786 step(self)
18787 })();
18788 self.capture_keep_on
18789 .store(false, std::sync::atomic::Ordering::Relaxed);
18790 w?;
18791 self.gpu.stream().synchronize()?;
18792 self.gpu
18793 .stream()
18794 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
18795 let r = step(self);
18796 let g = self.gpu.stream().end_capture(flags);
18797 r?;
18798 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
18799 graph.upload()?;
18800 Ok(graph)
18801 };
18802 let result = run();
18803 self.capture_keep_on
18804 .store(false, std::sync::atomic::Ordering::Relaxed);
18805 if was_tracking {
18806 unsafe {
18807 self.gpu.ctx.enable_event_tracking();
18808 }
18809 }
18810 let keeper = std::mem::take(&mut *self.capture_keep.lock().unwrap());
18811 Ok((result?, keeper))
18812 }
18813
18814 pub fn capture_graph<F>(
18815 &self,
18816 mut step: F,
18817 ) -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>>
18818 where
18819 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
18820 {
18821 use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
18822 let was_tracking = self.gpu.ctx.is_event_tracking();
18830 if was_tracking {
18831 unsafe {
18832 self.gpu.ctx.disable_event_tracking();
18833 }
18834 }
18835 let iflag = {
18842 static F: std::sync::OnceLock<CUgraphInstantiate_flags> = std::sync::OnceLock::new();
18843 *F.get_or_init(|| match std::env::var("MEMRA_GRAPH_IFLAG").as_deref() {
18844 Ok("upload") => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_UPLOAD,
18847 Ok("priority") => {
18848 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY
18849 }
18850 _ => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
18851 })
18852 };
18853 let ct = {
18860 static T: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
18861 *T.get_or_init(|| std::env::var("MEMRA_GRAPH_CAPTIME").as_deref() == Ok("1"))
18862 };
18863 let warmups = {
18886 static W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18887 *W.get_or_init(|| {
18888 std::env::var("MEMRA_GRAPH_WARMUPS")
18889 .ok()
18890 .and_then(|v| v.parse().ok())
18891 .filter(|n| *n >= 1)
18892 .unwrap_or(1)
18893 })
18894 };
18895 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
18896 let t_w = std::time::Instant::now();
18897 for _ in 0..warmups {
18899 step(self)?;
18900 }
18901 self.gpu.stream().synchronize()?;
18902 let ms_warm = t_w.elapsed().as_secs_f64() * 1e3;
18903 let t_c = std::time::Instant::now();
18905 self.gpu
18906 .stream()
18907 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
18908 let r = step(self);
18911 let ms_body = t_c.elapsed().as_secs_f64() * 1e3;
18912 let t_i = std::time::Instant::now();
18913 let g = self.gpu.stream().end_capture(iflag);
18914 let ms_inst = t_i.elapsed().as_secs_f64() * 1e3;
18915 r?;
18916 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
18917 let t_u = std::time::Instant::now();
18918 graph.upload()?;
18919 if ct {
18920 println!(
18921 "[graph-captime] warmup2x {ms_warm:.2} ms capture-body {ms_body:.2} ms \
18922 instantiate {ms_inst:.2} ms upload {:.2} ms",
18923 t_u.elapsed().as_secs_f64() * 1e3
18924 );
18925 }
18926 Ok(graph)
18927 };
18928 let result = run();
18929 if was_tracking {
18930 unsafe {
18931 self.gpu.ctx.enable_event_tracking();
18932 }
18933 }
18934 result
18935 }
18936
18937 pub fn gdn_scan_s128_view(
18939 &self,
18940 q: &CudaSlice<f32>,
18941 k: &CudaSlice<f32>,
18942 v: &CudaSlice<f32>,
18943 g: &CudaSlice<f32>,
18944 beta: &CudaSlice<f32>,
18945 state_in: &cudarc::driver::CudaView<f32>,
18946 state_out: &mut cudarc::driver::CudaViewMut<f32>,
18947 o: &mut CudaSlice<f32>,
18948 n_head: usize,
18949 t: usize,
18950 scale: f32,
18951 ) -> Result<(), Box<dyn std::error::Error>> {
18952 let f = self.func("gdn_scan_s128");
18953 const S_V: u32 = 128;
18954 const WARP: u32 = 32;
18955 const COLS: u32 = 4;
18956 let cfg = LaunchConfig {
18957 grid_dim: (n_head as u32, 1, S_V / COLS),
18958 block_dim: (WARP, COLS, 1),
18959 shared_mem_bytes: 0,
18960 };
18961 let (h, ti) = (n_head as i32, t as i32);
18962 let __s_b = self.gpu.stream();
18963 let mut b = __s_b.launch_builder(&f);
18964 b.arg(q)
18965 .arg(k)
18966 .arg(v)
18967 .arg(g)
18968 .arg(beta)
18969 .arg(state_in)
18970 .arg(state_out)
18971 .arg(o)
18972 .arg(&h)
18973 .arg(&ti)
18974 .arg(&scale);
18975 unsafe {
18976 b.launch(cfg)?;
18977 }
18978 Ok(())
18979 }
18980
18981 pub fn ssm_conv1d_view(
18983 &self,
18984 x: &cudarc::driver::CudaView<f32>,
18985 w: &CudaSlice<f32>,
18986 y: &mut CudaSlice<f32>,
18987 conv_dim: usize,
18988 t: usize,
18989 d_conv: usize,
18990 silu: bool,
18991 ) -> Result<(), Box<dyn std::error::Error>> {
18992 let f = self.func("ssm_conv1d_silu_f32");
18993 let cfg = LaunchConfig {
18995 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
18996 block_dim: (256, 1, 1),
18997 shared_mem_bytes: 0,
18998 };
18999 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
19000 let __s_b = self.gpu.stream();
19001 let mut b = __s_b.launch_builder(&f);
19002 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
19003 unsafe {
19004 b.launch(cfg)?;
19005 }
19006 Ok(())
19007 }
19008
19009 pub fn ssm_conv1d_tm(
19016 &self,
19017 qkv_tm: &CudaSlice<f32>,
19018 w: &CudaSlice<f32>,
19019 y: &mut CudaSlice<f32>,
19020 conv_dim: usize,
19021 t: usize,
19022 d_conv: usize,
19023 ) -> Result<(), Box<dyn std::error::Error>> {
19024 let f = self.func("ssm_conv1d_tm_f32");
19025 let cfg = LaunchConfig {
19026 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19027 block_dim: (256, 1, 1),
19028 shared_mem_bytes: 0,
19029 };
19030 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19031 let __s_b = self.gpu.stream();
19032 let mut b = __s_b.launch_builder(&f);
19033 b.arg(qkv_tm).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc);
19034 unsafe {
19035 b.launch(cfg)?;
19036 }
19037 Ok(())
19038 }
19039
19040 pub fn ssm_conv1d_tm_state(
19048 &self,
19049 qkv_tm: &CudaSlice<f32>,
19050 conv_state: &mut CudaSlice<f32>,
19051 w: &CudaSlice<f32>,
19052 y: &mut CudaSlice<f32>,
19053 conv_dim: usize,
19054 t: usize,
19055 d_conv: usize,
19056 ) -> Result<(), Box<dyn std::error::Error>> {
19057 self.ssm_conv1d_tm_state_pad(qkv_tm, conv_state, w, y, conv_dim, t, d_conv, None)
19058 }
19059
19060 #[allow(clippy::too_many_arguments)]
19063 pub fn ssm_conv1d_tm_state_pad(
19064 &self,
19065 qkv_tm: &CudaSlice<f32>,
19066 conv_state: &mut CudaSlice<f32>,
19067 w: &CudaSlice<f32>,
19068 y: &mut CudaSlice<f32>,
19069 conv_dim: usize,
19070 t: usize,
19071 d_conv: usize,
19072 pad_len: Option<&CudaSlice<i32>>,
19073 ) -> Result<(), Box<dyn std::error::Error>> {
19074 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
19075 let ring_old = if t < d_conv - 1 {
19079 Some(self.clone_dtod(conv_state)?)
19080 } else {
19081 None
19082 };
19083 {
19084 let f = self.func("ssm_conv1d_tm_state_f32");
19085 let cfg = LaunchConfig {
19086 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19087 block_dim: (256, 1, 1),
19088 shared_mem_bytes: 0,
19089 };
19090 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19091 let __s_b = self.gpu.stream();
19092 let mut b = __s_b.launch_builder(&f);
19093 b.arg(qkv_tm)
19094 .arg(&*conv_state)
19095 .arg(w)
19096 .arg(y)
19097 .arg(&cd)
19098 .arg(&ti)
19099 .arg(&dc);
19100 unsafe {
19101 b.launch(cfg)?;
19102 }
19103 }
19104 match (ring_old, pad_len) {
19105 (None, Some(len_d)) => {
19106 let f = self.func("ssm_conv_ring_update_dev_f32");
19107 let n = conv_dim * (d_conv - 1);
19108 let cfg = LaunchConfig::for_num_elems(n as u32);
19109 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19110 let __s_b = self.gpu.stream();
19111 let mut b = __s_b.launch_builder(&f);
19112 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
19113 unsafe {
19114 b.launch(cfg)?;
19115 }
19116 }
19117 (None, None) => {
19118 let f = self.func("ssm_conv_ring_update_f32");
19119 let n = conv_dim * (d_conv - 1);
19120 let cfg = LaunchConfig::for_num_elems(n as u32);
19121 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19122 let __s_b = self.gpu.stream();
19123 let mut b = __s_b.launch_builder(&f);
19124 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
19125 unsafe {
19126 b.launch(cfg)?;
19127 }
19128 }
19129 (Some(old), _) => {
19130 self.ssm_conv_ring_rebuild(qkv_tm, &old, conv_state, conv_dim, t, d_conv)?
19131 }
19132 }
19133 Ok(())
19134 }
19135
19136 pub fn ssm_conv1d_tm_state_pad_v(
19138 &self,
19139 qkv_tm: &cudarc::driver::CudaView<f32>,
19140 conv_state: &mut CudaSlice<f32>,
19141 w: &CudaSlice<f32>,
19142 y: &mut CudaSlice<f32>,
19143 conv_dim: usize,
19144 t: usize,
19145 d_conv: usize,
19146 pad_len: Option<&CudaSlice<i32>>,
19147 ) -> Result<(), Box<dyn std::error::Error>> {
19148 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
19149 let ring_old = if t < d_conv - 1 {
19153 Some(self.clone_dtod(conv_state)?)
19154 } else {
19155 None
19156 };
19157 {
19158 let f = self.func("ssm_conv1d_tm_state_f32");
19159 let cfg = LaunchConfig {
19160 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19161 block_dim: (256, 1, 1),
19162 shared_mem_bytes: 0,
19163 };
19164 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19165 let __s_b = self.gpu.stream();
19166 let mut b = __s_b.launch_builder(&f);
19167 b.arg(qkv_tm)
19168 .arg(&*conv_state)
19169 .arg(w)
19170 .arg(y)
19171 .arg(&cd)
19172 .arg(&ti)
19173 .arg(&dc);
19174 unsafe {
19175 b.launch(cfg)?;
19176 }
19177 }
19178 match (ring_old, pad_len) {
19179 (None, Some(len_d)) => {
19180 let f = self.func("ssm_conv_ring_update_dev_f32");
19181 let n = conv_dim * (d_conv - 1);
19182 let cfg = LaunchConfig::for_num_elems(n as u32);
19183 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19184 let __s_b = self.gpu.stream();
19185 let mut b = __s_b.launch_builder(&f);
19186 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
19187 unsafe {
19188 b.launch(cfg)?;
19189 }
19190 }
19191 (None, None) => {
19192 let f = self.func("ssm_conv_ring_update_f32");
19193 let n = conv_dim * (d_conv - 1);
19194 let cfg = LaunchConfig::for_num_elems(n as u32);
19195 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19196 let __s_b = self.gpu.stream();
19197 let mut b = __s_b.launch_builder(&f);
19198 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
19199 unsafe {
19200 b.launch(cfg)?;
19201 }
19202 }
19203 (Some(_), _) => unreachable!(
19204 "ssm_conv1d_tm_state_pad_v: T < d_conv-1 has no view path (PRIME_MIN_T gates it)"
19205 ),
19206 }
19207 Ok(())
19208 }
19209
19210 pub fn ssm_conv_ring_rebuild(
19215 &self,
19216 qkv_tm: &CudaSlice<f32>,
19217 ring_old: &CudaSlice<f32>,
19218 conv_state: &mut CudaSlice<f32>,
19219 conv_dim: usize,
19220 tc: usize,
19221 d_conv: usize,
19222 ) -> Result<(), Box<dyn std::error::Error>> {
19223 let f = self.func("ssm_conv_ring_rebuild_f32");
19224 let n = conv_dim * (d_conv - 1);
19225 let cfg = LaunchConfig::for_num_elems(n as u32);
19226 let (cd, ti, dc) = (conv_dim as i32, tc as i32, d_conv as i32);
19227 let __s_b = self.gpu.stream();
19228 let mut b = __s_b.launch_builder(&f);
19229 b.arg(qkv_tm)
19230 .arg(ring_old)
19231 .arg(conv_state)
19232 .arg(&cd)
19233 .arg(&ti)
19234 .arg(&dc);
19235 unsafe {
19236 b.launch(cfg)?;
19237 }
19238 Ok(())
19239 }
19240
19241 #[allow(clippy::too_many_arguments)]
19246 pub fn gdn_prep_decode(
19247 &self,
19248 conv_out: &CudaSlice<f32>,
19249 beta_raw: &CudaSlice<f32>,
19250 alpha: &CudaSlice<f32>,
19251 dt_bias: &CudaSlice<f32>,
19252 a: &CudaSlice<f32>,
19253 q_l2: &mut CudaSlice<f32>,
19254 k_l2: &mut CudaSlice<f32>,
19255 v_g: &mut CudaSlice<f32>,
19256 beta: &mut CudaSlice<f32>,
19257 g_log: &mut CudaSlice<f32>,
19258 d_state: usize,
19259 num_v: usize,
19260 num_k: usize,
19261 key_dim: usize,
19262 eps: f32,
19263 ) -> Result<(), Box<dyn std::error::Error>> {
19264 let f = self.func("gdn_prep_decode_f32");
19265 let cfg = LaunchConfig {
19266 grid_dim: (num_v as u32, 1, 1),
19267 block_dim: (32, 4, 1),
19268 shared_mem_bytes: 0,
19269 };
19270 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
19271 let __s_b = self.gpu.stream();
19272 let mut b = __s_b.launch_builder(&f);
19273 b.arg(conv_out)
19274 .arg(beta_raw)
19275 .arg(alpha)
19276 .arg(dt_bias)
19277 .arg(a)
19278 .arg(q_l2)
19279 .arg(k_l2)
19280 .arg(v_g)
19281 .arg(beta)
19282 .arg(g_log)
19283 .arg(&ds)
19284 .arg(&nv)
19285 .arg(&nk)
19286 .arg(&kd)
19287 .arg(&eps);
19288 unsafe {
19289 b.launch(cfg)?;
19290 }
19291 Ok(())
19292 }
19293
19294 #[allow(clippy::too_many_arguments)]
19298 pub fn ssm_conv1d_gdn(
19299 &self,
19300 qkv_tm: &CudaSlice<f32>,
19301 w: &CudaSlice<f32>,
19302 q_g: &mut CudaSlice<f32>,
19303 k_g: &mut CudaSlice<f32>,
19304 v_g: &mut CudaSlice<f32>,
19305 conv_dim: usize,
19306 t: usize,
19307 d_conv: usize,
19308 d_state: usize,
19309 num_v: usize,
19310 num_k: usize,
19311 key_dim: usize,
19312 ) -> Result<(), Box<dyn std::error::Error>> {
19313 let f = self.func("ssm_conv1d_gdn_f32");
19314 let cfg = LaunchConfig {
19315 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19316 block_dim: (256, 1, 1),
19317 shared_mem_bytes: 0,
19318 };
19319 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19320 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
19321 let __s_b = self.gpu.stream();
19322 let mut b = __s_b.launch_builder(&f);
19323 b.arg(qkv_tm)
19324 .arg(w)
19325 .arg(q_g)
19326 .arg(k_g)
19327 .arg(v_g)
19328 .arg(&cd)
19329 .arg(&ti)
19330 .arg(&dc)
19331 .arg(&ds)
19332 .arg(&nv)
19333 .arg(&nk)
19334 .arg(&kd);
19335 unsafe {
19336 b.launch(cfg)?;
19337 }
19338 Ok(())
19339 }
19340
19341 pub fn ssm_conv1d(
19342 &self,
19343 x: &CudaSlice<f32>,
19344 w: &CudaSlice<f32>,
19345 y: &mut CudaSlice<f32>,
19346 conv_dim: usize,
19347 t: usize,
19348 d_conv: usize,
19349 silu: bool,
19350 ) -> Result<(), Box<dyn std::error::Error>> {
19351 let f = self.func("ssm_conv1d_silu_f32");
19352 let cfg = LaunchConfig {
19353 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
19354 block_dim: (256, 1, 1),
19355 shared_mem_bytes: 0,
19356 };
19357 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
19358 let __s_b = self.gpu.stream();
19359 let mut b = __s_b.launch_builder(&f);
19360 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
19361 unsafe {
19362 b.launch(cfg)?;
19363 }
19364 Ok(())
19365 }
19366
19367 pub fn gdn_scan_s128(
19370 &self,
19371 q: &CudaSlice<f32>,
19372 k: &CudaSlice<f32>,
19373 v: &CudaSlice<f32>,
19374 g: &CudaSlice<f32>,
19375 beta: &CudaSlice<f32>,
19376 state_in: &CudaSlice<f32>,
19377 state_out: &mut CudaSlice<f32>,
19378 o: &mut CudaSlice<f32>,
19379 n_head: usize,
19380 t: usize,
19381 scale: f32,
19382 ) -> Result<(), Box<dyn std::error::Error>> {
19383 let f = self.func("gdn_scan_s128");
19384 const S_V: u32 = 128;
19385 const WARP: u32 = 32;
19386 const COLS_PER_BLOCK: u32 = 4;
19387 let cfg = LaunchConfig {
19388 grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
19389 block_dim: (WARP, COLS_PER_BLOCK, 1),
19390 shared_mem_bytes: 0,
19391 };
19392 let (h, ti) = (n_head as i32, t as i32);
19393 let __s_b = self.gpu.stream();
19394 let mut b = __s_b.launch_builder(&f);
19395 b.arg(q)
19396 .arg(k)
19397 .arg(v)
19398 .arg(g)
19399 .arg(beta)
19400 .arg(state_in)
19401 .arg(state_out)
19402 .arg(o)
19403 .arg(&h)
19404 .arg(&ti)
19405 .arg(&scale);
19406 unsafe {
19407 b.launch(cfg)?;
19408 }
19409 Ok(())
19410 }
19411
19412 #[allow(clippy::too_many_arguments)]
19417 pub fn ssm_conv1d_fused_decode_b(
19418 &self,
19419 qkv_cols: &CudaSlice<f32>,
19420 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
19421 w: &CudaSlice<f32>,
19422 conv_outs: &mut CudaSlice<f32>,
19423 conv_dim: usize,
19424 d_conv: usize,
19425 b_n: usize,
19426 ) -> Result<(), Box<dyn std::error::Error>> {
19427 let f = self.func("ssm_conv1d_fused_decode_b_f32");
19428 let cfg = LaunchConfig {
19429 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
19430 block_dim: (256, 1, 1),
19431 shared_mem_bytes: 0,
19432 };
19433 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19434 let __s_b = self.gpu.stream();
19435 let mut b = __s_b.launch_builder(&f);
19436 b.arg(qkv_cols)
19437 .arg(conv_state_ptrs)
19438 .arg(w)
19439 .arg(conv_outs)
19440 .arg(&cd)
19441 .arg(&dc);
19442 unsafe {
19443 b.launch(cfg)?;
19444 }
19445 Ok(())
19446 }
19447
19448 #[allow(clippy::too_many_arguments)]
19449 pub fn gdn_prep_decode_b(
19450 &self,
19451 conv_outs: &CudaSlice<f32>,
19452 beta_raws: &CudaSlice<f32>,
19453 alphas: &CudaSlice<f32>,
19454 dt_bias: &CudaSlice<f32>,
19455 a: &CudaSlice<f32>,
19456 q_l2: &mut CudaSlice<f32>,
19457 k_l2: &mut CudaSlice<f32>,
19458 v_g: &mut CudaSlice<f32>,
19459 beta: &mut CudaSlice<f32>,
19460 g_log: &mut CudaSlice<f32>,
19461 d_state: usize,
19462 num_v: usize,
19463 num_k: usize,
19464 key_dim: usize,
19465 eps: f32,
19466 conv_dim: usize,
19467 b_n: usize,
19468 ) -> Result<(), Box<dyn std::error::Error>> {
19469 let f = self.func("gdn_prep_decode_b_f32");
19470 let cfg = LaunchConfig {
19471 grid_dim: (num_v as u32, 1, b_n as u32),
19472 block_dim: (32, 4, 1),
19473 shared_mem_bytes: 0,
19474 };
19475 let (ds, nv, nk, kd, cd) = (
19476 d_state as i32,
19477 num_v as i32,
19478 num_k as i32,
19479 key_dim as i32,
19480 conv_dim as i32,
19481 );
19482 let __s_b = self.gpu.stream();
19483 let mut b = __s_b.launch_builder(&f);
19484 b.arg(conv_outs)
19485 .arg(beta_raws)
19486 .arg(alphas)
19487 .arg(dt_bias)
19488 .arg(a)
19489 .arg(q_l2)
19490 .arg(k_l2)
19491 .arg(v_g)
19492 .arg(beta)
19493 .arg(g_log)
19494 .arg(&ds)
19495 .arg(&nv)
19496 .arg(&nk)
19497 .arg(&kd)
19498 .arg(&eps)
19499 .arg(&cd);
19500 unsafe {
19501 b.launch(cfg)?;
19502 }
19503 Ok(())
19504 }
19505
19506 #[allow(clippy::too_many_arguments)]
19507 pub fn gdn_scan_s128_batched(
19508 &self,
19509 q: &CudaSlice<f32>,
19510 k: &CudaSlice<f32>,
19511 v: &CudaSlice<f32>,
19512 g: &CudaSlice<f32>,
19513 beta: &CudaSlice<f32>,
19514 state_in_ptrs: &cudarc::driver::CudaView<u64>,
19515 state_out_ptrs: &cudarc::driver::CudaView<u64>,
19516 o: &mut CudaSlice<f32>,
19517 n_head: usize,
19518 b_n: usize,
19519 scale: f32,
19520 ) -> Result<(), Box<dyn std::error::Error>> {
19521 let f = self.func("gdn_scan_s128_b");
19522 const S_V: u32 = 128;
19523 const WARP: u32 = 32;
19524 const COLS_PER_BLOCK: u32 = 4;
19525 let cfg = LaunchConfig {
19526 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
19527 block_dim: (WARP, COLS_PER_BLOCK, 1),
19528 shared_mem_bytes: 0,
19529 };
19530 let h = n_head as i32;
19531 let __s_b = self.gpu.stream();
19532 let mut b = __s_b.launch_builder(&f);
19533 b.arg(q)
19534 .arg(k)
19535 .arg(v)
19536 .arg(g)
19537 .arg(beta)
19538 .arg(state_in_ptrs)
19539 .arg(state_out_ptrs)
19540 .arg(o)
19541 .arg(&h)
19542 .arg(&scale);
19543 unsafe {
19544 b.launch(cfg)?;
19545 }
19546 Ok(())
19547 }
19548
19549 #[allow(clippy::too_many_arguments)]
19555 pub fn ssm_conv1d_fused_decode_b_view(
19556 &self,
19557 qkv_cols: &cudarc::driver::CudaView<f32>,
19558 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
19559 w: &CudaSlice<f32>,
19560 conv_outs: &mut CudaSlice<f32>,
19561 conv_dim: usize,
19562 d_conv: usize,
19563 b_n: usize,
19564 ) -> Result<(), Box<dyn std::error::Error>> {
19565 let f = self.func("ssm_conv1d_fused_decode_b_f32");
19566 let cfg = LaunchConfig {
19567 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
19568 block_dim: (256, 1, 1),
19569 shared_mem_bytes: 0,
19570 };
19571 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19572 let __s_b = self.gpu.stream();
19573 let mut b = __s_b.launch_builder(&f);
19574 b.arg(qkv_cols)
19575 .arg(conv_state_ptrs)
19576 .arg(w)
19577 .arg(conv_outs)
19578 .arg(&cd)
19579 .arg(&dc);
19580 unsafe {
19581 b.launch(cfg)?;
19582 }
19583 Ok(())
19584 }
19585
19586 #[allow(clippy::too_many_arguments)]
19587 pub fn gdn_prep_decode_b_view(
19588 &self,
19589 conv_outs: &CudaSlice<f32>,
19590 beta_raws: &cudarc::driver::CudaView<f32>,
19591 alphas: &cudarc::driver::CudaView<f32>,
19592 dt_bias: &CudaSlice<f32>,
19593 a: &CudaSlice<f32>,
19594 q_l2: &mut CudaSlice<f32>,
19595 k_l2: &mut CudaSlice<f32>,
19596 v_g: &mut CudaSlice<f32>,
19597 beta: &mut CudaSlice<f32>,
19598 g_log: &mut CudaSlice<f32>,
19599 d_state: usize,
19600 num_v: usize,
19601 num_k: usize,
19602 key_dim: usize,
19603 eps: f32,
19604 conv_dim: usize,
19605 b_n: usize,
19606 ) -> Result<(), Box<dyn std::error::Error>> {
19607 let f = self.func("gdn_prep_decode_b_f32");
19608 let cfg = LaunchConfig {
19609 grid_dim: (num_v as u32, 1, b_n as u32),
19610 block_dim: (32, 4, 1),
19611 shared_mem_bytes: 0,
19612 };
19613 let (ds, nv, nk, kd, cd) = (
19614 d_state as i32,
19615 num_v as i32,
19616 num_k as i32,
19617 key_dim as i32,
19618 conv_dim as i32,
19619 );
19620 let __s_b = self.gpu.stream();
19621 let mut b = __s_b.launch_builder(&f);
19622 b.arg(conv_outs)
19623 .arg(beta_raws)
19624 .arg(alphas)
19625 .arg(dt_bias)
19626 .arg(a)
19627 .arg(q_l2)
19628 .arg(k_l2)
19629 .arg(v_g)
19630 .arg(beta)
19631 .arg(g_log)
19632 .arg(&ds)
19633 .arg(&nv)
19634 .arg(&nk)
19635 .arg(&kd)
19636 .arg(&eps)
19637 .arg(&cd);
19638 unsafe {
19639 b.launch(cfg)?;
19640 }
19641 Ok(())
19642 }
19643
19644 #[allow(clippy::too_many_arguments)]
19645 pub fn gdn_scan_s128_batched_view(
19646 &self,
19647 q: &CudaSlice<f32>,
19648 k: &CudaSlice<f32>,
19649 v: &CudaSlice<f32>,
19650 g: &CudaSlice<f32>,
19651 beta: &CudaSlice<f32>,
19652 state_in_ptrs: &cudarc::driver::CudaView<u64>,
19653 state_out_ptrs: &cudarc::driver::CudaView<u64>,
19654 o: &mut cudarc::driver::CudaViewMut<f32>,
19655 n_head: usize,
19656 b_n: usize,
19657 scale: f32,
19658 ) -> Result<(), Box<dyn std::error::Error>> {
19659 let f = self.func("gdn_scan_s128_b");
19660 const S_V: u32 = 128;
19661 const WARP: u32 = 32;
19662 const COLS_PER_BLOCK: u32 = 4;
19663 let cfg = LaunchConfig {
19664 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
19665 block_dim: (WARP, COLS_PER_BLOCK, 1),
19666 shared_mem_bytes: 0,
19667 };
19668 let h = n_head as i32;
19669 let __s_b = self.gpu.stream();
19670 let mut b = __s_b.launch_builder(&f);
19671 b.arg(q)
19672 .arg(k)
19673 .arg(v)
19674 .arg(g)
19675 .arg(beta)
19676 .arg(state_in_ptrs)
19677 .arg(state_out_ptrs)
19678 .arg(o)
19679 .arg(&h)
19680 .arg(&scale);
19681 unsafe {
19682 b.launch(cfg)?;
19683 }
19684 Ok(())
19685 }
19686
19687 pub fn gdn_chunked_enabled() -> bool {
19696 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19697 *E.get_or_init(|| {
19698 std::env::var("MEMRA_GDN_CHUNKED")
19699 .map(|v| v != "0")
19700 .unwrap_or(true)
19701 })
19702 }
19703
19704 pub fn gdn_chunk_size() -> usize {
19709 static C: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
19710 *C.get_or_init(|| {
19711 let c: usize = std::env::var("MEMRA_GDN_CHUNK")
19712 .ok()
19713 .and_then(|v| v.parse().ok())
19714 .unwrap_or(32);
19715 c.clamp(32, 128) / 32 * 32
19716 })
19717 }
19718
19719 #[allow(clippy::too_many_arguments)]
19724 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
19727 #[allow(clippy::too_many_arguments)]
19728 pub fn gdn_chunk_k123(
19729 &self,
19730 q: &CudaSlice<f32>,
19731 k: &CudaSlice<f32>,
19732 v: &CudaSlice<f32>,
19733 g: &CudaSlice<f32>,
19734 beta: &CudaSlice<f32>,
19735 wb16: Option<&mut CudaSlice<u8>>,
19736 n_head: usize,
19737 t: usize,
19738 c: usize,
19739 hk: usize,
19740 k2w: Option<(&CudaSlice<u8>, &CudaSlice<u8>, &mut CudaSlice<u8>)>,
19741 ) -> Result<
19742 (
19743 CudaSlice<f32>,
19744 CudaSlice<f32>,
19745 CudaSlice<f32>,
19746 CudaSlice<f32>,
19747 ),
19748 Box<dyn std::error::Error>,
19749 > {
19750 const D: usize = 128;
19751 let h = n_head;
19752 let nc = (t + c - 1) / c;
19753 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
19754 let mut gcum = self.uninit(t * h)?;
19755 let mut a = self.uninit(nc * h * c * c)?;
19756 let mut p = self.uninit(nc * h * c * c)?;
19757 let mut u = self.uninit(nc * h * c * D)?;
19758 let mut w = self.uninit(nc * h * c * D)?;
19759 {
19760 let f = self.func("gdn_chunk_cumgate_f32");
19762 let cfg = LaunchConfig {
19763 grid_dim: (nc as u32, h as u32, 1),
19764 block_dim: (32, 1, 1),
19765 shared_mem_bytes: 0,
19766 };
19767 let __s_b = self.gpu.stream();
19768 let mut b = __s_b.launch_builder(&f);
19769 b.arg(g).arg(&mut gcum).arg(&hi).arg(&ti).arg(&ci);
19770 unsafe {
19771 b.launch(cfg)?;
19772 }
19773 }
19774 if let Some((qb, kb, pb)) = k2w {
19775 assert!(c == 32, "gdn_k2_wgmma is a C==32 tile");
19778 let f = self.func("gdn_k2_wgmma");
19779 let cfg = LaunchConfig {
19780 grid_dim: (nc as u32, h as u32, 1),
19781 block_dim: (128, 1, 1),
19782 shared_mem_bytes: 0,
19783 };
19784 let hki = hk as i32;
19785 let __s_b = self.gpu.stream();
19786 let mut b = __s_b.launch_builder(&f);
19787 b.arg(qb)
19788 .arg(kb)
19789 .arg(&gcum)
19790 .arg(beta)
19791 .arg(&mut a)
19792 .arg(&mut *pb)
19793 .arg(&hi)
19794 .arg(&ti)
19795 .arg(&ci)
19796 .arg(&hki);
19797 unsafe {
19798 b.launch(cfg)?;
19799 }
19800 } else if c <= 64 && !portable_mma_gated() {
19801 let f = self.func("gdn_chunk_attn_f32");
19803 let jt = ((c + 31) / 32) as u32;
19804 let cfg = LaunchConfig {
19805 grid_dim: (nc as u32, h as u32, jt),
19806 block_dim: (256, 1, 1),
19807 shared_mem_bytes: 0,
19808 };
19809 let hki = hk as i32;
19810 let __s_b = self.gpu.stream();
19811 let mut b = __s_b.launch_builder(&f);
19812 b.arg(q)
19813 .arg(k)
19814 .arg(&gcum)
19815 .arg(beta)
19816 .arg(&mut a)
19817 .arg(&mut p)
19818 .arg(&hi)
19819 .arg(&ti)
19820 .arg(&ci)
19821 .arg(&hki);
19822 unsafe {
19823 b.launch(cfg)?;
19824 }
19825 } else {
19826 assert!(
19828 hk == h,
19829 "generic K2 is broadcast-only (de-broadcast rides C==32)"
19830 );
19831 let f = self.func("gdn_chunk_attn_g_f32");
19832 let cfg = LaunchConfig {
19833 grid_dim: (nc as u32, h as u32, 1),
19834 block_dim: (32, 8, 1),
19835 shared_mem_bytes: 0,
19836 };
19837 let __s_b = self.gpu.stream();
19838 let mut b = __s_b.launch_builder(&f);
19839 b.arg(q)
19840 .arg(k)
19841 .arg(&gcum)
19842 .arg(beta)
19843 .arg(&mut a)
19844 .arg(&mut p)
19845 .arg(&hi)
19846 .arg(&ti)
19847 .arg(&ci);
19848 unsafe {
19849 b.launch(cfg)?;
19850 }
19851 }
19852 {
19853 let cfg = LaunchConfig {
19855 grid_dim: (nc as u32, h as u32, 1),
19856 block_dim: (256, 1, 1),
19857 shared_mem_bytes: 0,
19858 };
19859 match c {
19860 32 | 64 => {
19861 let f = self.func(if c == 32 {
19862 "gdn_chunk_solve32_f32"
19863 } else {
19864 "gdn_chunk_solve64_f32"
19865 });
19866 let wb: u64 = match wb16 {
19868 Some(d) => self.addr_u8(d),
19869 None => 0,
19870 };
19871 let hki = hk as i32;
19872 let __s_b = self.gpu.stream();
19873 let mut b = __s_b.launch_builder(&f);
19874 b.arg(v)
19875 .arg(k)
19876 .arg(&a)
19877 .arg(&gcum)
19878 .arg(&mut u)
19879 .arg(&mut w)
19880 .arg(&wb)
19881 .arg(&hi)
19882 .arg(&ti)
19883 .arg(&hki);
19884 unsafe {
19885 b.launch(cfg)?;
19886 }
19887 }
19888 _ => {
19889 assert!(hk == h, "generic K3 is broadcast-only");
19890 let f = self.func("gdn_chunk_solve_f32");
19891 let __s_b = self.gpu.stream();
19892 let mut b = __s_b.launch_builder(&f);
19893 b.arg(v)
19894 .arg(k)
19895 .arg(&a)
19896 .arg(&gcum)
19897 .arg(&mut u)
19898 .arg(&mut w)
19899 .arg(&hi)
19900 .arg(&ti)
19901 .arg(&ci);
19902 unsafe {
19903 b.launch(cfg)?;
19904 }
19905 }
19906 }
19907 }
19908 Ok((gcum, p, u, w))
19909 }
19910
19911 pub fn gdn_db_on() -> bool {
19915 std::env::var("MEMRA_GDN_DB").as_deref() != Ok("0")
19916 }
19917
19918 pub fn gdn_mma_enabled(&self, c: usize) -> bool {
19921 !portable_mma_gated()
19922 && c == 32
19923 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
19924 Ok("1") => true,
19925 Ok("0") => false,
19926 _ => cfg!(memra_hopper_mma),
19927 }
19928 }
19929
19930 pub fn gdn_wgmma_on(&self, c: usize) -> bool {
19933 self.gdn_mma_enabled(c)
19934 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
19935 Ok("0") => false,
19936 Ok("1") => true,
19937 _ => cfg!(memra_hopper_mma),
19938 }
19939 }
19940
19941 #[allow(clippy::too_many_arguments)]
19946 pub fn ssm_conv1d_gdn_state_pad(
19947 &self,
19948 qkv_tm: &cudarc::driver::CudaView<f32>,
19949 conv_state: &mut CudaSlice<f32>,
19950 w: &CudaSlice<f32>,
19951 q_g: &mut CudaSlice<f32>,
19952 k_g: &mut CudaSlice<f32>,
19953 v_g: &mut CudaSlice<f32>,
19954 conv_dim: usize,
19955 t: usize,
19956 d_conv: usize,
19957 d_state: usize,
19958 num_v: usize,
19959 num_k: usize,
19960 key_dim: usize,
19961 hk: usize,
19962 pad_len: Option<&CudaSlice<i32>>,
19963 ) -> Result<(), Box<dyn std::error::Error>> {
19964 assert!(
19965 t >= d_conv - 1,
19966 "fused state conv requires T >= pad (PRIME_MIN_T gates)"
19967 );
19968 {
19969 let f = self.func("ssm_conv1d_gdn_state_f32");
19970 let cfg = LaunchConfig {
19971 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19972 block_dim: (256, 1, 1),
19973 shared_mem_bytes: 0,
19974 };
19975 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19976 let (ds, nv, nk, kd, hki) = (
19977 d_state as i32,
19978 num_v as i32,
19979 num_k as i32,
19980 key_dim as i32,
19981 hk as i32,
19982 );
19983 let __s_b = self.gpu.stream();
19984 let mut b = __s_b.launch_builder(&f);
19985 b.arg(qkv_tm)
19986 .arg(&*conv_state)
19987 .arg(w)
19988 .arg(q_g)
19989 .arg(k_g)
19990 .arg(v_g)
19991 .arg(&cd)
19992 .arg(&ti)
19993 .arg(&dc)
19994 .arg(&ds)
19995 .arg(&nv)
19996 .arg(&nk)
19997 .arg(&kd)
19998 .arg(&hki);
19999 unsafe {
20000 b.launch(cfg)?;
20001 }
20002 }
20003 match pad_len {
20004 Some(len_d) => {
20005 let f = self.func("ssm_conv_ring_update_dev_f32");
20006 let n = conv_dim * (d_conv - 1);
20007 let cfg = LaunchConfig::for_num_elems(n as u32);
20008 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20009 let __s_b = self.gpu.stream();
20010 let mut b = __s_b.launch_builder(&f);
20011 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
20012 unsafe {
20013 b.launch(cfg)?;
20014 }
20015 }
20016 None => {
20017 let f = self.func("ssm_conv_ring_update_f32");
20018 let n = conv_dim * (d_conv - 1);
20019 let cfg = LaunchConfig::for_num_elems(n as u32);
20020 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20021 let __s_b = self.gpu.stream();
20022 let mut b = __s_b.launch_builder(&f);
20023 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
20024 unsafe {
20025 b.launch(cfg)?;
20026 }
20027 }
20028 }
20029 Ok(())
20030 }
20031
20032 pub fn gdn_chunk_alloc(
20036 &self,
20037 n_head: usize,
20038 t: usize,
20039 c: usize,
20040 hk: usize,
20041 ) -> Result<GdnChunkBufs, Box<dyn std::error::Error>> {
20042 const D: usize = 128;
20043 assert!(
20044 c == 32,
20045 "gdn_chunk_alloc: varlen chain is the C==32 mma pair"
20046 );
20047 let h = n_head;
20048 let nc = (t + c - 1) / c;
20049 Ok(GdnChunkBufs {
20050 gcum: self.uninit(t * h)?,
20051 a: self.uninit(nc * h * c * c)?,
20052 p: self.uninit(nc * h * c * c)?,
20053 u: self.uninit(nc * h * c * D)?,
20054 w: self.uninit(nc * h * c * D)?,
20055 kb16: self.alloc_u8_uninit(t * hk * D * 2)?,
20056 wb16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
20057 y16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
20058 ssnap16: self.alloc_u8_uninit(nc * h * D * D * 2)?,
20059 qb16: self.alloc_u8_uninit(t * hk * D * 2)?,
20060 pb16: self.alloc_u8_uninit(nc * h * c * c * 2)?,
20061 o: self.uninit(D * h * t)?,
20062 t,
20063 nc,
20064 })
20065 }
20066
20067 pub fn f32_to_bf16_v(
20069 &self,
20070 x: &cudarc::driver::CudaView<f32>,
20071 dst: &mut CudaSlice<u8>,
20072 n: usize,
20073 ) -> Result<(), Box<dyn std::error::Error>> {
20074 let f = self.func("f32_to_bf16_bulk");
20075 let ni = n as i64;
20076 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
20077 let __s_b = self.gpu.stream();
20078 let mut b = __s_b.launch_builder(&f);
20079 b.arg(x).arg(dst).arg(&ni);
20080 unsafe {
20081 b.launch(cfg)?;
20082 }
20083 Ok(())
20084 }
20085
20086 pub fn f32_to_bf16_into(
20088 &self,
20089 x: &CudaSlice<f32>,
20090 dst: &mut CudaSlice<u8>,
20091 n: usize,
20092 ) -> Result<(), Box<dyn std::error::Error>> {
20093 let f = self.func("f32_to_bf16_bulk");
20094 let ni = n as i64;
20095 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
20096 let __s_b = self.gpu.stream();
20097 let mut b = __s_b.launch_builder(&f);
20098 b.arg(x).arg(dst).arg(&ni);
20099 unsafe {
20100 b.launch(cfg)?;
20101 }
20102 Ok(())
20103 }
20104
20105 pub fn gdn_chunk_k123_vl8(
20108 &self,
20109 seqs: &[GdnSeqVl],
20110 n_head: usize,
20111 hk: usize,
20112 wq: Option<&GdnWVl8>,
20113 ) -> Result<(), Box<dyn std::error::Error>> {
20114 let b = seqs.len();
20115 assert!(b >= 1 && b <= 8, "gdn_chunk_k123_vl8: 1..=8 sequences");
20116 let mut packed = [GdnSeqVl::default(); 8];
20117 packed[..b].copy_from_slice(seqs);
20118 let v = GdnVl8(packed);
20119 let (hi, ci) = (n_head as i32, 32i32);
20120 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
20121 {
20122 let f = self.func("gdn_chunk_cumgate_vl");
20123 let cfg = LaunchConfig {
20124 grid_dim: (max_nc, n_head as u32, b as u32),
20125 block_dim: (32, 1, 1),
20126 shared_mem_bytes: 0,
20127 };
20128 let __s_lb = self.gpu.stream();
20129 let mut lb = __s_lb.launch_builder(&f);
20130 lb.arg(&v).arg(&hi).arg(&ci);
20131 unsafe {
20132 lb.launch(cfg)?;
20133 }
20134 }
20135 let hki = hk as i32;
20136 if let Some(w) = wq {
20137 let f = self.func("gdn_k2_wgmma_vl");
20139 let cfg = LaunchConfig {
20140 grid_dim: (max_nc, n_head as u32, b as u32),
20141 block_dim: (128, 1, 1),
20142 shared_mem_bytes: 0,
20143 };
20144 let __s_lb = self.gpu.stream();
20145 let mut lb = __s_lb.launch_builder(&f);
20146 lb.arg(&v).arg(w).arg(&hi).arg(&ci).arg(&hki);
20147 unsafe {
20148 lb.launch(cfg)?;
20149 }
20150 } else {
20151 let f = self.func("gdn_chunk_attn_vl");
20152 let cfg = LaunchConfig {
20153 grid_dim: (max_nc, n_head as u32, b as u32),
20154 block_dim: (256, 1, 1),
20155 shared_mem_bytes: 0,
20156 };
20157 let __s_lb = self.gpu.stream();
20158 let mut lb = __s_lb.launch_builder(&f);
20159 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
20160 unsafe {
20161 lb.launch(cfg)?;
20162 }
20163 }
20164 {
20165 let f = self.func("gdn_chunk_solve32_vl");
20166 let cfg = LaunchConfig {
20167 grid_dim: (max_nc, n_head as u32, b as u32),
20168 block_dim: (256, 1, 1),
20169 shared_mem_bytes: 0,
20170 };
20171 let __s_lb = self.gpu.stream();
20172 let mut lb = __s_lb.launch_builder(&f);
20173 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
20174 unsafe {
20175 lb.launch(cfg)?;
20176 }
20177 }
20178 Ok(())
20179 }
20180
20181 #[allow(clippy::too_many_arguments)]
20185 pub fn gdn_prep_vl8(
20186 &self,
20187 seqs: &[GdnPrepVl],
20188 conv_w: &CudaSlice<f32>,
20189 dt_bias: &CudaSlice<f32>,
20190 a: &CudaSlice<f32>,
20191 conv_dim: usize,
20192 d_conv: usize,
20193 d_state: usize,
20194 num_v: usize,
20195 num_k: usize,
20196 key_dim: usize,
20197 hk: usize,
20198 eps: f32,
20199 ) -> Result<(), Box<dyn std::error::Error>> {
20200 let b = seqs.len();
20201 assert!(b >= 1 && b <= 8);
20202 let mut packed = [GdnPrepVl::default(); 8];
20203 packed[..b].copy_from_slice(seqs);
20204 let v = GdnPrepVl8(packed);
20205 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
20206 let (cdi, dci) = (conv_dim as i32, d_conv as i32);
20207 let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
20208 assert!(
20209 conv_fuse || hk == num_v,
20210 "de-broadcast requires the fused conv"
20211 );
20212 if conv_fuse {
20213 let f = self.func("ssm_conv1d_gdn_state_vl");
20214 let cfg = LaunchConfig {
20215 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
20216 block_dim: (256, 1, 1),
20217 shared_mem_bytes: 0,
20218 };
20219 let (dsi, nvi, nki, kdi, hki) = (
20220 d_state as i32,
20221 num_v as i32,
20222 num_k as i32,
20223 key_dim as i32,
20224 hk as i32,
20225 );
20226 let __s_lb = self.gpu.stream();
20227 let mut lb = __s_lb.launch_builder(&f);
20228 lb.arg(&v)
20229 .arg(conv_w)
20230 .arg(&cdi)
20231 .arg(&dci)
20232 .arg(&dsi)
20233 .arg(&nvi)
20234 .arg(&nki)
20235 .arg(&kdi)
20236 .arg(&hki);
20237 unsafe {
20238 lb.launch(cfg)?;
20239 }
20240 } else {
20241 let f = self.func("ssm_conv1d_tm_state_vl");
20242 let cfg = LaunchConfig {
20243 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
20244 block_dim: (256, 1, 1),
20245 shared_mem_bytes: 0,
20246 };
20247 let __s_lb = self.gpu.stream();
20248 let mut lb = __s_lb.launch_builder(&f);
20249 lb.arg(&v).arg(conv_w).arg(&cdi).arg(&dci);
20250 unsafe {
20251 lb.launch(cfg)?;
20252 }
20253 }
20254 {
20255 let f = self.func("ssm_conv_ring_update_vl");
20256 let n = (conv_dim * (d_conv - 1)) as u32;
20257 let cfg = LaunchConfig {
20258 grid_dim: (n.div_ceil(256), 1, b as u32),
20259 block_dim: (256, 1, 1),
20260 shared_mem_bytes: 0,
20261 };
20262 let __s_lb = self.gpu.stream();
20263 let mut lb = __s_lb.launch_builder(&f);
20264 lb.arg(&v).arg(&cdi).arg(&dci);
20265 unsafe {
20266 lb.launch(cfg)?;
20267 }
20268 }
20269 if !conv_fuse {
20270 let f = self.func("qkv_to_gdn_repack_vl");
20271 let n = max_t * (num_v * d_state) as u32;
20272 let cfg = LaunchConfig {
20273 grid_dim: (n.div_ceil(256), 1, b as u32),
20274 block_dim: (256, 1, 1),
20275 shared_mem_bytes: 0,
20276 };
20277 let (dsi, nvi, nki, kdi) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
20278 let __s_lb = self.gpu.stream();
20279 let mut lb = __s_lb.launch_builder(&f);
20280 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&nki).arg(&kdi);
20281 unsafe {
20282 lb.launch(cfg)?;
20283 }
20284 }
20285 if Self::l2_v2_on(d_state) {
20286 let f = self.func("gdn_l2_v2_vl");
20287 let cfg = LaunchConfig {
20288 grid_dim: ((max_t * hk as u32).div_ceil(8), 2, b as u32),
20289 block_dim: (256, 1, 1),
20290 shared_mem_bytes: 0,
20291 };
20292 let (dsi, nvi) = (d_state as i32, hk as i32);
20293 let __s_lb = self.gpu.stream();
20294 let mut lb = __s_lb.launch_builder(&f);
20295 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
20296 unsafe {
20297 lb.launch(cfg)?;
20298 }
20299 } else {
20300 let f = self.func("gdn_l2_vl");
20301 let cfg = LaunchConfig {
20302 grid_dim: (max_t * hk as u32, 2, b as u32),
20303 block_dim: (256, 1, 1),
20304 shared_mem_bytes: 0,
20305 };
20306 let (dsi, nvi) = (d_state as i32, hk as i32);
20307 let __s_lb = self.gpu.stream();
20308 let mut lb = __s_lb.launch_builder(&f);
20309 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
20310 unsafe {
20311 lb.launch(cfg)?;
20312 }
20313 }
20314 {
20315 let f = self.func("gdn_gate_prep_vl");
20316 let n = max_t * num_v as u32;
20317 let cfg = LaunchConfig {
20318 grid_dim: (n.div_ceil(256), 1, b as u32),
20319 block_dim: (256, 1, 1),
20320 shared_mem_bytes: 0,
20321 };
20322 let nvi = num_v as i32;
20323 let __s_lb = self.gpu.stream();
20324 let mut lb = __s_lb.launch_builder(&f);
20325 lb.arg(&v).arg(dt_bias).arg(a).arg(&nvi);
20326 unsafe {
20327 lb.launch(cfg)?;
20328 }
20329 }
20330 Ok(())
20331 }
20332
20333 pub fn gdn_mirror_vl8(
20335 &self,
20336 seqs: &[GdnSeqVl],
20337 n_head: usize,
20338 which: i32,
20339 hk: usize,
20340 ) -> Result<(), Box<dyn std::error::Error>> {
20341 let b = seqs.len();
20342 assert!(b >= 1 && b <= 8);
20343 let mut packed = [GdnSeqVl::default(); 8];
20344 packed[..b].copy_from_slice(seqs);
20345 let v = GdnVl8(packed);
20346 let ept = (if which == 0 { hk } else { n_head } * 128) as i32;
20347 let max_n = seqs
20348 .iter()
20349 .map(|s| {
20350 if which == 0 {
20351 s.t as i64 * ept as i64
20352 } else {
20353 s.nc as i64 * ept as i64 * 32
20354 }
20355 })
20356 .max()
20357 .unwrap();
20358 let f = self.func("gdn_mirror_vl");
20359 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
20360 let cfg = LaunchConfig {
20361 grid_dim: (blocks, 1, b as u32),
20362 block_dim: (256, 1, 1),
20363 shared_mem_bytes: 0,
20364 };
20365 let __s_lb = self.gpu.stream();
20366 let mut lb = __s_lb.launch_builder(&f);
20367 lb.arg(&v).arg(&ept).arg(&which);
20368 unsafe {
20369 lb.launch(cfg)?;
20370 }
20371 Ok(())
20372 }
20373
20374 pub fn gdn_tail_vl8(
20376 &self,
20377 seqs: &[GdnPrepVl],
20378 norm_w: &CudaSlice<f32>,
20379 d_state: usize,
20380 num_v: usize,
20381 eps: f32,
20382 ) -> Result<(), Box<dyn std::error::Error>> {
20383 let b = seqs.len();
20384 assert!(b >= 1 && b <= 8);
20385 let mut packed = [GdnPrepVl::default(); 8];
20386 packed[..b].copy_from_slice(seqs);
20387 let v = GdnPrepVl8(packed);
20388 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
20389 let f = self.func("gated_rmsnorm_f16out_vl");
20390 let cfg = LaunchConfig {
20392 grid_dim: (max_t * num_v as u32, 1, b as u32),
20393 block_dim: (128, 1, 1),
20394 shared_mem_bytes: 0,
20395 };
20396 let (dsi, nvi) = (d_state as i32, num_v as i32);
20397 let __s_lb = self.gpu.stream();
20398 let mut lb = __s_lb.launch_builder(&f);
20399 lb.arg(&v).arg(norm_w).arg(&dsi).arg(&nvi).arg(&eps);
20400 unsafe {
20401 lb.launch(cfg)?;
20402 }
20403 Ok(())
20404 }
20405
20406 pub fn addr_f32(&self, x: &CudaSlice<f32>) -> u64 {
20409 use cudarc::driver::DevicePtr;
20410 let s = self.gpu.stream();
20411 let (p, _g) = x.device_ptr(&s);
20412 p as u64
20413 }
20414 pub fn addr_f32_mut(&self, x: &mut CudaSlice<f32>) -> u64 {
20415 use cudarc::driver::DevicePtrMut;
20416 let s = self.gpu.stream();
20417 let (p, _g) = x.device_ptr_mut(&s);
20418 p as u64
20419 }
20420 pub fn addr_f32v(&self, x: &cudarc::driver::CudaView<f32>) -> u64 {
20421 use cudarc::driver::DevicePtr;
20422 let s = self.gpu.stream();
20423 let (p, _g) = x.device_ptr(&s);
20424 p as u64
20425 }
20426 pub fn addr_u8(&self, x: &CudaSlice<u8>) -> u64 {
20427 use cudarc::driver::DevicePtr;
20428 let s = self.gpu.stream();
20429 let (p, _g) = x.device_ptr(&s);
20430 p as u64
20431 }
20432
20433 pub fn gdn_chunk_vl8(
20437 &self,
20438 seqs: &[GdnSeqVl],
20439 n_head: usize,
20440 scale: f32,
20441 hk: usize,
20442 wq: Option<&GdnWVl8>,
20443 ) -> Result<(), Box<dyn std::error::Error>> {
20444 const NSPLIT: u32 = 4;
20445 let b = seqs.len();
20446 assert!(b >= 1 && b <= 8, "gdn_chunk_vl8: 1..=8 sequences");
20447 let mut packed = [GdnSeqVl::default(); 8];
20448 packed[..b].copy_from_slice(seqs);
20449 let v = GdnVl8(packed);
20450 let (hi, ci) = (n_head as i32, 32i32);
20451 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
20452 let hki = hk as i32;
20453 if let Some(w) = wq {
20454 let f = self.func("gdn_k45_wgmma_vl");
20456 let cfg = LaunchConfig {
20457 grid_dim: (n_head as u32, NSPLIT, b as u32),
20458 block_dim: (256, 1, 1),
20459 shared_mem_bytes: 0,
20460 };
20461 let __s_lb = self.gpu.stream();
20462 let mut lb = __s_lb.launch_builder(&f);
20463 lb.arg(&v).arg(w).arg(&scale).arg(&hi).arg(&ci).arg(&hki);
20464 unsafe {
20465 lb.launch(cfg)?;
20466 }
20467 let _ = max_nc;
20468 return Ok(());
20469 }
20470 {
20471 let f = self.func("gdn_chunk_state_mma_vl");
20472 let cfg = LaunchConfig {
20473 grid_dim: (n_head as u32, NSPLIT, b as u32),
20474 block_dim: (256, 1, 1),
20475 shared_mem_bytes: 0,
20476 };
20477 let __s_lb = self.gpu.stream();
20478 let mut lb = __s_lb.launch_builder(&f);
20479 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
20480 unsafe {
20481 lb.launch(cfg)?;
20482 }
20483 }
20484 {
20485 let f = self.func("gdn_chunk_output_mma_vl");
20486 let cfg = LaunchConfig {
20487 grid_dim: (max_nc, n_head as u32, b as u32),
20488 block_dim: (256, 1, 1),
20489 shared_mem_bytes: 0,
20490 };
20491 let __s_lb = self.gpu.stream();
20492 let mut lb = __s_lb.launch_builder(&f);
20493 lb.arg(&v).arg(&hi).arg(&ci).arg(&scale).arg(&hki);
20494 unsafe {
20495 lb.launch(cfg)?;
20496 }
20497 }
20498 Ok(())
20499 }
20500 pub fn gdn_scan_chunked(
20501 &self,
20502 q: &CudaSlice<f32>,
20503 k: &CudaSlice<f32>,
20504 v: &CudaSlice<f32>,
20505 g: &CudaSlice<f32>,
20506 beta: &CudaSlice<f32>,
20507 kb16_pre: Option<&CudaSlice<u8>>,
20508 qb16_pre: Option<&CudaSlice<u8>>,
20509 state_in: &CudaSlice<f32>,
20510 state_out: &mut CudaSlice<f32>,
20511 o: &mut CudaSlice<f32>,
20512 n_head: usize,
20513 t: usize,
20514 scale: f32,
20515 c: usize,
20516 hk: usize,
20517 ) -> Result<(), Box<dyn std::error::Error>> {
20518 const D: usize = 128;
20519 const NSPLIT: u32 = 4;
20520 assert!(c >= 1 && c <= 128, "gdn_scan_chunked: C must be in 1..=128");
20521 let h = n_head;
20522 let nc = (t + c - 1) / c;
20523 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
20524 let gdn_mma_pre = !portable_mma_gated()
20528 && c == 32
20529 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
20530 Ok("1") => true,
20531 Ok("0") => false,
20532 _ => cfg!(memra_hopper_mma),
20533 };
20534 let mut wb16_pre: Option<CudaSlice<u8>> = if gdn_mma_pre {
20535 Some(self.alloc_u8_uninit(nc * h * c * D * 2)?)
20536 } else {
20537 None
20538 };
20539 let gdn_wgmma_pre = gdn_mma_pre
20543 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
20544 Ok("0") => false,
20545 Ok("1") => true,
20546 _ => cfg!(memra_hopper_mma),
20547 };
20548 let nk = t * hk * D;
20549 let mut kb16_local: Option<CudaSlice<u8>> = None;
20550 if gdn_mma_pre && kb16_pre.is_none() {
20551 let mut kb = self.alloc_u8_uninit(nk * 2)?;
20552 let f = self.func("f32_to_bf16_bulk");
20553 let n2 = nk as i64;
20554 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
20555 let __s_b = self.gpu.stream();
20556 let mut b = __s_b.launch_builder(&f);
20557 b.arg(k).arg(&mut kb).arg(&n2);
20558 unsafe {
20559 b.launch(cfg2)?;
20560 }
20561 kb16_local = Some(kb);
20562 }
20563 let kb16_ref0: Option<&CudaSlice<u8>> = kb16_local.as_ref().or(kb16_pre);
20564 if let Some(kb) = kb16_pre {
20565 assert!(kb.len() >= nk * 2, "kb16_pre too small");
20566 }
20567 let mut qb16: Option<CudaSlice<u8>> = None;
20568 let mut pb16: Option<CudaSlice<u8>> = None;
20569 if gdn_wgmma_pre {
20570 if qb16_pre.is_none() {
20573 let mut qb = self.alloc_u8_uninit(nk * 2)?;
20574 let f = self.func("f32_to_bf16_bulk");
20575 let n2 = nk as i64;
20576 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
20577 let __s_b = self.gpu.stream();
20578 let mut b = __s_b.launch_builder(&f);
20579 b.arg(q).arg(&mut qb).arg(&n2);
20580 unsafe {
20581 b.launch(cfg2)?;
20582 }
20583 qb16 = Some(qb);
20584 } else if let Some(qb) = qb16_pre {
20585 assert!(qb.len() >= nk * 2, "qb16_pre too small");
20586 }
20587 pb16 = Some(self.alloc_u8_uninit(nc * h * c * c * 2)?);
20588 }
20589 let qb16_ref0: Option<&CudaSlice<u8>> = qb16.as_ref().or(qb16_pre);
20590 let k2w = if gdn_wgmma_pre {
20591 Some((
20592 *qb16_ref0.as_ref().unwrap(),
20593 *kb16_ref0.as_ref().unwrap(),
20594 pb16.as_mut().unwrap(),
20595 ))
20596 } else {
20597 None
20598 };
20599 let (gcum, p, u, w) =
20600 self.gdn_chunk_k123(q, k, v, g, beta, wb16_pre.as_mut(), n_head, t, c, hk, k2w)?;
20601 let _ = &w;
20602 let mut y = self.uninit(nc * h * c * D)?;
20603 let mut ssnap = self.uninit(nc * h * D * D)?; let gdn_mma = !portable_mma_gated()
20615 && c == 32
20616 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
20617 Ok("1") => true,
20618 Ok("0") => false,
20619 _ => cfg!(memra_hopper_mma),
20620 };
20621 if gdn_mma {
20622 let wb16 = wb16_pre
20623 .take()
20624 .expect("mma path pre-allocates wb16 (K3 store fold)");
20625 let kb16_ref: &CudaSlice<u8> = kb16_ref0.expect("mma path pre-builds kb16 above K123");
20626 if gdn_wgmma_pre {
20638 let qb16 = qb16_ref0.unwrap();
20640 let pb16 = pb16.as_ref().unwrap();
20641 {
20642 let f = self.func("gdn_k45_wgmma");
20643 let cfg = LaunchConfig {
20644 grid_dim: (h as u32, 4, 1),
20645 block_dim: (256, 1, 1),
20646 shared_mem_bytes: 0,
20647 };
20648 let hki = hk as i32;
20649 let __s_b = self.gpu.stream();
20650 let mut b = __s_b.launch_builder(&f);
20651 b.arg(kb16_ref)
20652 .arg(&gcum)
20653 .arg(beta)
20654 .arg(&u)
20655 .arg(&wb16)
20656 .arg(qb16)
20657 .arg(pb16)
20658 .arg(o)
20659 .arg(&scale)
20660 .arg(state_in)
20661 .arg(&mut *state_out)
20662 .arg(&hi)
20663 .arg(&ti)
20664 .arg(&ci)
20665 .arg(&hki);
20666 unsafe {
20667 b.launch(cfg)?;
20668 }
20669 }
20670 return Ok(());
20671 }
20672 let mut y16 = self.alloc_u8_uninit(nc * h * c * D * 2)?;
20676 let mut ssnap16 = self.alloc_u8_uninit(nc * h * D * D * 2)?;
20677 {
20678 let f = self.func("gdn_chunk_state_mma");
20679 let cfg = LaunchConfig {
20680 grid_dim: (h as u32, NSPLIT, 1),
20681 block_dim: (256, 1, 1),
20682 shared_mem_bytes: 0,
20683 };
20684 let hki = hk as i32;
20685 let __s_b = self.gpu.stream();
20686 let mut b = __s_b.launch_builder(&f);
20687 b.arg(kb16_ref)
20688 .arg(&gcum)
20689 .arg(beta)
20690 .arg(&u)
20691 .arg(&wb16)
20692 .arg(&mut y16)
20693 .arg(&mut ssnap16)
20694 .arg(state_in)
20695 .arg(&mut *state_out)
20696 .arg(&hi)
20697 .arg(&ti)
20698 .arg(&ci)
20699 .arg(&hki);
20700 unsafe {
20701 b.launch(cfg)?;
20702 }
20703 }
20704 {
20705 let f = self.func("gdn_chunk_output_mma");
20707 let jt = ((c + 31) / 32) as u32;
20708 let cfg = LaunchConfig {
20709 grid_dim: (nc as u32, h as u32, jt),
20710 block_dim: (256, 1, 1),
20711 shared_mem_bytes: 0,
20712 };
20713 let hki = hk as i32;
20714 let __s_b = self.gpu.stream();
20715 let mut b = __s_b.launch_builder(&f);
20716 b.arg(q)
20717 .arg(&gcum)
20718 .arg(&p)
20719 .arg(&y16)
20720 .arg(&ssnap16)
20721 .arg(o)
20722 .arg(&hi)
20723 .arg(&ti)
20724 .arg(&ci)
20725 .arg(&scale)
20726 .arg(&hki);
20727 unsafe {
20728 b.launch(cfg)?;
20729 }
20730 }
20731 return Ok(());
20732 }
20733 {
20734 let f = self.func("gdn_chunk_state_f32");
20736 let cfg = LaunchConfig {
20737 grid_dim: (h as u32, NSPLIT, 1),
20738 block_dim: (256, 1, 1),
20739 shared_mem_bytes: 0,
20740 };
20741 let __s_b = self.gpu.stream();
20742 let mut b = __s_b.launch_builder(&f);
20743 b.arg(k)
20744 .arg(&gcum)
20745 .arg(beta)
20746 .arg(&u)
20747 .arg(&w)
20748 .arg(&mut y)
20749 .arg(&mut ssnap)
20750 .arg(state_in)
20751 .arg(&mut *state_out)
20752 .arg(&hi)
20753 .arg(&ti)
20754 .arg(&ci);
20755 unsafe {
20756 b.launch(cfg)?;
20757 }
20758 }
20759 {
20760 let f = self.func("gdn_chunk_output_f32");
20762 let jt = ((c + 31) / 32) as u32;
20763 let cfg = LaunchConfig {
20764 grid_dim: (nc as u32, h as u32, jt),
20765 block_dim: (256, 1, 1),
20766 shared_mem_bytes: 0,
20767 };
20768 let __s_b = self.gpu.stream();
20769 let mut b = __s_b.launch_builder(&f);
20770 b.arg(q)
20771 .arg(&gcum)
20772 .arg(&p)
20773 .arg(&y)
20774 .arg(&ssnap)
20775 .arg(o)
20776 .arg(&hi)
20777 .arg(&ti)
20778 .arg(&ci)
20779 .arg(&scale);
20780 unsafe {
20781 b.launch(cfg)?;
20782 }
20783 }
20784 Ok(())
20785 }
20786
20787 #[allow(clippy::too_many_arguments)]
20796 #[allow(clippy::too_many_arguments)]
20797 pub fn gdn_scan_prefill(
20798 &self,
20799 q: &CudaSlice<f32>,
20800 k: &CudaSlice<f32>,
20801 v: &CudaSlice<f32>,
20802 g: &CudaSlice<f32>,
20803 beta: &CudaSlice<f32>,
20804 kb16_pre: Option<&CudaSlice<u8>>,
20805 qb16_pre: Option<&CudaSlice<u8>>,
20806 state_in: &CudaSlice<f32>,
20807 state_out: &mut CudaSlice<f32>,
20808 o: &mut CudaSlice<f32>,
20809 n_head: usize,
20810 t: usize,
20811 scale: f32,
20812 hk: usize,
20813 ) -> Result<(), Box<dyn std::error::Error>> {
20814 if std::env::var("MEMRA_GDN_DIFF").is_ok() && t >= 16 {
20815 assert!(hk == n_head, "GDN_DIFF oracle is broadcast-only");
20816 return self.gdn_scan_diff(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale);
20817 }
20818 if Self::gdn_chunked_enabled() && t >= 16 {
20819 self.gdn_scan_chunked(
20820 q,
20821 k,
20822 v,
20823 g,
20824 beta,
20825 kb16_pre,
20826 qb16_pre,
20827 state_in,
20828 state_out,
20829 o,
20830 n_head,
20831 t,
20832 scale,
20833 Self::gdn_chunk_size(),
20834 hk,
20835 )
20836 } else {
20837 assert!(
20838 hk == n_head,
20839 "s128 scan is broadcast-only (prep guarantees by predicate)"
20840 );
20841 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)
20842 }
20843 }
20844
20845 #[allow(clippy::too_many_arguments)]
20847 fn gdn_scan_diff(
20848 &self,
20849 q: &CudaSlice<f32>,
20850 k: &CudaSlice<f32>,
20851 v: &CudaSlice<f32>,
20852 g: &CudaSlice<f32>,
20853 beta: &CudaSlice<f32>,
20854 state_in: &CudaSlice<f32>,
20855 state_out: &mut CudaSlice<f32>,
20856 o: &mut CudaSlice<f32>,
20857 n_head: usize,
20858 t: usize,
20859 scale: f32,
20860 ) -> Result<(), Box<dyn std::error::Error>> {
20861 static CALL: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
20862 let call = CALL.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
20863 let mut o_c = self.uninit(o.len())?;
20864 let mut st_c = self.uninit(state_out.len())?;
20865 self.gdn_scan_chunked(
20866 q,
20867 k,
20868 v,
20869 g,
20870 beta,
20871 None,
20872 None,
20873 state_in,
20874 &mut st_c,
20875 &mut o_c,
20876 n_head,
20877 t,
20878 scale,
20879 Self::gdn_chunk_size(),
20880 n_head,
20881 )?;
20882 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)?;
20883 let (oh_s, oh_c) = (self.dtoh(o)?, self.dtoh(&o_c)?);
20884 let (sh_s, sh_c) = (self.dtoh(state_out)?, self.dtoh(&st_c)?);
20885 let stats = |a: &[f32], b: &[f32]| -> (f32, f32, f64) {
20886 let mut max_abs = 0f32;
20887 let mut max_rel = 0f32;
20888 let mut sum_rel = 0f64;
20889 for (x, y) in a.iter().zip(b) {
20890 let ad = (x - y).abs();
20891 let rel = ad / x.abs().max(y.abs()).max(1e-3);
20892 if ad > max_abs {
20893 max_abs = ad;
20894 }
20895 if rel > max_rel {
20896 max_rel = rel;
20897 }
20898 sum_rel += rel as f64;
20899 }
20900 (max_abs, max_rel, sum_rel / a.len() as f64)
20901 };
20902 let (o_ma, o_mr, o_mean) = stats(&oh_s, &oh_c);
20903 let (s_ma, s_mr, s_mean) = stats(&sh_s, &sh_c);
20904 println!(
20905 "[gdn-diff call {call:3} T={t} C={}] out: max_abs={o_ma:.3e} max_rel={o_mr:.3e} mean_rel={o_mean:.3e} | \
20906 state: max_abs={s_ma:.3e} max_rel={s_mr:.3e} mean_rel={s_mean:.3e}",
20907 Self::gdn_chunk_size()
20908 );
20909 Ok(())
20910 }
20911
20912 pub fn gdn_glog(
20914 &self,
20915 alpha: &CudaSlice<f32>,
20916 dt_bias: &CudaSlice<f32>,
20917 a: &CudaSlice<f32>,
20918 g_log: &mut CudaSlice<f32>,
20919 n_head: usize,
20920 t: usize,
20921 ) -> Result<(), Box<dyn std::error::Error>> {
20922 let f = self.func("gdn_glog_f32");
20923 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
20924 let (h, ti) = (n_head as i32, t as i32);
20925 let __s_b = self.gpu.stream();
20926 let mut b = __s_b.launch_builder(&f);
20927 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
20928 unsafe {
20929 b.launch(cfg)?;
20930 }
20931 Ok(())
20932 }
20933
20934 pub fn sigmoid_v(
20937 &self,
20938 x: &cudarc::driver::CudaView<f32>,
20939 y: &mut CudaSlice<f32>,
20940 n: usize,
20941 ) -> Result<(), Box<dyn std::error::Error>> {
20942 let f = self.func("sigmoid_f32");
20943 let cfg = LaunchConfig::for_num_elems(n as u32);
20944 let ni = n as i32;
20945 let __s_b = self.gpu.stream();
20946 let mut b = __s_b.launch_builder(&f);
20947 b.arg(x).arg(y).arg(&ni);
20948 unsafe {
20949 b.launch(cfg)?;
20950 }
20951 Ok(())
20952 }
20953
20954 pub fn gdn_glog_v(
20955 &self,
20956 alpha: &cudarc::driver::CudaView<f32>,
20957 dt_bias: &CudaSlice<f32>,
20958 a: &CudaSlice<f32>,
20959 g_log: &mut CudaSlice<f32>,
20960 n_head: usize,
20961 t: usize,
20962 ) -> Result<(), Box<dyn std::error::Error>> {
20963 let f = self.func("gdn_glog_f32");
20964 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
20965 let (h, ti) = (n_head as i32, t as i32);
20966 let __s_b = self.gpu.stream();
20967 let mut b = __s_b.launch_builder(&f);
20968 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
20969 unsafe {
20970 b.launch(cfg)?;
20971 }
20972 Ok(())
20973 }
20974
20975 pub fn sigmoid(
20976 &self,
20977 x: &CudaSlice<f32>,
20978 y: &mut CudaSlice<f32>,
20979 n: usize,
20980 ) -> Result<(), Box<dyn std::error::Error>> {
20981 let f = self.func("sigmoid_f32");
20982 let cfg = LaunchConfig::for_num_elems(n as u32);
20983 let ni = n as i32;
20984 let __s_b = self.gpu.stream();
20985 let mut b = __s_b.launch_builder(&f);
20986 b.arg(x).arg(y).arg(&ni);
20987 unsafe {
20988 b.launch(cfg)?;
20989 }
20990 Ok(())
20991 }
20992
20993 pub fn sig_mul_f16out(
20996 &self,
20997 a: &CudaSlice<f32>,
20998 g: &CudaSlice<f32>,
20999 dst: &mut CudaSlice<f32>,
21000 dst16: &mut CudaSlice<u8>,
21001 n: usize,
21002 ) -> Result<(), Box<dyn std::error::Error>> {
21003 let f = self.func("sig_mul_f16out_f32");
21004 let cfg = LaunchConfig::for_num_elems(n as u32);
21005 let ni = n as i32;
21006 let __s_b = self.gpu.stream();
21007 let mut b = __s_b.launch_builder(&f);
21008 b.arg(a).arg(g).arg(dst).arg(dst16).arg(&ni);
21009 unsafe {
21010 b.launch(cfg)?;
21011 }
21012 Ok(())
21013 }
21014
21015 #[allow(clippy::too_many_arguments)]
21024 pub fn attn_head_gate(
21025 &self,
21026 a: &CudaSlice<f32>,
21027 g: &CudaSlice<f32>,
21028 dst: &mut CudaSlice<f32>,
21029 dst16: Option<&mut CudaSlice<u8>>,
21030 head_dim: usize,
21031 n_head: usize,
21032 t: usize,
21033 ) -> Result<(), Box<dyn std::error::Error>> {
21034 let f = self.func("attn_head_gate_f32");
21035 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
21036 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
21037 let d16: u64 = match dst16 {
21039 Some(d) => self.addr_u8(d),
21040 None => 0,
21041 };
21042 let __s_b = self.gpu.stream();
21043 let mut b = __s_b.launch_builder(&f);
21044 b.arg(a)
21045 .arg(g)
21046 .arg(dst)
21047 .arg(&d16)
21048 .arg(&hd)
21049 .arg(&nh)
21050 .arg(&ti);
21051 unsafe {
21052 b.launch(cfg)?;
21053 }
21054 Ok(())
21055 }
21056
21057 #[allow(clippy::too_many_arguments)]
21066 pub fn swiglu_clamped_mul_scaled(
21067 &self,
21068 gate: &CudaSlice<f32>,
21069 up: &CudaSlice<f32>,
21070 gs: f32,
21071 us: f32,
21072 limit: f32,
21073 dst: &mut CudaSlice<f32>,
21074 n: usize,
21075 ) -> Result<(), Box<dyn std::error::Error>> {
21076 debug_assert!(
21077 limit > 1e-6,
21078 "swiglu_clamped needs a live limit; use silu_mul_scaled"
21079 );
21080 let f = self.func("swiglu_clamped_mul_scaled_f32");
21081 let cfg = LaunchConfig::for_num_elems(n as u32);
21082 let ni = n as i32;
21083 let __s_b = self.gpu.stream();
21084 let mut b = __s_b.launch_builder(&f);
21085 b.arg(gate)
21086 .arg(up)
21087 .arg(&gs)
21088 .arg(&us)
21089 .arg(&limit)
21090 .arg(dst)
21091 .arg(&ni);
21092 unsafe {
21093 b.launch(cfg)?;
21094 }
21095 Ok(())
21096 }
21097
21098 pub fn gated_rmsnorm(
21100 &self,
21101 o: &CudaSlice<f32>,
21102 w: &CudaSlice<f32>,
21103 z: &CudaSlice<f32>,
21104 dst: &mut CudaSlice<f32>,
21105 ncols: usize,
21106 nrows: usize,
21107 eps: f32,
21108 ) -> Result<(), Box<dyn std::error::Error>> {
21109 let f = self.func("gated_rmsnorm_f32");
21110 let cfg = LaunchConfig {
21111 grid_dim: (nrows as u32, 1, 1),
21112 block_dim: (128, 1, 1),
21113 shared_mem_bytes: 0,
21114 };
21115 let (nc, e) = (ncols as i32, eps);
21116 let __s_b = self.gpu.stream();
21117 let mut b = __s_b.launch_builder(&f);
21118 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
21119 unsafe {
21120 b.launch(cfg)?;
21121 }
21122 Ok(())
21123 }
21124
21125 pub fn gated_rmsnorm_f16out(
21128 &self,
21129 o: &CudaSlice<f32>,
21130 w: &CudaSlice<f32>,
21131 z: &CudaSlice<f32>,
21132 dst: &mut CudaSlice<f32>,
21133 dst16: &mut CudaSlice<u8>,
21134 ncols: usize,
21135 nrows: usize,
21136 eps: f32,
21137 ) -> Result<(), Box<dyn std::error::Error>> {
21138 let f = self.func("gated_rmsnorm_f16out_f32");
21139 let cfg = LaunchConfig {
21141 grid_dim: (nrows as u32, 1, 1),
21142 block_dim: (128, 1, 1),
21143 shared_mem_bytes: 0,
21144 };
21145 let (nc, e) = (ncols as i32, eps);
21146 let __s_b = self.gpu.stream();
21147 let mut b = __s_b.launch_builder(&f);
21148 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
21149 unsafe {
21150 b.launch(cfg)?;
21151 }
21152 Ok(())
21153 }
21154
21155 #[allow(clippy::too_many_arguments)]
21159 pub fn add_rms_norm_zq8(
21160 &self,
21161 a: &CudaSlice<f32>,
21162 b_in: &CudaSlice<f32>,
21163 w: &CudaSlice<f32>,
21164 res: &mut CudaSlice<f32>,
21165 z: &mut CudaSlice<f32>,
21166 ncols: usize,
21167 nrows: usize,
21168 eps: f32,
21169 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
21170 assert!(ncols % 32 == 0);
21171 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
21172 let mut d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
21173 let f = self.func("add_rms_norm_zq8");
21174 let cfg = LaunchConfig {
21175 grid_dim: (nrows as u32, 1, 1),
21176 block_dim: (1024, 1, 1),
21177 shared_mem_bytes: 0,
21178 };
21179 let (nc, ep) = (ncols as i32, eps);
21180 let __s_b = self.gpu.stream();
21181 let mut b = __s_b.launch_builder(&f);
21182 b.arg(a)
21183 .arg(b_in)
21184 .arg(w)
21185 .arg(res)
21186 .arg(z)
21187 .arg(&mut q)
21188 .arg(&mut d)
21189 .arg(&nc)
21190 .arg(&ep);
21191 unsafe {
21192 b.launch(cfg)?;
21193 }
21194 Ok((q, d))
21195 }
21196
21197 pub fn gated_rmsnorm_zv(
21202 &self,
21203 o: &CudaSlice<f32>,
21204 w: &CudaSlice<f32>,
21205 z: &cudarc::driver::CudaView<f32>,
21206 dst: &mut CudaSlice<f32>,
21207 ncols: usize,
21208 nrows: usize,
21209 eps: f32,
21210 ) -> Result<(), Box<dyn std::error::Error>> {
21211 let f = self.func("gated_rmsnorm_f32");
21212 let cfg = LaunchConfig {
21213 grid_dim: (nrows as u32, 1, 1),
21214 block_dim: (128, 1, 1),
21215 shared_mem_bytes: 0,
21216 };
21217 let (nc, e) = (ncols as i32, eps);
21218 let __s_b = self.gpu.stream();
21219 let mut b = __s_b.launch_builder(&f);
21220 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
21221 unsafe {
21222 b.launch(cfg)?;
21223 }
21224 Ok(())
21225 }
21226
21227 pub fn gated_rmsnorm_f16out_zv(
21228 &self,
21229 o: &CudaSlice<f32>,
21230 w: &CudaSlice<f32>,
21231 z: &cudarc::driver::CudaView<f32>,
21232 dst: &mut CudaSlice<f32>,
21233 dst16: &mut CudaSlice<u8>,
21234 ncols: usize,
21235 nrows: usize,
21236 eps: f32,
21237 ) -> Result<(), Box<dyn std::error::Error>> {
21238 let f = self.func("gated_rmsnorm_f16out_f32");
21239 let cfg = LaunchConfig {
21241 grid_dim: (nrows as u32, 1, 1),
21242 block_dim: (128, 1, 1),
21243 shared_mem_bytes: 0,
21244 };
21245 let (nc, e) = (ncols as i32, eps);
21246 let __s_b = self.gpu.stream();
21247 let mut b = __s_b.launch_builder(&f);
21248 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
21249 unsafe {
21250 b.launch(cfg)?;
21251 }
21252 Ok(())
21253 }
21254
21255 pub fn gated_rmsnorm_q8_1(
21256 &self,
21257 o: &CudaSlice<f32>,
21258 w: &CudaSlice<f32>,
21259 z: &CudaSlice<f32>,
21260 ncols: usize,
21261 nrows: usize,
21262 eps: f32,
21263 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
21264 assert!(ncols % 32 == 0);
21265 let f = self.func("gated_rmsnorm_q8_1");
21266 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
21267 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
21268 let cfg = LaunchConfig {
21269 grid_dim: (nrows as u32, 1, 1),
21270 block_dim: (128, 1, 1),
21271 shared_mem_bytes: 0,
21272 };
21273 let (nc, ep) = (ncols as i32, eps);
21274 let __s_b = self.gpu.stream();
21275 let mut b = __s_b.launch_builder(&f);
21276 b.arg(o)
21277 .arg(w)
21278 .arg(z)
21279 .arg(&mut out_q)
21280 .arg(&mut out_d)
21281 .arg(&nc)
21282 .arg(&ep);
21283 unsafe {
21284 b.launch(cfg)?;
21285 }
21286 Ok((out_q, out_d))
21287 }
21288
21289 pub fn transpose(
21291 &self,
21292 inp: &CudaSlice<f32>,
21293 rows: usize,
21294 cols: usize,
21295 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21296 let f = self.func("transpose_f32");
21297 let mut out = self.zeros(rows * cols)?;
21298 let cfg = LaunchConfig::for_num_elems((rows * cols) as u32);
21299 let (r, c) = (rows as i32, cols as i32);
21300 let __s_b = self.gpu.stream();
21301 let mut b = __s_b.launch_builder(&f);
21302 b.arg(inp).arg(&mut out).arg(&r).arg(&c);
21303 unsafe {
21304 b.launch(cfg)?;
21305 }
21306 Ok(out)
21307 }
21308
21309 pub fn repeat_heads(
21311 &self,
21312 inp: &CudaSlice<f32>,
21313 out: &mut CudaSlice<f32>,
21314 head_dim: usize,
21315 n_in: usize,
21316 n_out: usize,
21317 t: usize,
21318 ) -> Result<(), Box<dyn std::error::Error>> {
21319 let f = self.func("repeat_heads_f32");
21320 let cfg = LaunchConfig::for_num_elems((head_dim * n_out * t) as u32);
21321 let (hd, ni, no, ti) = (head_dim as i32, n_in as i32, n_out as i32, t as i32);
21322 let __s_b = self.gpu.stream();
21323 let mut b = __s_b.launch_builder(&f);
21324 b.arg(inp).arg(out).arg(&hd).arg(&ni).arg(&no).arg(&ti);
21325 unsafe {
21326 b.launch(cfg)?;
21327 }
21328 Ok(())
21329 }
21330
21331 pub fn q_gate_split(
21334 &self,
21335 qf: &CudaSlice<f32>,
21336 q_out: &mut CudaSlice<f32>,
21337 gate_out: &mut CudaSlice<f32>,
21338 head_dim: usize,
21339 n_head: usize,
21340 t: usize,
21341 ) -> Result<(), Box<dyn std::error::Error>> {
21342 let f = self.func("q_gate_split_f32");
21343 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
21344 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
21345 let __s_b = self.gpu.stream();
21346 let mut b = __s_b.launch_builder(&f);
21347 b.arg(qf)
21348 .arg(q_out)
21349 .arg(gate_out)
21350 .arg(&hd)
21351 .arg(&nh)
21352 .arg(&ti);
21353 unsafe {
21354 b.launch(cfg)?;
21355 }
21356 Ok(())
21357 }
21358
21359 pub fn qkv_to_gdn_repack(
21363 &self,
21364 conv_out: &CudaSlice<f32>,
21365 q_g: &mut CudaSlice<f32>,
21366 k_g: &mut CudaSlice<f32>,
21367 v_g: &mut CudaSlice<f32>,
21368 d_state: usize,
21369 num_v: usize,
21370 num_k: usize,
21371 key_dim: usize,
21372 t: usize,
21373 ) -> Result<(), Box<dyn std::error::Error>> {
21374 let f = self.func("qkv_to_gdn_repack_f32");
21375 let cfg = LaunchConfig::for_num_elems((d_state * num_v * t) as u32);
21376 let (ds, nv, nk, kd, ti) = (
21377 d_state as i32,
21378 num_v as i32,
21379 num_k as i32,
21380 key_dim as i32,
21381 t as i32,
21382 );
21383 let __s_b = self.gpu.stream();
21384 let mut b = __s_b.launch_builder(&f);
21385 b.arg(conv_out)
21386 .arg(q_g)
21387 .arg(k_g)
21388 .arg(v_g)
21389 .arg(&ds)
21390 .arg(&nv)
21391 .arg(&nk)
21392 .arg(&kd)
21393 .arg(&ti);
21394 unsafe {
21395 b.launch(cfg)?;
21396 }
21397 Ok(())
21398 }
21399
21400 pub fn conv_left_pad(
21403 &self,
21404 src: &CudaSlice<f32>,
21405 dst: &mut CudaSlice<f32>,
21406 conv_dim: usize,
21407 t: usize,
21408 pad: usize,
21409 ) -> Result<(), Box<dyn std::error::Error>> {
21410 let f = self.func("conv_left_pad_f32");
21411 let cfg = LaunchConfig::for_num_elems((conv_dim * t) as u32);
21412 let (cd, ti, p) = (conv_dim as i32, t as i32, pad as i32);
21413 let __s_b = self.gpu.stream();
21414 let mut b = __s_b.launch_builder(&f);
21415 b.arg(src).arg(dst).arg(&cd).arg(&ti).arg(&p);
21416 unsafe {
21417 b.launch(cfg)?;
21418 }
21419 Ok(())
21420 }
21421
21422 pub fn conv_assemble_and_roll(
21426 &self,
21427 qkv_col: &CudaSlice<f32>,
21428 conv_state: &mut CudaSlice<f32>,
21429 conv_in: &mut CudaSlice<f32>,
21430 conv_dim: usize,
21431 pad: usize,
21432 ) -> Result<(), Box<dyn std::error::Error>> {
21433 let f = self.func("conv_assemble_and_roll_f32");
21434 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
21435 let (cd, p) = (conv_dim as i32, pad as i32);
21436 let __s_b = self.gpu.stream();
21437 let mut b = __s_b.launch_builder(&f);
21438 b.arg(qkv_col).arg(conv_state).arg(conv_in).arg(&cd).arg(&p);
21439 unsafe {
21440 b.launch(cfg)?;
21441 }
21442 Ok(())
21443 }
21444
21445 pub fn ssm_conv1d_fused_decode(
21451 &self,
21452 qkv_col: &CudaSlice<f32>,
21453 conv_state: &mut CudaSlice<f32>,
21454 w: &CudaSlice<f32>,
21455 conv_out: &mut CudaSlice<f32>,
21456 conv_dim: usize,
21457 d_conv: usize,
21458 ) -> Result<(), Box<dyn std::error::Error>> {
21459 let f = self.func("ssm_conv1d_fused_decode_f32");
21460 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
21461 let (cd, dc) = (conv_dim as i32, d_conv as i32);
21462 let __s_b = self.gpu.stream();
21463 let mut b = __s_b.launch_builder(&f);
21464 b.arg(qkv_col)
21465 .arg(conv_state)
21466 .arg(w)
21467 .arg(conv_out)
21468 .arg(&cd)
21469 .arg(&dc);
21470 unsafe {
21471 b.launch(cfg)?;
21472 }
21473 Ok(())
21474 }
21475
21476 pub fn slice_range(
21479 &self,
21480 src: &CudaSlice<f32>,
21481 start: usize,
21482 len: usize,
21483 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21484 let host = self.gpu.stream().clone_dtoh(src)?;
21485 self.gpu.stream().synchronize()?;
21486 Ok(self.htod(&host[start..start + len])?)
21487 }
21488}
21489
21490#[cfg(test)]
21491mod target_dispatch_tests {
21492 use super::legacy_quant_gemm_allowed;
21493
21494 #[test]
21495 fn legacy_quant_gemm_arch_policy_honors_the_escape_hatch() {
21496 assert!(legacy_quant_gemm_allowed(false, false, false));
21498 assert!(!legacy_quant_gemm_allowed(false, false, true));
21499 assert!(!legacy_quant_gemm_allowed(true, false, false));
21501 assert!(!legacy_quant_gemm_allowed(true, false, true));
21502 assert!(legacy_quant_gemm_allowed(true, true, false));
21504 assert!(!legacy_quant_gemm_allowed(true, true, true));
21505 }
21506
21507 #[cfg(all(memra_portable_cuda, not(memra_hopper_mma)))]
21508 #[test]
21509 fn portable_build_disables_legacy_quant_gemm_without_an_env_override() {
21510 assert!(!legacy_quant_gemm_allowed(
21511 cfg!(memra_portable_cuda),
21512 cfg!(memra_hopper_mma),
21513 false
21514 ));
21515 }
21516
21517 #[cfg(memra_hopper_mma)]
21518 #[test]
21519 fn hopper_mma_build_re_admits_legacy_quant_gemm() {
21520 assert!(legacy_quant_gemm_allowed(
21521 cfg!(memra_portable_cuda),
21522 cfg!(memra_hopper_mma),
21523 false
21524 ));
21525 assert!(super::portable_mma_gated() == false);
21526 }
21527}
21528
21529impl memra_kv::KvDev for Engine {
21532 fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21533 Engine::zeros(self, n)
21534 }
21535 fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21536 Engine::uninit(self, n)
21537 }
21538 fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
21539 Engine::alloc_u8(self, n)
21540 }
21541 fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
21542 Engine::htod_i32(self, v)
21543 }
21544 fn clone_dtod(
21545 &self,
21546 src: &CudaSlice<f32>,
21547 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21548 Engine::clone_dtod(self, src)
21549 }
21550 fn copy_into(
21551 &self,
21552 dst: &mut CudaSlice<f32>,
21553 off: usize,
21554 src: &CudaSlice<f32>,
21555 len: usize,
21556 ) -> Result<(), Box<dyn std::error::Error>> {
21557 Engine::copy_into(self, dst, off, src, len)
21558 }
21559 fn set_i32_one(
21560 &self,
21561 d: &mut CudaSlice<i32>,
21562 v: i32,
21563 ) -> Result<(), Box<dyn std::error::Error>> {
21564 Engine::set_i32_one(self, d, v)
21565 }
21566}