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 pub fn matmul_q8_fused2(
10550 &self,
10551 w0: &crate::model::GpuTensor,
10552 w1: &crate::model::GpuTensor,
10553 aq: &CudaSlice<i8>,
10554 ad: &CudaSlice<f32>,
10555 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10556 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
10562 return Ok(Some(self.e4m3_fused2_core(
10563 p0.0,
10564 p1.0,
10565 aq,
10566 ad,
10567 w0.in_features(),
10568 p0.1,
10569 p1.1,
10570 p0.2,
10571 p0.3,
10572 p1.3,
10573 )?));
10574 }
10575 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
10576 return Ok(None);
10577 };
10578 Ok(Some(self.q8_fused2_core(
10579 p0.0,
10580 p1.0,
10581 aq,
10582 ad,
10583 w0.in_features(),
10584 p0.1,
10585 p1.1,
10586 p0.2,
10587 )?))
10588 }
10589
10590 #[allow(clippy::too_many_arguments)]
10591 fn q8_fused2_core(
10592 &self,
10593 b0: &CudaSlice<u8>,
10594 b1: &CudaSlice<u8>,
10595 aq: &CudaSlice<i8>,
10596 ad: &CudaSlice<f32>,
10597 in_f: usize,
10598 out0: usize,
10599 out1: usize,
10600 row_bytes: usize,
10601 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10602 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
10604 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
10605 let f = self.func("qmatvec_q8_0_mmvq_fused2");
10606 let mut y0 = self.alloc_uninit::<f32>(out0)?;
10607 let mut y1 = self.alloc_uninit::<f32>(out1)?;
10608 let cfg = LaunchConfig {
10609 grid_dim: (nb0 + nb1, 1, 1),
10610 block_dim: (32, ROWS_PER_BLOCK, 1),
10611 shared_mem_bytes: 0,
10612 };
10613 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
10614 let __s_b = self.gpu.stream();
10615 let mut b = __s_b.launch_builder(&f);
10616 b.arg(b0)
10617 .arg(b1)
10618 .arg(aq)
10619 .arg(ad)
10620 .arg(&mut y0)
10621 .arg(&mut y1)
10622 .arg(&inf)
10623 .arg(&o0)
10624 .arg(&o1)
10625 .arg(&rbl);
10626 unsafe {
10627 b.launch(cfg)?;
10628 }
10629 Ok((y0, y1))
10630 }
10631
10632 pub fn matmul_q8_fused2_x(
10638 &self,
10639 w0: &crate::model::GpuTensor,
10640 w1: &crate::model::GpuTensor,
10641 x: &CudaSlice<f32>,
10642 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10643 if !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
10644 return Ok(None);
10645 }
10646 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
10647 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
10648 return Ok(Some(self.e4m3_fused2_core(
10649 p0.0,
10650 p1.0,
10651 &aq,
10652 &ad,
10653 w0.in_features(),
10654 p0.1,
10655 p1.1,
10656 p0.2,
10657 p0.3,
10658 p1.3,
10659 )?));
10660 }
10661 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
10662 return Ok(None);
10663 };
10664 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
10665 Ok(Some(self.q8_fused2_core(
10666 p0.0,
10667 p1.0,
10668 &aq,
10669 &ad,
10670 w0.in_features(),
10671 p0.1,
10672 p1.1,
10673 p0.2,
10674 )?))
10675 }
10676
10677 #[allow(clippy::too_many_arguments)]
10680 pub fn qmatvec_q8_fused2_raw(
10681 &self,
10682 b0: &CudaSlice<u8>,
10683 b1: &CudaSlice<u8>,
10684 x: &CudaSlice<f32>,
10685 in_f: usize,
10686 out0: usize,
10687 out1: usize,
10688 row_bytes: usize,
10689 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10690 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
10691 self.q8_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes)
10692 }
10693
10694 pub fn matmul_q4_fused3(
10700 &self,
10701 w0: &crate::model::GpuTensor,
10702 w1: &crate::model::GpuTensor,
10703 w2: &crate::model::GpuTensor,
10704 aq: &CudaSlice<i8>,
10705 ad: &CudaSlice<f32>,
10706 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
10707 {
10708 use crate::model::GpuTensor;
10709 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
10710 match w {
10711 GpuTensor::Quant {
10712 qtype, row_bytes, ..
10713 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
10714 _ => None,
10715 }
10716 };
10717 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
10718 return Ok(None);
10719 };
10720 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
10721 return Ok(None);
10722 }
10723 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
10727 match w {
10728 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
10729 Some(m) => (m, true),
10730 None => (bytes, *rp),
10731 },
10732 _ => unreachable!(),
10733 }
10734 }
10735 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
10736 if rp0 != rp1 || rp1 != rp2 {
10737 return Ok(None);
10738 }
10739 let rp = rp0;
10740 let rpb: u32 = 4;
10741 let mr1 = rp && Self::q40_mr1_on();
10745 let nb = |o: usize| {
10746 if mr1 {
10747 (o as u32).div_ceil(rpb)
10748 } else {
10749 (o as u32).div_ceil(2).div_ceil(rpb)
10750 }
10751 };
10752 let grid = nb(o0) + nb(o1) + nb(o2);
10753 let mut y0 = self.alloc_uninit::<f32>(o0)?;
10754 let mut y1 = self.alloc_uninit::<f32>(o1)?;
10755 let mut y2 = self.alloc_uninit::<f32>(o2)?;
10756 let f = self.func(if mr1 {
10757 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
10758 } else if rp {
10759 "qmatvec_q4_0_mmvq_fused3_rp"
10760 } else {
10761 "qmatvec_q4_0_mmvq_fused3"
10762 });
10763 let cfg = LaunchConfig {
10764 grid_dim: (grid, 1, 1),
10765 block_dim: (32, rpb, 1),
10766 shared_mem_bytes: 0,
10767 };
10768 let inf = w0.in_features() as i32;
10769 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
10770 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
10771 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
10774 {
10775 use cudarc::driver::{DevicePtr, DevicePtrMut};
10776 let s = &self.gpu.stream();
10777 let (p0, _g0) = b0.device_ptr(s);
10778 let (p1, _g1) = b1.device_ptr(s);
10779 let (p2, _g2) = b2.device_ptr(s);
10780 let (paq, _g3) = aq.device_ptr(s);
10781 let (pad, _g4) = ad.device_ptr(s);
10782 let (py0, _g5) = y0.device_ptr_mut(s);
10783 let (py1, _g6) = y1.device_ptr_mut(s);
10784 let (py2, _g7) = y2.device_ptr_mut(s);
10785 let mut ps = [
10786 &p0 as *const _ as *mut std::ffi::c_void,
10787 &p1 as *const _ as *mut _,
10788 &p2 as *const _ as *mut _,
10789 &paq as *const _ as *mut _,
10790 &pad as *const _ as *mut _,
10791 &py0 as *const _ as *mut _,
10792 &py1 as *const _ as *mut _,
10793 &py2 as *const _ as *mut _,
10794 &inf as *const _ as *mut _,
10795 &oo0 as *const _ as *mut _,
10796 &oo1 as *const _ as *mut _,
10797 &oo2 as *const _ as *mut _,
10798 &r0 as *const _ as *mut _,
10799 &r1 as *const _ as *mut _,
10800 &r2 as *const _ as *mut _,
10801 ];
10802 unsafe {
10803 self.launch_pdl(
10804 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
10805 (grid, 1, 1),
10806 (32, rpb, 1),
10807 &mut ps,
10808 )?;
10809 }
10810 }
10811 return Ok(Some((y0, y1, y2)));
10812 }
10813 let __s_b = self.gpu.stream();
10814 let mut b = __s_b.launch_builder(&f);
10815 b.arg(b0)
10816 .arg(b1)
10817 .arg(b2)
10818 .arg(aq)
10819 .arg(ad)
10820 .arg(&mut y0)
10821 .arg(&mut y1)
10822 .arg(&mut y2)
10823 .arg(&inf)
10824 .arg(&oo0)
10825 .arg(&oo1)
10826 .arg(&oo2)
10827 .arg(&r0)
10828 .arg(&r1)
10829 .arg(&r2);
10830 unsafe {
10831 b.launch(cfg)?;
10832 }
10833 Ok(Some((y0, y1, y2)))
10834 }
10835
10836 #[allow(clippy::too_many_arguments)]
10839 pub fn matmul_q4_fused3_into(
10840 &self,
10841 w0: &crate::model::GpuTensor,
10842 w1: &crate::model::GpuTensor,
10843 w2: &crate::model::GpuTensor,
10844 aq: &CudaSlice<i8>,
10845 ad: &CudaSlice<f32>,
10846 y0: &mut CudaSlice<f32>,
10847 y1: &mut CudaSlice<f32>,
10848 y2: &mut CudaSlice<f32>,
10849 ) -> Result<bool, Box<dyn std::error::Error>> {
10850 use crate::model::GpuTensor;
10851 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
10852 match w {
10853 GpuTensor::Quant {
10854 qtype, row_bytes, ..
10855 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
10856 _ => None,
10857 }
10858 };
10859 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
10860 return Ok(false);
10861 };
10862 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
10863 return Ok(false);
10864 }
10865 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
10866 match w {
10867 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
10868 Some(m) => (m, true),
10869 None => (bytes, *rp),
10870 },
10871 _ => unreachable!(),
10872 }
10873 }
10874 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
10875 if rp0 != rp1 || rp1 != rp2 {
10876 return Ok(false);
10877 }
10878 let rp = rp0;
10879 let rpb: u32 = 4;
10880 let mr1 = rp && Self::q40_mr1_on();
10881 let nb = |o: usize| {
10882 if mr1 {
10883 (o as u32).div_ceil(rpb)
10884 } else {
10885 (o as u32).div_ceil(2).div_ceil(rpb)
10886 }
10887 };
10888 let grid = nb(o0) + nb(o1) + nb(o2);
10889 debug_assert!(y0.len() >= o0 && y1.len() >= o1 && y2.len() >= o2);
10890 let f = self.func(if mr1 {
10891 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
10892 } else if rp {
10893 "qmatvec_q4_0_mmvq_fused3_rp"
10894 } else {
10895 "qmatvec_q4_0_mmvq_fused3"
10896 });
10897 let cfg = LaunchConfig {
10898 grid_dim: (grid, 1, 1),
10899 block_dim: (32, rpb, 1),
10900 shared_mem_bytes: 0,
10901 };
10902 let inf = w0.in_features() as i32;
10903 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
10904 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
10905 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
10907 use cudarc::driver::{DevicePtr, DevicePtrMut};
10908 let s = &self.gpu.stream();
10909 let (p0, _g0) = b0.device_ptr(s);
10910 let (p1, _g1) = b1.device_ptr(s);
10911 let (p2, _g2) = b2.device_ptr(s);
10912 let (paq, _g3) = aq.device_ptr(s);
10913 let (pad, _g4) = ad.device_ptr(s);
10914 let (py0, _g5) = y0.device_ptr_mut(s);
10915 let (py1, _g6) = y1.device_ptr_mut(s);
10916 let (py2, _g7) = y2.device_ptr_mut(s);
10917 let mut ps = [
10918 &p0 as *const _ as *mut std::ffi::c_void,
10919 &p1 as *const _ as *mut _,
10920 &p2 as *const _ as *mut _,
10921 &paq as *const _ as *mut _,
10922 &pad as *const _ as *mut _,
10923 &py0 as *const _ as *mut _,
10924 &py1 as *const _ as *mut _,
10925 &py2 as *const _ as *mut _,
10926 &inf as *const _ as *mut _,
10927 &oo0 as *const _ as *mut _,
10928 &oo1 as *const _ as *mut _,
10929 &oo2 as *const _ as *mut _,
10930 &r0 as *const _ as *mut _,
10931 &r1 as *const _ as *mut _,
10932 &r2 as *const _ as *mut _,
10933 ];
10934 unsafe {
10935 self.launch_pdl(
10936 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
10937 (grid, 1, 1),
10938 (32, rpb, 1),
10939 &mut ps,
10940 )?;
10941 }
10942 return Ok(true);
10943 }
10944 let __s_b = self.gpu.stream();
10945 let mut b = __s_b.launch_builder(&f);
10946 b.arg(b0)
10947 .arg(b1)
10948 .arg(b2)
10949 .arg(aq)
10950 .arg(ad)
10951 .arg(&mut *y0)
10952 .arg(&mut *y1)
10953 .arg(&mut *y2)
10954 .arg(&inf)
10955 .arg(&oo0)
10956 .arg(&oo1)
10957 .arg(&oo2)
10958 .arg(&r0)
10959 .arg(&r1)
10960 .arg(&r2);
10961 unsafe {
10962 b.launch(cfg)?;
10963 }
10964 Ok(true)
10965 }
10966
10967 pub fn matmul_q4_fused2(
10969 &self,
10970 w0: &crate::model::GpuTensor,
10971 w1: &crate::model::GpuTensor,
10972 aq: &CudaSlice<i8>,
10973 ad: &CudaSlice<f32>,
10974 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10975 use crate::model::GpuTensor;
10976 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
10977 match w {
10978 GpuTensor::Quant {
10979 qtype, row_bytes, ..
10980 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
10981 _ => None,
10982 }
10983 };
10984 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
10985 return Ok(None);
10986 };
10987 if w0.in_features() != w1.in_features() {
10988 return Ok(None);
10989 }
10990 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
10992 match w {
10993 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
10994 Some(m) => (m, true),
10995 None => (bytes, *rp),
10996 },
10997 _ => unreachable!(),
10998 }
10999 }
11000 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11001 if rp0 != rp1 {
11002 return Ok(None);
11003 }
11004 let rp = rp0;
11005 let rpb: u32 = 4;
11006 let mr1 = rp && Self::q40_mr1_on();
11008 let nb = |o: usize| {
11009 if mr1 {
11010 (o as u32).div_ceil(rpb)
11011 } else {
11012 (o as u32).div_ceil(2).div_ceil(rpb)
11013 }
11014 };
11015 let grid = nb(o0) + nb(o1);
11016 let mut y0 = self.alloc_uninit::<f32>(o0)?;
11017 let mut y1 = self.alloc_uninit::<f32>(o1)?;
11018 let f = self.func(if mr1 {
11019 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
11020 } else if rp {
11021 "qmatvec_q4_0_mmvq_fused2_rp"
11022 } else {
11023 "qmatvec_q4_0_mmvq_fused2"
11024 });
11025 let cfg = LaunchConfig {
11026 grid_dim: (grid, 1, 1),
11027 block_dim: (32, rpb, 1),
11028 shared_mem_bytes: 0,
11029 };
11030 let inf = w0.in_features() as i32;
11031 let (oo0, oo1) = (o0 as i32, o1 as i32);
11032 let (r0, r1) = (rb0 as i64, rb1 as i64);
11033 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11035 {
11036 use cudarc::driver::{DevicePtr, DevicePtrMut};
11037 let s = &self.gpu.stream();
11038 let (p0, _g0) = b0.device_ptr(s);
11039 let (p1, _g1) = b1.device_ptr(s);
11040 let (paq, _g2) = aq.device_ptr(s);
11041 let (pad, _g3) = ad.device_ptr(s);
11042 let (py0, _g4) = y0.device_ptr_mut(s);
11043 let (py1, _g5) = y1.device_ptr_mut(s);
11044 let mut ps = [
11045 &p0 as *const _ as *mut std::ffi::c_void,
11046 &p1 as *const _ as *mut _,
11047 &paq as *const _ as *mut _,
11048 &pad as *const _ as *mut _,
11049 &py0 as *const _ as *mut _,
11050 &py1 as *const _ as *mut _,
11051 &inf as *const _ as *mut _,
11052 &oo0 as *const _ as *mut _,
11053 &oo1 as *const _ as *mut _,
11054 &r0 as *const _ as *mut _,
11055 &r1 as *const _ as *mut _,
11056 ];
11057 unsafe {
11058 self.launch_pdl(
11059 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
11060 (grid, 1, 1),
11061 (32, rpb, 1),
11062 &mut ps,
11063 )?;
11064 }
11065 }
11066 return Ok(Some((y0, y1)));
11067 }
11068 let __s_b = self.gpu.stream();
11069 let mut b = __s_b.launch_builder(&f);
11070 b.arg(b0)
11071 .arg(b1)
11072 .arg(aq)
11073 .arg(ad)
11074 .arg(&mut y0)
11075 .arg(&mut y1)
11076 .arg(&inf)
11077 .arg(&oo0)
11078 .arg(&oo1)
11079 .arg(&r0)
11080 .arg(&r1);
11081 unsafe {
11082 b.launch(cfg)?;
11083 }
11084 Ok(Some((y0, y1)))
11085 }
11086
11087 pub fn matmul_q4_fused2_into(
11089 &self,
11090 w0: &crate::model::GpuTensor,
11091 w1: &crate::model::GpuTensor,
11092 aq: &CudaSlice<i8>,
11093 ad: &CudaSlice<f32>,
11094 y0: &mut CudaSlice<f32>,
11095 y1: &mut CudaSlice<f32>,
11096 ) -> Result<bool, Box<dyn std::error::Error>> {
11097 use crate::model::GpuTensor;
11098 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11099 match w {
11100 GpuTensor::Quant {
11101 qtype, row_bytes, ..
11102 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11103 _ => None,
11104 }
11105 };
11106 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
11107 return Ok(false);
11108 };
11109 if w0.in_features() != w1.in_features() {
11110 return Ok(false);
11111 }
11112 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11113 match w {
11114 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11115 Some(m) => (m, true),
11116 None => (bytes, *rp),
11117 },
11118 _ => unreachable!(),
11119 }
11120 }
11121 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11122 if rp0 != rp1 {
11123 return Ok(false);
11124 }
11125 let rp = rp0;
11126 let rpb: u32 = 4;
11127 let mr1 = rp && Self::q40_mr1_on();
11128 let nb = |o: usize| {
11129 if mr1 {
11130 (o as u32).div_ceil(rpb)
11131 } else {
11132 (o as u32).div_ceil(2).div_ceil(rpb)
11133 }
11134 };
11135 let grid = nb(o0) + nb(o1);
11136 debug_assert!(y0.len() >= o0 && y1.len() >= o1);
11137 let f = self.func(if mr1 {
11138 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
11139 } else if rp {
11140 "qmatvec_q4_0_mmvq_fused2_rp"
11141 } else {
11142 "qmatvec_q4_0_mmvq_fused2"
11143 });
11144 let cfg = LaunchConfig {
11145 grid_dim: (grid, 1, 1),
11146 block_dim: (32, rpb, 1),
11147 shared_mem_bytes: 0,
11148 };
11149 let inf = w0.in_features() as i32;
11150 let (oo0, oo1) = (o0 as i32, o1 as i32);
11151 let (r0, r1) = (rb0 as i64, rb1 as i64);
11152 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11154 use cudarc::driver::{DevicePtr, DevicePtrMut};
11155 let s = &self.gpu.stream();
11156 let (p0, _g0) = b0.device_ptr(s);
11157 let (p1, _g1) = b1.device_ptr(s);
11158 let (paq, _g2) = aq.device_ptr(s);
11159 let (pad, _g3) = ad.device_ptr(s);
11160 let (py0, _g4) = y0.device_ptr_mut(s);
11161 let (py1, _g5) = y1.device_ptr_mut(s);
11162 let mut ps = [
11163 &p0 as *const _ as *mut std::ffi::c_void,
11164 &p1 as *const _ as *mut _,
11165 &paq as *const _ as *mut _,
11166 &pad as *const _ as *mut _,
11167 &py0 as *const _ as *mut _,
11168 &py1 as *const _ as *mut _,
11169 &inf as *const _ as *mut _,
11170 &oo0 as *const _ as *mut _,
11171 &oo1 as *const _ as *mut _,
11172 &r0 as *const _ as *mut _,
11173 &r1 as *const _ as *mut _,
11174 ];
11175 unsafe {
11176 self.launch_pdl(
11177 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
11178 (grid, 1, 1),
11179 (32, rpb, 1),
11180 &mut ps,
11181 )?;
11182 }
11183 return Ok(true);
11184 }
11185 let __s_b = self.gpu.stream();
11186 let mut b = __s_b.launch_builder(&f);
11187 b.arg(b0)
11188 .arg(b1)
11189 .arg(aq)
11190 .arg(ad)
11191 .arg(&mut *y0)
11192 .arg(&mut *y1)
11193 .arg(&inf)
11194 .arg(&oo0)
11195 .arg(&oo1)
11196 .arg(&r0)
11197 .arg(&r1);
11198 unsafe {
11199 b.launch(cfg)?;
11200 }
11201 Ok(true)
11202 }
11203
11204 pub fn matmul_q4_fused2_batched(
11209 &self,
11210 w0: &crate::model::GpuTensor,
11211 w1: &crate::model::GpuTensor,
11212 aq: &CudaSlice<i8>,
11213 ad: &CudaSlice<f32>,
11214 m: usize,
11215 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11216 use crate::model::GpuTensor;
11217 if m < 2 || m > 8 {
11218 return Ok(None);
11219 }
11220 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11221 match w {
11222 GpuTensor::Quant {
11223 qtype, row_bytes, ..
11224 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11225 _ => None,
11226 }
11227 };
11228 let (Some((rb0, o0)), Some((_rb1, o1))) = (q4(w0), q4(w1)) else {
11229 return Ok(None);
11230 };
11231 if w0.in_features() != w1.in_features() {
11232 return Ok(None);
11233 }
11234 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11235 match w {
11236 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11237 Some(mr) => (mr, true),
11238 None => (bytes, *rp),
11239 },
11240 _ => unreachable!(),
11241 }
11242 }
11243 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11244 if !rp0 || !rp1 {
11245 return Ok(None);
11246 }
11247 let mcols = Self::batched_mcols(m);
11248 let rpb: u32 = 4;
11249 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
11250 let grid = nb(o0) + nb(o1);
11251 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11252 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11253 let f = self.func(match mcols {
11254 2 => "qmatvec_q4_0_mmvq_b2_f2_rp",
11255 4 => "qmatvec_q4_0_mmvq_b4_f2_rp",
11256 _ => "qmatvec_q4_0_mmvq_b8_f2_rp",
11257 });
11258 let cfg = LaunchConfig {
11259 grid_dim: (grid, 1, 1),
11260 block_dim: (32, rpb, 1),
11261 shared_mem_bytes: 0,
11262 };
11263 let inf = w0.in_features() as i32;
11264 let (oo0, oo1, mi) = (o0 as i32, o1 as i32, m as i32);
11265 let rb = rb0 as i64;
11266 let __s_b = self.gpu.stream();
11267 let mut b = __s_b.launch_builder(&f);
11268 b.arg(b0)
11269 .arg(b1)
11270 .arg(aq)
11271 .arg(ad)
11272 .arg(&mut y0)
11273 .arg(&mut y1)
11274 .arg(&inf)
11275 .arg(&oo0)
11276 .arg(&oo1)
11277 .arg(&mi)
11278 .arg(&rb);
11279 unsafe {
11280 b.launch(cfg)?;
11281 }
11282 Ok(Some((y0, y1)))
11283 }
11284
11285 #[allow(clippy::too_many_arguments)]
11288 pub fn matmul_q4_fused3_batched(
11289 &self,
11290 w0: &crate::model::GpuTensor,
11291 w1: &crate::model::GpuTensor,
11292 w2: &crate::model::GpuTensor,
11293 aq: &CudaSlice<i8>,
11294 ad: &CudaSlice<f32>,
11295 m: usize,
11296 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11297 {
11298 use crate::model::GpuTensor;
11299 if m < 2 || m > 8 {
11300 return Ok(None);
11301 }
11302 let q4 = |w: &GpuTensor| -> Option<usize> {
11303 match w {
11304 GpuTensor::Quant { qtype, .. } if *qtype == QT_Q4_0 => Some(w.out_features()),
11305 _ => None,
11306 }
11307 };
11308 let (Some(o0), Some(o1), Some(o2)) = (q4(w0), q4(w1), q4(w2)) else {
11309 return Ok(None);
11310 };
11311 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
11312 return Ok(None);
11313 }
11314 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11315 match w {
11316 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11317 Some(mr) => (mr, true),
11318 None => (bytes, *rp),
11319 },
11320 _ => unreachable!(),
11321 }
11322 }
11323 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
11324 if !rp0 || !rp1 || !rp2 {
11325 return Ok(None);
11326 }
11327 let mcols = Self::batched_mcols(m);
11328 let rpb: u32 = 4;
11329 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
11330 let grid = nb(o0) + nb(o1) + nb(o2);
11331 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11332 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11333 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
11334 let f = self.func(match mcols {
11335 2 => "qmatvec_q4_0_mmvq_b2_f3_rp",
11336 4 => "qmatvec_q4_0_mmvq_b4_f3_rp",
11337 _ => "qmatvec_q4_0_mmvq_b8_f3_rp",
11338 });
11339 let cfg = LaunchConfig {
11340 grid_dim: (grid, 1, 1),
11341 block_dim: (32, rpb, 1),
11342 shared_mem_bytes: 0,
11343 };
11344 let inf = w0.in_features() as i32;
11345 let (oo0, oo1, oo2, mi) = (o0 as i32, o1 as i32, o2 as i32, m as i32);
11346 let rb = 0i64;
11347 let __s_b = self.gpu.stream();
11348 let mut b = __s_b.launch_builder(&f);
11349 b.arg(b0)
11350 .arg(b1)
11351 .arg(b2)
11352 .arg(aq)
11353 .arg(ad)
11354 .arg(&mut y0)
11355 .arg(&mut y1)
11356 .arg(&mut y2)
11357 .arg(&inf)
11358 .arg(&oo0)
11359 .arg(&oo1)
11360 .arg(&oo2)
11361 .arg(&mi)
11362 .arg(&rb);
11363 unsafe {
11364 b.launch(cfg)?;
11365 }
11366 Ok(Some((y0, y1, y2)))
11367 }
11368
11369 pub fn matmul_q8_fused3(
11370 &self,
11371 w0: &crate::model::GpuTensor,
11372 w1: &crate::model::GpuTensor,
11373 w2: &crate::model::GpuTensor,
11374 aq: &CudaSlice<i8>,
11375 ad: &CudaSlice<f32>,
11376 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11377 {
11378 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
11381 return Ok(Some(self.e4m3_fused3_core(
11382 p0.0,
11383 p1.0,
11384 p2.0,
11385 aq,
11386 ad,
11387 w0.in_features(),
11388 p0.1,
11389 p1.1,
11390 p2.1,
11391 p0.2,
11392 p0.3,
11393 p1.3,
11394 p2.3,
11395 )?));
11396 }
11397 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
11398 return Ok(None);
11399 };
11400 Ok(Some(self.q8_fused3_core(
11401 p0.0,
11402 p1.0,
11403 p2.0,
11404 aq,
11405 ad,
11406 w0.in_features(),
11407 p0.1,
11408 p1.1,
11409 p2.1,
11410 p0.2,
11411 )?))
11412 }
11413
11414 #[allow(clippy::too_many_arguments)]
11415 fn q8_fused3_core(
11416 &self,
11417 b0: &CudaSlice<u8>,
11418 b1: &CudaSlice<u8>,
11419 b2: &CudaSlice<u8>,
11420 aq: &CudaSlice<i8>,
11421 ad: &CudaSlice<f32>,
11422 in_f: usize,
11423 out0: usize,
11424 out1: usize,
11425 out2: usize,
11426 row_bytes: usize,
11427 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11428 const ROWS_PER_BLOCK: u32 = 4;
11429 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11430 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11431 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
11432 let f = self.func("qmatvec_q8_0_mmvq_fused3");
11433 let mut y0 = self.alloc_uninit::<f32>(out0)?;
11434 let mut y1 = self.alloc_uninit::<f32>(out1)?;
11435 let mut y2 = self.alloc_uninit::<f32>(out2)?;
11436 let cfg = LaunchConfig {
11437 grid_dim: (nb0 + nb1 + nb2, 1, 1),
11438 block_dim: (32, ROWS_PER_BLOCK, 1),
11439 shared_mem_bytes: 0,
11440 };
11441 let (inf, o0, o1, o2, rbl) = (
11442 in_f as i32,
11443 out0 as i32,
11444 out1 as i32,
11445 out2 as i32,
11446 row_bytes as i64,
11447 );
11448 let __s_b = self.gpu.stream();
11449 let mut b = __s_b.launch_builder(&f);
11450 b.arg(b0)
11451 .arg(b1)
11452 .arg(b2)
11453 .arg(aq)
11454 .arg(ad)
11455 .arg(&mut y0)
11456 .arg(&mut y1)
11457 .arg(&mut y2)
11458 .arg(&inf)
11459 .arg(&o0)
11460 .arg(&o1)
11461 .arg(&o2)
11462 .arg(&rbl);
11463 unsafe {
11464 b.launch(cfg)?;
11465 }
11466 Ok((y0, y1, y2))
11467 }
11468
11469 #[allow(clippy::too_many_arguments)]
11471 pub fn qmatvec_q8_fused3_raw(
11472 &self,
11473 b0: &CudaSlice<u8>,
11474 b1: &CudaSlice<u8>,
11475 b2: &CudaSlice<u8>,
11476 x: &CudaSlice<f32>,
11477 in_f: usize,
11478 out0: usize,
11479 out1: usize,
11480 out2: usize,
11481 row_bytes: usize,
11482 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11483 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
11484 self.q8_fused3_core(b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes)
11485 }
11486
11487 pub fn matmul_q8_fused2_t(
11498 &self,
11499 w0: &crate::model::GpuTensor,
11500 w1: &crate::model::GpuTensor,
11501 aq: &CudaSlice<i8>,
11502 ad: &CudaSlice<f32>,
11503 m: usize,
11504 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11505 if !(2..=8).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
11509 return Ok(None);
11510 }
11511 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
11514 if m > 4 && !Self::b8_enabled() {
11515 return Ok(None);
11516 }
11517 return Ok(Some(self.e4m3_fused2_t_core(
11518 p0.0,
11519 p1.0,
11520 aq,
11521 ad,
11522 m,
11523 w0.in_features(),
11524 p0.1,
11525 p1.1,
11526 p0.2,
11527 p0.3,
11528 p1.3,
11529 )?));
11530 }
11531 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
11532 return Ok(None);
11533 };
11534 Ok(Some(self.q8_fused2_t_core(
11535 p0.0,
11536 p1.0,
11537 aq,
11538 ad,
11539 m,
11540 w0.in_features(),
11541 p0.1,
11542 p1.1,
11543 p0.2,
11544 )?))
11545 }
11546
11547 #[allow(clippy::too_many_arguments)]
11548 fn q8_fused2_t_core(
11549 &self,
11550 b0: &CudaSlice<u8>,
11551 b1: &CudaSlice<u8>,
11552 aq: &CudaSlice<i8>,
11553 ad: &CudaSlice<f32>,
11554 m: usize,
11555 in_f: usize,
11556 out0: usize,
11557 out1: usize,
11558 row_bytes: usize,
11559 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11560 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11562 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11563 let f = self.func(match Self::batched_mcols(m) {
11564 2 => "qmatvec_q8_0_mmvq_fused2_b2",
11565 4 => "qmatvec_q8_0_mmvq_fused2_b4",
11566 _ => "qmatvec_q8_0_mmvq_fused2_b8",
11568 });
11569 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
11570 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
11571 let cfg = LaunchConfig {
11572 grid_dim: (nb0 + nb1, 1, 1),
11573 block_dim: (32, ROWS_PER_BLOCK, 1),
11574 shared_mem_bytes: 0,
11575 };
11576 let (inf, o0, o1, mi, rbl) = (
11577 in_f as i32,
11578 out0 as i32,
11579 out1 as i32,
11580 m as i32,
11581 row_bytes as i64,
11582 );
11583 let __s_b = self.gpu.stream();
11584 let mut b = __s_b.launch_builder(&f);
11585 b.arg(b0)
11586 .arg(b1)
11587 .arg(aq)
11588 .arg(ad)
11589 .arg(&mut y0)
11590 .arg(&mut y1)
11591 .arg(&inf)
11592 .arg(&o0)
11593 .arg(&o1)
11594 .arg(&mi)
11595 .arg(&rbl);
11596 unsafe {
11597 b.launch(cfg)?;
11598 }
11599 Ok((y0, y1))
11600 }
11601
11602 #[allow(clippy::too_many_arguments)]
11605 pub fn qmatvec_q8_fused2_t_raw(
11606 &self,
11607 b0: &CudaSlice<u8>,
11608 b1: &CudaSlice<u8>,
11609 x: &CudaSlice<f32>,
11610 m: usize,
11611 in_f: usize,
11612 out0: usize,
11613 out1: usize,
11614 row_bytes: usize,
11615 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11616 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
11617 self.q8_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes)
11618 }
11619
11620 #[allow(clippy::too_many_arguments)]
11623 pub fn matmul_q8_fused3_t(
11624 &self,
11625 w0: &crate::model::GpuTensor,
11626 w1: &crate::model::GpuTensor,
11627 w2: &crate::model::GpuTensor,
11628 aq: &CudaSlice<i8>,
11629 ad: &CudaSlice<f32>,
11630 m: usize,
11631 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11632 {
11633 if !(2..=4).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
11634 return Ok(None);
11635 }
11636 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
11637 return Ok(Some(self.e4m3_fused3_t_core(
11638 p0.0,
11639 p1.0,
11640 p2.0,
11641 aq,
11642 ad,
11643 m,
11644 w0.in_features(),
11645 p0.1,
11646 p1.1,
11647 p2.1,
11648 p0.2,
11649 p0.3,
11650 p1.3,
11651 p2.3,
11652 )?));
11653 }
11654 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
11655 return Ok(None);
11656 };
11657 Ok(Some(self.q8_fused3_t_core(
11658 p0.0,
11659 p1.0,
11660 p2.0,
11661 aq,
11662 ad,
11663 m,
11664 w0.in_features(),
11665 p0.1,
11666 p1.1,
11667 p2.1,
11668 p0.2,
11669 )?))
11670 }
11671
11672 #[allow(clippy::too_many_arguments)]
11673 fn q8_fused3_t_core(
11674 &self,
11675 b0: &CudaSlice<u8>,
11676 b1: &CudaSlice<u8>,
11677 b2: &CudaSlice<u8>,
11678 aq: &CudaSlice<i8>,
11679 ad: &CudaSlice<f32>,
11680 m: usize,
11681 in_f: usize,
11682 out0: usize,
11683 out1: usize,
11684 out2: usize,
11685 row_bytes: usize,
11686 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11687 const ROWS_PER_BLOCK: u32 = 4;
11688 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11689 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11690 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
11691 let f = self.func(if Self::batched_mcols(m) == 2 {
11692 "qmatvec_q8_0_mmvq_fused3_b2"
11693 } else {
11694 "qmatvec_q8_0_mmvq_fused3_b4"
11695 });
11696 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
11697 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
11698 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
11699 let cfg = LaunchConfig {
11700 grid_dim: (nb0 + nb1 + nb2, 1, 1),
11701 block_dim: (32, ROWS_PER_BLOCK, 1),
11702 shared_mem_bytes: 0,
11703 };
11704 let (inf, o0, o1, o2, mi, rbl) = (
11705 in_f as i32,
11706 out0 as i32,
11707 out1 as i32,
11708 out2 as i32,
11709 m as i32,
11710 row_bytes as i64,
11711 );
11712 let __s_b = self.gpu.stream();
11713 let mut b = __s_b.launch_builder(&f);
11714 b.arg(b0)
11715 .arg(b1)
11716 .arg(b2)
11717 .arg(aq)
11718 .arg(ad)
11719 .arg(&mut y0)
11720 .arg(&mut y1)
11721 .arg(&mut y2)
11722 .arg(&inf)
11723 .arg(&o0)
11724 .arg(&o1)
11725 .arg(&o2)
11726 .arg(&mi)
11727 .arg(&rbl);
11728 unsafe {
11729 b.launch(cfg)?;
11730 }
11731 Ok((y0, y1, y2))
11732 }
11733
11734 #[allow(clippy::too_many_arguments)]
11736 pub fn qmatvec_q8_fused3_t_raw(
11737 &self,
11738 b0: &CudaSlice<u8>,
11739 b1: &CudaSlice<u8>,
11740 b2: &CudaSlice<u8>,
11741 x: &CudaSlice<f32>,
11742 m: usize,
11743 in_f: usize,
11744 out0: usize,
11745 out1: usize,
11746 out2: usize,
11747 row_bytes: usize,
11748 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11749 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
11750 self.q8_fused3_t_core(b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes)
11751 }
11752
11753 pub fn q8_ffn_fuse2_on(&self) -> bool {
11757 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11758 *ON.get_or_init(|| std::env::var("MEMRA_Q8_FFN_FUSE2").as_deref() != Ok("0"))
11759 }
11760
11761 #[allow(clippy::type_complexity)]
11767 fn q8_fused_params<'w, const N: usize>(
11768 &self,
11769 ws: &[&'w crate::model::GpuTensor; N],
11770 ) -> Option<[(&'w CudaSlice<u8>, usize, usize); N]> {
11771 use crate::model::GpuTensor;
11772 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
11773 return None;
11774 }
11775 if std::env::var("MEMRA_Q8_DUAL").is_ok_and(|v| v == "0") {
11776 return None;
11777 }
11778 let in_f = ws[0].in_features();
11779 let mut out: [Option<(&CudaSlice<u8>, usize, usize)>; N] = [None; N];
11780 for (i, w) in ws.iter().enumerate() {
11781 match w {
11782 GpuTensor::Quant {
11783 bytes,
11784 qtype,
11785 row_bytes,
11786 scale,
11787 ..
11788 } if *qtype == QT_Q8_0 && *scale == 1.0 && w.in_features() == in_f => {
11789 out[i] = Some((bytes, w.out_features(), *row_bytes))
11790 }
11791 _ => return None,
11792 }
11793 }
11794 Some(out.map(|o| o.unwrap()))
11795 }
11796
11797 pub fn e4m3_dual_on(&self) -> bool {
11800 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11801 *ON.get_or_init(|| std::env::var("MEMRA_E4M3_DUAL").as_deref() != Ok("0"))
11802 }
11803
11804 #[allow(clippy::type_complexity)]
11816 fn e4m3_fused_params<'w, const N: usize>(
11817 &self,
11818 ws: &[&'w crate::model::GpuTensor; N],
11819 ) -> Option<[(&'w CudaSlice<u8>, usize, usize, f32); N]> {
11820 use crate::model::GpuTensor;
11821 if !self.e4m3_dual_on() {
11822 return None;
11823 }
11824 let in_f = ws[0].in_features();
11825 let mut out: [Option<(&CudaSlice<u8>, usize, usize, f32)>; N] = [None; N];
11826 for (i, w) in ws.iter().enumerate() {
11827 match w {
11828 GpuTensor::Quant {
11829 bytes,
11830 qtype,
11831 row_bytes,
11832 scale,
11833 rp,
11834 rp4,
11835 ..
11836 } if *qtype == QT_F8_E4M3
11837 && w.in_features() == in_f
11838 && *row_bytes == in_f
11839 && !*rp
11840 && rp4.is_none() =>
11841 {
11842 out[i] = Some((bytes, w.out_features(), *row_bytes, *scale))
11843 }
11844 _ => return None,
11845 }
11846 }
11847 Some(out.map(|o| o.unwrap()))
11848 }
11849
11850 #[allow(clippy::too_many_arguments)]
11854 fn e4m3_fused2_core(
11855 &self,
11856 b0: &CudaSlice<u8>,
11857 b1: &CudaSlice<u8>,
11858 aq: &CudaSlice<i8>,
11859 ad: &CudaSlice<f32>,
11860 in_f: usize,
11861 out0: usize,
11862 out1: usize,
11863 row_bytes: usize,
11864 ws0: f32,
11865 ws1: f32,
11866 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11867 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11869 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11870 let f = self.func("qmatvec_e4m3_mmvq_fused2");
11871 let mut y0 = self.alloc_uninit::<f32>(out0)?;
11872 let mut y1 = self.alloc_uninit::<f32>(out1)?;
11873 let cfg = LaunchConfig {
11874 grid_dim: (nb0 + nb1, 1, 1),
11875 block_dim: (32, ROWS_PER_BLOCK, 1),
11876 shared_mem_bytes: 0,
11877 };
11878 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
11879 let __s_b = self.gpu.stream();
11880 let mut b = __s_b.launch_builder(&f);
11881 b.arg(b0)
11882 .arg(b1)
11883 .arg(aq)
11884 .arg(ad)
11885 .arg(&mut y0)
11886 .arg(&mut y1)
11887 .arg(&inf)
11888 .arg(&o0)
11889 .arg(&o1)
11890 .arg(&rbl)
11891 .arg(&ws0)
11892 .arg(&ws1);
11893 unsafe {
11894 b.launch(cfg)?;
11895 }
11896 Ok((y0, y1))
11897 }
11898
11899 #[allow(clippy::too_many_arguments)]
11901 fn e4m3_fused3_core(
11902 &self,
11903 b0: &CudaSlice<u8>,
11904 b1: &CudaSlice<u8>,
11905 b2: &CudaSlice<u8>,
11906 aq: &CudaSlice<i8>,
11907 ad: &CudaSlice<f32>,
11908 in_f: usize,
11909 out0: usize,
11910 out1: usize,
11911 out2: usize,
11912 row_bytes: usize,
11913 ws0: f32,
11914 ws1: f32,
11915 ws2: f32,
11916 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11917 const ROWS_PER_BLOCK: u32 = 4;
11918 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11919 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11920 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
11921 let f = self.func("qmatvec_e4m3_mmvq_fused3");
11922 let mut y0 = self.alloc_uninit::<f32>(out0)?;
11923 let mut y1 = self.alloc_uninit::<f32>(out1)?;
11924 let mut y2 = self.alloc_uninit::<f32>(out2)?;
11925 let cfg = LaunchConfig {
11926 grid_dim: (nb0 + nb1 + nb2, 1, 1),
11927 block_dim: (32, ROWS_PER_BLOCK, 1),
11928 shared_mem_bytes: 0,
11929 };
11930 let (inf, o0, o1, o2, rbl) = (
11931 in_f as i32,
11932 out0 as i32,
11933 out1 as i32,
11934 out2 as i32,
11935 row_bytes as i64,
11936 );
11937 let __s_b = self.gpu.stream();
11938 let mut b = __s_b.launch_builder(&f);
11939 b.arg(b0)
11940 .arg(b1)
11941 .arg(b2)
11942 .arg(aq)
11943 .arg(ad)
11944 .arg(&mut y0)
11945 .arg(&mut y1)
11946 .arg(&mut y2)
11947 .arg(&inf)
11948 .arg(&o0)
11949 .arg(&o1)
11950 .arg(&o2)
11951 .arg(&rbl)
11952 .arg(&ws0)
11953 .arg(&ws1)
11954 .arg(&ws2);
11955 unsafe {
11956 b.launch(cfg)?;
11957 }
11958 Ok((y0, y1, y2))
11959 }
11960
11961 #[allow(clippy::too_many_arguments)]
11965 fn e4m3_fused2_t_core(
11966 &self,
11967 b0: &CudaSlice<u8>,
11968 b1: &CudaSlice<u8>,
11969 aq: &CudaSlice<i8>,
11970 ad: &CudaSlice<f32>,
11971 m: usize,
11972 in_f: usize,
11973 out0: usize,
11974 out1: usize,
11975 row_bytes: usize,
11976 ws0: f32,
11977 ws1: f32,
11978 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11979 const ROWS_PER_BLOCK: u32 = 4;
11980 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11981 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11982 let f = self.func(match Self::batched_mcols(m) {
11983 2 => "qmatvec_e4m3_mmvq_fused2_b2",
11984 4 => "qmatvec_e4m3_mmvq_fused2_b4",
11985 _ => "qmatvec_e4m3_mmvq_fused2_b8",
11986 });
11987 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
11988 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
11989 let cfg = LaunchConfig {
11990 grid_dim: (nb0 + nb1, 1, 1),
11991 block_dim: (32, ROWS_PER_BLOCK, 1),
11992 shared_mem_bytes: 0,
11993 };
11994 let (inf, o0, o1, mi, rbl) = (
11995 in_f as i32,
11996 out0 as i32,
11997 out1 as i32,
11998 m as i32,
11999 row_bytes as i64,
12000 );
12001 let __s_b = self.gpu.stream();
12002 let mut b = __s_b.launch_builder(&f);
12003 b.arg(b0)
12004 .arg(b1)
12005 .arg(aq)
12006 .arg(ad)
12007 .arg(&mut y0)
12008 .arg(&mut y1)
12009 .arg(&inf)
12010 .arg(&o0)
12011 .arg(&o1)
12012 .arg(&mi)
12013 .arg(&rbl);
12014 unsafe {
12015 b.launch(cfg)?;
12016 }
12017 if ws0 != 1.0 {
12018 self.scale_inplace(&mut y0, ws0, m * out0)?;
12019 }
12020 if ws1 != 1.0 {
12021 self.scale_inplace(&mut y1, ws1, m * out1)?;
12022 }
12023 Ok((y0, y1))
12024 }
12025
12026 #[allow(clippy::too_many_arguments)]
12028 fn e4m3_fused3_t_core(
12029 &self,
12030 b0: &CudaSlice<u8>,
12031 b1: &CudaSlice<u8>,
12032 b2: &CudaSlice<u8>,
12033 aq: &CudaSlice<i8>,
12034 ad: &CudaSlice<f32>,
12035 m: usize,
12036 in_f: usize,
12037 out0: usize,
12038 out1: usize,
12039 out2: usize,
12040 row_bytes: usize,
12041 ws0: f32,
12042 ws1: f32,
12043 ws2: f32,
12044 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12045 const ROWS_PER_BLOCK: u32 = 4;
12046 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12047 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12048 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12049 let f = self.func(if Self::batched_mcols(m) == 2 {
12050 "qmatvec_e4m3_mmvq_fused3_b2"
12051 } else {
12052 "qmatvec_e4m3_mmvq_fused3_b4"
12053 });
12054 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12055 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12056 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
12057 let cfg = LaunchConfig {
12058 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12059 block_dim: (32, ROWS_PER_BLOCK, 1),
12060 shared_mem_bytes: 0,
12061 };
12062 let (inf, o0, o1, o2, mi, rbl) = (
12063 in_f as i32,
12064 out0 as i32,
12065 out1 as i32,
12066 out2 as i32,
12067 m as i32,
12068 row_bytes as i64,
12069 );
12070 let __s_b = self.gpu.stream();
12071 let mut b = __s_b.launch_builder(&f);
12072 b.arg(b0)
12073 .arg(b1)
12074 .arg(b2)
12075 .arg(aq)
12076 .arg(ad)
12077 .arg(&mut y0)
12078 .arg(&mut y1)
12079 .arg(&mut y2)
12080 .arg(&inf)
12081 .arg(&o0)
12082 .arg(&o1)
12083 .arg(&o2)
12084 .arg(&mi)
12085 .arg(&rbl);
12086 unsafe {
12087 b.launch(cfg)?;
12088 }
12089 if ws0 != 1.0 {
12090 self.scale_inplace(&mut y0, ws0, m * out0)?;
12091 }
12092 if ws1 != 1.0 {
12093 self.scale_inplace(&mut y1, ws1, m * out1)?;
12094 }
12095 if ws2 != 1.0 {
12096 self.scale_inplace(&mut y2, ws2, m * out2)?;
12097 }
12098 Ok((y0, y1, y2))
12099 }
12100
12101 pub fn qmatvec_e4m3_blk_mmvq(
12111 &self,
12112 bytes: &CudaSlice<u8>,
12113 aq: &CudaSlice<i8>,
12114 ad: &CudaSlice<f32>,
12115 scales: &CudaSlice<f32>,
12116 m: usize,
12117 in_f: usize,
12118 out_f: usize,
12119 row_bytes: usize,
12120 scale_cols: usize,
12121 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12122 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_e4m3_blk_mmvq_into(
12124 bytes, aq, ad, scales, m, in_f, out_f, row_bytes, scale_cols, &mut y,
12125 )?;
12126 Ok(y)
12127 }
12128
12129 #[allow(clippy::too_many_arguments)]
12131 pub fn qmatvec_e4m3_blk_mmvq_into(
12132 &self,
12133 bytes: &CudaSlice<u8>,
12134 aq: &CudaSlice<i8>,
12135 ad: &CudaSlice<f32>,
12136 scales: &CudaSlice<f32>,
12137 m: usize,
12138 in_f: usize,
12139 out_f: usize,
12140 row_bytes: usize,
12141 scale_cols: usize,
12142 y: &mut CudaSlice<f32>,
12143 ) -> Result<(), Box<dyn std::error::Error>> {
12144 const ROWS_PER_BLOCK: u32 = 4; let f = self.func("qmatvec_e4m3_blk_mmvq");
12146 let cfg = LaunchConfig {
12147 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), m as u32, 1),
12148 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
12151 let (inf, outf, mi, rb, sc) = (
12152 in_f as i32,
12153 out_f as i32,
12154 m as i32,
12155 row_bytes as i64,
12156 scale_cols as i32,
12157 );
12158 let __s_b = self.gpu.stream();
12159 let mut b = __s_b.launch_builder(&f);
12160 b.arg(bytes)
12161 .arg(aq)
12162 .arg(ad)
12163 .arg(scales)
12164 .arg(&mut *y)
12165 .arg(&inf)
12166 .arg(&outf)
12167 .arg(&mi)
12168 .arg(&rb)
12169 .arg(&sc);
12170 unsafe {
12171 b.launch(cfg)?;
12172 }
12173 Ok(())
12174 }
12175
12176 #[allow(clippy::too_many_arguments)]
12182 pub fn qmatvec_e4m3_blk_mmvq_batched(
12183 &self,
12184 bytes: &CudaSlice<u8>,
12185 aq: &CudaSlice<i8>,
12186 ad: &CudaSlice<f32>,
12187 scales: &CudaSlice<f32>,
12188 m: usize,
12189 in_f: usize,
12190 out_f: usize,
12191 row_bytes: usize,
12192 scale_cols: usize,
12193 mcols: usize,
12194 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12195 const ROWS_PER_BLOCK: u32 = 4; debug_assert!(mcols >= m, "blk batched: mcols {mcols} < m {m}");
12197 let name = match mcols {
12198 2 => "qmatvec_e4m3_blk_mmvq_b2",
12199 4 => "qmatvec_e4m3_blk_mmvq_b4",
12200 8 => "qmatvec_e4m3_blk_mmvq_b8",
12201 16 => "qmatvec_e4m3_blk_mmvq_b16",
12202 _ => {
12203 return Err(
12204 format!("qmatvec_e4m3_blk_mmvq_batched: no kernel for mcols {mcols}").into(),
12205 );
12206 }
12207 };
12208 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
12209 let f = self.func(name);
12210 let cfg = LaunchConfig {
12211 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), 1, 1),
12212 block_dim: (32, ROWS_PER_BLOCK, 1),
12213 shared_mem_bytes: 0,
12214 };
12215 let (inf, outf, mi, rb, sc) = (
12216 in_f as i32,
12217 out_f as i32,
12218 m as i32,
12219 row_bytes as i64,
12220 scale_cols as i32,
12221 );
12222 let __s_b = self.gpu.stream();
12223 let mut b = __s_b.launch_builder(&f);
12224 b.arg(bytes)
12225 .arg(aq)
12226 .arg(ad)
12227 .arg(scales)
12228 .arg(&mut y)
12229 .arg(&inf)
12230 .arg(&outf)
12231 .arg(&mi)
12232 .arg(&rb)
12233 .arg(&sc);
12234 unsafe {
12235 b.launch(cfg)?;
12236 }
12237 Ok(y)
12238 }
12239
12240 #[allow(clippy::too_many_arguments)]
12243 pub fn qmatvec_e4m3_blk_batched_raw(
12244 &self,
12245 bytes: &CudaSlice<u8>,
12246 x: &CudaSlice<f32>,
12247 scales: &CudaSlice<f32>,
12248 m: usize,
12249 in_f: usize,
12250 out_f: usize,
12251 row_bytes: usize,
12252 scale_cols: usize,
12253 mcols: usize,
12254 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12255 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12256 self.qmatvec_e4m3_blk_mmvq_batched(
12257 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols, mcols,
12258 )
12259 }
12260
12261 #[allow(clippy::too_many_arguments)]
12264 pub fn qmatvec_e4m3_blk_mmvq_raw(
12265 &self,
12266 bytes: &CudaSlice<u8>,
12267 x: &CudaSlice<f32>,
12268 scales: &CudaSlice<f32>,
12269 m: usize,
12270 in_f: usize,
12271 out_f: usize,
12272 row_bytes: usize,
12273 scale_cols: usize,
12274 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12275 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12276 self.qmatvec_e4m3_blk_mmvq(
12277 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols,
12278 )
12279 }
12280
12281 #[allow(clippy::too_many_arguments)]
12284 pub fn qmatvec_e4m3_fused2_raw(
12285 &self,
12286 b0: &CudaSlice<u8>,
12287 b1: &CudaSlice<u8>,
12288 x: &CudaSlice<f32>,
12289 in_f: usize,
12290 out0: usize,
12291 out1: usize,
12292 row_bytes: usize,
12293 ws0: f32,
12294 ws1: f32,
12295 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12296 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
12297 self.e4m3_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes, ws0, ws1)
12298 }
12299
12300 #[allow(clippy::too_many_arguments)]
12301 pub fn qmatvec_e4m3_fused3_raw(
12302 &self,
12303 b0: &CudaSlice<u8>,
12304 b1: &CudaSlice<u8>,
12305 b2: &CudaSlice<u8>,
12306 x: &CudaSlice<f32>,
12307 in_f: usize,
12308 out0: usize,
12309 out1: usize,
12310 out2: usize,
12311 row_bytes: usize,
12312 ws0: f32,
12313 ws1: f32,
12314 ws2: f32,
12315 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12316 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
12317 self.e4m3_fused3_core(
12318 b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
12319 )
12320 }
12321
12322 #[allow(clippy::too_many_arguments)]
12323 pub fn qmatvec_e4m3_fused2_t_raw(
12324 &self,
12325 b0: &CudaSlice<u8>,
12326 b1: &CudaSlice<u8>,
12327 x: &CudaSlice<f32>,
12328 m: usize,
12329 in_f: usize,
12330 out0: usize,
12331 out1: usize,
12332 row_bytes: usize,
12333 ws0: f32,
12334 ws1: f32,
12335 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12336 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12337 self.e4m3_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes, ws0, ws1)
12338 }
12339
12340 #[allow(clippy::too_many_arguments)]
12341 pub fn qmatvec_e4m3_fused3_t_raw(
12342 &self,
12343 b0: &CudaSlice<u8>,
12344 b1: &CudaSlice<u8>,
12345 b2: &CudaSlice<u8>,
12346 x: &CudaSlice<f32>,
12347 m: usize,
12348 in_f: usize,
12349 out0: usize,
12350 out1: usize,
12351 out2: usize,
12352 row_bytes: usize,
12353 ws0: f32,
12354 ws1: f32,
12355 ws2: f32,
12356 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12357 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12358 self.e4m3_fused3_t_core(
12359 b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
12360 )
12361 }
12362
12363 fn try_e4m3_blk_pre(
12374 &self,
12375 w: &crate::model::GpuTensor,
12376 aq: &CudaSlice<i8>,
12377 ad: &CudaSlice<f32>,
12378 m: usize,
12379 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
12380 use crate::model::GpuTensor;
12381 if let GpuTensor::Quant {
12382 bytes,
12383 qtype,
12384 row_bytes,
12385 blk: Some(g),
12386 ..
12387 } = w
12388 {
12389 if *qtype == QT_F8_E4M3_BLK {
12390 if (2..=16).contains(&m)
12396 && std::env::var("MEMRA_NO_BATCHED").is_err()
12397 && (m <= 4 || Self::b8_enabled())
12398 {
12399 let mcols = Self::batched_mcols(m);
12400 return Ok(Some(self.qmatvec_e4m3_blk_mmvq_batched(
12401 bytes,
12402 aq,
12403 ad,
12404 &g.scales,
12405 m,
12406 w.in_features(),
12407 w.out_features(),
12408 *row_bytes,
12409 g.cols,
12410 mcols,
12411 )?));
12412 }
12413 return Ok(Some(self.qmatvec_e4m3_blk_mmvq(
12414 bytes,
12415 aq,
12416 ad,
12417 &g.scales,
12418 m,
12419 w.in_features(),
12420 w.out_features(),
12421 *row_bytes,
12422 g.cols,
12423 )?));
12424 }
12425 }
12426 Ok(None)
12427 }
12428
12429 fn try_e4m3_blk_prefill(
12476 &self,
12477 w: &crate::model::GpuTensor,
12478 x: &CudaSlice<f32>,
12479 m: usize,
12480 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
12481 use crate::model::GpuTensor;
12482 let GpuTensor::Quant {
12483 bytes,
12484 qtype,
12485 blk: Some(g),
12486 ..
12487 } = w
12488 else {
12489 return Ok(None);
12490 };
12491 if *qtype != QT_F8_E4M3_BLK {
12492 return Ok(None);
12493 }
12494 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
12499 return Ok(Some(y));
12500 }
12501 let (in_f, out_f) = (w.in_features(), w.out_features());
12502 let slab = self.fp8_blk_dequant_q8_0_dev(bytes, &g.scales, out_f, in_f)?;
12503 let tmp = GpuTensor::Quant {
12504 bytes: slab,
12505 qtype: QT_Q8_0,
12506 row_bytes: in_f / 32 * 34,
12507 ne: vec![in_f as u64, out_f as u64],
12508 scale: 1.0,
12509 rp: false,
12510 #[cfg(memra_cutlass)]
12511 cutlass: None,
12512 fp8: None,
12513 blk: None,
12514 f16: None,
12515 rp4: None,
12516 };
12517 Ok(Some(self.matmul(&tmp, x, m)?))
12519 }
12520
12521 pub fn matmul_pre_noscale(
12522 &self,
12523 w: &crate::model::GpuTensor,
12524 aq: &CudaSlice<i8>,
12525 ad: &CudaSlice<f32>,
12526 m: usize,
12527 ) -> Result<Option<(CudaSlice<f32>, f32)>, Box<dyn std::error::Error>> {
12528 use crate::model::GpuTensor;
12529 if m == 1 {
12533 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
12534 return Ok(Some((y, 1.0)));
12535 }
12536 }
12537 if m != 1 || !self.uses_q8_1_fast(w) {
12539 return Ok(None);
12540 }
12541 let in_f = w.in_features();
12542 let out_f = w.out_features();
12543 let (bytes, qtype, row_bytes, scale, rp) = match w {
12544 GpuTensor::Quant {
12545 bytes,
12546 qtype,
12547 row_bytes,
12548 scale,
12549 rp,
12550 ..
12551 } => (bytes, *qtype, *row_bytes, *scale, *rp),
12552 _ => return Ok(None),
12553 };
12554 if self.mmvq_supports(qtype) {
12556 let (mbytes, mrp) = match w {
12558 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
12559 _ => (bytes, rp),
12560 };
12561 let y = self.qmatvec_mmvq(
12562 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, 1.0, mrp,
12563 )?;
12564 return Ok(Some((y, scale)));
12565 }
12566 let name = match qtype {
12568 QT_Q8_0 => "qmatvec_q8_0_dp4a",
12569 QT_Q4_K => "qmatvec_q4_K_dp4a",
12570 QT_Q6_K => "qmatvec_q6_K_dp4a",
12571 QT_Q5_K => "qmatvec_q5_K_dp4a",
12572 QT_Q3_K => "qmatvec_q3_K_dp4a",
12573 QT_NVFP4 => {
12574 if rp {
12575 "qmatvec_nvfp4_dp4a_rp"
12576 } else {
12577 "qmatvec_nvfp4_dp4a"
12578 }
12579 }
12580 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
12581 _ => return Ok(None),
12582 };
12583 let f = self.func(name);
12584 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
12585 let cfg = LaunchConfig {
12586 grid_dim: (out_f as u32, m as u32, 1),
12587 block_dim: (128, 1, 1),
12588 shared_mem_bytes: 0,
12589 };
12590 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
12591 let __s_b = self.gpu.stream();
12592 let mut b = __s_b.launch_builder(&f);
12593 b.arg(bytes)
12594 .arg(aq)
12595 .arg(ad)
12596 .arg(&mut y)
12597 .arg(&inf)
12598 .arg(&outf)
12599 .arg(&mi)
12600 .arg(&rb);
12601 unsafe {
12602 b.launch(cfg)?;
12603 }
12604 Ok(Some((y, scale)))
12605 }
12606
12607 pub fn mmvq_supports(&self, qtype: i32) -> bool {
12610 if qtype == QT_F8_E4M3 {
12615 return true;
12616 }
12617 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
12618 return false;
12619 }
12620 matches!(
12621 qtype,
12622 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_Q4_0
12623 )
12624 }
12625
12626 pub fn qmatvec_mmvq(
12631 &self,
12632 bytes: &CudaSlice<u8>,
12633 aq: &CudaSlice<i8>,
12634 ad: &CudaSlice<f32>,
12635 m: usize,
12636 in_f: usize,
12637 out_f: usize,
12638 qtype: i32,
12639 row_bytes: usize,
12640 scale: f32,
12641 rp: bool,
12642 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12643 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_mmvq_into(
12645 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp, &mut y,
12646 )?;
12647 Ok(y)
12648 }
12649
12650 #[allow(clippy::too_many_arguments)]
12652 pub fn qmatvec_mmvq_into(
12653 &self,
12654 bytes: &CudaSlice<u8>,
12655 aq: &CudaSlice<i8>,
12656 ad: &CudaSlice<f32>,
12657 m: usize,
12658 in_f: usize,
12659 out_f: usize,
12660 qtype: i32,
12661 row_bytes: usize,
12662 scale: f32,
12663 rp: bool,
12664 y: &mut CudaSlice<f32>,
12665 ) -> Result<(), Box<dyn std::error::Error>> {
12666 debug_assert!(y.len() >= m * out_f);
12667 const ROWS_PER_BLOCK: u32 = 4; if qtype == QT_Q8_0
12673 && rp
12674 && m == 1
12675 && out_f >= 64
12676 && (out_f as u32).div_ceil(ROWS_PER_BLOCK) < 4 * self.sm_count() as u32
12677 && {
12678 static G2: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12679 *G2.get_or_init(|| std::env::var("MEMRA_Q80_G2").as_deref() != Ok("0"))
12680 }
12681 {
12682 let f = self.func("qmatvec_q8_0_mmvq_rp_g2");
12683 let cfg = LaunchConfig {
12684 grid_dim: ((out_f as u32).div_ceil(2), 1, 1),
12685 block_dim: (32, 2, 1),
12686 shared_mem_bytes: 0,
12687 };
12688 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, row_bytes as i64);
12689 let __s_b = self.gpu.stream();
12690 let mut b = __s_b.launch_builder(&f);
12691 b.arg(bytes)
12692 .arg(aq)
12693 .arg(ad)
12694 .arg(&mut *y)
12695 .arg(&inf)
12696 .arg(&outf)
12697 .arg(&mi)
12698 .arg(&rb);
12699 unsafe {
12700 b.launch(cfg)?;
12701 }
12702 if scale != 1.0 {
12703 self.scale_inplace(y, scale, out_f)?;
12704 }
12705 return Ok(());
12706 }
12707 let mut mr: u32 = if m == 1 && (qtype == QT_NVFP4 || qtype == QT_Q5_K) {
12716 2
12717 } else {
12718 1
12719 };
12720 if m == 1 && qtype == QT_Q4_0 {
12725 static Q40MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
12726 mr = *Q40MR.get_or_init(|| {
12729 std::env::var("MEMRA_Q40_MR")
12730 .ok()
12731 .and_then(|v| v.parse().ok())
12732 .unwrap_or(1)
12733 });
12734 }
12735 let q5_mode = std::env::var("MEMRA_Q5K_ISSUE").ok();
12746 let q5_force = q5_mode.as_deref() == Some("2");
12747 let q5_il = qtype == QT_Q5_K
12750 && m == 1
12751 && (q5_force || q5_mode.as_deref().map(|v| v != "0").unwrap_or(true));
12752 if q5_il && !q5_force && out_f > 65536 {
12753 mr = 1;
12754 }
12755 if qtype == QT_Q4_0 && rp && mr != 1 {
12758 mr = 2;
12759 }
12760 if qtype == QT_Q8_0 && rp {
12764 static Q80MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
12765 mr = *Q80MR.get_or_init(|| {
12766 std::env::var("MEMRA_Q80_MR")
12767 .ok()
12768 .and_then(|v| v.parse().ok())
12769 .unwrap_or(1)
12770 });
12771 }
12772 let name = match (qtype, mr, rp) {
12773 (QT_NVFP4, 2, false) => "qmatvec_nvfp4_mmvq_mr2",
12774 (QT_NVFP4, 2, true) => "qmatvec_nvfp4_mmvq_mr2_rp",
12775 (QT_NVFP4, _, true) => "qmatvec_nvfp4_mmvq_rp",
12776 (QT_Q4_0, 1, true) => "qmatvec_q4_0_mmvq_rp",
12777 (QT_Q4_0, _, true) => "qmatvec_q4_0_mmvq_mr2_rp",
12778 (QT_Q5_K, 2, _) => {
12779 if q5_il {
12780 "qmatvec_q5_K_mmvq_mr2_il"
12781 } else {
12782 "qmatvec_q5_K_mmvq_mr2"
12783 }
12784 }
12785 (QT_Q8_0, 2, true) => "qmatvec_q8_0_mmvq_mr2_rp",
12786 (QT_Q8_0, _, true)
12791 if in_f % 1024 == 0 && {
12792 static CA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12793 *CA.get_or_init(|| std::env::var("MEMRA_Q80_CA").as_deref() == Ok("1"))
12794 } =>
12795 {
12796 "qmatvec_q8_0_mmvq_rpca"
12797 }
12798 (QT_Q8_0, _, true) => "qmatvec_q8_0_mmvq_rp",
12799 (QT_Q8_0, _, _) => "qmatvec_q8_0_mmvq",
12800 (QT_Q4_K, _, true) => "qmatvec_q4_K_mmvq_rp",
12804 (QT_Q6_K, _, true) => "qmatvec_q6_K_mmvq_rp",
12805 (QT_Q4_K, _, _) => "qmatvec_q4_K_mmvq",
12806 (QT_Q4_0, 2, false) => "qmatvec_q4_0_mmvq_mr2",
12807 (QT_Q4_0, _, false) => "qmatvec_q4_0_mmvq",
12808 (QT_Q5_K, _, _) => {
12809 if q5_il {
12810 "qmatvec_q5_K_mmvq_il"
12811 } else {
12812 "qmatvec_q5_K_mmvq"
12813 }
12814 }
12815 (QT_Q6_K, _, _) => "qmatvec_q6_K_mmvq",
12816 (QT_NVFP4, _, false) => "qmatvec_nvfp4_mmvq",
12817 (QT_F8_E4M3, _, _) => "qmatvec_e4m3_mmvq",
12818 _ => panic!("qmatvec_mmvq: qtype {qtype} has no MMVQ kernel"),
12819 };
12820 let f = self.func(name);
12821 let rows_per_block = ROWS_PER_BLOCK * mr;
12823 let cfg = LaunchConfig {
12824 grid_dim: (
12825 (out_f as u32 + rows_per_block - 1) / rows_per_block,
12826 m as u32,
12827 1,
12828 ),
12829 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
12832 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
12833 let __s_b = self.gpu.stream();
12834 let mut b = __s_b.launch_builder(&f);
12835 if qtype == QT_NVFP4 || qtype == QT_F8_E4M3 {
12840 b.arg(bytes)
12841 .arg(aq)
12842 .arg(ad)
12843 .arg(&mut *y)
12844 .arg(&inf)
12845 .arg(&outf)
12846 .arg(&mi)
12847 .arg(&rb)
12848 .arg(&scale);
12849 unsafe {
12850 b.launch(cfg)?;
12851 }
12852 } else if Self::pdl_on()
12853 && Self::pdl_mmvq_on()
12854 && matches!(
12855 name,
12856 "qmatvec_q4_0_mmvq_rp" | "qmatvec_q6_K_mmvq" | "qmatvec_q6_K_mmvq_rp"
12857 )
12858 {
12859 {
12863 use cudarc::driver::{DevicePtr, DevicePtrMut};
12864 let s = &self.gpu.stream();
12865 let (pw, _g0) = bytes.device_ptr(s);
12866 let (paq, _g1) = aq.device_ptr(s);
12867 let (pad, _g2) = ad.device_ptr(s);
12868 let (py, _g3) = y.device_ptr_mut(s);
12869 let mut ps = [
12870 &pw as *const _ as *mut std::ffi::c_void,
12871 &paq as *const _ as *mut _,
12872 &pad as *const _ as *mut _,
12873 &py as *const _ as *mut _,
12874 &inf as *const _ as *mut _,
12875 &outf as *const _ as *mut _,
12876 &mi as *const _ as *mut _,
12877 &rb as *const _ as *mut _,
12878 ];
12879 unsafe {
12880 self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
12881 }
12882 }
12883 if scale != 1.0 {
12884 self.scale_inplace(y, scale, m * out_f)?;
12885 }
12886 } else {
12887 b.arg(bytes)
12888 .arg(aq)
12889 .arg(ad)
12890 .arg(&mut *y)
12891 .arg(&inf)
12892 .arg(&outf)
12893 .arg(&mi)
12894 .arg(&rb);
12895 unsafe {
12896 b.launch(cfg)?;
12897 }
12898 if scale != 1.0 {
12899 self.scale_inplace(y, scale, m * out_f)?;
12900 }
12901 }
12902 Ok(())
12903 }
12904
12905 pub fn qmatvec_mmvq_raw(
12909 &self,
12910 bytes: &CudaSlice<u8>,
12911 x: &CudaSlice<f32>,
12912 m: usize,
12913 in_f: usize,
12914 out_f: usize,
12915 qtype: i32,
12916 row_bytes: usize,
12917 rp: bool,
12918 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12919 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12920 self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, 1.0, rp)
12921 }
12922
12923 pub fn batched_supports(&self, qtype: i32) -> bool {
12927 matches!(
12928 qtype,
12929 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_F8_E4M3 | QT_Q4_0
12930 )
12931 }
12932
12933 pub fn iq_fast_enabled() -> bool {
12941 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12942 *ON.get_or_init(|| {
12943 std::env::var("MEMRA_IQ_FAST")
12944 .map(|v| v != "0")
12945 .unwrap_or(true)
12946 })
12947 }
12948
12949 pub fn b8_enabled() -> bool {
12952 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12953 *ON.get_or_init(|| std::env::var("MEMRA_B8").map(|v| v != "0").unwrap_or(true))
12954 }
12955
12956 pub fn batched_mcols(m: usize) -> usize {
12958 if m == 2 {
12959 2
12960 } else if m <= 4 {
12961 4
12962 } else if m <= 8 {
12963 8
12964 } else {
12965 16
12966 }
12967 }
12968
12969 fn batched_kernel_name(qtype: i32, mcols: usize) -> Option<&'static str> {
12974 Some(match (qtype, mcols) {
12975 (QT_Q8_0, 2) => "qmatvec_q8_0_mmvq_b2",
12976 (QT_Q8_0, 4) => "qmatvec_q8_0_mmvq_b4",
12977 (QT_Q8_0, 8) => "qmatvec_q8_0_mmvq_b8",
12978 (QT_Q8_0, 16) => "qmatvec_q8_0_mmvq_b16",
12984 (QT_Q4_K, 2) => "qmatvec_q4_K_mmvq_b2",
12985 (QT_Q4_K, 4) => "qmatvec_q4_K_mmvq_b4",
12986 (QT_Q4_K, 8) => "qmatvec_q4_K_mmvq_b8",
12987 (QT_Q4_K, 16) => "qmatvec_q4_K_mmvq_b16",
12990 (QT_Q5_K, 2) => "qmatvec_q5_K_mmvq_b2",
12991 (QT_Q5_K, 4) => "qmatvec_q5_K_mmvq_b4",
12992 (QT_Q5_K, 8) => "qmatvec_q5_K_mmvq_b8",
12993 (QT_Q5_K, 16) => "qmatvec_q5_K_mmvq_b16",
12996 (QT_Q6_K, 2) => "qmatvec_q6_K_mmvq_b2",
12997 (QT_Q6_K, 4) => "qmatvec_q6_K_mmvq_b4",
12998 (QT_Q6_K, 8) => "qmatvec_q6_K_mmvq_b8",
12999 (QT_Q6_K, 16) => "qmatvec_q6_K_mmvq_b16",
13000 (QT_NVFP4, 2) => "qmatvec_nvfp4_mmvq_b2",
13001 (QT_NVFP4, 4) => "qmatvec_nvfp4_mmvq_b4",
13002 (QT_NVFP4, 8) => "qmatvec_nvfp4_mmvq_b8",
13003 (QT_NVFP4, 16) => "qmatvec_nvfp4_mmvq_b16",
13007 (QT_F8_E4M3, 2) => "qmatvec_e4m3_mmvq_b2",
13008 (QT_F8_E4M3, 4) => "qmatvec_e4m3_mmvq_b4",
13009 (QT_F8_E4M3, 8) => "qmatvec_e4m3_mmvq_b8",
13010 (QT_F8_E4M3, 16) => "qmatvec_e4m3_mmvq_b16",
13014 (QT_Q4_0, 2) => "qmatvec_q4_0_mmvq_b2",
13015 (QT_Q4_0, 4) => "qmatvec_q4_0_mmvq_b4",
13016 (QT_Q4_0, 8) => "qmatvec_q4_0_mmvq_b8",
13017 (QT_Q4_0, 16) => "qmatvec_q4_0_mmvq_b16",
13018 _ => return None,
13019 })
13020 }
13021
13022 pub fn sm_count(&self) -> i32 {
13057 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
13058 *SMS.get_or_init(|| {
13059 use cudarc::driver::sys::CUdevice_attribute_enum as A;
13060 self.gpu
13061 .ctx
13062 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
13063 .unwrap_or(82)
13064 })
13065 }
13066
13067 pub fn batched_variant(
13068 &self,
13069 _m: usize,
13070 in_f: usize,
13071 out_f: usize,
13072 qtype: i32,
13073 row_bytes: usize,
13074 mcols: usize,
13075 rp: bool,
13076 ) -> &'static str {
13077 if qtype == QT_Q8_0 {
13082 return if rp { "rp" } else { "base" };
13083 }
13084 static BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13085 let bv = *BV.get_or_init(|| match std::env::var("MEMRA_MMVQ_BV").as_deref() {
13086 Ok("base") => "base",
13087 Ok("pf") => "pf",
13088 Ok("r2") => "r2",
13089 Ok("r2w8") => "r2w8",
13090 Ok("pfr2") => "pfr2",
13091 Ok("ca") => "ca",
13092 Ok("car2") => "car2",
13093 Ok("rp") => "rp",
13096 Ok("rpr2") => "rpr2",
13097 Ok("rpr2w8") => "rpr2w8",
13098 Ok("rpca") => "rpca",
13101 Ok("rpcar2") => "rpcar2",
13102 Ok("rpsc") => "rpsc",
13109 Ok("rpms") => "rpms",
13110 Ok("rpmsc") => "rpmsc",
13111 Ok("rpks") => "rpks",
13112 Ok("rpksc") => "rpksc",
13113 _ => "auto",
13114 });
13115 let ca_ok = qtype == QT_NVFP4 && (row_bytes % 16 == 0) && (in_f % 1024 == 0);
13119 static KS_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13124 let ks_on = *KS_ON.get_or_init(|| std::env::var("MEMRA_KS").as_deref() != Ok("0"));
13125 let sc_ok = ks_on && qtype == QT_NVFP4 && (in_f % 256 == 0) && (in_f / 64 <= 272);
13126 let ks_ok = ks_on && qtype == QT_NVFP4 && (in_f % 512 == 0) && (in_f / 64 <= 272);
13127 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
13128 let sms = *SMS.get_or_init(|| {
13129 use cudarc::driver::sys::CUdevice_attribute_enum as A;
13130 self.gpu
13131 .ctx
13132 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
13133 .unwrap_or(82)
13134 });
13135 let kq_r2 = matches!(qtype, QT_Q4_K | QT_Q5_K | QT_Q6_K);
13155 static KQBV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13158 let kq_bv = *KQBV.get_or_init(|| match std::env::var("MEMRA_KQ_BV").as_deref() {
13159 Ok("base") => "base",
13160 Ok("r2") => "r2",
13161 Ok("r2w8") => "r2w8",
13162 _ => "auto",
13163 });
13164 let variant: &'static str = if qtype == QT_Q4_0 {
13165 static Q40BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13169 let q40 = *Q40BV.get_or_init(|| match std::env::var("MEMRA_Q40_BV").as_deref() {
13170 Ok("base") => "base",
13176 Ok("r2") => "r2",
13177 Ok("ms") => "ms",
13178 Ok("sm") => "sm",
13179 Ok("la") => "la",
13180 _ => "auto",
13181 });
13182 let v = if q40 != "auto" {
13183 q40
13184 } else if (out_f as u32).div_ceil(8) >= 4 * sms as u32 {
13185 "r2"
13186 } else {
13187 "base"
13188 };
13189 if rp {
13194 match v {
13195 "ms" => "r2ms_rp",
13196 "sm" => "r2sm_rp",
13197 "la" => "r2la_rp",
13198 "r2" => "r2_rp",
13199 _ => "rp",
13200 }
13201 } else if matches!(v, "ms" | "sm" | "la") {
13202 "r2"
13203 } else {
13204 v
13205 }
13206 } else if qtype != QT_NVFP4 && !kq_r2 {
13207 "base"
13208 } else if kq_r2 && rp {
13209 "rp"
13213 } else if kq_r2 {
13214 if kq_bv != "auto" {
13217 if kq_bv == "r2w8" && mcols != 4 {
13218 "r2"
13219 } else {
13220 kq_bv
13221 }
13222 } else if bv != "auto" {
13223 match bv {
13224 "r2" | "pfr2" | "rpr2" | "car2" => "r2",
13225 "r2w8" | "rpr2w8" => {
13226 if mcols != 4 {
13227 "r2"
13228 } else {
13229 "r2w8"
13230 }
13231 }
13232 _ => "base", }
13234 } else {
13235 let blocks = (out_f + 7) / 8;
13236 let waves = blocks as f64 / (7 * sms as usize) as f64;
13237 let filled = blocks >= 4 * sms as usize;
13238 let use_r2 = if qtype == QT_Q4_K {
13239 filled
13240 } else {
13241 waves >= 2.0
13242 };
13243 if use_r2 { "r2" } else { "base" }
13244 }
13245 } else if bv != "auto" {
13246 let v = if bv == "r2w8" && mcols == 2 {
13251 "r2"
13252 } else if bv == "ca" && (!ca_ok || mcols == 8) {
13253 "pf"
13254 } else if bv == "car2" && (!ca_ok || mcols == 8) {
13255 "r2"
13256 } else if bv == "pfr2" && mcols == 8 {
13257 "r2"
13258 } else if (bv == "rpr2w8" || bv == "rpr2") && mcols == 2 {
13259 "rpr2"
13260 }
13261 else if (bv == "rpca" || bv == "rpcar2") && (!ca_ok || mcols == 8) {
13263 if mcols == 8 { "rpr2w8" } else { "rpr2" }
13264 } else if bv == "rpcar2" && mcols == 2 {
13265 "rpca"
13266 }
13267 else if (bv == "rpsc" || bv == "rpmsc") && !sc_ok {
13270 "rpr2"
13271 } else if (bv == "rpks" || bv == "rpksc") && !ks_ok {
13272 "rpr2"
13273 } else {
13274 bv
13275 };
13276 if rp {
13277 match v {
13278 "base" | "pf" | "ca" | "rp" => "rp",
13279 "r2" | "pfr2" | "car2" | "rpr2" => "rpr2",
13280 "r2w8" | "rpr2w8" => {
13281 if mcols == 2 {
13282 "rpr2"
13283 } else {
13284 "rpr2w8"
13285 }
13286 }
13287 other => other, }
13289 } else {
13290 v
13291 }
13292 } else if mcols == 8 {
13293 if rp {
13304 if sc_ok { "rpsc" } else { "rpr2w8" }
13305 } else {
13306 "r2w8"
13307 }
13308 } else if mcols >= 4 {
13309 let blocks = (out_f + 7) / 8;
13313 let r7 = 7 * sms as usize;
13314 let r8 = 8 * sms as usize;
13315 let waves = blocks as f64 / r7 as f64;
13316 let filled = blocks >= 4 * sms as usize;
13317 if filled && blocks.div_ceil(r8) < blocks.div_ceil(r7) {
13321 if rp { "rpr2w8" } else { "r2w8" }
13325 } else if waves >= 2.0 || (waves <= 1.0 && filled) {
13326 if rp { "rpr2" } else { "r2" }
13329 } else {
13330 if rp { "rp" } else { "pf" }
13334 }
13335 } else if in_f >= 6144 {
13336 if rp { "rpr2" } else { "r2" }
13340 } else if rp {
13341 let waves = ((out_f + 7) / 8) as f64 / (7 * sms as usize) as f64;
13346 if sc_ok && waves >= 0.9 && waves <= 1.1 {
13347 "rpsc"
13348 } else {
13349 "rp"
13350 }
13351 } else {
13352 "base"
13353 };
13354 variant
13355 }
13356
13357 pub fn qmatvec_mmvq_batched(
13358 &self,
13359 bytes: &CudaSlice<u8>,
13360 aq: &CudaSlice<i8>,
13361 ad: &CudaSlice<f32>,
13362 m: usize,
13363 in_f: usize,
13364 out_f: usize,
13365 qtype: i32,
13366 row_bytes: usize,
13367 mcols: usize,
13368 scale: f32,
13369 rp: bool,
13370 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13371 const ROWS_PER_BLOCK: u32 = 4;
13372 let forced: Option<&'static str> = {
13377 static V: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
13378 V.get_or_init(|| std::env::var("MEMRA_BVAR").ok())
13379 .as_deref()
13380 .map(|s| Box::leak(s.to_string().into_boxed_str()) as &'static str)
13381 };
13382 let variant = match forced {
13383 Some(v) if !rp || v.contains("rp") => v,
13384 _ => self.batched_variant(m, in_f, out_f, qtype, row_bytes, mcols, rp),
13385 };
13386 let base_name = Self::batched_kernel_name(qtype, mcols).ok_or_else(|| {
13387 format!("qmatvec_mmvq_batched: no kernel for qtype {qtype} mcols {mcols}")
13388 })?;
13389 let variant = if mcols == 16 {
13393 if rp { "rp" } else { "base" }
13394 } else {
13395 variant
13396 };
13397 static B567: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13404 let b567 = *B567.get_or_init(|| std::env::var("MEMRA_B567").as_deref() != Ok("0"));
13405 if b567
13406 && qtype == QT_NVFP4
13407 && rp
13408 && mcols == 8
13409 && (5..=7).contains(&m)
13410 && matches!(variant, "rpsc" | "rpr2w8")
13411 {
13412 let f = self.func(&format!("qmatvec_nvfp4_mmvq_b{m}_{variant}"));
13413 let rows_per_block = ROWS_PER_BLOCK * 2; let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13415 let cfg = LaunchConfig {
13416 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
13417 block_dim: (32, ROWS_PER_BLOCK, 1),
13418 shared_mem_bytes: 0,
13419 };
13420 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13421 let __s_b = self.gpu.stream();
13422 let mut b = __s_b.launch_builder(&f);
13423 b.arg(bytes)
13424 .arg(aq)
13425 .arg(ad)
13426 .arg(&mut y)
13427 .arg(&inf)
13428 .arg(&outf)
13429 .arg(&mi)
13430 .arg(&rb);
13431 unsafe {
13432 b.launch(cfg)?;
13433 }
13434 if scale != 1.0 {
13435 self.scale_inplace(&mut y, scale, m * out_f)?;
13436 }
13437 return Ok(y);
13438 }
13439 let (name, rows_per_block): (std::borrow::Cow<'static, str>, u32) = match variant {
13440 "base" => (base_name.into(), ROWS_PER_BLOCK),
13441 "pf" => (format!("{base_name}_pf").into(), ROWS_PER_BLOCK),
13442 "ca" => (format!("{base_name}_ca").into(), ROWS_PER_BLOCK),
13443 "rp" => (format!("{base_name}_rp").into(), ROWS_PER_BLOCK),
13444 "rpca" => (format!("{base_name}_rpca").into(), ROWS_PER_BLOCK), "rpks" => (format!("{base_name}_rpks").into(), ROWS_PER_BLOCK),
13448 "rpksc" => (format!("{base_name}_rpksc").into(), ROWS_PER_BLOCK),
13449 "rpms" => (format!("{base_name}_rpms").into(), ROWS_PER_BLOCK),
13450 "rpmsc" => (format!("{base_name}_rpmsc").into(), ROWS_PER_BLOCK),
13451 "r2ms_rp" => (format!("{base_name}_r2ms_rp").into(), ROWS_PER_BLOCK),
13452 "r2sm_rp" => (format!("{base_name}_r2sm_rp").into(), ROWS_PER_BLOCK * 2),
13453 "r2la_rp" => (format!("{base_name}_r2la_rp").into(), ROWS_PER_BLOCK * 2),
13454 v => (format!("{base_name}_{v}").into(), ROWS_PER_BLOCK * 2), };
13456 debug_assert!(
13457 !rp || name.contains("_rp"),
13458 "rp weight dispatched to a GGUF-layout kernel"
13459 );
13460 let f = self.func(&name);
13461 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13462 let smem = if name.contains("_r2sm_rp") {
13464 (mcols * 32 * 9 * 4 + mcols * 32 * 4) as u32
13465 } else {
13466 0
13467 };
13468 let cfg = LaunchConfig {
13469 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
13470 block_dim: (32, ROWS_PER_BLOCK, 1),
13471 shared_mem_bytes: smem,
13472 };
13473 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13474 let __s_b = self.gpu.stream();
13475 let mut b = __s_b.launch_builder(&f);
13476 b.arg(bytes)
13477 .arg(aq)
13478 .arg(ad)
13479 .arg(&mut y)
13480 .arg(&inf)
13481 .arg(&outf)
13482 .arg(&mi)
13483 .arg(&rb);
13484 unsafe {
13485 b.launch(cfg)?;
13486 }
13487 if scale != 1.0 {
13488 self.scale_inplace(&mut y, scale, m * out_f)?;
13489 }
13490 Ok(y)
13491 }
13492
13493 pub fn qmatvec_batched_raw(
13497 &self,
13498 bytes: &CudaSlice<u8>,
13499 x: &CudaSlice<f32>,
13500 m: usize,
13501 in_f: usize,
13502 out_f: usize,
13503 qtype: i32,
13504 row_bytes: usize,
13505 mcols: usize,
13506 rp: bool,
13507 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13508 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13509 self.qmatvec_mmvq_batched(
13510 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, 1.0, rp,
13511 )
13512 }
13513
13514 pub fn qmatvec_nvfp4_batched_raw(
13516 &self,
13517 bytes: &CudaSlice<u8>,
13518 x: &CudaSlice<f32>,
13519 m: usize,
13520 in_f: usize,
13521 out_f: usize,
13522 row_bytes: usize,
13523 mcols: usize,
13524 rp: bool,
13525 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13526 self.qmatvec_batched_raw(bytes, x, m, in_f, out_f, QT_NVFP4, row_bytes, mcols, rp)
13527 }
13528
13529 fn try_fp4_gemm(
13533 &self,
13534 w: &crate::model::GpuTensor,
13535 x: &CudaSlice<f32>,
13536 m: usize,
13537 in_f: usize,
13538 out_f: usize,
13539 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13540 use crate::model::GpuTensor;
13541 if cfg!(memra_portable_cuda) {
13542 return Ok(None);
13543 }
13544 if std::env::var("MEMRA_FP4").is_err() {
13545 return Ok(None);
13546 }
13547 #[cfg(memra_cutlass)]
13556 if m >= 128 && std::env::var("MEMRA_FP4_CUTLASS").is_ok() {
13557 if let GpuTensor::Quant {
13558 bytes,
13559 qtype,
13560 scale,
13561 row_bytes,
13562 cutlass,
13563 ..
13564 } = w
13565 {
13566 if *qtype == QT_NVFP4 && in_f % 64 == 0 {
13567 if let Some(cw) = cutlass {
13568 let y = self.cutlass_fp4_gemm(
13570 &cw.b_packed,
13571 &cw.sfb_swizzled,
13572 x,
13573 *scale,
13574 m,
13575 out_f,
13576 in_f,
13577 )?;
13578 return Ok(Some(y));
13579 } else if std::env::var("MEMRA_FP4_CUTLASS_OTF").is_ok() {
13580 let (b_packed, sfb_sw) =
13585 self.build_cutlass_weight(bytes, out_f, in_f, *row_bytes)?;
13586 let y =
13587 self.cutlass_fp4_gemm(&b_packed, &sfb_sw, x, *scale, m, out_f, in_f)?;
13588 return Ok(Some(y));
13589 }
13590 }
13591 }
13592 }
13593 if let GpuTensor::Quant {
13594 bytes,
13595 qtype,
13596 row_bytes,
13597 scale,
13598 rp,
13599 ..
13600 } = w
13601 {
13602 if *qtype == QT_NVFP4 && in_f % 64 == 0 && !*rp {
13605 let y =
13606 self.qmatvec_gemm_nvfp4_fp4(bytes, x, m, in_f, out_f, *row_bytes, *scale)?;
13607 return Ok(Some(y));
13608 }
13609 }
13610 Ok(None)
13611 }
13612
13613 pub fn rms_norm_f16out(
13617 &self,
13618 x: &CudaSlice<f32>,
13619 w: &CudaSlice<f32>,
13620 dst: &mut CudaSlice<f32>,
13621 dst16: &mut CudaSlice<u8>,
13622 ncols: usize,
13623 nrows: usize,
13624 eps: f32,
13625 ) -> Result<(), Box<dyn std::error::Error>> {
13626 let f = self.func("rms_norm_f16out_f32");
13627 let cfg = LaunchConfig {
13628 grid_dim: (nrows as u32, 1, 1),
13629 block_dim: (rms_block(), 1, 1),
13630 shared_mem_bytes: 0,
13631 };
13632 let (nc, e) = (ncols as i32, eps);
13633 let __s_b = self.gpu.stream();
13634 let mut b = __s_b.launch_builder(&f);
13635 b.arg(x).arg(w).arg(dst).arg(dst16).arg(&nc).arg(&e);
13636 unsafe {
13637 b.launch(cfg)?;
13638 }
13639 Ok(())
13640 }
13641
13642 #[allow(clippy::too_many_arguments)]
13645 pub fn add_rms_norm_f16out(
13646 &self,
13647 a: &CudaSlice<f32>,
13648 b: &CudaSlice<f32>,
13649 w: &CudaSlice<f32>,
13650 res: &mut CudaSlice<f32>,
13651 dst: &mut CudaSlice<f32>,
13652 dst16: &mut CudaSlice<u8>,
13653 ncols: usize,
13654 nrows: usize,
13655 eps: f32,
13656 ) -> Result<(), Box<dyn std::error::Error>> {
13657 let f = self.func("add_rms_norm_f16out_f32");
13658 let cfg = LaunchConfig {
13659 grid_dim: (nrows as u32, 1, 1),
13660 block_dim: (rms_block(), 1, 1),
13661 shared_mem_bytes: 0,
13662 };
13663 let (nc, e) = (ncols as i32, eps);
13664 let __s_lb = self.gpu.stream();
13665 let mut lb = __s_lb.launch_builder(&f);
13666 lb.arg(a)
13667 .arg(b)
13668 .arg(w)
13669 .arg(res)
13670 .arg(dst)
13671 .arg(dst16)
13672 .arg(&nc)
13673 .arg(&e);
13674 unsafe {
13675 lb.launch(cfg)?;
13676 }
13677 Ok(())
13678 }
13679
13680 pub fn matmul_group_xh(
13683 &self,
13684 ws: &[&crate::model::GpuTensor],
13685 x: &CudaSlice<f32>,
13686 xh: &CudaSlice<u8>,
13687 m: usize,
13688 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13689 let mut out = Vec::with_capacity(ws.len());
13690 let in_f = ws[0].in_features();
13691 for w in ws {
13692 if w.in_features() == in_f && m >= 16 && !self.verify_exact_on() {
13693 if let Some(y) = self.try_f16_gemm_pre(w, xh, m)? {
13694 out.push(y);
13695 continue;
13696 }
13697 }
13698 out.push(self.matmul(w, x, m)?);
13699 }
13700 Ok(out)
13701 }
13702
13703 pub fn gdn_pad_mask(
13706 &self,
13707 beta: &mut CudaSlice<f32>,
13708 g_log: &mut CudaSlice<f32>,
13709 len_d: &CudaSlice<i32>,
13710 h: usize,
13711 t: usize,
13712 ) -> Result<(), Box<dyn std::error::Error>> {
13713 let f = self.func("gdn_pad_mask_f32");
13714 let cfg = LaunchConfig::for_num_elems((t * h) as u32);
13715 let (hi, ti) = (h as i32, t as i32);
13716 let __s_b = self.gpu.stream();
13717 let mut b = __s_b.launch_builder(&f);
13718 b.arg(beta).arg(g_log).arg(len_d).arg(&hi).arg(&ti);
13719 unsafe {
13720 b.launch(cfg)?;
13721 }
13722 Ok(())
13723 }
13724
13725 pub fn row_gather_dev(
13728 &self,
13729 src: &CudaSlice<f32>,
13730 dst: &mut CudaSlice<f32>,
13731 len_d: &CudaSlice<i32>,
13732 ncols: usize,
13733 ) -> Result<(), Box<dyn std::error::Error>> {
13734 let f = self.func("row_gather_dev_f32");
13735 let cfg = LaunchConfig::for_num_elems(ncols as u32);
13736 let nc = ncols as i32;
13737 let __s_b = self.gpu.stream();
13738 let mut b = __s_b.launch_builder(&f);
13739 b.arg(src).arg(dst).arg(len_d).arg(&nc);
13740 unsafe {
13741 b.launch(cfg)?;
13742 }
13743 Ok(())
13744 }
13745
13746 pub fn matmul_group(
13753 &self,
13754 ws: &[&crate::model::GpuTensor],
13755 x: &CudaSlice<f32>,
13756 m: usize,
13757 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13758 use crate::model::GpuTensor;
13759 let mut out = Vec::with_capacity(ws.len());
13760 let any_mirror = ws
13761 .iter()
13762 .any(|w| matches!(w, GpuTensor::Quant { f16: Some(_), .. }));
13763 if m >= 16 && any_mirror && !self.verify_exact_on() {
13764 let in_f = ws[0].in_features();
13765 let xh = self.f16_act(x, m * in_f, in_f)?;
13766 for w in ws {
13767 if w.in_features() == in_f {
13768 if let Some(y) = self.try_f16_gemm_pre(w, &xh, m)? {
13769 out.push(y);
13770 continue;
13771 }
13772 }
13773 out.push(self.matmul(w, x, m)?);
13774 }
13775 return Ok(out);
13776 }
13777 for w in ws {
13778 out.push(self.matmul(w, x, m)?);
13779 }
13780 Ok(out)
13781 }
13782
13783 pub fn matmul_group_multi(
13790 &self,
13791 ws: &[&crate::model::GpuTensor],
13792 xs: &[&CudaSlice<f32>],
13793 ms: &[usize],
13794 ) -> Result<Vec<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
13795 assert_eq!(xs.len(), ms.len());
13796 let in_f = ws[0].in_features();
13797 let total: usize = ms.iter().sum();
13798 let mut xcat = self.uninit(total * in_f)?;
13799 let mut off = 0usize;
13800 for (x, &m) in xs.iter().zip(ms) {
13801 self.copy_into(&mut xcat, off * in_f, x, m * in_f)?;
13802 off += m;
13803 }
13804 let ys = self.matmul_group(ws, &xcat, total)?;
13805 let mut out: Vec<Vec<CudaSlice<f32>>> = (0..xs.len()).map(|_| Vec::new()).collect();
13806 for (w, y) in ws.iter().zip(ys) {
13807 let out_f = w.out_features();
13808 let mut off = 0usize;
13809 for (s, &m) in ms.iter().enumerate() {
13810 let mut ys_s = self.uninit(m * out_f)?;
13811 let src = y.slice(off * out_f..(off + m) * out_f);
13812 self.gpu.stream().memcpy_dtod(&src, &mut ys_s)?;
13813 out[s].push(ys_s);
13814 off += m;
13815 }
13816 }
13817 Ok(out)
13818 }
13819
13820 pub fn gemm_supports(&self, w: &crate::model::GpuTensor) -> bool {
13830 use crate::model::GpuTensor;
13831 if !legacy_quant_gemm_allowed(
13832 cfg!(memra_portable_cuda),
13833 cfg!(memra_hopper_mma),
13834 std::env::var_os("MEMRA_NO_GEMM").is_some(),
13835 ) {
13836 return false;
13837 }
13838 match w {
13839 GpuTensor::Quant { qtype, .. } => {
13840 matches!(*qtype, QT_Q8_0 | QT_Q4_K | QT_Q6_K | QT_Q5_K | QT_Q4_0)
13841 || (*qtype == QT_NVFP4 && w.in_features() % 64 == 0)
13842 }
13843 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
13844 }
13845 }
13846
13847 pub fn qmatvec_gemm(
13854 &self,
13855 w: &crate::model::GpuTensor,
13856 aq: &CudaSlice<i8>,
13857 ad: &CudaSlice<f32>,
13858 m: usize,
13859 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13860 use crate::model::GpuTensor;
13861 let in_f = w.in_features();
13862 let out_f = w.out_features();
13863 let (bytes, qtype, row_bytes, scale, rp) = match w {
13864 GpuTensor::Quant {
13865 bytes,
13866 qtype,
13867 row_bytes,
13868 scale,
13869 rp,
13870 ..
13871 } => (bytes, *qtype, *row_bytes, *scale, *rp),
13872 _ => unreachable!("gemm_supports guaranteed Quant"),
13873 };
13874 if cfg!(memra_hopper_mma) && qtype == QT_Q8_0 && out_f % 64 == 0 && wgmma_gemm_enabled() {
13880 if let GpuTensor::Quant { rp4: Some(m4), .. } = w {
13881 let mut y = self.qmatvec_gemm_q8_0_wgmma_raw(m4, aq, ad, m, in_f, out_f)?;
13882 if scale != 1.0 {
13883 self.scale_inplace(&mut y, scale, m * out_f)?;
13884 }
13885 return Ok(y);
13886 }
13887 }
13888 let name = match qtype {
13889 QT_Q8_0 => "qmatvec_gemm_q8_0",
13890 QT_Q4_K => "qmatvec_gemm_q4_K",
13891 QT_Q4_0 => {
13892 if rp {
13893 "qmatvec_gemm_q4_0_rp"
13894 } else {
13895 "qmatvec_gemm_q4_0"
13896 }
13897 }
13898 QT_Q5_K => "qmatvec_gemm_q5_K",
13899 QT_Q6_K => "qmatvec_gemm_q6_K",
13900 QT_NVFP4 => {
13901 if rp {
13902 "qmatvec_gemm_nvfp4_rp"
13903 } else {
13904 "qmatvec_gemm_nvfp4"
13905 }
13906 }
13907 _ => unreachable!(),
13908 };
13909 let f = self.func(name);
13910 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);
13915 let k1_tile = if is_k1 {
13917 k1_launch_override().unwrap_or((128, 128, 8))
13918 } else {
13919 (128, 128, 8)
13920 };
13921 let (bm, bn): (u32, u32) = if is_k1 {
13922 (k1_tile.0, k1_tile.1)
13923 } else {
13924 (64, 256)
13925 };
13926 let warps: u32 = if is_k1 {
13927 k1_tile.2
13928 } else {
13929 match qtype {
13930 QT_NVFP4 => 8,
13931 _ => 4,
13932 }
13933 };
13934 let cfg = LaunchConfig {
13935 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
13936 block_dim: (32, warps, 1),
13937 shared_mem_bytes: 0,
13938 };
13939 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13940 let __s_b = self.gpu.stream();
13941 let mut b = __s_b.launch_builder(&f);
13942 b.arg(bytes)
13943 .arg(aq)
13944 .arg(ad)
13945 .arg(&mut y)
13946 .arg(&inf)
13947 .arg(&outf)
13948 .arg(&mi)
13949 .arg(&rb);
13950 unsafe {
13951 b.launch(cfg)?;
13952 }
13953 if scale != 1.0 {
13954 self.scale_inplace(&mut y, scale, m * out_f)?;
13955 }
13956 Ok(y)
13957 }
13958
13959 pub fn qmatvec_gemm_raw(
13964 &self,
13965 bytes: &CudaSlice<u8>,
13966 x: &CudaSlice<f32>,
13967 m: usize,
13968 in_f: usize,
13969 out_f: usize,
13970 qtype: i32,
13971 row_bytes: usize,
13972 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13973 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13974 let name = match qtype {
13975 QT_Q8_0 => "qmatvec_gemm_q8_0",
13976 QT_Q4_K => "qmatvec_gemm_q4_K",
13977 QT_Q4_0 => "qmatvec_gemm_q4_0",
13978 QT_Q5_K => "qmatvec_gemm_q5_K",
13979 QT_Q6_K => "qmatvec_gemm_q6_K",
13980 QT_NVFP4 => "qmatvec_gemm_nvfp4",
13981 QT_NVFP4_RP => "qmatvec_gemm_nvfp4_rp",
13982 _ => panic!("qmatvec_gemm_raw: qtype {qtype} has no GEMM kernel"),
13983 };
13984 let f = self.func(name);
13985 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);
13989 let k1_tile = if is_k1 {
13991 k1_launch_override().unwrap_or((128, 128, 8))
13992 } else {
13993 (128, 128, 8)
13994 };
13995 let (bm, bn): (u32, u32) = if is_k1 {
13996 (k1_tile.0, k1_tile.1)
13997 } else {
13998 (64, 256)
13999 };
14000 let warps: u32 = if is_k1 {
14001 k1_tile.2
14002 } else {
14003 match qtype {
14004 QT_NVFP4 | QT_NVFP4_RP => 8,
14005 _ => 4,
14006 }
14007 };
14008 let cfg = LaunchConfig {
14009 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
14010 block_dim: (32, warps, 1),
14011 shared_mem_bytes: 0,
14012 };
14013 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14014 let __s_b = self.gpu.stream();
14015 let mut b = __s_b.launch_builder(&f);
14016 b.arg(bytes)
14017 .arg(&aq)
14018 .arg(&ad)
14019 .arg(&mut y)
14020 .arg(&inf)
14021 .arg(&outf)
14022 .arg(&mi)
14023 .arg(&rb);
14024 unsafe {
14025 b.launch(cfg)?;
14026 }
14027 Ok(y)
14028 }
14029
14030 pub fn qmatvec_gemm_q8_0_wgmma_raw(
14037 &self,
14038 rp4: &CudaSlice<u8>,
14039 aq: &CudaSlice<i8>,
14040 ad: &CudaSlice<f32>,
14041 m: usize,
14042 in_f: usize,
14043 out_f: usize,
14044 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14045 assert!(
14046 out_f % 64 == 0 && in_f % 32 == 0,
14047 "wgmma GEMM needs out_f%64==0, in_f%32==0"
14048 );
14049 let f = self.func("qmatvec_gemm_q8_0_wgmma");
14050 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
14052 grid_dim: ((out_f / 64) as u32, (m as u32).div_ceil(64), 1),
14053 block_dim: (128, 1, 1),
14054 shared_mem_bytes: 0,
14055 };
14056 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
14057 let __s_b = self.gpu.stream();
14058 let mut b = __s_b.launch_builder(&f);
14059 b.arg(rp4)
14060 .arg(aq)
14061 .arg(ad)
14062 .arg(&mut y)
14063 .arg(&inf)
14064 .arg(&outf)
14065 .arg(&mi);
14066 unsafe {
14067 b.launch(cfg)?;
14068 }
14069 Ok(y)
14070 }
14071
14072 pub fn scale_inplace(
14074 &self,
14075 y: &mut CudaSlice<f32>,
14076 s: f32,
14077 n: usize,
14078 ) -> Result<(), Box<dyn std::error::Error>> {
14079 let f = self.func("scale_f32");
14080 let cfg = LaunchConfig::for_num_elems(n as u32);
14081 let (sf, ni) = (s, n as i32);
14082 let __s_b = self.gpu.stream();
14083 let mut b = __s_b.launch_builder(&f);
14084 b.arg(y).arg(&sf).arg(&ni);
14085 unsafe {
14086 b.launch(cfg)?;
14087 }
14088 Ok(())
14089 }
14090
14091 pub fn bf16_to_f32(
14096 &self,
14097 data: &cudarc::driver::CudaView<'_, u8>,
14098 n: usize,
14099 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14100 let mut out = self.alloc_uninit::<f32>(n)?;
14101 let f = self.func("bf16_to_f32");
14102 let cfg = LaunchConfig::for_num_elems(n as u32);
14103 let ni = n as i32;
14104 let __s_b = self.gpu.stream();
14105 let mut b = __s_b.launch_builder(&f);
14106 b.arg(data).arg(&mut out).arg(&ni);
14107 unsafe {
14108 b.launch(cfg)?;
14109 }
14110 Ok(out)
14111 }
14112
14113 fn linear_bf16_chunked(
14120 &self,
14121 x: &CudaSlice<f32>,
14122 data: &CudaSlice<u8>,
14123 m: usize,
14124 in_f: usize,
14125 out_f: usize,
14126 exact: bool,
14127 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14128 const CHUNK_BYTES: usize = 256 << 20;
14129 let chunk_rows = (CHUNK_BYTES / (in_f * 4)).max(1).min(out_f);
14130 if chunk_rows >= out_f {
14131 let wf32 = self.bf16_to_f32(&data.slice(0..in_f * out_f * 2), in_f * out_f)?;
14132 return if exact {
14133 self.linear_decode_exact(x, &wf32, m, in_f, out_f)
14134 } else {
14135 self.linear(x, &wf32, m, in_f, out_f)
14136 };
14137 }
14138 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
14139 let mut r0 = 0usize;
14140 while r0 < out_f {
14141 let rows = chunk_rows.min(out_f - r0);
14142 let wslice = data.slice(r0 * in_f * 2..(r0 + rows) * in_f * 2);
14143 let wf32 = self.bf16_to_f32(&wslice, in_f * rows)?;
14144 let yc = if exact {
14145 self.linear_decode_exact(x, &wf32, m, in_f, rows)?
14146 } else {
14147 self.linear(x, &wf32, m, in_f, rows)?
14148 };
14149 for mi in 0..m {
14151 let src = yc.slice(mi * rows..(mi + 1) * rows);
14152 let mut dst = y.slice_mut(mi * out_f + r0..mi * out_f + r0 + rows);
14153 self.gpu.stream().memcpy_dtod(&src, &mut dst)?;
14154 }
14155 r0 += rows;
14156 }
14157 Ok(y)
14158 }
14159
14160 pub fn linear_decode_exact(
14167 &self,
14168 x: &CudaSlice<f32>,
14169 w: &CudaSlice<f32>,
14170 m_tokens: usize,
14171 in_f: usize,
14172 out_f: usize,
14173 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14174 if m_tokens == 1 {
14175 return self.linear(x, w, 1, in_f, out_f);
14176 }
14177 let xv = self.view(x, m_tokens * in_f);
14178 let mut y = self.alloc_uninit::<f32>(m_tokens * out_f)?;
14179 for t in 0..m_tokens {
14180 let row = xv.slice(t * in_f..(t + 1) * in_f);
14181 let mut xr = self.alloc_uninit::<f32>(in_f)?;
14182 self.copy_view_into(&mut xr, 0, &row, in_f)?;
14183 let yr = self.linear(&xr, w, 1, in_f, out_f)?;
14184 self.copy_into(&mut y, t * out_f, &yr, out_f)?;
14185 }
14186 Ok(y)
14187 }
14188
14189 pub fn linear(
14190 &self,
14191 x: &CudaSlice<f32>,
14192 w: &CudaSlice<f32>,
14193 m_tokens: usize,
14194 in_f: usize,
14195 out_f: usize,
14196 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14197 use cudarc::cublaslt::{Matmul, MatmulConfig};
14198 let mut c = self.alloc_uninit::<f32>(m_tokens * out_f)?; let cfg = MatmulConfig {
14200 transa: true,
14201 transb: false,
14202 transc: false,
14203 m: out_f as u64,
14204 n: m_tokens as u64,
14205 k: in_f as u64,
14206 alpha: 1.0,
14207 lda: in_f as i64,
14208 ldb: in_f as i64,
14209 beta: 0.0,
14210 ldc: out_f as i64,
14211 stride_a: None,
14212 stride_b: None,
14213 stride_c: None,
14214 stride_bias: None,
14215 batch_size: None,
14216 };
14217 unsafe {
14218 self.gpu.blas.matmul(cfg, w, x, &mut c, None, None)?;
14219 }
14220 Ok(c)
14221 }
14222
14223 pub fn sdpa_naive(
14225 &self,
14226 q: &CudaSlice<f32>,
14227 k: &CudaSlice<f32>,
14228 v: &CudaSlice<f32>,
14229 o: &mut CudaSlice<f32>,
14230 head_dim: usize,
14231 n_head: usize,
14232 n_head_kv: usize,
14233 t: usize,
14234 t_kv: usize,
14235 scale: f32,
14236 causal: bool,
14237 ) -> Result<(), Box<dyn std::error::Error>> {
14238 let f = self.func("sdpa_naive_f32");
14239 let cfg = LaunchConfig {
14240 grid_dim: (n_head as u32, t as u32, 1),
14241 block_dim: (128, 1, 1),
14242 shared_mem_bytes: (t_kv * 4) as u32,
14243 };
14244 let (hd, nh, nhkv, ti, tkvi, cz) = (
14245 head_dim as i32,
14246 n_head as i32,
14247 n_head_kv as i32,
14248 t as i32,
14249 t_kv as i32,
14250 causal as i32,
14251 );
14252 let __s_b = self.gpu.stream();
14253 let mut b = __s_b.launch_builder(&f);
14254 b.arg(q)
14255 .arg(k)
14256 .arg(v)
14257 .arg(o)
14258 .arg(&hd)
14259 .arg(&nh)
14260 .arg(&nhkv)
14261 .arg(&ti)
14262 .arg(&tkvi)
14263 .arg(&scale)
14264 .arg(&cz);
14265 unsafe {
14266 b.launch(cfg)?;
14267 }
14268 Ok(())
14269 }
14270
14271 #[allow(clippy::too_many_arguments)]
14273 pub fn sdpa_naive_w(
14274 &self,
14275 q: &CudaSlice<f32>,
14276 k: &CudaSlice<f32>,
14277 v: &CudaSlice<f32>,
14278 o: &mut CudaSlice<f32>,
14279 head_dim: usize,
14280 n_head: usize,
14281 n_head_kv: usize,
14282 t: usize,
14283 t_kv: usize,
14284 scale: f32,
14285 causal: bool,
14286 window: usize,
14287 ) -> Result<(), Box<dyn std::error::Error>> {
14288 let f = self.func("sdpa_naive_w_f32");
14289 let cfg = LaunchConfig {
14290 grid_dim: (n_head as u32, t as u32, 1),
14291 block_dim: (128, 1, 1),
14292 shared_mem_bytes: (t_kv * 4) as u32,
14293 };
14294 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
14295 head_dim as i32,
14296 n_head as i32,
14297 n_head_kv as i32,
14298 t as i32,
14299 t_kv as i32,
14300 causal as i32,
14301 window as i32,
14302 );
14303 let __s_b = self.gpu.stream();
14304 let mut b = __s_b.launch_builder(&f);
14305 b.arg(q)
14306 .arg(k)
14307 .arg(v)
14308 .arg(o)
14309 .arg(&hd)
14310 .arg(&nh)
14311 .arg(&nhkv)
14312 .arg(&ti)
14313 .arg(&tkvi)
14314 .arg(&scale)
14315 .arg(&cz)
14316 .arg(&wi);
14317 unsafe {
14318 b.launch(cfg)?;
14319 }
14320 Ok(())
14321 }
14322
14323 pub fn sdpa_naive_view(
14325 &self,
14326 q: &CudaSlice<f32>,
14327 k: &cudarc::driver::CudaView<f32>,
14328 v: &cudarc::driver::CudaView<f32>,
14329 o: &mut CudaSlice<f32>,
14330 head_dim: usize,
14331 n_head: usize,
14332 n_head_kv: usize,
14333 t: usize,
14334 t_kv: usize,
14335 scale: f32,
14336 causal: bool,
14337 ) -> Result<(), Box<dyn std::error::Error>> {
14338 let f = self.func("sdpa_naive_f32");
14339 let cfg = LaunchConfig {
14340 grid_dim: (n_head as u32, t as u32, 1),
14341 block_dim: (128, 1, 1),
14342 shared_mem_bytes: (t_kv * 4) as u32,
14343 };
14344 let (hd, nh, nhkv, ti, tkvi, cz) = (
14345 head_dim as i32,
14346 n_head as i32,
14347 n_head_kv as i32,
14348 t as i32,
14349 t_kv as i32,
14350 causal as i32,
14351 );
14352 let __s_b = self.gpu.stream();
14353 let mut b = __s_b.launch_builder(&f);
14354 b.arg(q)
14355 .arg(k)
14356 .arg(v)
14357 .arg(o)
14358 .arg(&hd)
14359 .arg(&nh)
14360 .arg(&nhkv)
14361 .arg(&ti)
14362 .arg(&tkvi)
14363 .arg(&scale)
14364 .arg(&cz);
14365 unsafe {
14366 b.launch(cfg)?;
14367 }
14368 Ok(())
14369 }
14370
14371 #[allow(clippy::too_many_arguments)]
14379 pub fn fa_dequant_kv_view_f32(
14380 &self,
14381 k: &cudarc::driver::CudaView<u8>,
14382 v: &cudarc::driver::CudaView<u8>,
14383 kf: &mut CudaSlice<f32>,
14384 vf: &mut CudaSlice<f32>,
14385 kv_dim_k: usize,
14386 kv_dim_v: usize,
14387 t_kv: usize,
14388 k_tok_bytes: usize,
14389 v_tok_bytes: usize,
14390 g: bool,
14391 ) -> Result<(), Box<dyn std::error::Error>> {
14392 let f = if g {
14393 self.func_g("fa_dequant_kv_ws_f32")
14394 } else {
14395 self.func("fa_dequant_kv_ws_f32")
14396 };
14397 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
14398 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
14399 let cfg = LaunchConfig {
14400 grid_dim: (nblk.max(1), 1, 1),
14401 block_dim: (256, 1, 1),
14402 shared_mem_bytes: 0,
14403 };
14404 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
14405 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
14406 let __s_b = self.gpu.stream();
14407 let mut b = __s_b.launch_builder(&f);
14408 b.arg(k)
14409 .arg(v)
14410 .arg(&mut *kf)
14411 .arg(&mut *vf)
14412 .arg(&kdk)
14413 .arg(&kdv)
14414 .arg(&tkvi)
14415 .arg(&ktb)
14416 .arg(&vtb);
14417 unsafe {
14418 b.launch(cfg)?;
14419 }
14420 Ok(())
14421 }
14422
14423 #[allow(clippy::too_many_arguments)]
14424 pub fn sdpa_naive_quantized_view(
14425 &self,
14426 q: &CudaSlice<f32>,
14427 k: &cudarc::driver::CudaView<u8>,
14428 v: &cudarc::driver::CudaView<u8>,
14429 o: &mut CudaSlice<f32>,
14430 head_dim: usize,
14431 n_head: usize,
14432 n_head_kv: usize,
14433 t: usize,
14434 t_kv: usize,
14435 scale: f32,
14436 causal: bool,
14437 k_tok_bytes: usize,
14438 v_tok_bytes: usize,
14439 ) -> Result<(), Box<dyn std::error::Error>> {
14440 let kv_dim = n_head_kv * head_dim;
14441 let mut kf = self.uninit(t_kv * kv_dim)?;
14442 let mut vf = self.uninit(t_kv * kv_dim)?;
14443 let f = self.func("fa_dequant_kv_ws_f32");
14444 let total = (2 * t_kv * kv_dim) as u64;
14445 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
14446 let cfg = LaunchConfig {
14447 grid_dim: (nblk.max(1), 1, 1),
14448 block_dim: (256, 1, 1),
14449 shared_mem_bytes: 0,
14450 };
14451 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
14452 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
14453 let __s_b = self.gpu.stream();
14454 let mut b = __s_b.launch_builder(&f);
14455 b.arg(k)
14456 .arg(v)
14457 .arg(&mut kf)
14458 .arg(&mut vf)
14459 .arg(&kv_dim_i)
14460 .arg(&kv_dim_i)
14461 .arg(&t_kv_i)
14462 .arg(&k_tok_bytes_i)
14463 .arg(&v_tok_bytes_i);
14464 unsafe { b.launch(cfg)? };
14465 self.sdpa_naive(
14466 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
14467 )
14468 }
14469
14470 #[allow(clippy::too_many_arguments)]
14482 pub fn sdpa_naive_w_quantized_view(
14483 &self,
14484 q: &CudaSlice<f32>,
14485 k: &cudarc::driver::CudaView<u8>,
14486 v: &cudarc::driver::CudaView<u8>,
14487 o: &mut CudaSlice<f32>,
14488 head_dim: usize,
14489 n_head: usize,
14490 n_head_kv: usize,
14491 t: usize,
14492 t_kv: usize,
14493 scale: f32,
14494 causal: bool,
14495 window: usize,
14496 k_tok_bytes: usize,
14497 v_tok_bytes: usize,
14498 ) -> Result<(), Box<dyn std::error::Error>> {
14499 let kv_dim = n_head_kv * head_dim;
14500 let mut kf = self.uninit(t_kv * kv_dim)?;
14501 let mut vf = self.uninit(t_kv * kv_dim)?;
14502 let f = self.func("fa_dequant_kv_ws_f32");
14503 let total = (2 * t_kv * kv_dim) as u64;
14504 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
14505 let cfg = LaunchConfig {
14506 grid_dim: (nblk.max(1), 1, 1),
14507 block_dim: (256, 1, 1),
14508 shared_mem_bytes: 0,
14509 };
14510 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
14511 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
14512 let __s_b = self.gpu.stream();
14513 let mut b = __s_b.launch_builder(&f);
14514 b.arg(k)
14515 .arg(v)
14516 .arg(&mut kf)
14517 .arg(&mut vf)
14518 .arg(&kv_dim_i)
14519 .arg(&kv_dim_i)
14520 .arg(&t_kv_i)
14521 .arg(&k_tok_bytes_i)
14522 .arg(&v_tok_bytes_i);
14523 unsafe { b.launch(cfg)? };
14524 self.sdpa_naive_w(
14525 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
14526 )
14527 }
14528
14529 pub fn fa_prefill(
14533 &self,
14534 q: &CudaSlice<f32>,
14535 k: &CudaSlice<f32>,
14536 v: &CudaSlice<f32>,
14537 o: &mut CudaSlice<f32>,
14538 head_dim: usize,
14539 n_head: usize,
14540 n_head_kv: usize,
14541 t: usize,
14542 t_kv: usize,
14543 scale: f32,
14544 causal: bool,
14545 ) -> Result<(), Box<dyn std::error::Error>> {
14546 if portable_mma_gated() {
14547 return self.sdpa_naive(
14548 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
14549 );
14550 }
14551 let fa3_on = head_dim == 256
14559 && causal
14560 && t == t_kv
14561 && match std::env::var("MEMRA_FA3").as_deref() {
14562 Ok("0") => false,
14563 Ok("1") => true,
14564 _ => cfg!(memra_hopper_mma),
14565 };
14566 if fa3_on {
14567 let n = t * n_head * head_dim;
14568 let nkv = t * n_head_kv * head_dim;
14569 let mut q16 = self.alloc_u8_uninit(n * 2)?;
14570 let mut k16 = self.alloc_u8_uninit(nkv * 2)?;
14571 let mut v16 = self.alloc_u8_uninit(nkv * 2)?;
14572 self.f32_to_bf16_into(q, &mut q16, n)?;
14573 self.f32_to_bf16_into(k, &mut k16, nkv)?;
14574 self.f32_to_bf16_into(v, &mut v16, nkv)?;
14575 let rc = {
14576 use cudarc::driver::{DevicePtr, DevicePtrMut};
14577 let stream = self.gpu.stream();
14578 let (qp, _g1) = q16.device_ptr(&stream);
14579 let (kp, _g2) = k16.device_ptr(&stream);
14580 let (vp, _g3) = v16.device_ptr(&stream);
14581 let (op, _g4) = o.device_ptr_mut(&stream);
14582 unsafe {
14583 memra_fa3_prefill(
14584 qp as *const core::ffi::c_void,
14585 kp as *const core::ffi::c_void,
14586 vp as *const core::ffi::c_void,
14587 op as *mut f32,
14588 t as i32,
14589 n_head as i32,
14590 n_head_kv as i32,
14591 head_dim as i32,
14592 scale,
14593 stream.cu_stream() as *mut core::ffi::c_void,
14594 )
14595 }
14596 };
14597 if rc != 0 {
14598 return Err(format!("memra_fa3_prefill rc={rc}").into());
14599 }
14600 return Ok(());
14601 }
14602 static FA_P1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14607 let fa_p1 = *FA_P1.get_or_init(|| std::env::var("MEMRA_FA_P1").as_deref() == Ok("1"));
14608 if fa_p1 && head_dim == 256 && !std::env::var("MEMRA_FA_FLOOR").is_ok() {
14609 const BLOCK_Q: usize = 64;
14610 const BKX: usize = 32;
14611 let f = self.func("fa_prefill_bf16_p1");
14612 let shmem = (2 * (2 * BKX * head_dim + BLOCK_Q * BKX)
14613 + 4 * (BLOCK_Q * BKX + 2 * BLOCK_Q)) as u32;
14614 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14615 f.set_attribute(
14616 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14617 shmem as i32,
14618 )?;
14619 let cfg = LaunchConfig {
14620 grid_dim: (
14621 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
14622 n_head as u32,
14623 1,
14624 ),
14625 block_dim: (32, 4, 1),
14626 shared_mem_bytes: shmem,
14627 };
14628 let (hd, nh, nhkv, ti, tkvi, cz) = (
14629 head_dim as i32,
14630 n_head as i32,
14631 n_head_kv as i32,
14632 t as i32,
14633 t_kv as i32,
14634 causal as i32,
14635 );
14636 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
14637 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
14638 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
14639 let __s_b = self.gpu.stream();
14640 let mut b = __s_b.launch_builder(&f);
14641 b.arg(&qb)
14642 .arg(&kb)
14643 .arg(&vb)
14644 .arg(o)
14645 .arg(&hd)
14646 .arg(&nh)
14647 .arg(&nhkv)
14648 .arg(&ti)
14649 .arg(&tkvi)
14650 .arg(&scale)
14651 .arg(&cz);
14652 unsafe {
14653 b.launch(cfg)?;
14654 }
14655 return Ok(());
14656 }
14657 const BK: usize = 32;
14663 let w2 = std::env::var("MEMRA_FA_PP_W2").as_deref() == Ok("1");
14666 let (block_q, warps, w2_sfx): (usize, u32, &str) =
14667 if w2 { (32, 2, "_w2") } else { (64, 4, "") };
14668 let hd_sfx = fa_hd_suffix(head_dim)?;
14672 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
14673 let bf16kv = !floor && !w2 && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0");
14678 let (kb16, vb16) = if bf16kv {
14679 let n = t_kv * n_head_kv * head_dim;
14680 let mut kb = self.alloc_u8_uninit(n * 2)?;
14681 let mut vb = self.alloc_u8_uninit(n * 2)?;
14682 let fcv = self.func("f32_to_bf16_bulk");
14683 let ni = n as i64;
14684 let cfgc = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
14685 let __s_b = self.gpu.stream();
14686 let mut b = __s_b.launch_builder(&fcv);
14687 b.arg(k).arg(&mut kb).arg(&ni);
14688 unsafe {
14689 b.launch(cfgc)?;
14690 }
14691 let __s_b = self.gpu.stream();
14692 let mut b = __s_b.launch_builder(&fcv);
14693 b.arg(v).arg(&mut vb).arg(&ni);
14694 unsafe {
14695 b.launch(cfgc)?;
14696 }
14697 (Some(kb), Some(vb))
14698 } else {
14699 (None, None)
14700 };
14701 let f = self.func(&if bf16kv {
14702 format!("fa_prefill_bf16kv_pp{hd_sfx}")
14703 } else {
14704 format!(
14705 "fa_prefill_f32{}{}{hd_sfx}",
14706 if floor { "" } else { "_pp" },
14707 if floor { "" } else { w2_sfx }
14708 )
14709 });
14710 let kv_stages = if bf16kv { 2 } else { 1 };
14713 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
14714 + 4 * (block_q * BK + 2 * block_q)) as u32;
14715 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14716 f.set_attribute(
14717 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14718 shmem as i32,
14719 )?;
14720 let cfg = LaunchConfig {
14721 grid_dim: (
14722 (t as u32 + block_q as u32 - 1) / block_q as u32,
14723 n_head as u32,
14724 1,
14725 ),
14726 block_dim: (32, warps, 1),
14727 shared_mem_bytes: shmem,
14728 };
14729 let (hd, nh, nhkv, ti, tkvi, cz) = (
14730 head_dim as i32,
14731 n_head as i32,
14732 n_head_kv as i32,
14733 t as i32,
14734 t_kv as i32,
14735 causal as i32,
14736 );
14737 let __s_b = self.gpu.stream();
14738 let mut b = __s_b.launch_builder(&f);
14739 b.arg(q);
14740 match (&kb16, &vb16) {
14741 (Some(kb), Some(vb)) => {
14742 b.arg(kb).arg(vb);
14743 }
14744 _ => {
14745 b.arg(k).arg(v);
14746 }
14747 }
14748 b.arg(o)
14749 .arg(&hd)
14750 .arg(&nh)
14751 .arg(&nhkv)
14752 .arg(&ti)
14753 .arg(&tkvi)
14754 .arg(&scale)
14755 .arg(&cz);
14756 unsafe {
14757 b.launch(cfg)?;
14758 }
14759 Ok(())
14760 }
14761
14762 #[allow(clippy::too_many_arguments)]
14766 pub fn fa_prefill_w(
14767 &self,
14768 q: &CudaSlice<f32>,
14769 k: &CudaSlice<f32>,
14770 v: &CudaSlice<f32>,
14771 o: &mut CudaSlice<f32>,
14772 head_dim: usize,
14773 n_head: usize,
14774 n_head_kv: usize,
14775 t: usize,
14776 t_kv: usize,
14777 scale: f32,
14778 causal: bool,
14779 window: usize,
14780 ) -> Result<(), Box<dyn std::error::Error>> {
14781 if portable_mma_gated() {
14784 return self.sdpa_naive_w(
14785 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
14786 );
14787 }
14788 static FAW_F32: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14792 let faw_f32 =
14793 *FAW_F32.get_or_init(|| std::env::var("MEMRA_FAW_STAGE").as_deref() == Ok("f32"));
14794 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
14795 self.fa_prefill_w_arm(
14796 q,
14797 k,
14798 v,
14799 o,
14800 head_dim,
14801 n_head,
14802 n_head_kv,
14803 t,
14804 t_kv,
14805 scale,
14806 causal,
14807 window,
14808 floor || faw_f32,
14809 floor,
14810 )
14811 }
14812
14813 #[allow(clippy::too_many_arguments)]
14816 pub fn fa_prefill_w_pre(
14817 &self,
14818 qb: &CudaSlice<u8>,
14819 kb: &CudaSlice<u8>,
14820 vb: &CudaSlice<u8>,
14821 o: &mut CudaSlice<f32>,
14822 head_dim: usize,
14823 n_head: usize,
14824 n_head_kv: usize,
14825 t: usize,
14826 t_kv: usize,
14827 scale: f32,
14828 causal: bool,
14829 window: usize,
14830 v_f16: bool,
14831 ) -> Result<(), Box<dyn std::error::Error>> {
14832 const BLOCK_Q: usize = 64;
14833 const BK: usize = 32;
14834 debug_assert_eq!(head_dim, 256);
14835 let hp = fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
14836 debug_assert!(!v_f16 || hp, "f16 V emitted but the SWA hp arm is off");
14837 if hp {
14838 const BLOCK_QH: usize = 32;
14839 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
14842 let vh: &CudaSlice<u8> = if v_f16 {
14843 vb
14844 } else {
14845 let n = t_kv * n_head_kv * head_dim;
14846 if vguard.as_ref().map(|b| b.len() < n * 2).unwrap_or(true) {
14847 *vguard = Some(self.alloc_uninit::<u8>(n * 2)?);
14848 }
14849 self.bf16_to_f16_into(vb, n, vguard.as_mut().unwrap())?;
14850 vguard.as_ref().unwrap()
14851 };
14852 let f = self.func("fa_prefill_w_bf16_p1h2");
14853 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
14854 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14855 f.set_attribute(
14856 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14857 shmem as i32,
14858 )?;
14859 let cfg = LaunchConfig {
14860 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
14861 block_dim: (32, 4, 1),
14862 shared_mem_bytes: shmem,
14863 };
14864 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
14865 head_dim as i32,
14866 n_head as i32,
14867 n_head_kv as i32,
14868 t as i32,
14869 t_kv as i32,
14870 causal as i32,
14871 window as i32,
14872 );
14873 let __s_b = self.gpu.stream();
14874 let mut b = __s_b.launch_builder(&f);
14875 b.arg(qb)
14876 .arg(kb)
14877 .arg(vh)
14878 .arg(o)
14879 .arg(&hd)
14880 .arg(&nh)
14881 .arg(&nhkv)
14882 .arg(&ti)
14883 .arg(&tkvi)
14884 .arg(&scale)
14885 .arg(&cz)
14886 .arg(&wi);
14887 unsafe {
14888 b.launch(cfg)?;
14889 }
14890 return Ok(());
14891 }
14892 let f = self.func("fa_prefill_w_bf16_p1");
14893 let shmem =
14894 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
14895 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14896 f.set_attribute(
14897 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14898 shmem as i32,
14899 )?;
14900 let cfg = LaunchConfig {
14901 grid_dim: (
14902 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
14903 n_head as u32,
14904 1,
14905 ),
14906 block_dim: (32, 4, 1),
14907 shared_mem_bytes: shmem,
14908 };
14909 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
14910 head_dim as i32,
14911 n_head as i32,
14912 n_head_kv as i32,
14913 t as i32,
14914 t_kv as i32,
14915 causal as i32,
14916 window as i32,
14917 );
14918 let __s_b = self.gpu.stream();
14919 let mut b = __s_b.launch_builder(&f);
14920 b.arg(qb)
14921 .arg(kb)
14922 .arg(vb)
14923 .arg(o)
14924 .arg(&hd)
14925 .arg(&nh)
14926 .arg(&nhkv)
14927 .arg(&ti)
14928 .arg(&tkvi)
14929 .arg(&scale)
14930 .arg(&cz)
14931 .arg(&wi);
14932 unsafe {
14933 b.launch(cfg)?;
14934 }
14935 Ok(())
14936 }
14937
14938 #[allow(clippy::too_many_arguments)]
14940 pub fn fa_prefill_w_arm(
14941 &self,
14942 q: &CudaSlice<f32>,
14943 k: &CudaSlice<f32>,
14944 v: &CudaSlice<f32>,
14945 o: &mut CudaSlice<f32>,
14946 head_dim: usize,
14947 n_head: usize,
14948 n_head_kv: usize,
14949 t: usize,
14950 t_kv: usize,
14951 scale: f32,
14952 causal: bool,
14953 window: usize,
14954 f32_stage: bool,
14955 floor: bool,
14956 ) -> Result<(), Box<dyn std::error::Error>> {
14957 const BLOCK_Q: usize = 64;
14958 const BK: usize = 32;
14959 debug_assert_eq!(head_dim, 256, "fa_prefill_w is stamped hd256 only");
14960 static P1_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14964 let p1 = !floor
14965 && !f32_stage
14966 && *P1_ON.get_or_init(|| {
14967 std::env::var("MEMRA_FAW_P1")
14968 .map(|v| v != "0")
14969 .unwrap_or(true)
14970 });
14971 let hp =
14972 p1 && fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
14973 if hp {
14974 const BLOCK_QH: usize = 32;
14975 let f = self.func("fa_prefill_w_bf16_p1h2");
14976 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
14977 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14978 f.set_attribute(
14979 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14980 shmem as i32,
14981 )?;
14982 let cfg = LaunchConfig {
14983 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
14984 block_dim: (32, 4, 1),
14985 shared_mem_bytes: shmem,
14986 };
14987 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
14988 head_dim as i32,
14989 n_head as i32,
14990 n_head_kv as i32,
14991 t as i32,
14992 t_kv as i32,
14993 causal as i32,
14994 window as i32,
14995 );
14996 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
14997 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
14998 let vh = self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?;
14999 let __s_b = self.gpu.stream();
15000 let mut b = __s_b.launch_builder(&f);
15001 b.arg(&qb)
15002 .arg(&kb)
15003 .arg(&vh)
15004 .arg(o)
15005 .arg(&hd)
15006 .arg(&nh)
15007 .arg(&nhkv)
15008 .arg(&ti)
15009 .arg(&tkvi)
15010 .arg(&scale)
15011 .arg(&cz)
15012 .arg(&wi);
15013 unsafe {
15014 b.launch(cfg)?;
15015 }
15016 return Ok(());
15017 }
15018 if p1 {
15019 let f = self.func("fa_prefill_w_bf16_p1");
15020 let shmem =
15021 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15022 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15023 f.set_attribute(
15024 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15025 shmem as i32,
15026 )?;
15027 let cfg = LaunchConfig {
15028 grid_dim: (
15029 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15030 n_head as u32,
15031 1,
15032 ),
15033 block_dim: (32, 4, 1),
15034 shared_mem_bytes: shmem,
15035 };
15036 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15037 head_dim as i32,
15038 n_head as i32,
15039 n_head_kv as i32,
15040 t as i32,
15041 t_kv as i32,
15042 causal as i32,
15043 window as i32,
15044 );
15045 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15046 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15047 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15048 let __s_b = self.gpu.stream();
15049 let mut b = __s_b.launch_builder(&f);
15050 b.arg(&qb)
15051 .arg(&kb)
15052 .arg(&vb)
15053 .arg(o)
15054 .arg(&hd)
15055 .arg(&nh)
15056 .arg(&nhkv)
15057 .arg(&ti)
15058 .arg(&tkvi)
15059 .arg(&scale)
15060 .arg(&cz)
15061 .arg(&wi);
15062 unsafe {
15063 b.launch(cfg)?;
15064 }
15065 return Ok(());
15066 }
15067 static G4_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15070 let g4 = !floor
15071 && !f32_stage
15072 && n_head_kv == 1
15073 && n_head % 4 == 0
15074 && *G4_ON.get_or_init(|| {
15075 std::env::var("MEMRA_FAW_G4")
15076 .map(|v| v != "0")
15077 .unwrap_or(true)
15078 });
15079 if g4 {
15080 const SP_M: usize = 16;
15081 static O2_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15084 let o2 = *O2_ON.get_or_init(|| {
15085 std::env::var("MEMRA_FAW_O2")
15086 .map(|v| v != "0")
15087 .unwrap_or(true)
15088 });
15089 let f = self.func(if o2 {
15090 "fa_prefill_w_bf16_g4o2"
15091 } else {
15092 "fa_prefill_w_bf16_g4"
15093 });
15094 let shmem = if o2 {
15095 (2 * (4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M)) as u32
15096 } else {
15097 (2 * (2 * BK * head_dim + 4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M))
15098 as u32
15099 };
15100 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15101 f.set_attribute(
15102 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15103 shmem as i32,
15104 )?;
15105 let cfg = LaunchConfig {
15106 grid_dim: ((t as u32).div_ceil(SP_M as u32), (n_head / 4) as u32, 1),
15107 block_dim: (32, 4, 1),
15108 shared_mem_bytes: shmem,
15109 };
15110 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15111 head_dim as i32,
15112 n_head as i32,
15113 n_head_kv as i32,
15114 t as i32,
15115 t_kv as i32,
15116 causal as i32,
15117 window as i32,
15118 );
15119 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15120 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15121 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15122 let __s_b = self.gpu.stream();
15123 let mut b = __s_b.launch_builder(&f);
15124 b.arg(&qb)
15125 .arg(&kb)
15126 .arg(&vb)
15127 .arg(o)
15128 .arg(&hd)
15129 .arg(&nh)
15130 .arg(&nhkv)
15131 .arg(&ti)
15132 .arg(&tkvi)
15133 .arg(&scale)
15134 .arg(&cz)
15135 .arg(&wi);
15136 unsafe {
15137 b.launch(cfg)?;
15138 }
15139 return Ok(());
15140 }
15141 let f = self.func(if floor {
15142 "fa_prefill_w_f32"
15143 } else if f32_stage {
15144 "fa_prefill_w_f32_pp"
15145 } else {
15146 "fa_prefill_w_bf16_pp"
15147 });
15148 let shmem =
15149 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15150 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15151 f.set_attribute(
15152 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15153 shmem as i32,
15154 )?;
15155 let cfg = LaunchConfig {
15156 grid_dim: (
15157 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15158 n_head as u32,
15159 1,
15160 ),
15161 block_dim: (32, 4, 1),
15162 shared_mem_bytes: shmem,
15163 };
15164 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15165 head_dim as i32,
15166 n_head as i32,
15167 n_head_kv as i32,
15168 t as i32,
15169 t_kv as i32,
15170 causal as i32,
15171 window as i32,
15172 );
15173 if f32_stage {
15174 let __s_b = self.gpu.stream();
15175 let mut b = __s_b.launch_builder(&f);
15176 b.arg(q)
15177 .arg(k)
15178 .arg(v)
15179 .arg(o)
15180 .arg(&hd)
15181 .arg(&nh)
15182 .arg(&nhkv)
15183 .arg(&ti)
15184 .arg(&tkvi)
15185 .arg(&scale)
15186 .arg(&cz)
15187 .arg(&wi);
15188 unsafe {
15189 b.launch(cfg)?;
15190 }
15191 } else {
15192 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15193 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15194 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15195 let __s_b = self.gpu.stream();
15196 let mut b = __s_b.launch_builder(&f);
15197 b.arg(&qb)
15198 .arg(&kb)
15199 .arg(&vb)
15200 .arg(o)
15201 .arg(&hd)
15202 .arg(&nh)
15203 .arg(&nhkv)
15204 .arg(&ti)
15205 .arg(&tkvi)
15206 .arg(&scale)
15207 .arg(&cz)
15208 .arg(&wi);
15209 unsafe {
15210 b.launch(cfg)?;
15211 }
15212 }
15213 Ok(())
15214 }
15215
15216 #[allow(clippy::too_many_arguments)]
15220 pub fn fa_prefill_hd512(
15221 &self,
15222 q: &CudaSlice<f32>,
15223 k: &CudaSlice<f32>,
15224 v: &CudaSlice<f32>,
15225 o: &mut CudaSlice<f32>,
15226 head_dim: usize,
15227 n_head: usize,
15228 n_head_kv: usize,
15229 t: usize,
15230 t_kv: usize,
15231 scale: f32,
15232 causal: bool,
15233 ) -> Result<(), Box<dyn std::error::Error>> {
15234 if portable_mma_gated() {
15236 return self.sdpa_naive(
15237 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
15238 );
15239 }
15240 static F32_STAGE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15246 let f32_stage =
15247 *F32_STAGE.get_or_init(|| std::env::var("MEMRA_FA512_STAGE").as_deref() == Ok("f32"));
15248 static SP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15252 let sp = !f32_stage
15253 && *SP_ON.get_or_init(|| {
15254 std::env::var("MEMRA_FA512_SP")
15255 .map(|v| v != "0")
15256 .unwrap_or(true)
15257 });
15258 self.fa_prefill_hd512_arm(
15259 q,
15260 k,
15261 v,
15262 o,
15263 head_dim,
15264 n_head,
15265 n_head_kv,
15266 t,
15267 t_kv,
15268 scale,
15269 causal,
15270 f32_stage,
15271 sp,
15272 sp && fa_f16pv_on(),
15273 )
15274 }
15275
15276 #[allow(clippy::too_many_arguments)]
15278 pub fn fa_prefill_hd512_pre(
15279 &self,
15280 qb: &CudaSlice<u8>,
15281 kb: &CudaSlice<u8>,
15282 vb: &CudaSlice<u8>,
15283 o: &mut CudaSlice<f32>,
15284 head_dim: usize,
15285 n_head: usize,
15286 n_head_kv: usize,
15287 t: usize,
15288 t_kv: usize,
15289 scale: f32,
15290 causal: bool,
15291 v_f16: bool,
15292 ) -> Result<(), Box<dyn std::error::Error>> {
15293 debug_assert_eq!(head_dim, 512);
15294 const SP_M: usize = 16;
15295 const BKS: usize = 32;
15296 let f16pv = fa_f16pv_on();
15300 let nw = if f16pv { fa512_wide_warps() } else { 2 };
15301 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15302 debug_assert!(!v_f16 || f16pv, "f16 V emitted without the door on");
15303 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
15304 let vref: &CudaSlice<u8> = if f16pv && !v_f16 {
15305 let n = t_kv * n_head_kv * head_dim;
15307 let need = n * 2;
15308 if vguard.as_ref().map(|b| b.len() < need).unwrap_or(true) {
15309 *vguard = Some(self.alloc_uninit::<u8>(need)?);
15310 }
15311 let dst = vguard.as_mut().unwrap();
15312 self.bf16_to_f16_into(vb, n, dst)?;
15313 vguard.as_ref().unwrap()
15314 } else {
15315 vb
15316 };
15317 let f = self.func(if hp {
15318 "fa_prefill_bf16_hd512_sp16h2"
15319 } else {
15320 match (f16pv, nw) {
15321 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
15322 (true, _) => "fa_prefill_bf16_hd512_sp16",
15323 _ => "fa_prefill_bf16_hd512_sp",
15324 }
15325 });
15326 let (nwarp, npart) = if hp {
15327 (4usize, 4usize)
15328 } else if nw > 2 {
15329 (nw, nw)
15330 } else {
15331 (2, 1)
15332 };
15333 let shmem = if hp {
15335 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS) + 4 * (2 * npart * SP_M * BKS + 2 * SP_M))
15336 as u32
15337 } else {
15338 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
15339 + 4 * (npart * SP_M * BKS + SP_M)) as u32
15340 };
15341 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15342 f.set_attribute(
15343 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15344 shmem as i32,
15345 )?;
15346 let grid_y = if hp {
15347 (n_head / 2) as u32
15348 } else {
15349 n_head as u32
15350 };
15351 let cfg = LaunchConfig {
15352 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
15353 block_dim: (32, nwarp as u32, 1),
15354 shared_mem_bytes: shmem,
15355 };
15356 let (hd, nh, nhkv, ti, tkvi, cz) = (
15357 head_dim as i32,
15358 n_head as i32,
15359 n_head_kv as i32,
15360 t as i32,
15361 t_kv as i32,
15362 causal as i32,
15363 );
15364 let __s_b = self.gpu.stream();
15365 let mut b = __s_b.launch_builder(&f);
15366 b.arg(qb)
15367 .arg(kb)
15368 .arg(vref)
15369 .arg(o)
15370 .arg(&hd)
15371 .arg(&nh)
15372 .arg(&nhkv)
15373 .arg(&ti)
15374 .arg(&tkvi)
15375 .arg(&scale)
15376 .arg(&cz);
15377 unsafe {
15378 b.launch(cfg)?;
15379 }
15380 Ok(())
15381 }
15382
15383 #[allow(clippy::too_many_arguments)]
15386 pub fn fa_prefill_hd512_arm(
15387 &self,
15388 q: &CudaSlice<f32>,
15389 k: &CudaSlice<f32>,
15390 v: &CudaSlice<f32>,
15391 o: &mut CudaSlice<f32>,
15392 head_dim: usize,
15393 n_head: usize,
15394 n_head_kv: usize,
15395 t: usize,
15396 t_kv: usize,
15397 scale: f32,
15398 causal: bool,
15399 f32_stage: bool,
15400 sp: bool,
15401 f16pv: bool,
15402 ) -> Result<(), Box<dyn std::error::Error>> {
15403 debug_assert_eq!(head_dim, 512, "fa_prefill_hd512 is hd512 only");
15404 if sp && !f32_stage {
15405 const SP_M: usize = 16;
15409 const BKS: usize = 32;
15410 let nw = if f16pv { fa512_wide_warps() } else { 2 };
15411 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15412 let f = self.func(if hp {
15413 "fa_prefill_bf16_hd512_sp16h2"
15414 } else {
15415 match (f16pv, nw) {
15416 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
15417 (true, _) => "fa_prefill_bf16_hd512_sp16",
15418 _ => "fa_prefill_bf16_hd512_sp",
15419 }
15420 });
15421 let (nwarp, npart) = if hp {
15422 (4usize, 4usize)
15423 } else if nw > 2 {
15424 (nw, nw)
15425 } else {
15426 (2, 1)
15427 };
15428 let shmem = if hp {
15429 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS)
15430 + 4 * (2 * npart * SP_M * BKS + 2 * SP_M)) as u32
15431 } else {
15432 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
15433 + 4 * (npart * SP_M * BKS + SP_M)) as u32
15434 };
15435 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15436 f.set_attribute(
15437 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15438 shmem as i32,
15439 )?;
15440 let grid_y = if hp {
15441 (n_head / 2) as u32
15442 } else {
15443 n_head as u32
15444 };
15445 let cfg = LaunchConfig {
15446 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
15447 block_dim: (32, nwarp as u32, 1),
15448 shared_mem_bytes: shmem,
15449 };
15450 let (hd, nh, nhkv, ti, tkvi, cz) = (
15451 head_dim as i32,
15452 n_head as i32,
15453 n_head_kv as i32,
15454 t as i32,
15455 t_kv as i32,
15456 causal as i32,
15457 );
15458 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15459 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15460 let vb = if f16pv {
15461 self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?
15462 } else {
15463 self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?
15464 };
15465 let __s_b = self.gpu.stream();
15466 let mut b = __s_b.launch_builder(&f);
15467 b.arg(&qb)
15468 .arg(&kb)
15469 .arg(&vb)
15470 .arg(o)
15471 .arg(&hd)
15472 .arg(&nh)
15473 .arg(&nhkv)
15474 .arg(&ti)
15475 .arg(&tkvi)
15476 .arg(&scale)
15477 .arg(&cz);
15478 unsafe {
15479 b.launch(cfg)?;
15480 }
15481 return Ok(());
15482 }
15483 const BLOCK_Q: usize = 32;
15484 const BK: usize = 32;
15485 const HALF: usize = 256;
15486 let f = self.func(if f32_stage {
15487 "fa_prefill_f32_hd512"
15488 } else {
15489 "fa_prefill_bf16_hd512"
15490 });
15491 let shmem = (2 * (BLOCK_Q * head_dim + BK * head_dim + BK * HALF + BLOCK_Q * BK)
15493 + 4 * BLOCK_Q) as u32;
15494 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15495 f.set_attribute(
15496 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15497 shmem as i32,
15498 )?;
15499 let cfg = LaunchConfig {
15500 grid_dim: (
15501 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15502 n_head as u32,
15503 2,
15504 ),
15505 block_dim: (32, 2, 1),
15506 shared_mem_bytes: shmem,
15507 };
15508 let (hd, nh, nhkv, ti, tkvi, cz) = (
15509 head_dim as i32,
15510 n_head as i32,
15511 n_head_kv as i32,
15512 t as i32,
15513 t_kv as i32,
15514 causal as i32,
15515 );
15516 if f32_stage {
15517 let __s_b = self.gpu.stream();
15518 let mut b = __s_b.launch_builder(&f);
15519 b.arg(q)
15520 .arg(k)
15521 .arg(v)
15522 .arg(o)
15523 .arg(&hd)
15524 .arg(&nh)
15525 .arg(&nhkv)
15526 .arg(&ti)
15527 .arg(&tkvi)
15528 .arg(&scale)
15529 .arg(&cz);
15530 unsafe {
15531 b.launch(cfg)?;
15532 }
15533 } else {
15534 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15535 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15536 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15537 let __s_b = self.gpu.stream();
15538 let mut b = __s_b.launch_builder(&f);
15539 b.arg(&qb)
15540 .arg(&kb)
15541 .arg(&vb)
15542 .arg(o)
15543 .arg(&hd)
15544 .arg(&nh)
15545 .arg(&nhkv)
15546 .arg(&ti)
15547 .arg(&tkvi)
15548 .arg(&scale)
15549 .arg(&cz);
15550 unsafe {
15551 b.launch(cfg)?;
15552 }
15553 }
15554 Ok(())
15555 }
15556
15557 #[allow(clippy::too_many_arguments)]
15561 pub fn rope_neox2_bf16e(
15562 &self,
15563 q: &mut CudaSlice<f32>,
15564 k: &mut CudaSlice<f32>,
15565 qb: &mut CudaSlice<u8>,
15566 kb: &mut CudaSlice<u8>,
15567 pos: &CudaSlice<i32>,
15568 head_dim: usize,
15569 n_dims: usize,
15570 nh_q: usize,
15571 nh_k: usize,
15572 n_tokens: usize,
15573 base: f32,
15574 freq_scale: f32,
15575 ff: Option<&CudaSlice<f32>>,
15576 ) -> Result<(), Box<dyn std::error::Error>> {
15577 let f = self.func("rope_neox2_bf16e_f32");
15578 let rows = ((nh_q + nh_k) * n_tokens) as u32;
15579 let cfg = LaunchConfig {
15580 grid_dim: (rows, 1, 1),
15581 block_dim: ((head_dim / 2) as u32, 1, 1),
15582 shared_mem_bytes: 0,
15583 };
15584 let theta_scale = base.powf(-2.0 / n_dims as f32);
15585 let (hd, nd, nhq, nhk, nt) = (
15586 head_dim as i32,
15587 n_dims as i32,
15588 nh_q as i32,
15589 nh_k as i32,
15590 n_tokens as i32,
15591 );
15592 let __s_b = self.gpu.stream();
15593 let mut b = __s_b.launch_builder(&f);
15594 match ff {
15595 Some(t) => {
15596 b.arg(&mut *q)
15597 .arg(&mut *k)
15598 .arg(&mut *qb)
15599 .arg(&mut *kb)
15600 .arg(pos)
15601 .arg(&hd)
15602 .arg(&nd)
15603 .arg(&nhq)
15604 .arg(&nhk)
15605 .arg(&nt)
15606 .arg(&theta_scale)
15607 .arg(&freq_scale)
15608 .arg(t);
15609 unsafe {
15610 b.launch(cfg)?;
15611 }
15612 }
15613 None => {
15614 let null: u64 = 0;
15615 b.arg(&mut *q)
15616 .arg(&mut *k)
15617 .arg(&mut *qb)
15618 .arg(&mut *kb)
15619 .arg(pos)
15620 .arg(&hd)
15621 .arg(&nd)
15622 .arg(&nhq)
15623 .arg(&nhk)
15624 .arg(&nt)
15625 .arg(&theta_scale)
15626 .arg(&freq_scale)
15627 .arg(&null);
15628 unsafe {
15629 b.launch(cfg)?;
15630 }
15631 }
15632 }
15633 Ok(())
15634 }
15635
15636 pub fn f32_to_bf16(
15639 &self,
15640 x: &CudaSlice<f32>,
15641 n: usize,
15642 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
15643 assert!(n % 4 == 0, "f32_to_bf16 requires n % 4 == 0, got {n}");
15644 let mut y = self.alloc_uninit::<u8>(n * 2)?;
15645 let f = self.func("f32_to_bf16_flat");
15646 let n_i = n as i64;
15647 let cfg = LaunchConfig {
15648 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
15649 block_dim: (256, 1, 1),
15650 shared_mem_bytes: 0,
15651 };
15652 let __s_b = self.gpu.stream();
15653 let mut b = __s_b.launch_builder(&f);
15654 b.arg(x).arg(&mut y).arg(&n_i);
15655 unsafe {
15656 b.launch(cfg)?;
15657 }
15658 Ok(y)
15659 }
15660
15661 pub fn f32_to_f16(
15662 &self,
15663 x: &CudaSlice<f32>,
15664 n: usize,
15665 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
15666 assert!(n % 4 == 0, "f32_to_f16 requires n % 4 == 0, got {n}");
15667 let mut y = self.alloc_uninit::<u8>(n * 2)?;
15668 let f = self.func("f32_to_f16_flat");
15669 let n_i = n as i64;
15670 let cfg = LaunchConfig {
15671 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
15672 block_dim: (256, 1, 1),
15673 shared_mem_bytes: 0,
15674 };
15675 let __s_b = self.gpu.stream();
15676 let mut b = __s_b.launch_builder(&f);
15677 b.arg(x).arg(&mut y).arg(&n_i);
15678 unsafe {
15679 b.launch(cfg)?;
15680 }
15681 Ok(y)
15682 }
15683
15684 pub fn bf16_to_f16(
15686 &self,
15687 xb: &CudaSlice<u8>,
15688 n: usize,
15689 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
15690 let mut y = self.alloc_uninit::<u8>(n * 2)?;
15691 self.bf16_to_f16_into(xb, n, &mut y)?;
15692 Ok(y)
15693 }
15694
15695 pub fn bf16_to_f16_into(
15697 &self,
15698 xb: &CudaSlice<u8>,
15699 n: usize,
15700 y: &mut CudaSlice<u8>,
15701 ) -> Result<(), Box<dyn std::error::Error>> {
15702 assert!(n % 2 == 0, "bf16_to_f16 requires n % 2 == 0, got {n}");
15703 assert!(y.len() >= n * 2);
15704 let f = self.func("bf16_to_f16_flat");
15705 let n2 = (n / 2) as i64;
15706 let cfg = LaunchConfig {
15707 grid_dim: (((n / 2) as u32).div_ceil(256), 1, 1),
15708 block_dim: (256, 1, 1),
15709 shared_mem_bytes: 0,
15710 };
15711 let __s_b = self.gpu.stream();
15712 let mut b = __s_b.launch_builder(&f);
15713 b.arg(xb).arg(y).arg(&n2);
15714 unsafe {
15715 b.launch(cfg)?;
15716 }
15717 Ok(())
15718 }
15719
15720 #[allow(clippy::too_many_arguments)]
15725 pub fn fa_prefill_vl8(
15726 &self,
15727 seqs: &[FaSeqVl],
15728 head_dim: usize,
15729 n_head: usize,
15730 n_head_kv: usize,
15731 scale: f32,
15732 ) -> Result<(), Box<dyn std::error::Error>> {
15733 const BK: usize = 32;
15734 let b = seqs.len();
15735 assert!(b >= 1 && b <= 8);
15736 let mut packed = [FaSeqVl::default(); 8];
15737 packed[..b].copy_from_slice(seqs);
15738 let v = FaVl8(packed);
15739 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
15740 let ept = (n_head_kv * head_dim) as i32;
15741 {
15742 let f = self.func("fa_mirror_vl");
15743 let max_n = (max_t as i64) * ept as i64;
15744 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
15745 for which in 0..2i32 {
15746 let cfg = LaunchConfig {
15747 grid_dim: (blocks, 1, b as u32),
15748 block_dim: (256, 1, 1),
15749 shared_mem_bytes: 0,
15750 };
15751 let __s_lb = self.gpu.stream();
15752 let mut lb = __s_lb.launch_builder(&f);
15753 lb.arg(&v).arg(&ept).arg(&which);
15754 unsafe {
15755 lb.launch(cfg)?;
15756 }
15757 }
15758 }
15759 let hd_sfx = fa_hd_suffix(head_dim)?;
15760 let f = self.func(&format!("fa_prefill_bf16kv_vl{hd_sfx}"));
15761 let block_q = 64usize;
15762 let kv_stages = 2usize;
15763 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
15764 + 4 * (block_q * BK + 2 * block_q)) as u32;
15765 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15766 f.set_attribute(
15767 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15768 shmem as i32,
15769 )?;
15770 let cfg = LaunchConfig {
15771 grid_dim: (max_t.div_ceil(block_q as u32), n_head as u32, b as u32),
15772 block_dim: (32, 4, 1),
15773 shared_mem_bytes: shmem,
15774 };
15775 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
15776 let __s_lb = self.gpu.stream();
15777 let mut lb = __s_lb.launch_builder(&f);
15778 lb.arg(&v).arg(&hd).arg(&nh).arg(&nhkv).arg(&scale);
15779 unsafe {
15780 lb.launch(cfg)?;
15781 }
15782 Ok(())
15783 }
15784
15785 #[allow(clippy::too_many_arguments)]
15789 pub fn attn_pre_vl8(
15790 &self,
15791 seqs: &[AttnPreVl],
15792 wq: &CudaSlice<f32>,
15793 wk: &CudaSlice<f32>,
15794 head_dim: usize,
15795 rope_dims: usize,
15796 n_head: usize,
15797 n_head_kv: usize,
15798 eps: f32,
15799 freq_base: f32,
15800 freq_scale: f32,
15801 kv_dim_k: usize,
15802 kv_dim_v: usize,
15803 k_tok_bytes: usize,
15804 v_tok_bytes: usize,
15805 ) -> Result<(), Box<dyn std::error::Error>> {
15806 let b = seqs.len();
15807 assert!(b >= 1 && b <= 8);
15808 let mut packed = [AttnPreVl::default(); 8];
15809 packed[..b].copy_from_slice(seqs);
15810 let v = AttnPreVl8(packed);
15811 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
15812 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
15813 {
15814 let f = self.func("q_gate_split_vl");
15815 let n = max_t * (n_head * head_dim) as u32;
15816 let cfg = LaunchConfig {
15817 grid_dim: (n.div_ceil(256), 1, b as u32),
15818 block_dim: (256, 1, 1),
15819 shared_mem_bytes: 0,
15820 };
15821 let __s_lb = self.gpu.stream();
15822 let mut lb = __s_lb.launch_builder(&f);
15823 lb.arg(&v).arg(&hd).arg(&nh);
15824 unsafe {
15825 lb.launch(cfg)?;
15826 }
15827 }
15828 {
15829 let f = self.func("attn_rms_vl");
15830 let cfg = LaunchConfig {
15831 grid_dim: (max_t * n_head as u32, 2, b as u32),
15832 block_dim: (rms_block(), 1, 1),
15833 shared_mem_bytes: 0,
15834 };
15835 let __s_lb = self.gpu.stream();
15836 let mut lb = __s_lb.launch_builder(&f);
15837 lb.arg(&v)
15838 .arg(wq)
15839 .arg(wk)
15840 .arg(&hd)
15841 .arg(&nh)
15842 .arg(&nhkv)
15843 .arg(&eps);
15844 unsafe {
15845 lb.launch(cfg)?;
15846 }
15847 }
15848 {
15849 let f = self.func("attn_rope_vl");
15850 let theta_scale = freq_base.powf(-2.0 / rope_dims as f32);
15851 let nd = rope_dims as i32;
15852 let cfg = LaunchConfig {
15853 grid_dim: (max_t * n_head as u32, 2, b as u32),
15854 block_dim: ((head_dim / 2) as u32, 1, 1),
15855 shared_mem_bytes: 0,
15856 };
15857 let __s_lb = self.gpu.stream();
15858 let mut lb = __s_lb.launch_builder(&f);
15859 lb.arg(&v)
15860 .arg(&hd)
15861 .arg(&nd)
15862 .arg(&nh)
15863 .arg(&nhkv)
15864 .arg(&theta_scale)
15865 .arg(&freq_scale);
15866 unsafe {
15867 lb.launch(cfg)?;
15868 }
15869 }
15870 {
15871 let f = self.func("append_kv_vl");
15872 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
15873 let cfg = LaunchConfig {
15874 grid_dim: (nblk, max_t, b as u32),
15875 block_dim: (32, 1, 1),
15876 shared_mem_bytes: 0,
15877 };
15878 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
15879 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
15880 let __s_lb = self.gpu.stream();
15881 let mut lb = __s_lb.launch_builder(&f);
15882 lb.arg(&v).arg(&kdk).arg(&kdv).arg(&ktb).arg(&vtb);
15883 unsafe {
15884 lb.launch(cfg)?;
15885 }
15886 }
15887 Ok(())
15888 }
15889
15890 pub fn fa_prefill_view(
15895 &self,
15896 q: &CudaSlice<f32>,
15897 k: &cudarc::driver::CudaView<u8>,
15898 v: &cudarc::driver::CudaView<u8>,
15899 o: &mut CudaSlice<f32>,
15900 head_dim: usize,
15901 n_head: usize,
15902 n_head_kv: usize,
15903 t: usize,
15904 t_kv: usize,
15905 scale: f32,
15906 causal: bool,
15907 k_tok_bytes: usize,
15908 v_tok_bytes: usize,
15909 g: bool,
15910 ) -> Result<(), Box<dyn std::error::Error>> {
15911 if portable_mma_gated() {
15912 return self.sdpa_naive_quantized_view(
15913 q,
15914 k,
15915 v,
15916 o,
15917 head_dim,
15918 n_head,
15919 n_head_kv,
15920 t,
15921 t_kv,
15922 scale,
15923 causal,
15924 k_tok_bytes,
15925 v_tok_bytes,
15926 );
15927 }
15928 const BLOCK_Q: usize = 64;
15929 const BK: usize = 32;
15930 let name = format!("fa_prefill_q{}", fa_hd_suffix(head_dim)?);
15933 let f = if g {
15934 self.func_g(&name)
15935 } else {
15936 self.func(&name)
15937 };
15938 let shmem =
15939 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15940 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15941 f.set_attribute(
15942 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15943 shmem as i32,
15944 )?;
15945 let cfg = LaunchConfig {
15946 grid_dim: (
15947 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15948 n_head as u32,
15949 1,
15950 ),
15951 block_dim: (32, 4, 1),
15952 shared_mem_bytes: shmem,
15953 };
15954 let (hd, nh, nhkv, ti, tkvi, cz) = (
15955 head_dim as i32,
15956 n_head as i32,
15957 n_head_kv as i32,
15958 t as i32,
15959 t_kv as i32,
15960 causal as i32,
15961 );
15962 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
15963 let __s_b = self.gpu.stream();
15964 let mut b = __s_b.launch_builder(&f);
15965 b.arg(q)
15966 .arg(k)
15967 .arg(v)
15968 .arg(o)
15969 .arg(&hd)
15970 .arg(&nh)
15971 .arg(&nhkv)
15972 .arg(&ti)
15973 .arg(&tkvi)
15974 .arg(&scale)
15975 .arg(&cz)
15976 .arg(&ktb)
15977 .arg(&vtb);
15978 unsafe {
15979 b.launch(cfg)?;
15980 }
15981 Ok(())
15982 }
15983
15984 #[allow(clippy::too_many_arguments)]
15994 pub fn fa_prefill_view_ws(
15995 &self,
15996 q: &CudaSlice<f32>,
15997 k: &cudarc::driver::CudaView<u8>,
15998 v: &cudarc::driver::CudaView<u8>,
15999 o: &mut CudaSlice<f32>,
16000 head_dim: usize,
16001 n_head: usize,
16002 n_head_kv: usize,
16003 t: usize,
16004 t_kv: usize,
16005 scale: f32,
16006 causal: bool,
16007 k_tok_bytes: usize,
16008 v_tok_bytes: usize,
16009 g: bool,
16010 ) -> Result<(), Box<dyn std::error::Error>> {
16011 if portable_mma_gated() {
16012 return self.sdpa_naive_quantized_view(
16013 q,
16014 k,
16015 v,
16016 o,
16017 head_dim,
16018 n_head,
16019 n_head_kv,
16020 t,
16021 t_kv,
16022 scale,
16023 causal,
16024 k_tok_bytes,
16025 v_tok_bytes,
16026 );
16027 }
16028 const BLOCK_Q: usize = 64;
16029 const BK: usize = 32;
16030 let kv_dim_k = n_head_kv * head_dim;
16031 let kv_dim_v = n_head_kv * head_dim;
16032 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
16034 let mut guard = self.prime_deqw_ws.lock().unwrap();
16036 let need_grow = match guard.as_ref() {
16037 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
16038 None => true,
16039 };
16040 if need_grow {
16041 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
16042 let (ck, cv) = guard
16043 .as_ref()
16044 .map(|(a, b)| (a.len(), b.len()))
16045 .unwrap_or((0, 0));
16046 *guard = Some((
16047 self.alloc_u8(grow(ck, k_ws_bytes))?,
16048 self.alloc_u8(grow(cv, v_ws_bytes))?,
16049 ));
16050 }
16051 let (kw, vw) = guard.as_mut().unwrap();
16052 {
16054 let f = if g {
16056 self.func_g("fa_dequant_kv_ws_bf16")
16057 } else {
16058 self.func("fa_dequant_kv_ws_bf16")
16059 };
16060 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
16061 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
16062 let cfg = LaunchConfig {
16063 grid_dim: (nblk.max(1), 1, 1),
16064 block_dim: (256, 1, 1),
16065 shared_mem_bytes: 0,
16066 };
16067 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
16068 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16069 let __s_b = self.gpu.stream();
16070 let mut b = __s_b.launch_builder(&f);
16071 b.arg(k)
16072 .arg(v)
16073 .arg(&mut *kw)
16074 .arg(&mut *vw)
16075 .arg(&kdk)
16076 .arg(&kdv)
16077 .arg(&tkvi)
16078 .arg(&ktb)
16079 .arg(&vtb);
16080 unsafe {
16081 b.launch(cfg)?;
16082 }
16083 }
16084 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
16092 .map(|v| v != "0")
16093 .unwrap_or(true);
16094 {
16095 let hd_sfx = fa_hd_suffix(head_dim)?;
16096 let f = self.func(&format!(
16097 "fa_prefill_qw{}{hd_sfx}",
16098 if db { "_db" } else { "" }
16099 ));
16100 let shmem = if db {
16101 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
16103 } else {
16104 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
16105 };
16106 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16107 f.set_attribute(
16108 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16109 shmem as i32,
16110 )?;
16111 let cfg = LaunchConfig {
16112 grid_dim: (
16113 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16114 n_head as u32,
16115 1,
16116 ),
16117 block_dim: (32, 4, 1),
16118 shared_mem_bytes: shmem,
16119 };
16120 let (hd, nh, nhkv, ti, tkvi, cz) = (
16121 head_dim as i32,
16122 n_head as i32,
16123 n_head_kv as i32,
16124 t as i32,
16125 t_kv as i32,
16126 causal as i32,
16127 );
16128 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
16129 let __s_b = self.gpu.stream();
16130 let mut b = __s_b.launch_builder(&f);
16131 b.arg(q)
16132 .arg(&*kw)
16133 .arg(&*vw)
16134 .arg(o)
16135 .arg(&hd)
16136 .arg(&nh)
16137 .arg(&nhkv)
16138 .arg(&ti)
16139 .arg(&tkvi)
16140 .arg(&scale)
16141 .arg(&cz)
16142 .arg(&kdk)
16143 .arg(&kdv);
16144 unsafe {
16145 b.launch(cfg)?;
16146 }
16147 }
16148 Ok(())
16149 }
16150
16151 #[allow(clippy::too_many_arguments)]
16167 pub fn fa_prefill_view_ws_w_hd128(
16168 &self,
16169 q: &CudaSlice<f32>,
16170 k: &cudarc::driver::CudaView<u8>,
16171 v: &cudarc::driver::CudaView<u8>,
16172 o: &mut CudaSlice<f32>,
16173 head_dim: usize,
16174 n_head: usize,
16175 n_head_kv: usize,
16176 t: usize,
16177 t_kv: usize,
16178 scale: f32,
16179 causal: bool,
16180 window: usize,
16181 k_tok_bytes: usize,
16182 v_tok_bytes: usize,
16183 ) -> Result<(), Box<dyn std::error::Error>> {
16184 assert_eq!(
16185 head_dim, 128,
16186 "fa_prefill_view_ws_w_hd128: only the hd128 twin is stamped"
16187 );
16188 if portable_mma_gated() {
16189 return self.sdpa_naive_w_quantized_view(
16190 q,
16191 k,
16192 v,
16193 o,
16194 head_dim,
16195 n_head,
16196 n_head_kv,
16197 t,
16198 t_kv,
16199 scale,
16200 causal,
16201 window,
16202 k_tok_bytes,
16203 v_tok_bytes,
16204 );
16205 }
16206 const BLOCK_Q: usize = 64;
16207 const BK: usize = 32;
16208 let kv_dim_k = n_head_kv * head_dim;
16209 let kv_dim_v = n_head_kv * head_dim;
16210 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
16212 let mut guard = self.prime_deqw_ws.lock().unwrap();
16213 let need_grow = match guard.as_ref() {
16214 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
16215 None => true,
16216 };
16217 if need_grow {
16218 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
16219 let (ck, cv) = guard
16220 .as_ref()
16221 .map(|(a, b)| (a.len(), b.len()))
16222 .unwrap_or((0, 0));
16223 *guard = Some((
16224 self.alloc_u8(grow(ck, k_ws_bytes))?,
16225 self.alloc_u8(grow(cv, v_ws_bytes))?,
16226 ));
16227 }
16228 let (kw, vw) = guard.as_mut().unwrap();
16229 {
16232 let f = self.func("fa_dequant_kv_ws_bf16");
16233 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
16234 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
16235 let cfg = LaunchConfig {
16236 grid_dim: (nblk.max(1), 1, 1),
16237 block_dim: (256, 1, 1),
16238 shared_mem_bytes: 0,
16239 };
16240 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
16241 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16242 let __s_b = self.gpu.stream();
16243 let mut b = __s_b.launch_builder(&f);
16244 b.arg(k)
16245 .arg(v)
16246 .arg(&mut *kw)
16247 .arg(&mut *vw)
16248 .arg(&kdk)
16249 .arg(&kdv)
16250 .arg(&tkvi)
16251 .arg(&ktb)
16252 .arg(&vtb);
16253 unsafe {
16254 b.launch(cfg)?;
16255 }
16256 }
16257 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
16259 .map(|v| v != "0")
16260 .unwrap_or(true);
16261 {
16262 let f = self.func(if db {
16263 "fa_prefill_qw_db_w_hd128"
16264 } else {
16265 "fa_prefill_qw_w_hd128"
16266 });
16267 let shmem = if db {
16268 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
16269 } else {
16270 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
16271 };
16272 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16273 f.set_attribute(
16274 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16275 shmem as i32,
16276 )?;
16277 let cfg = LaunchConfig {
16278 grid_dim: (
16279 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16280 n_head as u32,
16281 1,
16282 ),
16283 block_dim: (32, 4, 1),
16284 shared_mem_bytes: shmem,
16285 };
16286 let (hd, nh, nhkv, ti, tkvi, cz) = (
16287 head_dim as i32,
16288 n_head as i32,
16289 n_head_kv as i32,
16290 t as i32,
16291 t_kv as i32,
16292 causal as i32,
16293 );
16294 let (kdk, kdv, wnd) = (kv_dim_k as i32, kv_dim_v as i32, window as i32);
16295 let __s_b = self.gpu.stream();
16296 let mut b = __s_b.launch_builder(&f);
16297 b.arg(q)
16298 .arg(&*kw)
16299 .arg(&*vw)
16300 .arg(o)
16301 .arg(&hd)
16302 .arg(&nh)
16303 .arg(&nhkv)
16304 .arg(&ti)
16305 .arg(&tkvi)
16306 .arg(&scale)
16307 .arg(&cz)
16308 .arg(&kdk)
16309 .arg(&kdv)
16310 .arg(&wnd);
16311 unsafe {
16312 b.launch(cfg)?;
16313 }
16314 }
16315 Ok(())
16316 }
16317
16318 pub fn fa_decode(
16322 &self,
16323 q: &CudaSlice<f32>,
16324 k: &cudarc::driver::CudaView<u8>,
16325 v: &cudarc::driver::CudaView<u8>,
16326 o: &mut CudaSlice<f32>,
16327 head_dim: usize,
16328 n_head: usize,
16329 n_head_kv: usize,
16330 t_kv: usize,
16331 scale: f32,
16332 k_tok_bytes: usize,
16333 v_tok_bytes: usize,
16334 ) -> Result<(), Box<dyn std::error::Error>> {
16335 self.fa_decode_kvmod(
16336 q,
16337 k,
16338 v,
16339 o,
16340 head_dim,
16341 n_head,
16342 n_head_kv,
16343 t_kv,
16344 scale,
16345 k_tok_bytes,
16346 v_tok_bytes,
16347 false,
16348 )
16349 }
16350
16351 #[allow(clippy::too_many_arguments)]
16355 #[allow(clippy::too_many_arguments)]
16359 #[allow(clippy::too_many_arguments)]
16360 fn fa_decode_scalar_unified(
16361 &self,
16362 q: &cudarc::driver::CudaView<f32>,
16363 k: &cudarc::driver::CudaView<u8>,
16364 v: &cudarc::driver::CudaView<u8>,
16365 o: &mut cudarc::driver::CudaViewMut<f32>,
16366 head_dim: usize,
16367 n_head: usize,
16368 n_head_kv: usize,
16369 t_kv_host: usize,
16370 t_kv_dev: Option<&CudaSlice<i32>>,
16371 scale: f32,
16372 n_splits: usize,
16373 split_keys: usize,
16374 k_tok_bytes: usize,
16375 v_tok_bytes: usize,
16376 g: bool,
16377 part_o: &mut CudaSlice<f32>,
16378 part_m: &mut CudaSlice<f32>,
16379 part_l: &mut CudaSlice<f32>,
16380 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
16381 ) -> Result<(), Box<dyn std::error::Error>> {
16382 let f = if g {
16383 self.func_g("fa_decode_f32")
16384 } else {
16385 self.fa_func("fa_decode_f32", head_dim)
16386 };
16387 let cfg = LaunchConfig {
16388 grid_dim: (n_head as u32, n_splits as u32, 1),
16389 block_dim: (head_dim as u32, 1, 1),
16390 shared_mem_bytes: (4 * (head_dim + 32)) as u32,
16391 };
16392 let (hd, nh, nhkv, nsp) = (
16393 head_dim as i32,
16394 n_head as i32,
16395 n_head_kv as i32,
16396 n_splits as i32,
16397 );
16398 let (ktb, vtb, tkvi, ski) = (
16399 k_tok_bytes as i64,
16400 v_tok_bytes as i64,
16401 t_kv_host as i32,
16402 split_keys as i32,
16403 );
16404 let __s_b = self.gpu.stream();
16405 let mut b = __s_b.launch_builder(&f);
16406 match t_kv_dev {
16407 Some(d) => {
16408 b.arg(q)
16409 .arg(k)
16410 .arg(v)
16411 .arg(&mut *part_o)
16412 .arg(&mut *part_m)
16413 .arg(&mut *part_l)
16414 .arg(&hd)
16415 .arg(&nh)
16416 .arg(&nhkv)
16417 .arg(&tkvi)
16418 .arg(d)
16419 .arg(&scale)
16420 .arg(&nsp)
16421 .arg(&ski)
16422 .arg(&ktb)
16423 .arg(&vtb);
16424 unsafe {
16425 b.launch(cfg)?;
16426 }
16427 }
16428 None => {
16429 let null: u64 = 0;
16430 b.arg(q)
16431 .arg(k)
16432 .arg(v)
16433 .arg(&mut *part_o)
16434 .arg(&mut *part_m)
16435 .arg(&mut *part_l)
16436 .arg(&hd)
16437 .arg(&nh)
16438 .arg(&nhkv)
16439 .arg(&tkvi)
16440 .arg(&null)
16441 .arg(&scale)
16442 .arg(&nsp)
16443 .arg(&ski)
16444 .arg(&ktb)
16445 .arg(&vtb);
16446 unsafe {
16447 b.launch(cfg)?;
16448 }
16449 }
16450 }
16451 let cfg2 = LaunchConfig {
16452 grid_dim: (n_head as u32, 1, 1),
16453 block_dim: (head_dim as u32, 1, 1),
16454 shared_mem_bytes: 0,
16455 };
16456 if let Some((oq, od)) = q8_out {
16457 let fc = if g {
16459 self.func_g("fa_decode_combine_q8_1")
16460 } else {
16461 self.fa_func("fa_decode_combine_q8_1", head_dim)
16462 };
16463 let __s_b2 = self.gpu.stream();
16464 let mut b2 = __s_b2.launch_builder(&fc);
16465 b2.arg(&*part_o)
16466 .arg(&*part_m)
16467 .arg(&*part_l)
16468 .arg(oq)
16469 .arg(od)
16470 .arg(&hd)
16471 .arg(&nh)
16472 .arg(&nsp);
16473 unsafe {
16474 b2.launch(cfg2)?;
16475 }
16476 return Ok(());
16477 }
16478 let fc = if g {
16479 self.func_g("fa_decode_combine_f32")
16480 } else {
16481 self.fa_func("fa_decode_combine_f32", head_dim)
16482 };
16483 let __s_b2 = self.gpu.stream();
16484 let mut b2 = __s_b2.launch_builder(&fc);
16485 b2.arg(&*part_o)
16486 .arg(&*part_m)
16487 .arg(&*part_l)
16488 .arg(o)
16489 .arg(&hd)
16490 .arg(&nh)
16491 .arg(&nsp);
16492 unsafe {
16493 b2.launch(cfg2)?;
16494 }
16495 Ok(())
16496 }
16497
16498 pub fn fa_decode_kvmod(
16499 &self,
16500 q: &CudaSlice<f32>,
16501 k: &cudarc::driver::CudaView<u8>,
16502 v: &cudarc::driver::CudaView<u8>,
16503 o: &mut CudaSlice<f32>,
16504 head_dim: usize,
16505 n_head: usize,
16506 n_head_kv: usize,
16507 t_kv: usize,
16508 scale: f32,
16509 k_tok_bytes: usize,
16510 v_tok_bytes: usize,
16511 g: bool,
16512 ) -> Result<(), Box<dyn std::error::Error>> {
16513 let q_view = q.as_view();
16514 let mut o_view = o.as_view_mut();
16515 self.fa_decode_kvmod_view(
16516 &q_view,
16517 k,
16518 v,
16519 &mut o_view,
16520 head_dim,
16521 n_head,
16522 n_head_kv,
16523 t_kv,
16524 scale,
16525 k_tok_bytes,
16526 v_tok_bytes,
16527 g,
16528 )
16529 }
16530
16531 #[allow(clippy::too_many_arguments)]
16536 pub fn fa_decode_kvmod_view(
16537 &self,
16538 q: &cudarc::driver::CudaView<f32>,
16539 k: &cudarc::driver::CudaView<u8>,
16540 v: &cudarc::driver::CudaView<u8>,
16541 o: &mut cudarc::driver::CudaViewMut<f32>,
16542 head_dim: usize,
16543 n_head: usize,
16544 n_head_kv: usize,
16545 t_kv: usize,
16546 scale: f32,
16547 k_tok_bytes: usize,
16548 v_tok_bytes: usize,
16549 g: bool,
16550 ) -> Result<(), Box<dyn std::error::Error>> {
16551 let mut fa_vec = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
16572 if g && head_dim == 256 && !fa_v4_at(t_kv) {
16576 fa_vec = false;
16577 }
16578 let sp = fa_split_keys(t_kv, n_head_kv);
16579 let n_splits = if fa_vec {
16580 ((t_kv + sp - 1) / sp).max(1)
16581 } else {
16582 ((t_kv + 255) / 256).max(1)
16583 };
16584 let o_len = n_head * n_splits * head_dim;
16585 let ml_len = n_head * n_splits;
16586 let mut part_guard = self.fa_part_pool.lock().unwrap();
16587 if part_guard
16588 .as_ref()
16589 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
16590 .unwrap_or(true)
16591 {
16592 let old = part_guard.take();
16603 let (co, cm) = old
16604 .as_ref()
16605 .map(|pp| (pp.0.len(), pp.1.len()))
16606 .unwrap_or((0, 0));
16607 if let Some(old) = old {
16608 self.fa_part_retired.lock().unwrap().push(old);
16609 }
16610 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
16611 eprintln!(
16612 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
16613 co, o_len, cm, ml_len
16614 );
16615 }
16616 *part_guard = Some((
16617 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
16618 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
16619 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
16620 ));
16621 }
16622 let pg = part_guard.as_mut().unwrap();
16623 self.gpu
16624 .stream()
16625 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
16626 self.gpu
16627 .stream()
16628 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
16629 self.gpu
16630 .stream()
16631 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
16632 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
16633 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
16634 let (hd, nh, nhkv, tkvi, nsp) = (
16635 head_dim as i32,
16636 n_head as i32,
16637 n_head_kv as i32,
16638 t_kv as i32,
16639 n_splits as i32,
16640 );
16641 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16642 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
16646 let fa512_min = fa512_min_tkv();
16651 let deep = fa_vec
16654 && head_dim == 256
16655 && fa_v4_at(t_kv)
16656 && !g
16657 && fa_deep_at(t_kv)
16658 && !matches!(fa_v4_mode(), "noB3" | "stage");
16659 let (f, cfg) = if fa_vec && head_dim == 512 && t_kv >= fa512_min {
16660 let gqa = (n_head / n_head_kv).max(1) as u32;
16663 let fv = self.fa_func("fa_decode_vec_q_dpl16", head_dim);
16664 (
16665 fv,
16666 LaunchConfig {
16667 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16668 block_dim: (32, gqa, 1),
16669 shared_mem_bytes: 0,
16670 },
16671 )
16672 } else if fa_vec && head_dim <= 256 {
16673 let gqa = (n_head / n_head_kv).max(1) as u32;
16674 static SMEM_TKV: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
16685 let smem_tkv = *SMEM_TKV.get_or_init(|| {
16686 std::env::var("MEMRA_FA_SMEM_TKV")
16687 .ok()
16688 .and_then(|v| v.parse().ok())
16689 .unwrap_or_else(|| {
16690 FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
16691 })
16692 });
16693 if fa_v4_at(t_kv) && head_dim == 256 {
16694 let v4name = match fa_v4_mode() {
16698 "noB3" => "fa_decode_vec_q_v4_noB3", "stage" => "fa_decode_vec_q_v4_stage", _ if deep => "fa_decode_vec_q_v4_deep",
16701 _ => "fa_decode_vec_q_v4",
16702 };
16703 let fv = if g {
16704 self.func_g(v4name)
16705 } else {
16706 self.func(v4name)
16707 };
16708 let shmem = (if deep { 12160 } else { 11520 }
16711 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
16712 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16713 fv.set_attribute(
16714 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16715 shmem as i32,
16716 )?;
16717 (
16718 fv,
16719 LaunchConfig {
16720 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16721 block_dim: (32, gqa, 1),
16722 shared_mem_bytes: shmem,
16723 },
16724 )
16725 } else if fa_v3_active(head_dim) {
16726 let fv = if g {
16729 self.func_g("fa_decode_vec_q_v3")
16730 } else {
16731 self.func("fa_decode_vec_q_v3")
16732 };
16733 let shmem = (32 * head_dim * 2) as u32; (
16735 fv,
16736 LaunchConfig {
16737 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16738 block_dim: (32, gqa, 1),
16739 shared_mem_bytes: shmem,
16740 },
16741 )
16742 } else if fa_v2_on() {
16743 let fv = if g {
16747 self.func_g("fa_decode_vec_q_v2")
16748 } else {
16749 self.func("fa_decode_vec_q_v2")
16750 };
16751 let shmem = (2 * 32 * head_dim * 2) as u32; (
16753 fv,
16754 LaunchConfig {
16755 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16756 block_dim: (32, gqa, 1),
16757 shared_mem_bytes: shmem,
16758 },
16759 )
16760 } else if smem_tkv > 0 && t_kv >= smem_tkv && !g && !(head_dim == 512 && Self::gkv_on())
16761 {
16762 let fv = if g {
16766 self.func_g("fa_decode_vec_q_smem")
16767 } else {
16768 self.func("fa_decode_vec_q_smem")
16769 };
16770 let shmem = (2 * 32 * head_dim * 2) as u32; use cudarc::driver::sys::CUfunction_attribute_enum as A;
16772 fv.set_attribute(
16773 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16774 shmem as i32,
16775 )?;
16776 (
16777 fv,
16778 LaunchConfig {
16779 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16780 block_dim: (32, gqa, 1),
16781 shared_mem_bytes: shmem,
16782 },
16783 )
16784 } else {
16785 let fv = if g {
16788 self.func_g("fa_decode_vec_q")
16789 } else {
16790 self.func("fa_decode_vec_q")
16791 };
16792 (
16793 fv,
16794 LaunchConfig {
16795 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16796 block_dim: (32, gqa, 1),
16797 shared_mem_bytes: 0,
16798 },
16799 )
16800 }
16801 } else {
16802 return self.fa_decode_scalar_unified(
16805 q,
16806 k,
16807 v,
16808 o,
16809 head_dim,
16810 n_head,
16811 n_head_kv,
16812 t_kv,
16813 None,
16814 scale,
16815 n_splits,
16816 if fa_vec { sp } else { 256 },
16817 k_tok_bytes,
16818 v_tok_bytes,
16819 g,
16820 part_o,
16821 part_m,
16822 part_l,
16823 None,
16824 );
16825 };
16826 let __s_b = self.gpu.stream();
16827 let mut b = __s_b.launch_builder(&f);
16828 b.arg(q)
16829 .arg(k)
16830 .arg(v)
16831 .arg(&mut *part_o)
16832 .arg(&mut *part_m)
16833 .arg(&mut *part_l)
16834 .arg(&hd)
16835 .arg(&nh)
16836 .arg(&nhkv)
16837 .arg(&tkvi)
16838 .arg(&scale)
16839 .arg(&nsp)
16840 .arg(&ktb)
16841 .arg(&vtb);
16842 unsafe {
16843 b.launch(cfg)?;
16844 }
16845 let (fc, cfg2) = (
16848 if g {
16849 self.func_g("fa_decode_combine_f32")
16850 } else {
16851 self.fa_func("fa_decode_combine_f32", head_dim)
16852 },
16853 LaunchConfig {
16854 grid_dim: (n_head as u32, 1, 1),
16855 block_dim: (head_dim as u32, 1, 1),
16856 shared_mem_bytes: 0,
16857 },
16858 );
16859 let __s_b2 = self.gpu.stream();
16860 let mut b2 = __s_b2.launch_builder(&fc);
16861 b2.arg(&*part_o)
16862 .arg(&*part_m)
16863 .arg(&*part_l)
16864 .arg(o)
16865 .arg(&hd)
16866 .arg(&nh)
16867 .arg(&nsp);
16868 unsafe {
16869 b2.launch(cfg2)?;
16870 }
16871 Ok(())
16872 }
16873
16874 #[allow(clippy::too_many_arguments)]
16885 pub fn fa_decode_batch_seqs_v4(
16886 &self,
16887 q: &CudaSlice<f32>,
16888 kv_ptrs: &cudarc::driver::CudaView<u64>,
16889 pos_seq: &CudaSlice<i32>,
16890 o: &mut CudaSlice<f32>,
16891 head_dim: usize,
16892 n_head: usize,
16893 n_head_kv: usize,
16894 b_n: usize,
16895 t_kv_max: usize,
16896 scale: f32,
16897 split_keys: usize,
16898 k_tok_bytes: usize,
16899 v_tok_bytes: usize,
16900 ) -> Result<(), Box<dyn std::error::Error>> {
16901 debug_assert!(head_dim == 256, "seqs twin is v4-stamped (hd256 only)");
16902 let n_splits_max = (t_kv_max + split_keys - 1) / split_keys;
16903 let o_len = b_n * n_head * n_splits_max * head_dim;
16904 let ml_len = b_n * n_head * n_splits_max;
16905 let mut part_guard = self.fa_part_pool.lock().unwrap();
16906 if part_guard
16907 .as_ref()
16908 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
16909 .unwrap_or(true)
16910 {
16911 let old = part_guard.take();
16922 let (co, cm) = old
16923 .as_ref()
16924 .map(|pp| (pp.0.len(), pp.1.len()))
16925 .unwrap_or((0, 0));
16926 if let Some(old) = old {
16927 self.fa_part_retired.lock().unwrap().push(old);
16928 }
16929 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
16930 eprintln!(
16931 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
16932 co, o_len, cm, ml_len
16933 );
16934 }
16935 *part_guard = Some((
16936 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
16937 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
16938 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
16939 ));
16940 }
16941 let pg = part_guard.as_mut().unwrap();
16942 self.gpu
16943 .stream()
16944 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
16945 self.gpu
16946 .stream()
16947 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
16948 self.gpu
16949 .stream()
16950 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
16951 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
16952 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
16953 let (nspm, spk) = (n_splits_max as i32, split_keys as i32);
16954 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16955 let gqa = (n_head / n_head_kv).max(1) as u32;
16956 let f = self.func("fa_decode_vec_q_seqs_v4");
16957 let shmem = (11520 + 32 * head_dim * 2) as u32;
16959 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16960 f.set_attribute(
16961 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16962 shmem as i32,
16963 )?;
16964 let cfg = LaunchConfig {
16965 grid_dim: (n_head_kv as u32, n_splits_max as u32, b_n as u32),
16966 block_dim: (32, gqa, 1),
16967 shared_mem_bytes: shmem,
16968 };
16969 {
16970 let __s_b = self.gpu.stream();
16971 let mut b = __s_b.launch_builder(&f);
16972 b.arg(q)
16973 .arg(kv_ptrs)
16974 .arg(pos_seq)
16975 .arg(&mut *part_o)
16976 .arg(&mut *part_m)
16977 .arg(&mut *part_l)
16978 .arg(&hd)
16979 .arg(&nh)
16980 .arg(&nhkv)
16981 .arg(&scale)
16982 .arg(&nspm)
16983 .arg(&spk)
16984 .arg(&ktb)
16985 .arg(&vtb);
16986 unsafe {
16987 b.launch(cfg)?;
16988 }
16989 }
16990 let fc = self.func("fa_decode_combine_seqs");
16991 let cfg2 = LaunchConfig {
16992 grid_dim: (n_head as u32, b_n as u32, 1),
16993 block_dim: (head_dim as u32, 1, 1),
16994 shared_mem_bytes: 0,
16995 };
16996 let __s_b2 = self.gpu.stream();
16997 let mut b2 = __s_b2.launch_builder(&fc);
16998 b2.arg(&*part_o)
16999 .arg(&*part_m)
17000 .arg(&*part_l)
17001 .arg(o)
17002 .arg(&hd)
17003 .arg(&nh)
17004 .arg(pos_seq)
17005 .arg(&nspm)
17006 .arg(&spk);
17007 unsafe {
17008 b2.launch(cfg2)?;
17009 }
17010 Ok(())
17011 }
17012
17013 #[allow(clippy::too_many_arguments)]
17020 pub fn append_kv_quantized_seqs(
17021 &self,
17022 k_rows: &CudaSlice<f32>,
17023 v_rows: &CudaSlice<f32>,
17024 kv_ptrs: &cudarc::driver::CudaView<u64>,
17025 pos_seq: &CudaSlice<i32>,
17026 b_n: usize,
17027 kv_dim_k: usize,
17028 kv_dim_v: usize,
17029 k_tok_bytes: usize,
17030 v_tok_bytes: usize,
17031 ) -> Result<(), Box<dyn std::error::Error>> {
17032 let f = self.func("append_quantize_kv_q8_0_q5_1_seqs");
17033 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
17034 let cfg = LaunchConfig {
17035 grid_dim: (nblk, b_n as u32, 1),
17036 block_dim: (32, 1, 1),
17037 shared_mem_bytes: 0,
17038 };
17039 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
17040 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17041 let __s_b = self.gpu.stream();
17042 let mut b = __s_b.launch_builder(&f);
17043 b.arg(k_rows)
17044 .arg(v_rows)
17045 .arg(kv_ptrs)
17046 .arg(pos_seq)
17047 .arg(&kdk)
17048 .arg(&kdv)
17049 .arg(&ktb)
17050 .arg(&vtb);
17051 unsafe {
17052 b.launch(cfg)?;
17053 }
17054 Ok(())
17055 }
17056
17057 pub fn fa_rows_eligible(&self, base_len: usize, head_dim: usize) -> bool {
17063 std::env::var("MEMRA_NO_FA_VEC").is_err()
17064 && std::env::var("MEMRA_FA_ROWS_OFF").is_err()
17065 && base_len + 1 >= fa_vec_min_tkv()
17066 && head_dim <= 256
17067 && head_dim % 32 == 0
17068 }
17069
17070 #[allow(clippy::too_many_arguments)]
17079 pub fn fa_decode_rows(
17080 &self,
17081 q: &CudaSlice<f32>,
17082 k: &cudarc::driver::CudaView<u8>,
17083 v: &cudarc::driver::CudaView<u8>,
17084 o: &mut CudaSlice<f32>,
17085 head_dim: usize,
17086 n_head: usize,
17087 n_head_kv: usize,
17088 base_len: usize,
17089 t: usize,
17090 scale: f32,
17091 k_tok_bytes: usize,
17092 v_tok_bytes: usize,
17093 base_dev: Option<(&CudaSlice<i32>, i32)>,
17097 kv_shared: bool,
17100 g: bool,
17104 mut q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
17107 ) -> Result<(), Box<dyn std::error::Error>> {
17108 debug_assert!(base_len + 1 >= fa_vec_min_tkv() && head_dim <= 512 && head_dim % 32 == 0);
17109 let t_kv_max = base_len + t; let mut sp = fa_split_keys(t_kv_max, n_head_kv); if head_dim == 512 {
17116 static SP512: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17117 let v = *SP512.get_or_init(|| {
17120 std::env::var("MEMRA_FA_SP512")
17121 .ok()
17122 .and_then(|x| x.parse().ok())
17123 .unwrap_or(0)
17124 });
17125 sp = if v >= 8 {
17126 v
17127 } else {
17128 FA_SP512_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
17129 };
17130 }
17131 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17132 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17133 let gqa = (n_head / n_head_kv).max(1) as u32;
17134 let mut groups: Vec<(usize, usize, usize)> = Vec::new(); if head_dim == 512 || fa_split_keys(base_len + 1, n_head_kv) == sp {
17145 groups.push((0, t, sp));
17146 } else {
17147 let mut r0 = 0usize;
17148 while r0 < t {
17149 let sp_g = fa_split_keys(base_len + r0 + 1, n_head_kv);
17150 let mut r1 = r0 + 1;
17151 while r1 < t && fa_split_keys(base_len + r1 + 1, n_head_kv) == sp_g {
17152 r1 += 1;
17153 }
17154 groups.push((r0, r1 - r0, sp_g));
17155 r0 = r1;
17156 }
17157 }
17158 static SMEM_TKV_R: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17162 let smem_tkv = *SMEM_TKV_R.get_or_init(|| {
17163 std::env::var("MEMRA_FA_SMEM_TKV")
17164 .ok()
17165 .and_then(|v| v.parse().ok())
17166 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
17167 });
17168 let v4 = fa_v4_at(base_len + t) && head_dim == 256;
17169 let v3 = fa_v3_active(head_dim);
17170 let smem_rows =
17171 head_dim <= 256 && !v3 && !fa_v2_on() && smem_tkv > 0 && t_kv_max >= smem_tkv;
17172 let _ = kv_shared;
17177 let i2 = head_dim == 512 && std::env::var("MEMRA_FA_I2").as_deref() != Ok("0");
17180 static TB512: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
17194 let tb512 = head_dim == 512
17196 && sp <= 32
17197 && n_head / n_head_kv.max(1) <= 16
17198 && *TB512.get_or_init(|| std::env::var("MEMRA_FA_TB512").as_deref() != Ok("0"));
17199 let fname = if tb512 {
17200 "fa_decode_vec_q_rows_v4_512_tb"
17201 } else if i2 {
17202 "fa_decode_vec_q_rows_dpl16_i2"
17203 } else if head_dim == 512 {
17204 "fa_decode_vec_q_rows_dpl16"
17205 }
17206 else if v4 {
17208 "fa_decode_vec_q_rows_v4"
17209 } else if v3 {
17210 "fa_decode_vec_q_rows_v3"
17211 } else if fa_v2_on() {
17212 "fa_decode_vec_q_rows_v2"
17213 } else if smem_rows {
17214 "fa_decode_vec_q_rows_smem"
17215 } else {
17216 "fa_decode_vec_q_rows"
17217 };
17218 let f = if head_dim == 512 {
17219 self.fa_func(fname, head_dim)
17220 } else if g {
17221 self.func_g(if smem_rows {
17229 "fa_decode_vec_q_rows"
17230 } else {
17231 fname
17232 })
17233 } else {
17234 self.func(fname)
17235 };
17236 let shmem = if tb512 {
17237 let gk = Self::gkv_on();
17239 let sh =
17240 (8192 + 1024 + 32 * 512 + 32 * 64 + 32 * head_dim * if gk { 1 } else { 2 }) as u32;
17241 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17242 f.set_attribute(
17243 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17244 sh as i32,
17245 )?;
17246 sh
17247 } else if v4 || v3 || smem_rows || fa_v2_on() {
17248 let sh = (if v4 {
17250 11520 + 32 * head_dim * if g { 1 } else { 2 }
17251 } else if v3 {
17252 32 * head_dim * 2
17253 } else {
17254 2 * 32 * head_dim * 2
17255 }) as u32;
17256 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17257 f.set_attribute(
17258 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17259 sh as i32,
17260 )?;
17261 sh
17262 } else {
17263 0
17264 };
17265 for &(r0, t_g, sp_g) in &groups {
17269 let n_splits_g = (base_len + r0 + t_g).div_ceil(sp_g);
17270 let (nspm, spk) = (n_splits_g as i32, sp_g as i32);
17271 let base_i = (base_len + r0) as i32;
17272 let o_len = t_g * n_head * n_splits_g * head_dim;
17273 let ml_len = t_g * n_head * n_splits_g;
17274 let mut part_guard = self.fa_part_pool.lock().unwrap();
17275 if part_guard
17276 .as_ref()
17277 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17278 .unwrap_or(true)
17279 {
17280 let old = part_guard.take();
17291 let (co, cm) = old
17292 .as_ref()
17293 .map(|pp| (pp.0.len(), pp.1.len()))
17294 .unwrap_or((0, 0));
17295 if let Some(old) = old {
17296 self.fa_part_retired.lock().unwrap().push(old);
17297 }
17298 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17299 eprintln!(
17300 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17301 co, o_len, cm, ml_len
17302 );
17303 }
17304 *part_guard = Some((
17305 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17306 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17307 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17308 ));
17309 }
17310 let pg = part_guard.as_mut().unwrap();
17311 self.gpu
17312 .stream()
17313 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17314 self.gpu
17315 .stream()
17316 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17317 self.gpu
17318 .stream()
17319 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17320 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17321 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
17322 let qv = self.view(q, t * n_head * head_dim);
17323 let q_g = qv.slice(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
17324 let cfg = LaunchConfig {
17325 grid_dim: (n_head_kv as u32, n_splits_g as u32, t_g as u32),
17326 block_dim: (32, gqa, 1),
17327 shared_mem_bytes: shmem,
17328 };
17329 {
17330 let __s_b = self.gpu.stream();
17331 let mut b = __s_b.launch_builder(&f);
17332 if tb512 {
17333 let (bd, plus) =
17335 base_dev.expect("hd512 rows twin requires a device base counter");
17336 let plus_g = plus + r0 as i32;
17337 let nr = t_g as i32;
17338 if Self::pdl_on() && Self::pdl_wb_on() {
17339 use cudarc::driver::{DevicePtr, DevicePtrMut};
17341 let s = &self.gpu.stream();
17342 let (pq, _b0) = q_g.device_ptr(s);
17343 let (pk, _b1) = k.device_ptr(s);
17344 let (pv, _b2) = v.device_ptr(s);
17345 let (po, _b3) = part_o.device_ptr_mut(s);
17346 let (pm, _b4) = part_m.device_ptr_mut(s);
17347 let (pl, _b5) = part_l.device_ptr_mut(s);
17348 let (pb, _b6) = bd.device_ptr(s);
17349 let mut ps = [
17350 &pq as *const _ as *mut std::ffi::c_void,
17351 &pk as *const _ as *mut _,
17352 &pv as *const _ as *mut _,
17353 &po as *const _ as *mut _,
17354 &pm as *const _ as *mut _,
17355 &pl as *const _ as *mut _,
17356 &hd as *const _ as *mut _,
17357 &nh as *const _ as *mut _,
17358 &nhkv as *const _ as *mut _,
17359 &pb as *const _ as *mut _,
17360 &plus_g as *const _ as *mut _,
17361 &scale as *const _ as *mut _,
17362 &nspm as *const _ as *mut _,
17363 &spk as *const _ as *mut _,
17364 &ktb as *const _ as *mut _,
17365 &vtb as *const _ as *mut _,
17366 &nr as *const _ as *mut _,
17367 ];
17368 unsafe {
17369 self.launch_pdl_flash(
17370 Self::gkv_on(),
17371 "fa_decode_vec_q_rows_v4_512_tb",
17372 (n_head_kv as u32, n_splits_g as u32, 1),
17373 (32, gqa, 1),
17374 shmem,
17375 &mut ps,
17376 )?;
17377 }
17378 } else {
17379 let cfg_tb = LaunchConfig {
17380 grid_dim: (n_head_kv as u32, n_splits_g as u32, 1),
17381 block_dim: (32, gqa, 1),
17382 shared_mem_bytes: shmem,
17383 };
17384 b.arg(&q_g)
17385 .arg(k)
17386 .arg(v)
17387 .arg(&mut *part_o)
17388 .arg(&mut *part_m)
17389 .arg(&mut *part_l)
17390 .arg(&hd)
17391 .arg(&nh)
17392 .arg(&nhkv)
17393 .arg(bd)
17394 .arg(&plus_g)
17395 .arg(&scale)
17396 .arg(&nspm)
17397 .arg(&spk)
17398 .arg(&ktb)
17399 .arg(&vtb)
17400 .arg(&nr);
17401 unsafe {
17402 b.launch(cfg_tb)?;
17403 }
17404 }
17405 } else if head_dim == 512 {
17406 let (bd, plus) =
17407 base_dev.expect("hd512 rows twin requires a device base counter");
17408 let plus_g = plus + r0 as i32;
17409 b.arg(&q_g)
17410 .arg(k)
17411 .arg(v)
17412 .arg(&mut *part_o)
17413 .arg(&mut *part_m)
17414 .arg(&mut *part_l)
17415 .arg(&hd)
17416 .arg(&nh)
17417 .arg(&nhkv)
17418 .arg(bd)
17419 .arg(&plus_g)
17420 .arg(&scale)
17421 .arg(&nspm)
17422 .arg(&spk)
17423 .arg(&ktb)
17424 .arg(&vtb);
17425 unsafe {
17426 b.launch(cfg)?;
17427 }
17428 } else {
17429 b.arg(&q_g)
17430 .arg(k)
17431 .arg(v)
17432 .arg(&mut *part_o)
17433 .arg(&mut *part_m)
17434 .arg(&mut *part_l)
17435 .arg(&hd)
17436 .arg(&nh)
17437 .arg(&nhkv)
17438 .arg(&base_i)
17439 .arg(&scale)
17440 .arg(&nspm)
17441 .arg(&spk)
17442 .arg(&ktb)
17443 .arg(&vtb);
17444 unsafe {
17445 b.launch(cfg)?;
17446 }
17447 }
17448 }
17449 let cfg2 = LaunchConfig {
17450 grid_dim: (n_head as u32, t_g as u32, 1),
17451 block_dim: (head_dim as u32, 1, 1),
17452 shared_mem_bytes: 0,
17453 };
17454 let mut o_g = o.slice_mut(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
17455 if head_dim == 512 {
17456 let (bd, plus) = base_dev.unwrap();
17459 let plus_g = plus + r0 as i32;
17460 if let Some((oq, od)) = q8_out.as_mut() {
17461 debug_assert!(t == 1, "rows q8 emit is a t=1 decode arm");
17463 if Self::pdl_on() && Self::pdl_wb_on() {
17464 use cudarc::driver::{DevicePtr, DevicePtrMut};
17466 let s = &self.gpu.stream();
17467 let (po, _g0) = part_o.device_ptr(s);
17468 let (pm, _g1) = part_m.device_ptr(s);
17469 let (pl, _g2) = part_l.device_ptr(s);
17470 let (pq, _g3) = oq.device_ptr_mut(s);
17471 let (pd, _g4) = od.device_ptr_mut(s);
17472 let (pb, _g5) = bd.device_ptr(s);
17473 let mut ps = [
17474 &po as *const _ as *mut std::ffi::c_void,
17475 &pm as *const _ as *mut _,
17476 &pl as *const _ as *mut _,
17477 &pq as *const _ as *mut _,
17478 &pd as *const _ as *mut _,
17479 &hd as *const _ as *mut _,
17480 &nh as *const _ as *mut _,
17481 &pb as *const _ as *mut _,
17482 &plus_g as *const _ as *mut _,
17483 &nspm as *const _ as *mut _,
17484 &spk as *const _ as *mut _,
17485 ];
17486 unsafe {
17487 self.launch_pdl_flash(
17488 Self::gkv_on(),
17489 "fa_decode_combine_rows_dc_q8_1",
17490 cfg2.grid_dim,
17491 cfg2.block_dim,
17492 0,
17493 &mut ps,
17494 )?;
17495 }
17496 continue;
17497 }
17498 let fc = self.fa_func("fa_decode_combine_rows_dc_q8_1", head_dim);
17499 let __s_b2 = self.gpu.stream();
17500 let mut b2 = __s_b2.launch_builder(&fc);
17501 b2.arg(&*part_o)
17502 .arg(&*part_m)
17503 .arg(&*part_l)
17504 .arg(&mut **oq)
17505 .arg(&mut **od)
17506 .arg(&hd)
17507 .arg(&nh)
17508 .arg(bd)
17509 .arg(&plus_g)
17510 .arg(&nspm)
17511 .arg(&spk);
17512 unsafe {
17513 b2.launch(cfg2)?;
17514 }
17515 continue;
17516 }
17517 let fc = self.fa_func("fa_decode_combine_rows_dc", head_dim);
17518 let __s_b2 = self.gpu.stream();
17519 let mut b2 = __s_b2.launch_builder(&fc);
17520 b2.arg(&*part_o)
17521 .arg(&*part_m)
17522 .arg(&*part_l)
17523 .arg(&mut o_g)
17524 .arg(&hd)
17525 .arg(&nh)
17526 .arg(bd)
17527 .arg(&plus_g)
17528 .arg(&nspm)
17529 .arg(&spk);
17530 unsafe {
17531 b2.launch(cfg2)?;
17532 }
17533 } else {
17534 assert!(
17537 q8_out.is_none(),
17538 "rows q8 emit requires the hd512 dc combine"
17539 );
17540 let fc = self.func("fa_decode_combine_rows");
17541 let __s_b2 = self.gpu.stream();
17542 let mut b2 = __s_b2.launch_builder(&fc);
17543 b2.arg(&*part_o)
17544 .arg(&*part_m)
17545 .arg(&*part_l)
17546 .arg(&mut o_g)
17547 .arg(&hd)
17548 .arg(&nh)
17549 .arg(&base_i)
17550 .arg(&nspm)
17551 .arg(&spk);
17552 unsafe {
17553 b2.launch(cfg2)?;
17554 }
17555 }
17556 }
17557 Ok(())
17558 }
17559
17560 #[allow(clippy::too_many_arguments)]
17564 pub fn fa_decode_rows_w(
17565 &self,
17566 q: &CudaSlice<f32>,
17567 k: &cudarc::driver::CudaView<u8>,
17568 v: &cudarc::driver::CudaView<u8>,
17569 o: &mut CudaSlice<f32>,
17570 head_dim: usize,
17571 n_head: usize,
17572 n_head_kv: usize,
17573 base_dev: &CudaSlice<i32>,
17574 base_plus: i32,
17575 t: usize,
17576 scale: f32,
17577 window: usize,
17578 k_tok_bytes: usize,
17579 v_tok_bytes: usize,
17580 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
17581 ) -> Result<(), Box<dyn std::error::Error>> {
17582 debug_assert!(head_dim == 256);
17587 let sp = {
17595 static SPW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17596 let v = *SPW.get_or_init(|| {
17597 std::env::var("MEMRA_FA_SPW")
17598 .ok()
17599 .and_then(|x| x.parse().ok())
17600 .unwrap_or(0)
17601 });
17602 if v >= 8 {
17603 v
17604 } else {
17605 FA_SPW_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
17606 }
17607 };
17608 let n_splits_max = (window + sp - 1) / sp;
17609 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17610 let (nspm, spk, wini) = (n_splits_max as i32, sp as i32, window as i32);
17611 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17612 let gqa = (n_head / n_head_kv).max(1) as u32;
17613 let o_len = t * n_head * n_splits_max * head_dim;
17614 let ml_len = t * n_head * n_splits_max;
17615 let mut part_guard = self.fa_part_pool.lock().unwrap();
17616 if part_guard
17617 .as_ref()
17618 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17619 .unwrap_or(true)
17620 {
17621 let old = part_guard.take();
17632 let (co, cm) = old
17633 .as_ref()
17634 .map(|pp| (pp.0.len(), pp.1.len()))
17635 .unwrap_or((0, 0));
17636 if let Some(old) = old {
17637 self.fa_part_retired.lock().unwrap().push(old);
17638 }
17639 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17640 eprintln!(
17641 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17642 co, o_len, cm, ml_len
17643 );
17644 }
17645 *part_guard = Some((
17646 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17647 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17648 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17649 ));
17650 }
17651 let pg = part_guard.as_mut().unwrap();
17652 self.gpu
17653 .stream()
17654 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17655 self.gpu
17656 .stream()
17657 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17658 self.gpu
17659 .stream()
17660 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17661 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17662 static SMEM_TKV_W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17668 let smem_tkv = *SMEM_TKV_W.get_or_init(|| {
17669 std::env::var("MEMRA_FA_SMEM_TKV")
17670 .ok()
17671 .and_then(|v| v.parse().ok())
17672 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
17673 });
17674 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17680 let wg = Self::wkv_on();
17685 let sp2 =
17688 gqa <= 4 && fa_v4_at(window) && std::env::var("MEMRA_FA_SPW2").as_deref() != Ok("0");
17689 if sp2 {
17690 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
17691 if Self::pdl_on() && Self::pdl_wb_on() {
17692 use cudarc::driver::{DevicePtr, DevicePtrMut};
17694 let s = &self.gpu.stream();
17695 let (pq, _b0) = q.device_ptr(s);
17696 let (pk, _b1) = k.device_ptr(s);
17697 let (pv, _b2) = v.device_ptr(s);
17698 let (po, _b3) = part_o.device_ptr_mut(s);
17699 let (pm, _b4) = part_m.device_ptr_mut(s);
17700 let (pl, _b5) = part_l.device_ptr_mut(s);
17701 let (pb, _b6) = base_dev.device_ptr(s);
17702 let mut ps = [
17703 &pq as *const _ as *mut std::ffi::c_void,
17704 &pk as *const _ as *mut _,
17705 &pv as *const _ as *mut _,
17706 &po as *const _ as *mut _,
17707 &pm as *const _ as *mut _,
17708 &pl as *const _ as *mut _,
17709 &hd as *const _ as *mut _,
17710 &nh as *const _ as *mut _,
17711 &nhkv as *const _ as *mut _,
17712 &pb as *const _ as *mut _,
17713 &base_plus as *const _ as *mut _,
17714 &scale as *const _ as *mut _,
17715 &nspm as *const _ as *mut _,
17716 &spk as *const _ as *mut _,
17717 &ktb as *const _ as *mut _,
17718 &vtb as *const _ as *mut _,
17719 &wini as *const _ as *mut _,
17720 ];
17721 unsafe {
17722 self.launch_pdl_flash(
17723 wg,
17724 "fa_decode_vec_q_rows_v4_w_sp",
17725 (n_head_kv as u32, n_splits_max as u32, t as u32),
17726 (32, gqa + 1, 1),
17727 sh,
17728 &mut ps,
17729 )?;
17730 }
17731 } else {
17732 let f = if wg {
17733 self.func_g("fa_decode_vec_q_rows_v4_w_sp")
17734 } else {
17735 self.func("fa_decode_vec_q_rows_v4_w_sp")
17736 };
17737 f.set_attribute(
17738 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17739 sh as i32,
17740 )?;
17741 let cfg = LaunchConfig {
17742 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
17743 block_dim: (32, gqa + 1, 1),
17744 shared_mem_bytes: sh,
17745 };
17746 let __s_b = self.gpu.stream();
17747 let mut b = __s_b.launch_builder(&f);
17748 b.arg(q)
17749 .arg(k)
17750 .arg(v)
17751 .arg(&mut *part_o)
17752 .arg(&mut *part_m)
17753 .arg(&mut *part_l)
17754 .arg(&hd)
17755 .arg(&nh)
17756 .arg(&nhkv)
17757 .arg(base_dev)
17758 .arg(&base_plus)
17759 .arg(&scale)
17760 .arg(&nspm)
17761 .arg(&spk)
17762 .arg(&ktb)
17763 .arg(&vtb)
17764 .arg(&wini);
17765 unsafe {
17766 b.launch(cfg)?;
17767 }
17768 }
17769 } else {
17770 if fa_v4_at(window) && Self::pdl_on() && Self::pdl_wb_on() {
17771 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
17773 use cudarc::driver::{DevicePtr, DevicePtrMut};
17774 let s = &self.gpu.stream();
17775 let (pq, _b0) = q.device_ptr(s);
17776 let (pk, _b1) = k.device_ptr(s);
17777 let (pv, _b2) = v.device_ptr(s);
17778 let (po, _b3) = part_o.device_ptr_mut(s);
17779 let (pm, _b4) = part_m.device_ptr_mut(s);
17780 let (pl, _b5) = part_l.device_ptr_mut(s);
17781 let (pb, _b6) = base_dev.device_ptr(s);
17782 let mut ps = [
17783 &pq as *const _ as *mut std::ffi::c_void,
17784 &pk as *const _ as *mut _,
17785 &pv as *const _ as *mut _,
17786 &po as *const _ as *mut _,
17787 &pm as *const _ as *mut _,
17788 &pl as *const _ as *mut _,
17789 &hd as *const _ as *mut _,
17790 &nh as *const _ as *mut _,
17791 &nhkv as *const _ as *mut _,
17792 &pb as *const _ as *mut _,
17793 &base_plus as *const _ as *mut _,
17794 &scale as *const _ as *mut _,
17795 &nspm as *const _ as *mut _,
17796 &spk as *const _ as *mut _,
17797 &ktb as *const _ as *mut _,
17798 &vtb as *const _ as *mut _,
17799 &wini as *const _ as *mut _,
17800 ];
17801 unsafe {
17802 self.launch_pdl_flash(
17803 wg,
17804 "fa_decode_vec_q_rows_v4_w",
17805 (n_head_kv as u32, n_splits_max as u32, t as u32),
17806 (32, gqa, 1),
17807 sh,
17808 &mut ps,
17809 )?;
17810 }
17811 } else {
17812 let pick = |name: &str| {
17813 if wg {
17814 self.func_g(name)
17815 } else {
17816 self.func(name)
17817 }
17818 };
17819 let (f, sh) = if fa_v4_at(window) {
17820 let f = pick("fa_decode_vec_q_rows_v4_w");
17821 (f, (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32)
17822 } else if smem_tkv > 0 && window >= smem_tkv {
17823 (
17826 pick("fa_decode_vec_q_rows_smem_w"),
17827 (2 * 32 * head_dim * 2) as u32,
17828 )
17829 } else {
17830 (pick("fa_decode_vec_q_rows_reg_w"), 0u32)
17831 };
17832 f.set_attribute(
17833 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17834 sh as i32,
17835 )?;
17836 let cfg = LaunchConfig {
17837 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
17838 block_dim: (32, gqa, 1),
17839 shared_mem_bytes: sh,
17840 };
17841 let __s_b = self.gpu.stream();
17842 let mut b = __s_b.launch_builder(&f);
17843 b.arg(q)
17844 .arg(k)
17845 .arg(v)
17846 .arg(&mut *part_o)
17847 .arg(&mut *part_m)
17848 .arg(&mut *part_l)
17849 .arg(&hd)
17850 .arg(&nh)
17851 .arg(&nhkv)
17852 .arg(base_dev)
17853 .arg(&base_plus)
17854 .arg(&scale)
17855 .arg(&nspm)
17856 .arg(&spk)
17857 .arg(&ktb)
17858 .arg(&vtb)
17859 .arg(&wini);
17860 unsafe {
17861 b.launch(cfg)?;
17862 }
17863 }
17864 }
17865 let cfg2 = LaunchConfig {
17866 grid_dim: (n_head as u32, t as u32, 1),
17867 block_dim: (head_dim as u32, 1, 1),
17868 shared_mem_bytes: 0,
17869 };
17870 if let Some((oq, od)) = q8_out {
17871 if Self::pdl_on() && Self::pdl_wb_on() {
17874 use cudarc::driver::{DevicePtr, DevicePtrMut};
17876 let s = &self.gpu.stream();
17877 let (po, _g0) = part_o.device_ptr(s);
17878 let (pm, _g1) = part_m.device_ptr(s);
17879 let (pl, _g2) = part_l.device_ptr(s);
17880 let (pq, _g3) = oq.device_ptr_mut(s);
17881 let (pd, _g4) = od.device_ptr_mut(s);
17882 let mut ps = [
17883 &po as *const _ as *mut std::ffi::c_void,
17884 &pm as *const _ as *mut _,
17885 &pl as *const _ as *mut _,
17886 &pq as *const _ as *mut _,
17887 &pd as *const _ as *mut _,
17888 &hd as *const _ as *mut _,
17889 &nh as *const _ as *mut _,
17890 &nspm as *const _ as *mut _,
17891 &spk as *const _ as *mut _,
17892 &wini as *const _ as *mut _,
17893 ];
17894 unsafe {
17895 self.launch_pdl_flash(
17896 wg,
17897 "fa_decode_combine_rows_w_q8_1",
17898 cfg2.grid_dim,
17899 cfg2.block_dim,
17900 0,
17901 &mut ps,
17902 )?;
17903 }
17904 return Ok(());
17905 }
17906 let fc = if wg {
17907 self.func_g("fa_decode_combine_rows_w_q8_1")
17908 } else {
17909 self.func("fa_decode_combine_rows_w_q8_1")
17910 };
17911 let __s_b2 = self.gpu.stream();
17912 let mut b2 = __s_b2.launch_builder(&fc);
17913 b2.arg(&*part_o)
17914 .arg(&*part_m)
17915 .arg(&*part_l)
17916 .arg(oq)
17917 .arg(od)
17918 .arg(&hd)
17919 .arg(&nh)
17920 .arg(&nspm)
17921 .arg(&spk)
17922 .arg(&wini);
17923 unsafe {
17924 b2.launch(cfg2)?;
17925 }
17926 return Ok(());
17927 }
17928 let fc = if wg {
17929 self.func_g("fa_decode_combine_rows_w")
17930 } else {
17931 self.func("fa_decode_combine_rows_w")
17932 };
17933 let __s_b2 = self.gpu.stream();
17934 let mut b2 = __s_b2.launch_builder(&fc);
17935 b2.arg(&*part_o)
17936 .arg(&*part_m)
17937 .arg(&*part_l)
17938 .arg(o)
17939 .arg(&hd)
17940 .arg(&nh)
17941 .arg(&nspm)
17942 .arg(&spk)
17943 .arg(&wini);
17944 unsafe {
17945 b2.launch(cfg2)?;
17946 }
17947 Ok(())
17948 }
17949
17950 #[allow(clippy::too_many_arguments)]
17956 pub fn fa_decode_rows_dc(
17957 &self,
17958 q: &CudaSlice<f32>,
17959 k: &cudarc::driver::CudaView<u8>,
17960 v: &cudarc::driver::CudaView<u8>,
17961 o: &mut CudaSlice<f32>,
17962 head_dim: usize,
17963 n_head: usize,
17964 n_head_kv: usize,
17965 base_dev: &CudaSlice<i32>,
17966 t_kv_upper: usize,
17967 t: usize,
17968 scale: f32,
17969 k_tok_bytes: usize,
17970 v_tok_bytes: usize,
17971 base_plus: i32,
17972 g: bool,
17973 ) -> Result<(), Box<dyn std::error::Error>> {
17974 let v4 = head_dim == 256 && fa_v4_at(t_kv_upper);
17975 assert!(
17976 v4 || fa_v3_active(head_dim),
17977 "stream fa rows requires the v3 or v4 lane"
17978 );
17979 assert!(v4 || base_plus == 0, "v3_dc kernel takes no plus arg");
17980 if v4 {
17981 let sp = fa_split_keys(t_kv_upper, n_head_kv);
17982 let n_splits_max = (t_kv_upper + sp - 1) / sp;
17983 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17984 let (nspm, spk) = (n_splits_max as i32, sp as i32);
17985 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17986 let gqa = (n_head / n_head_kv).max(1) as u32;
17987 let o_len = t * n_head * n_splits_max * head_dim;
17988 let ml_len = t * n_head * n_splits_max;
17989 let mut part_guard = self.fa_part_pool.lock().unwrap();
17990 if part_guard
17991 .as_ref()
17992 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17993 .unwrap_or(true)
17994 {
17995 let old = part_guard.take();
18006 let (co, cm) = old
18007 .as_ref()
18008 .map(|pp| (pp.0.len(), pp.1.len()))
18009 .unwrap_or((0, 0));
18010 if let Some(old) = old {
18011 self.fa_part_retired.lock().unwrap().push(old);
18012 }
18013 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18014 eprintln!(
18015 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18016 co, o_len, cm, ml_len
18017 );
18018 }
18019 *part_guard = Some((
18020 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18021 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18022 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18023 ));
18024 }
18025 let pg = part_guard.as_mut().unwrap();
18026 self.gpu
18027 .stream()
18028 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18029 self.gpu
18030 .stream()
18031 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18032 self.gpu
18033 .stream()
18034 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18035 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18036 let f = if g {
18037 self.func_g("fa_decode_vec_q_rows_v4_dc")
18038 } else {
18039 self.func("fa_decode_vec_q_rows_v4_dc")
18040 };
18041 let sh = (11520 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
18042 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18043 f.set_attribute(
18044 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18045 sh as i32,
18046 )?;
18047 let cfg = LaunchConfig {
18048 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18049 block_dim: (32, gqa, 1),
18050 shared_mem_bytes: sh,
18051 };
18052 let __s_b = self.gpu.stream();
18053 let mut b = __s_b.launch_builder(&f);
18054 b.arg(q)
18055 .arg(k)
18056 .arg(v)
18057 .arg(&mut *part_o)
18058 .arg(&mut *part_m)
18059 .arg(&mut *part_l)
18060 .arg(&hd)
18061 .arg(&nh)
18062 .arg(&nhkv)
18063 .arg(base_dev)
18064 .arg(&base_plus)
18065 .arg(&scale)
18066 .arg(&nspm)
18067 .arg(&spk)
18068 .arg(&ktb)
18069 .arg(&vtb);
18070 unsafe {
18071 b.launch(cfg)?;
18072 }
18073 let fc = self.func("fa_decode_combine_rows_dc");
18074 let cfg2 = LaunchConfig {
18075 grid_dim: (n_head as u32, t as u32, 1),
18076 block_dim: (head_dim as u32, 1, 1),
18077 shared_mem_bytes: 0,
18078 };
18079 let __s_b2 = self.gpu.stream();
18080 let mut b2 = __s_b2.launch_builder(&fc);
18081 b2.arg(&*part_o)
18082 .arg(&*part_m)
18083 .arg(&*part_l)
18084 .arg(o)
18085 .arg(&hd)
18086 .arg(&nh)
18087 .arg(base_dev)
18088 .arg(&base_plus)
18089 .arg(&nspm)
18090 .arg(&spk);
18091 unsafe {
18092 b2.launch(cfg2)?;
18093 }
18094 return Ok(());
18095 }
18096 let sp = fa_split_keys(t_kv_upper, n_head_kv);
18097 let n_splits_max = (t_kv_upper + sp - 1) / sp;
18098 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18099 let (nspm, spk) = (n_splits_max as i32, sp as i32);
18100 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18101 let gqa = (n_head / n_head_kv).max(1) as u32;
18102 let o_len = t * n_head * n_splits_max * head_dim;
18103 let ml_len = t * n_head * n_splits_max;
18104 let mut part_guard = self.fa_part_pool.lock().unwrap();
18105 if part_guard
18106 .as_ref()
18107 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18108 .unwrap_or(true)
18109 {
18110 let old = part_guard.take();
18121 let (co, cm) = old
18122 .as_ref()
18123 .map(|pp| (pp.0.len(), pp.1.len()))
18124 .unwrap_or((0, 0));
18125 if let Some(old) = old {
18126 self.fa_part_retired.lock().unwrap().push(old);
18127 }
18128 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18129 eprintln!(
18130 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18131 co, o_len, cm, ml_len
18132 );
18133 }
18134 *part_guard = Some((
18135 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18136 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18137 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18138 ));
18139 }
18140 let pg = part_guard.as_mut().unwrap();
18141 self.gpu
18142 .stream()
18143 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18144 self.gpu
18145 .stream()
18146 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18147 self.gpu
18148 .stream()
18149 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18150 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18151 let f = self.func("fa_decode_vec_q_rows_v3_dc");
18152 let sh = (32 * head_dim * 2) as u32;
18153 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18154 f.set_attribute(
18155 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18156 sh as i32,
18157 )?;
18158 let cfg = LaunchConfig {
18159 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18160 block_dim: (32, gqa, 1),
18161 shared_mem_bytes: sh,
18162 };
18163 let __s_b = self.gpu.stream();
18164 let mut b = __s_b.launch_builder(&f);
18165 b.arg(q)
18166 .arg(k)
18167 .arg(v)
18168 .arg(&mut *part_o)
18169 .arg(&mut *part_m)
18170 .arg(&mut *part_l)
18171 .arg(&hd)
18172 .arg(&nh)
18173 .arg(&nhkv)
18174 .arg(base_dev)
18175 .arg(&scale)
18176 .arg(&nspm)
18177 .arg(&spk)
18178 .arg(&ktb)
18179 .arg(&vtb);
18180 unsafe {
18181 b.launch(cfg)?;
18182 }
18183 let fc = self.func("fa_decode_combine_rows_dc");
18184 let cfg2 = LaunchConfig {
18185 grid_dim: (n_head as u32, t as u32, 1),
18186 block_dim: (head_dim as u32, 1, 1),
18187 shared_mem_bytes: 0,
18188 };
18189 let plus0 = 0i32;
18190 let __s_b2 = self.gpu.stream();
18191 let mut b2 = __s_b2.launch_builder(&fc);
18192 b2.arg(&*part_o)
18193 .arg(&*part_m)
18194 .arg(&*part_l)
18195 .arg(o)
18196 .arg(&hd)
18197 .arg(&nh)
18198 .arg(base_dev)
18199 .arg(&plus0)
18200 .arg(&nspm)
18201 .arg(&spk);
18202 unsafe {
18203 b2.launch(cfg2)?;
18204 }
18205 Ok(())
18206 }
18207
18208 pub fn fa_decode_dc(
18219 &self,
18220 q: &CudaSlice<f32>,
18221 k: &cudarc::driver::CudaView<u8>,
18222 v: &cudarc::driver::CudaView<u8>,
18223 o: &mut CudaSlice<f32>,
18224 head_dim: usize,
18225 n_head: usize,
18226 n_head_kv: usize,
18227 t_kv_dev: &CudaSlice<i32>,
18228 bucket_max: usize,
18229 scale: f32,
18230 k_tok_bytes: usize,
18231 v_tok_bytes: usize,
18232 g: bool,
18233 ) -> Result<(), Box<dyn std::error::Error>> {
18234 self.fa_decode_dc_q8(
18235 q,
18236 k,
18237 v,
18238 o,
18239 head_dim,
18240 n_head,
18241 n_head_kv,
18242 t_kv_dev,
18243 bucket_max,
18244 scale,
18245 k_tok_bytes,
18246 v_tok_bytes,
18247 g,
18248 None,
18249 )
18250 }
18251
18252 #[allow(clippy::too_many_arguments)]
18255 pub fn fa_decode_dc_q8(
18256 &self,
18257 q: &CudaSlice<f32>,
18258 k: &cudarc::driver::CudaView<u8>,
18259 v: &cudarc::driver::CudaView<u8>,
18260 o: &mut CudaSlice<f32>,
18261 head_dim: usize,
18262 n_head: usize,
18263 n_head_kv: usize,
18264 t_kv_dev: &CudaSlice<i32>,
18265 bucket_max: usize,
18266 scale: f32,
18267 k_tok_bytes: usize,
18268 v_tok_bytes: usize,
18269 g: bool,
18270 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
18271 ) -> Result<(), Box<dyn std::error::Error>> {
18272 let mut fa_vec =
18280 std::env::var("MEMRA_NO_FA_VEC").is_err() && bucket_max >= fa_vec_min_tkv();
18281 if g && head_dim == 256 && !fa_v4_at(bucket_max) {
18282 fa_vec = false;
18283 } let sp = fa_split_keys(bucket_max, n_head_kv);
18285 let n_splits = if fa_vec {
18286 ((bucket_max + sp - 1) / sp).max(1)
18287 } else {
18288 ((bucket_max + 255) / 256).max(1)
18289 };
18290 let o_len = n_head * n_splits * head_dim;
18291 let ml_len = n_head * n_splits;
18292 let mut part_guard = self.fa_part_pool.lock().unwrap();
18293 if part_guard
18294 .as_ref()
18295 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18296 .unwrap_or(true)
18297 {
18298 let old = part_guard.take();
18309 let (co, cm) = old
18310 .as_ref()
18311 .map(|pp| (pp.0.len(), pp.1.len()))
18312 .unwrap_or((0, 0));
18313 if let Some(old) = old {
18314 self.fa_part_retired.lock().unwrap().push(old);
18315 }
18316 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18317 eprintln!(
18318 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18319 co, o_len, cm, ml_len
18320 );
18321 }
18322 *part_guard = Some((
18323 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18324 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18325 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18326 ));
18327 }
18328 let pg = part_guard.as_mut().unwrap();
18329 self.gpu
18330 .stream()
18331 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18332 self.gpu
18333 .stream()
18334 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18335 self.gpu
18336 .stream()
18337 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18338 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18339 let (hd, nh, nhkv, nsp) = (
18340 head_dim as i32,
18341 n_head as i32,
18342 n_head_kv as i32,
18343 n_splits as i32,
18344 );
18345 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18346 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
18347 let deep = fa_vec
18350 && head_dim == 256
18351 && fa_v4_at(bucket_max)
18352 && !g
18353 && fa_deep_at(bucket_max)
18354 && !matches!(fa_v4_mode(), "noB3" | "stage");
18355 let (f, cfg) = if fa_vec
18356 && head_dim == 512
18357 && bucket_max >= {
18358 static FA512_MIN_DC: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18359 *FA512_MIN_DC.get_or_init(|| {
18360 std::env::var("MEMRA_FA512_MIN")
18361 .ok()
18362 .and_then(|v| v.parse().ok())
18363 .unwrap_or(512)
18364 })
18365 } {
18366 let gqa = (n_head / n_head_kv).max(1) as u32;
18368 (
18369 self.fa_func("fa_decode_vec_q_dpl16_dc", head_dim),
18370 LaunchConfig {
18371 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18372 block_dim: (32, gqa, 1),
18373 shared_mem_bytes: 0,
18374 },
18375 )
18376 } else if fa_vec && head_dim == 512 {
18377 let q_view = q.as_view();
18380 let mut o_view = o.as_view_mut();
18381 return self.fa_decode_scalar_unified(
18382 &q_view,
18383 k,
18384 v,
18385 &mut o_view,
18386 head_dim,
18387 n_head,
18388 n_head_kv,
18389 0,
18390 Some(t_kv_dev),
18391 scale,
18392 n_splits,
18393 sp,
18394 k_tok_bytes,
18395 v_tok_bytes,
18396 g,
18397 &mut *part_o,
18398 &mut *part_m,
18399 &mut *part_l,
18400 q8_out,
18401 );
18402 } else if fa_vec && head_dim == 256 && fa_v4_at(bucket_max) {
18403 let gqa = (n_head / n_head_kv).max(1) as u32;
18406 let fv = if g {
18407 self.func_g("fa_decode_vec_q_v4_dc")
18408 } else if deep {
18409 self.func("fa_decode_vec_q_v4_deep_dc")
18410 } else {
18411 self.func("fa_decode_vec_q_v4_dc")
18412 };
18413 let shmem =
18414 (if deep { 12160 } else { 11520 } + 32 * head_dim * if g { 1 } else { 2 }) as u32;
18415 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18416 fv.set_attribute(
18417 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18418 shmem as i32,
18419 )?;
18420 (
18421 fv,
18422 LaunchConfig {
18423 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18424 block_dim: (32, gqa, 1),
18425 shared_mem_bytes: shmem,
18426 },
18427 )
18428 } else if fa_vec && fa_v3_active(head_dim) {
18429 let gqa = (n_head / n_head_kv).max(1) as u32;
18432 let fv = if g {
18433 self.func_g("fa_decode_vec_q_v3_dc")
18434 } else {
18435 self.func("fa_decode_vec_q_v3_dc")
18436 };
18437 let shmem = (32 * head_dim * 2) as u32; (
18439 fv,
18440 LaunchConfig {
18441 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18442 block_dim: (32, gqa, 1),
18443 shared_mem_bytes: shmem,
18444 },
18445 )
18446 } else if fa_vec && fa_v2_on() {
18447 let gqa = (n_head / n_head_kv).max(1) as u32;
18451 let fv = if g {
18452 self.func_g("fa_decode_vec_q_v2_dc")
18453 } else {
18454 self.func("fa_decode_vec_q_v2_dc")
18455 };
18456 let shmem = (2 * 32 * head_dim * 2) as u32; (
18458 fv,
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: shmem,
18463 },
18464 )
18465 } else if fa_vec {
18466 let gqa = (n_head / n_head_kv).max(1) as u32;
18467 let fv = if g {
18469 self.func_g("fa_decode_vec_q_dc")
18470 } else {
18471 self.func("fa_decode_vec_q_dc")
18472 };
18473 (
18474 fv,
18475 LaunchConfig {
18476 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18477 block_dim: (32, gqa, 1),
18478 shared_mem_bytes: 0,
18479 },
18480 )
18481 } else {
18482 let q_view = q.as_view();
18483 let mut o_view = o.as_view_mut();
18484 return self.fa_decode_scalar_unified(
18485 &q_view,
18486 k,
18487 v,
18488 &mut o_view,
18489 head_dim,
18490 n_head,
18491 n_head_kv,
18492 0,
18493 Some(t_kv_dev),
18494 scale,
18495 n_splits,
18496 if fa_vec { sp } else { 256 },
18497 k_tok_bytes,
18498 v_tok_bytes,
18499 g,
18500 &mut *part_o,
18501 &mut *part_m,
18502 &mut *part_l,
18503 q8_out,
18504 );
18505 };
18506 let ski = sp as i32; let __s_b = self.gpu.stream();
18508 let mut b = __s_b.launch_builder(&f);
18509 b.arg(q)
18510 .arg(k)
18511 .arg(v)
18512 .arg(&mut *part_o)
18513 .arg(&mut *part_m)
18514 .arg(&mut *part_l)
18515 .arg(&hd)
18516 .arg(&nh)
18517 .arg(&nhkv)
18518 .arg(t_kv_dev)
18519 .arg(&scale)
18520 .arg(&nsp)
18521 .arg(&ski)
18522 .arg(&ktb)
18523 .arg(&vtb);
18524 unsafe {
18525 b.launch(cfg)?;
18526 }
18527 let cfg2 = LaunchConfig {
18528 grid_dim: (n_head as u32, 1, 1),
18529 block_dim: (head_dim as u32, 1, 1),
18530 shared_mem_bytes: 0,
18531 };
18532 if let Some((oq, od)) = q8_out {
18533 let fc = if g {
18534 self.func_g("fa_decode_combine_q8_1")
18535 } else {
18536 self.fa_func("fa_decode_combine_q8_1", head_dim)
18537 };
18538 let __s_b2 = self.gpu.stream();
18539 let mut b2 = __s_b2.launch_builder(&fc);
18540 b2.arg(&*part_o)
18541 .arg(&*part_m)
18542 .arg(&*part_l)
18543 .arg(oq)
18544 .arg(od)
18545 .arg(&hd)
18546 .arg(&nh)
18547 .arg(&nsp);
18548 unsafe {
18549 b2.launch(cfg2)?;
18550 }
18551 return Ok(());
18552 }
18553 let fc = if g {
18554 self.func_g("fa_decode_combine_f32")
18555 } else {
18556 self.fa_func("fa_decode_combine_f32", head_dim)
18557 };
18558 let __s_b2 = self.gpu.stream();
18559 let mut b2 = __s_b2.launch_builder(&fc);
18560 b2.arg(&*part_o)
18561 .arg(&*part_m)
18562 .arg(&*part_l)
18563 .arg(o)
18564 .arg(&hd)
18565 .arg(&nh)
18566 .arg(&nsp);
18567 unsafe {
18568 b2.launch(cfg2)?;
18569 }
18570 Ok(())
18571 }
18572
18573 pub fn fa_geom_eager(
18579 &self,
18580 t_kv: usize,
18581 head_dim: usize,
18582 n_head_kv: usize,
18583 g: bool,
18584 ) -> (bool, usize) {
18585 let fa_ok = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
18589 let vec512 = fa_ok && head_dim == 512 && t_kv >= fa512_min_tkv();
18595 let mut fa_vec = vec512 || (fa_ok && head_dim <= 256 && head_dim % 32 == 0);
18596 if g && head_dim == 256 && !fa_v4_at(t_kv) {
18602 fa_vec = false;
18603 }
18604 let sp = fa_split_keys(t_kv, n_head_kv);
18605 let n_splits = if fa_vec {
18606 ((t_kv + sp - 1) / sp).max(1)
18607 } else {
18608 ((t_kv + 255) / 256).max(1)
18609 };
18610 (fa_vec, n_splits)
18611 }
18612
18613 pub fn fa_bucket_key(
18619 &self,
18620 t_kv: usize,
18621 head_dim: usize,
18622 n_head_kv: usize,
18623 g: bool,
18624 ) -> (bool, usize) {
18625 self.fa_geom_eager(t_kv, head_dim, n_head_kv, g)
18626 }
18627
18628 pub fn capture_graph_retained<F>(
18640 &self,
18641 step: F,
18642 ) -> Result<
18643 (
18644 cudarc::driver::CudaGraph,
18645 Vec<Box<dyn std::any::Any + Send>>,
18646 ),
18647 Box<dyn std::error::Error>,
18648 >
18649 where
18650 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
18651 {
18652 use cudarc::driver::sys::CUgraphInstantiate_flags;
18653 self.capture_graph_retained_flags(
18654 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
18655 step,
18656 )
18657 }
18658
18659 pub fn capture_graph_retained_flags<F>(
18664 &self,
18665 flags: cudarc::driver::sys::CUgraphInstantiate_flags,
18666 mut step: F,
18667 ) -> Result<
18668 (
18669 cudarc::driver::CudaGraph,
18670 Vec<Box<dyn std::any::Any + Send>>,
18671 ),
18672 Box<dyn std::error::Error>,
18673 >
18674 where
18675 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
18676 {
18677 use cudarc::driver::sys::CUstreamCaptureMode;
18678 self.capture_keep.lock().unwrap().clear();
18686 let was_tracking = self.gpu.ctx.is_event_tracking();
18687 if was_tracking {
18688 unsafe {
18689 self.gpu.ctx.disable_event_tracking();
18690 }
18691 }
18692 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
18693 self.capture_keep_on
18694 .store(true, std::sync::atomic::Ordering::Relaxed);
18695 let w = (|| {
18696 step(self)?;
18697 step(self)
18698 })();
18699 self.capture_keep_on
18700 .store(false, std::sync::atomic::Ordering::Relaxed);
18701 w?;
18702 self.gpu.stream().synchronize()?;
18703 self.gpu
18704 .stream()
18705 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
18706 let r = step(self);
18707 let g = self.gpu.stream().end_capture(flags);
18708 r?;
18709 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
18710 graph.upload()?;
18711 Ok(graph)
18712 };
18713 let result = run();
18714 self.capture_keep_on
18715 .store(false, std::sync::atomic::Ordering::Relaxed);
18716 if was_tracking {
18717 unsafe {
18718 self.gpu.ctx.enable_event_tracking();
18719 }
18720 }
18721 let keeper = std::mem::take(&mut *self.capture_keep.lock().unwrap());
18722 Ok((result?, keeper))
18723 }
18724
18725 pub fn capture_graph<F>(
18726 &self,
18727 mut step: F,
18728 ) -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>>
18729 where
18730 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
18731 {
18732 use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
18733 let was_tracking = self.gpu.ctx.is_event_tracking();
18741 if was_tracking {
18742 unsafe {
18743 self.gpu.ctx.disable_event_tracking();
18744 }
18745 }
18746 let iflag = {
18753 static F: std::sync::OnceLock<CUgraphInstantiate_flags> = std::sync::OnceLock::new();
18754 *F.get_or_init(|| match std::env::var("MEMRA_GRAPH_IFLAG").as_deref() {
18755 Ok("upload") => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_UPLOAD,
18758 Ok("priority") => {
18759 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY
18760 }
18761 _ => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
18762 })
18763 };
18764 let ct = {
18771 static T: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
18772 *T.get_or_init(|| std::env::var("MEMRA_GRAPH_CAPTIME").as_deref() == Ok("1"))
18773 };
18774 let warmups = {
18797 static W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18798 *W.get_or_init(|| {
18799 std::env::var("MEMRA_GRAPH_WARMUPS")
18800 .ok()
18801 .and_then(|v| v.parse().ok())
18802 .filter(|n| *n >= 1)
18803 .unwrap_or(1)
18804 })
18805 };
18806 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
18807 let t_w = std::time::Instant::now();
18808 for _ in 0..warmups {
18810 step(self)?;
18811 }
18812 self.gpu.stream().synchronize()?;
18813 let ms_warm = t_w.elapsed().as_secs_f64() * 1e3;
18814 let t_c = std::time::Instant::now();
18816 self.gpu
18817 .stream()
18818 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
18819 let r = step(self);
18822 let ms_body = t_c.elapsed().as_secs_f64() * 1e3;
18823 let t_i = std::time::Instant::now();
18824 let g = self.gpu.stream().end_capture(iflag);
18825 let ms_inst = t_i.elapsed().as_secs_f64() * 1e3;
18826 r?;
18827 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
18828 let t_u = std::time::Instant::now();
18829 graph.upload()?;
18830 if ct {
18831 println!(
18832 "[graph-captime] warmup2x {ms_warm:.2} ms capture-body {ms_body:.2} ms \
18833 instantiate {ms_inst:.2} ms upload {:.2} ms",
18834 t_u.elapsed().as_secs_f64() * 1e3
18835 );
18836 }
18837 Ok(graph)
18838 };
18839 let result = run();
18840 if was_tracking {
18841 unsafe {
18842 self.gpu.ctx.enable_event_tracking();
18843 }
18844 }
18845 result
18846 }
18847
18848 pub fn gdn_scan_s128_view(
18850 &self,
18851 q: &CudaSlice<f32>,
18852 k: &CudaSlice<f32>,
18853 v: &CudaSlice<f32>,
18854 g: &CudaSlice<f32>,
18855 beta: &CudaSlice<f32>,
18856 state_in: &cudarc::driver::CudaView<f32>,
18857 state_out: &mut cudarc::driver::CudaViewMut<f32>,
18858 o: &mut CudaSlice<f32>,
18859 n_head: usize,
18860 t: usize,
18861 scale: f32,
18862 ) -> Result<(), Box<dyn std::error::Error>> {
18863 let f = self.func("gdn_scan_s128");
18864 const S_V: u32 = 128;
18865 const WARP: u32 = 32;
18866 const COLS: u32 = 4;
18867 let cfg = LaunchConfig {
18868 grid_dim: (n_head as u32, 1, S_V / COLS),
18869 block_dim: (WARP, COLS, 1),
18870 shared_mem_bytes: 0,
18871 };
18872 let (h, ti) = (n_head as i32, t as i32);
18873 let __s_b = self.gpu.stream();
18874 let mut b = __s_b.launch_builder(&f);
18875 b.arg(q)
18876 .arg(k)
18877 .arg(v)
18878 .arg(g)
18879 .arg(beta)
18880 .arg(state_in)
18881 .arg(state_out)
18882 .arg(o)
18883 .arg(&h)
18884 .arg(&ti)
18885 .arg(&scale);
18886 unsafe {
18887 b.launch(cfg)?;
18888 }
18889 Ok(())
18890 }
18891
18892 pub fn ssm_conv1d_view(
18894 &self,
18895 x: &cudarc::driver::CudaView<f32>,
18896 w: &CudaSlice<f32>,
18897 y: &mut CudaSlice<f32>,
18898 conv_dim: usize,
18899 t: usize,
18900 d_conv: usize,
18901 silu: bool,
18902 ) -> Result<(), Box<dyn std::error::Error>> {
18903 let f = self.func("ssm_conv1d_silu_f32");
18904 let cfg = LaunchConfig {
18906 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
18907 block_dim: (256, 1, 1),
18908 shared_mem_bytes: 0,
18909 };
18910 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
18911 let __s_b = self.gpu.stream();
18912 let mut b = __s_b.launch_builder(&f);
18913 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
18914 unsafe {
18915 b.launch(cfg)?;
18916 }
18917 Ok(())
18918 }
18919
18920 pub fn ssm_conv1d_tm(
18927 &self,
18928 qkv_tm: &CudaSlice<f32>,
18929 w: &CudaSlice<f32>,
18930 y: &mut CudaSlice<f32>,
18931 conv_dim: usize,
18932 t: usize,
18933 d_conv: usize,
18934 ) -> Result<(), Box<dyn std::error::Error>> {
18935 let f = self.func("ssm_conv1d_tm_f32");
18936 let cfg = LaunchConfig {
18937 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
18938 block_dim: (256, 1, 1),
18939 shared_mem_bytes: 0,
18940 };
18941 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
18942 let __s_b = self.gpu.stream();
18943 let mut b = __s_b.launch_builder(&f);
18944 b.arg(qkv_tm).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc);
18945 unsafe {
18946 b.launch(cfg)?;
18947 }
18948 Ok(())
18949 }
18950
18951 pub fn ssm_conv1d_tm_state(
18959 &self,
18960 qkv_tm: &CudaSlice<f32>,
18961 conv_state: &mut CudaSlice<f32>,
18962 w: &CudaSlice<f32>,
18963 y: &mut CudaSlice<f32>,
18964 conv_dim: usize,
18965 t: usize,
18966 d_conv: usize,
18967 ) -> Result<(), Box<dyn std::error::Error>> {
18968 self.ssm_conv1d_tm_state_pad(qkv_tm, conv_state, w, y, conv_dim, t, d_conv, None)
18969 }
18970
18971 #[allow(clippy::too_many_arguments)]
18974 pub fn ssm_conv1d_tm_state_pad(
18975 &self,
18976 qkv_tm: &CudaSlice<f32>,
18977 conv_state: &mut CudaSlice<f32>,
18978 w: &CudaSlice<f32>,
18979 y: &mut CudaSlice<f32>,
18980 conv_dim: usize,
18981 t: usize,
18982 d_conv: usize,
18983 pad_len: Option<&CudaSlice<i32>>,
18984 ) -> Result<(), Box<dyn std::error::Error>> {
18985 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
18986 let ring_old = if t < d_conv - 1 {
18990 Some(self.clone_dtod(conv_state)?)
18991 } else {
18992 None
18993 };
18994 {
18995 let f = self.func("ssm_conv1d_tm_state_f32");
18996 let cfg = LaunchConfig {
18997 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
18998 block_dim: (256, 1, 1),
18999 shared_mem_bytes: 0,
19000 };
19001 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19002 let __s_b = self.gpu.stream();
19003 let mut b = __s_b.launch_builder(&f);
19004 b.arg(qkv_tm)
19005 .arg(&*conv_state)
19006 .arg(w)
19007 .arg(y)
19008 .arg(&cd)
19009 .arg(&ti)
19010 .arg(&dc);
19011 unsafe {
19012 b.launch(cfg)?;
19013 }
19014 }
19015 match (ring_old, pad_len) {
19016 (None, Some(len_d)) => {
19017 let f = self.func("ssm_conv_ring_update_dev_f32");
19018 let n = conv_dim * (d_conv - 1);
19019 let cfg = LaunchConfig::for_num_elems(n as u32);
19020 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19021 let __s_b = self.gpu.stream();
19022 let mut b = __s_b.launch_builder(&f);
19023 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
19024 unsafe {
19025 b.launch(cfg)?;
19026 }
19027 }
19028 (None, None) => {
19029 let f = self.func("ssm_conv_ring_update_f32");
19030 let n = conv_dim * (d_conv - 1);
19031 let cfg = LaunchConfig::for_num_elems(n as u32);
19032 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19033 let __s_b = self.gpu.stream();
19034 let mut b = __s_b.launch_builder(&f);
19035 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
19036 unsafe {
19037 b.launch(cfg)?;
19038 }
19039 }
19040 (Some(old), _) => {
19041 self.ssm_conv_ring_rebuild(qkv_tm, &old, conv_state, conv_dim, t, d_conv)?
19042 }
19043 }
19044 Ok(())
19045 }
19046
19047 pub fn ssm_conv1d_tm_state_pad_v(
19049 &self,
19050 qkv_tm: &cudarc::driver::CudaView<f32>,
19051 conv_state: &mut CudaSlice<f32>,
19052 w: &CudaSlice<f32>,
19053 y: &mut CudaSlice<f32>,
19054 conv_dim: usize,
19055 t: usize,
19056 d_conv: usize,
19057 pad_len: Option<&CudaSlice<i32>>,
19058 ) -> Result<(), Box<dyn std::error::Error>> {
19059 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
19060 let ring_old = if t < d_conv - 1 {
19064 Some(self.clone_dtod(conv_state)?)
19065 } else {
19066 None
19067 };
19068 {
19069 let f = self.func("ssm_conv1d_tm_state_f32");
19070 let cfg = LaunchConfig {
19071 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19072 block_dim: (256, 1, 1),
19073 shared_mem_bytes: 0,
19074 };
19075 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19076 let __s_b = self.gpu.stream();
19077 let mut b = __s_b.launch_builder(&f);
19078 b.arg(qkv_tm)
19079 .arg(&*conv_state)
19080 .arg(w)
19081 .arg(y)
19082 .arg(&cd)
19083 .arg(&ti)
19084 .arg(&dc);
19085 unsafe {
19086 b.launch(cfg)?;
19087 }
19088 }
19089 match (ring_old, pad_len) {
19090 (None, Some(len_d)) => {
19091 let f = self.func("ssm_conv_ring_update_dev_f32");
19092 let n = conv_dim * (d_conv - 1);
19093 let cfg = LaunchConfig::for_num_elems(n as u32);
19094 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19095 let __s_b = self.gpu.stream();
19096 let mut b = __s_b.launch_builder(&f);
19097 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
19098 unsafe {
19099 b.launch(cfg)?;
19100 }
19101 }
19102 (None, None) => {
19103 let f = self.func("ssm_conv_ring_update_f32");
19104 let n = conv_dim * (d_conv - 1);
19105 let cfg = LaunchConfig::for_num_elems(n as u32);
19106 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19107 let __s_b = self.gpu.stream();
19108 let mut b = __s_b.launch_builder(&f);
19109 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
19110 unsafe {
19111 b.launch(cfg)?;
19112 }
19113 }
19114 (Some(_), _) => unreachable!(
19115 "ssm_conv1d_tm_state_pad_v: T < d_conv-1 has no view path (PRIME_MIN_T gates it)"
19116 ),
19117 }
19118 Ok(())
19119 }
19120
19121 pub fn ssm_conv_ring_rebuild(
19126 &self,
19127 qkv_tm: &CudaSlice<f32>,
19128 ring_old: &CudaSlice<f32>,
19129 conv_state: &mut CudaSlice<f32>,
19130 conv_dim: usize,
19131 tc: usize,
19132 d_conv: usize,
19133 ) -> Result<(), Box<dyn std::error::Error>> {
19134 let f = self.func("ssm_conv_ring_rebuild_f32");
19135 let n = conv_dim * (d_conv - 1);
19136 let cfg = LaunchConfig::for_num_elems(n as u32);
19137 let (cd, ti, dc) = (conv_dim as i32, tc as i32, d_conv as i32);
19138 let __s_b = self.gpu.stream();
19139 let mut b = __s_b.launch_builder(&f);
19140 b.arg(qkv_tm)
19141 .arg(ring_old)
19142 .arg(conv_state)
19143 .arg(&cd)
19144 .arg(&ti)
19145 .arg(&dc);
19146 unsafe {
19147 b.launch(cfg)?;
19148 }
19149 Ok(())
19150 }
19151
19152 #[allow(clippy::too_many_arguments)]
19157 pub fn gdn_prep_decode(
19158 &self,
19159 conv_out: &CudaSlice<f32>,
19160 beta_raw: &CudaSlice<f32>,
19161 alpha: &CudaSlice<f32>,
19162 dt_bias: &CudaSlice<f32>,
19163 a: &CudaSlice<f32>,
19164 q_l2: &mut CudaSlice<f32>,
19165 k_l2: &mut CudaSlice<f32>,
19166 v_g: &mut CudaSlice<f32>,
19167 beta: &mut CudaSlice<f32>,
19168 g_log: &mut CudaSlice<f32>,
19169 d_state: usize,
19170 num_v: usize,
19171 num_k: usize,
19172 key_dim: usize,
19173 eps: f32,
19174 ) -> Result<(), Box<dyn std::error::Error>> {
19175 let f = self.func("gdn_prep_decode_f32");
19176 let cfg = LaunchConfig {
19177 grid_dim: (num_v as u32, 1, 1),
19178 block_dim: (32, 4, 1),
19179 shared_mem_bytes: 0,
19180 };
19181 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
19182 let __s_b = self.gpu.stream();
19183 let mut b = __s_b.launch_builder(&f);
19184 b.arg(conv_out)
19185 .arg(beta_raw)
19186 .arg(alpha)
19187 .arg(dt_bias)
19188 .arg(a)
19189 .arg(q_l2)
19190 .arg(k_l2)
19191 .arg(v_g)
19192 .arg(beta)
19193 .arg(g_log)
19194 .arg(&ds)
19195 .arg(&nv)
19196 .arg(&nk)
19197 .arg(&kd)
19198 .arg(&eps);
19199 unsafe {
19200 b.launch(cfg)?;
19201 }
19202 Ok(())
19203 }
19204
19205 #[allow(clippy::too_many_arguments)]
19209 pub fn ssm_conv1d_gdn(
19210 &self,
19211 qkv_tm: &CudaSlice<f32>,
19212 w: &CudaSlice<f32>,
19213 q_g: &mut CudaSlice<f32>,
19214 k_g: &mut CudaSlice<f32>,
19215 v_g: &mut CudaSlice<f32>,
19216 conv_dim: usize,
19217 t: usize,
19218 d_conv: usize,
19219 d_state: usize,
19220 num_v: usize,
19221 num_k: usize,
19222 key_dim: usize,
19223 ) -> Result<(), Box<dyn std::error::Error>> {
19224 let f = self.func("ssm_conv1d_gdn_f32");
19225 let cfg = LaunchConfig {
19226 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19227 block_dim: (256, 1, 1),
19228 shared_mem_bytes: 0,
19229 };
19230 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19231 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
19232 let __s_b = self.gpu.stream();
19233 let mut b = __s_b.launch_builder(&f);
19234 b.arg(qkv_tm)
19235 .arg(w)
19236 .arg(q_g)
19237 .arg(k_g)
19238 .arg(v_g)
19239 .arg(&cd)
19240 .arg(&ti)
19241 .arg(&dc)
19242 .arg(&ds)
19243 .arg(&nv)
19244 .arg(&nk)
19245 .arg(&kd);
19246 unsafe {
19247 b.launch(cfg)?;
19248 }
19249 Ok(())
19250 }
19251
19252 pub fn ssm_conv1d(
19253 &self,
19254 x: &CudaSlice<f32>,
19255 w: &CudaSlice<f32>,
19256 y: &mut CudaSlice<f32>,
19257 conv_dim: usize,
19258 t: usize,
19259 d_conv: usize,
19260 silu: bool,
19261 ) -> Result<(), Box<dyn std::error::Error>> {
19262 let f = self.func("ssm_conv1d_silu_f32");
19263 let cfg = LaunchConfig {
19264 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
19265 block_dim: (256, 1, 1),
19266 shared_mem_bytes: 0,
19267 };
19268 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
19269 let __s_b = self.gpu.stream();
19270 let mut b = __s_b.launch_builder(&f);
19271 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
19272 unsafe {
19273 b.launch(cfg)?;
19274 }
19275 Ok(())
19276 }
19277
19278 pub fn gdn_scan_s128(
19281 &self,
19282 q: &CudaSlice<f32>,
19283 k: &CudaSlice<f32>,
19284 v: &CudaSlice<f32>,
19285 g: &CudaSlice<f32>,
19286 beta: &CudaSlice<f32>,
19287 state_in: &CudaSlice<f32>,
19288 state_out: &mut CudaSlice<f32>,
19289 o: &mut CudaSlice<f32>,
19290 n_head: usize,
19291 t: usize,
19292 scale: f32,
19293 ) -> Result<(), Box<dyn std::error::Error>> {
19294 let f = self.func("gdn_scan_s128");
19295 const S_V: u32 = 128;
19296 const WARP: u32 = 32;
19297 const COLS_PER_BLOCK: u32 = 4;
19298 let cfg = LaunchConfig {
19299 grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
19300 block_dim: (WARP, COLS_PER_BLOCK, 1),
19301 shared_mem_bytes: 0,
19302 };
19303 let (h, ti) = (n_head as i32, t as i32);
19304 let __s_b = self.gpu.stream();
19305 let mut b = __s_b.launch_builder(&f);
19306 b.arg(q)
19307 .arg(k)
19308 .arg(v)
19309 .arg(g)
19310 .arg(beta)
19311 .arg(state_in)
19312 .arg(state_out)
19313 .arg(o)
19314 .arg(&h)
19315 .arg(&ti)
19316 .arg(&scale);
19317 unsafe {
19318 b.launch(cfg)?;
19319 }
19320 Ok(())
19321 }
19322
19323 #[allow(clippy::too_many_arguments)]
19328 pub fn ssm_conv1d_fused_decode_b(
19329 &self,
19330 qkv_cols: &CudaSlice<f32>,
19331 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
19332 w: &CudaSlice<f32>,
19333 conv_outs: &mut CudaSlice<f32>,
19334 conv_dim: usize,
19335 d_conv: usize,
19336 b_n: usize,
19337 ) -> Result<(), Box<dyn std::error::Error>> {
19338 let f = self.func("ssm_conv1d_fused_decode_b_f32");
19339 let cfg = LaunchConfig {
19340 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
19341 block_dim: (256, 1, 1),
19342 shared_mem_bytes: 0,
19343 };
19344 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19345 let __s_b = self.gpu.stream();
19346 let mut b = __s_b.launch_builder(&f);
19347 b.arg(qkv_cols)
19348 .arg(conv_state_ptrs)
19349 .arg(w)
19350 .arg(conv_outs)
19351 .arg(&cd)
19352 .arg(&dc);
19353 unsafe {
19354 b.launch(cfg)?;
19355 }
19356 Ok(())
19357 }
19358
19359 #[allow(clippy::too_many_arguments)]
19360 pub fn gdn_prep_decode_b(
19361 &self,
19362 conv_outs: &CudaSlice<f32>,
19363 beta_raws: &CudaSlice<f32>,
19364 alphas: &CudaSlice<f32>,
19365 dt_bias: &CudaSlice<f32>,
19366 a: &CudaSlice<f32>,
19367 q_l2: &mut CudaSlice<f32>,
19368 k_l2: &mut CudaSlice<f32>,
19369 v_g: &mut CudaSlice<f32>,
19370 beta: &mut CudaSlice<f32>,
19371 g_log: &mut CudaSlice<f32>,
19372 d_state: usize,
19373 num_v: usize,
19374 num_k: usize,
19375 key_dim: usize,
19376 eps: f32,
19377 conv_dim: usize,
19378 b_n: usize,
19379 ) -> Result<(), Box<dyn std::error::Error>> {
19380 let f = self.func("gdn_prep_decode_b_f32");
19381 let cfg = LaunchConfig {
19382 grid_dim: (num_v as u32, 1, b_n as u32),
19383 block_dim: (32, 4, 1),
19384 shared_mem_bytes: 0,
19385 };
19386 let (ds, nv, nk, kd, cd) = (
19387 d_state as i32,
19388 num_v as i32,
19389 num_k as i32,
19390 key_dim as i32,
19391 conv_dim as i32,
19392 );
19393 let __s_b = self.gpu.stream();
19394 let mut b = __s_b.launch_builder(&f);
19395 b.arg(conv_outs)
19396 .arg(beta_raws)
19397 .arg(alphas)
19398 .arg(dt_bias)
19399 .arg(a)
19400 .arg(q_l2)
19401 .arg(k_l2)
19402 .arg(v_g)
19403 .arg(beta)
19404 .arg(g_log)
19405 .arg(&ds)
19406 .arg(&nv)
19407 .arg(&nk)
19408 .arg(&kd)
19409 .arg(&eps)
19410 .arg(&cd);
19411 unsafe {
19412 b.launch(cfg)?;
19413 }
19414 Ok(())
19415 }
19416
19417 #[allow(clippy::too_many_arguments)]
19418 pub fn gdn_scan_s128_batched(
19419 &self,
19420 q: &CudaSlice<f32>,
19421 k: &CudaSlice<f32>,
19422 v: &CudaSlice<f32>,
19423 g: &CudaSlice<f32>,
19424 beta: &CudaSlice<f32>,
19425 state_in_ptrs: &cudarc::driver::CudaView<u64>,
19426 state_out_ptrs: &cudarc::driver::CudaView<u64>,
19427 o: &mut CudaSlice<f32>,
19428 n_head: usize,
19429 b_n: usize,
19430 scale: f32,
19431 ) -> Result<(), Box<dyn std::error::Error>> {
19432 let f = self.func("gdn_scan_s128_b");
19433 const S_V: u32 = 128;
19434 const WARP: u32 = 32;
19435 const COLS_PER_BLOCK: u32 = 4;
19436 let cfg = LaunchConfig {
19437 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
19438 block_dim: (WARP, COLS_PER_BLOCK, 1),
19439 shared_mem_bytes: 0,
19440 };
19441 let h = n_head as i32;
19442 let __s_b = self.gpu.stream();
19443 let mut b = __s_b.launch_builder(&f);
19444 b.arg(q)
19445 .arg(k)
19446 .arg(v)
19447 .arg(g)
19448 .arg(beta)
19449 .arg(state_in_ptrs)
19450 .arg(state_out_ptrs)
19451 .arg(o)
19452 .arg(&h)
19453 .arg(&scale);
19454 unsafe {
19455 b.launch(cfg)?;
19456 }
19457 Ok(())
19458 }
19459
19460 #[allow(clippy::too_many_arguments)]
19466 pub fn ssm_conv1d_fused_decode_b_view(
19467 &self,
19468 qkv_cols: &cudarc::driver::CudaView<f32>,
19469 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
19470 w: &CudaSlice<f32>,
19471 conv_outs: &mut CudaSlice<f32>,
19472 conv_dim: usize,
19473 d_conv: usize,
19474 b_n: usize,
19475 ) -> Result<(), Box<dyn std::error::Error>> {
19476 let f = self.func("ssm_conv1d_fused_decode_b_f32");
19477 let cfg = LaunchConfig {
19478 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
19479 block_dim: (256, 1, 1),
19480 shared_mem_bytes: 0,
19481 };
19482 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19483 let __s_b = self.gpu.stream();
19484 let mut b = __s_b.launch_builder(&f);
19485 b.arg(qkv_cols)
19486 .arg(conv_state_ptrs)
19487 .arg(w)
19488 .arg(conv_outs)
19489 .arg(&cd)
19490 .arg(&dc);
19491 unsafe {
19492 b.launch(cfg)?;
19493 }
19494 Ok(())
19495 }
19496
19497 #[allow(clippy::too_many_arguments)]
19498 pub fn gdn_prep_decode_b_view(
19499 &self,
19500 conv_outs: &CudaSlice<f32>,
19501 beta_raws: &cudarc::driver::CudaView<f32>,
19502 alphas: &cudarc::driver::CudaView<f32>,
19503 dt_bias: &CudaSlice<f32>,
19504 a: &CudaSlice<f32>,
19505 q_l2: &mut CudaSlice<f32>,
19506 k_l2: &mut CudaSlice<f32>,
19507 v_g: &mut CudaSlice<f32>,
19508 beta: &mut CudaSlice<f32>,
19509 g_log: &mut CudaSlice<f32>,
19510 d_state: usize,
19511 num_v: usize,
19512 num_k: usize,
19513 key_dim: usize,
19514 eps: f32,
19515 conv_dim: usize,
19516 b_n: usize,
19517 ) -> Result<(), Box<dyn std::error::Error>> {
19518 let f = self.func("gdn_prep_decode_b_f32");
19519 let cfg = LaunchConfig {
19520 grid_dim: (num_v as u32, 1, b_n as u32),
19521 block_dim: (32, 4, 1),
19522 shared_mem_bytes: 0,
19523 };
19524 let (ds, nv, nk, kd, cd) = (
19525 d_state as i32,
19526 num_v as i32,
19527 num_k as i32,
19528 key_dim as i32,
19529 conv_dim as i32,
19530 );
19531 let __s_b = self.gpu.stream();
19532 let mut b = __s_b.launch_builder(&f);
19533 b.arg(conv_outs)
19534 .arg(beta_raws)
19535 .arg(alphas)
19536 .arg(dt_bias)
19537 .arg(a)
19538 .arg(q_l2)
19539 .arg(k_l2)
19540 .arg(v_g)
19541 .arg(beta)
19542 .arg(g_log)
19543 .arg(&ds)
19544 .arg(&nv)
19545 .arg(&nk)
19546 .arg(&kd)
19547 .arg(&eps)
19548 .arg(&cd);
19549 unsafe {
19550 b.launch(cfg)?;
19551 }
19552 Ok(())
19553 }
19554
19555 #[allow(clippy::too_many_arguments)]
19556 pub fn gdn_scan_s128_batched_view(
19557 &self,
19558 q: &CudaSlice<f32>,
19559 k: &CudaSlice<f32>,
19560 v: &CudaSlice<f32>,
19561 g: &CudaSlice<f32>,
19562 beta: &CudaSlice<f32>,
19563 state_in_ptrs: &cudarc::driver::CudaView<u64>,
19564 state_out_ptrs: &cudarc::driver::CudaView<u64>,
19565 o: &mut cudarc::driver::CudaViewMut<f32>,
19566 n_head: usize,
19567 b_n: usize,
19568 scale: f32,
19569 ) -> Result<(), Box<dyn std::error::Error>> {
19570 let f = self.func("gdn_scan_s128_b");
19571 const S_V: u32 = 128;
19572 const WARP: u32 = 32;
19573 const COLS_PER_BLOCK: u32 = 4;
19574 let cfg = LaunchConfig {
19575 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
19576 block_dim: (WARP, COLS_PER_BLOCK, 1),
19577 shared_mem_bytes: 0,
19578 };
19579 let h = n_head as i32;
19580 let __s_b = self.gpu.stream();
19581 let mut b = __s_b.launch_builder(&f);
19582 b.arg(q)
19583 .arg(k)
19584 .arg(v)
19585 .arg(g)
19586 .arg(beta)
19587 .arg(state_in_ptrs)
19588 .arg(state_out_ptrs)
19589 .arg(o)
19590 .arg(&h)
19591 .arg(&scale);
19592 unsafe {
19593 b.launch(cfg)?;
19594 }
19595 Ok(())
19596 }
19597
19598 pub fn gdn_chunked_enabled() -> bool {
19607 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19608 *E.get_or_init(|| {
19609 std::env::var("MEMRA_GDN_CHUNKED")
19610 .map(|v| v != "0")
19611 .unwrap_or(true)
19612 })
19613 }
19614
19615 pub fn gdn_chunk_size() -> usize {
19620 static C: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
19621 *C.get_or_init(|| {
19622 let c: usize = std::env::var("MEMRA_GDN_CHUNK")
19623 .ok()
19624 .and_then(|v| v.parse().ok())
19625 .unwrap_or(32);
19626 c.clamp(32, 128) / 32 * 32
19627 })
19628 }
19629
19630 #[allow(clippy::too_many_arguments)]
19635 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
19638 #[allow(clippy::too_many_arguments)]
19639 pub fn gdn_chunk_k123(
19640 &self,
19641 q: &CudaSlice<f32>,
19642 k: &CudaSlice<f32>,
19643 v: &CudaSlice<f32>,
19644 g: &CudaSlice<f32>,
19645 beta: &CudaSlice<f32>,
19646 wb16: Option<&mut CudaSlice<u8>>,
19647 n_head: usize,
19648 t: usize,
19649 c: usize,
19650 hk: usize,
19651 k2w: Option<(&CudaSlice<u8>, &CudaSlice<u8>, &mut CudaSlice<u8>)>,
19652 ) -> Result<
19653 (
19654 CudaSlice<f32>,
19655 CudaSlice<f32>,
19656 CudaSlice<f32>,
19657 CudaSlice<f32>,
19658 ),
19659 Box<dyn std::error::Error>,
19660 > {
19661 const D: usize = 128;
19662 let h = n_head;
19663 let nc = (t + c - 1) / c;
19664 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
19665 let mut gcum = self.uninit(t * h)?;
19666 let mut a = self.uninit(nc * h * c * c)?;
19667 let mut p = self.uninit(nc * h * c * c)?;
19668 let mut u = self.uninit(nc * h * c * D)?;
19669 let mut w = self.uninit(nc * h * c * D)?;
19670 {
19671 let f = self.func("gdn_chunk_cumgate_f32");
19673 let cfg = LaunchConfig {
19674 grid_dim: (nc as u32, h as u32, 1),
19675 block_dim: (32, 1, 1),
19676 shared_mem_bytes: 0,
19677 };
19678 let __s_b = self.gpu.stream();
19679 let mut b = __s_b.launch_builder(&f);
19680 b.arg(g).arg(&mut gcum).arg(&hi).arg(&ti).arg(&ci);
19681 unsafe {
19682 b.launch(cfg)?;
19683 }
19684 }
19685 if let Some((qb, kb, pb)) = k2w {
19686 assert!(c == 32, "gdn_k2_wgmma is a C==32 tile");
19689 let f = self.func("gdn_k2_wgmma");
19690 let cfg = LaunchConfig {
19691 grid_dim: (nc as u32, h as u32, 1),
19692 block_dim: (128, 1, 1),
19693 shared_mem_bytes: 0,
19694 };
19695 let hki = hk as i32;
19696 let __s_b = self.gpu.stream();
19697 let mut b = __s_b.launch_builder(&f);
19698 b.arg(qb)
19699 .arg(kb)
19700 .arg(&gcum)
19701 .arg(beta)
19702 .arg(&mut a)
19703 .arg(&mut *pb)
19704 .arg(&hi)
19705 .arg(&ti)
19706 .arg(&ci)
19707 .arg(&hki);
19708 unsafe {
19709 b.launch(cfg)?;
19710 }
19711 } else if c <= 64 && !portable_mma_gated() {
19712 let f = self.func("gdn_chunk_attn_f32");
19714 let jt = ((c + 31) / 32) as u32;
19715 let cfg = LaunchConfig {
19716 grid_dim: (nc as u32, h as u32, jt),
19717 block_dim: (256, 1, 1),
19718 shared_mem_bytes: 0,
19719 };
19720 let hki = hk as i32;
19721 let __s_b = self.gpu.stream();
19722 let mut b = __s_b.launch_builder(&f);
19723 b.arg(q)
19724 .arg(k)
19725 .arg(&gcum)
19726 .arg(beta)
19727 .arg(&mut a)
19728 .arg(&mut p)
19729 .arg(&hi)
19730 .arg(&ti)
19731 .arg(&ci)
19732 .arg(&hki);
19733 unsafe {
19734 b.launch(cfg)?;
19735 }
19736 } else {
19737 assert!(
19739 hk == h,
19740 "generic K2 is broadcast-only (de-broadcast rides C==32)"
19741 );
19742 let f = self.func("gdn_chunk_attn_g_f32");
19743 let cfg = LaunchConfig {
19744 grid_dim: (nc as u32, h as u32, 1),
19745 block_dim: (32, 8, 1),
19746 shared_mem_bytes: 0,
19747 };
19748 let __s_b = self.gpu.stream();
19749 let mut b = __s_b.launch_builder(&f);
19750 b.arg(q)
19751 .arg(k)
19752 .arg(&gcum)
19753 .arg(beta)
19754 .arg(&mut a)
19755 .arg(&mut p)
19756 .arg(&hi)
19757 .arg(&ti)
19758 .arg(&ci);
19759 unsafe {
19760 b.launch(cfg)?;
19761 }
19762 }
19763 {
19764 let cfg = LaunchConfig {
19766 grid_dim: (nc as u32, h as u32, 1),
19767 block_dim: (256, 1, 1),
19768 shared_mem_bytes: 0,
19769 };
19770 match c {
19771 32 | 64 => {
19772 let f = self.func(if c == 32 {
19773 "gdn_chunk_solve32_f32"
19774 } else {
19775 "gdn_chunk_solve64_f32"
19776 });
19777 let wb: u64 = match wb16 {
19779 Some(d) => self.addr_u8(d),
19780 None => 0,
19781 };
19782 let hki = hk as i32;
19783 let __s_b = self.gpu.stream();
19784 let mut b = __s_b.launch_builder(&f);
19785 b.arg(v)
19786 .arg(k)
19787 .arg(&a)
19788 .arg(&gcum)
19789 .arg(&mut u)
19790 .arg(&mut w)
19791 .arg(&wb)
19792 .arg(&hi)
19793 .arg(&ti)
19794 .arg(&hki);
19795 unsafe {
19796 b.launch(cfg)?;
19797 }
19798 }
19799 _ => {
19800 assert!(hk == h, "generic K3 is broadcast-only");
19801 let f = self.func("gdn_chunk_solve_f32");
19802 let __s_b = self.gpu.stream();
19803 let mut b = __s_b.launch_builder(&f);
19804 b.arg(v)
19805 .arg(k)
19806 .arg(&a)
19807 .arg(&gcum)
19808 .arg(&mut u)
19809 .arg(&mut w)
19810 .arg(&hi)
19811 .arg(&ti)
19812 .arg(&ci);
19813 unsafe {
19814 b.launch(cfg)?;
19815 }
19816 }
19817 }
19818 }
19819 Ok((gcum, p, u, w))
19820 }
19821
19822 pub fn gdn_db_on() -> bool {
19826 std::env::var("MEMRA_GDN_DB").as_deref() != Ok("0")
19827 }
19828
19829 pub fn gdn_mma_enabled(&self, c: usize) -> bool {
19832 !portable_mma_gated()
19833 && c == 32
19834 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
19835 Ok("1") => true,
19836 Ok("0") => false,
19837 _ => cfg!(memra_hopper_mma),
19838 }
19839 }
19840
19841 pub fn gdn_wgmma_on(&self, c: usize) -> bool {
19844 self.gdn_mma_enabled(c)
19845 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
19846 Ok("0") => false,
19847 Ok("1") => true,
19848 _ => cfg!(memra_hopper_mma),
19849 }
19850 }
19851
19852 #[allow(clippy::too_many_arguments)]
19857 pub fn ssm_conv1d_gdn_state_pad(
19858 &self,
19859 qkv_tm: &cudarc::driver::CudaView<f32>,
19860 conv_state: &mut CudaSlice<f32>,
19861 w: &CudaSlice<f32>,
19862 q_g: &mut CudaSlice<f32>,
19863 k_g: &mut CudaSlice<f32>,
19864 v_g: &mut CudaSlice<f32>,
19865 conv_dim: usize,
19866 t: usize,
19867 d_conv: usize,
19868 d_state: usize,
19869 num_v: usize,
19870 num_k: usize,
19871 key_dim: usize,
19872 hk: usize,
19873 pad_len: Option<&CudaSlice<i32>>,
19874 ) -> Result<(), Box<dyn std::error::Error>> {
19875 assert!(
19876 t >= d_conv - 1,
19877 "fused state conv requires T >= pad (PRIME_MIN_T gates)"
19878 );
19879 {
19880 let f = self.func("ssm_conv1d_gdn_state_f32");
19881 let cfg = LaunchConfig {
19882 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19883 block_dim: (256, 1, 1),
19884 shared_mem_bytes: 0,
19885 };
19886 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19887 let (ds, nv, nk, kd, hki) = (
19888 d_state as i32,
19889 num_v as i32,
19890 num_k as i32,
19891 key_dim as i32,
19892 hk as i32,
19893 );
19894 let __s_b = self.gpu.stream();
19895 let mut b = __s_b.launch_builder(&f);
19896 b.arg(qkv_tm)
19897 .arg(&*conv_state)
19898 .arg(w)
19899 .arg(q_g)
19900 .arg(k_g)
19901 .arg(v_g)
19902 .arg(&cd)
19903 .arg(&ti)
19904 .arg(&dc)
19905 .arg(&ds)
19906 .arg(&nv)
19907 .arg(&nk)
19908 .arg(&kd)
19909 .arg(&hki);
19910 unsafe {
19911 b.launch(cfg)?;
19912 }
19913 }
19914 match pad_len {
19915 Some(len_d) => {
19916 let f = self.func("ssm_conv_ring_update_dev_f32");
19917 let n = conv_dim * (d_conv - 1);
19918 let cfg = LaunchConfig::for_num_elems(n as u32);
19919 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19920 let __s_b = self.gpu.stream();
19921 let mut b = __s_b.launch_builder(&f);
19922 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
19923 unsafe {
19924 b.launch(cfg)?;
19925 }
19926 }
19927 None => {
19928 let f = self.func("ssm_conv_ring_update_f32");
19929 let n = conv_dim * (d_conv - 1);
19930 let cfg = LaunchConfig::for_num_elems(n as u32);
19931 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19932 let __s_b = self.gpu.stream();
19933 let mut b = __s_b.launch_builder(&f);
19934 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
19935 unsafe {
19936 b.launch(cfg)?;
19937 }
19938 }
19939 }
19940 Ok(())
19941 }
19942
19943 pub fn gdn_chunk_alloc(
19947 &self,
19948 n_head: usize,
19949 t: usize,
19950 c: usize,
19951 hk: usize,
19952 ) -> Result<GdnChunkBufs, Box<dyn std::error::Error>> {
19953 const D: usize = 128;
19954 assert!(
19955 c == 32,
19956 "gdn_chunk_alloc: varlen chain is the C==32 mma pair"
19957 );
19958 let h = n_head;
19959 let nc = (t + c - 1) / c;
19960 Ok(GdnChunkBufs {
19961 gcum: self.uninit(t * h)?,
19962 a: self.uninit(nc * h * c * c)?,
19963 p: self.uninit(nc * h * c * c)?,
19964 u: self.uninit(nc * h * c * D)?,
19965 w: self.uninit(nc * h * c * D)?,
19966 kb16: self.alloc_u8_uninit(t * hk * D * 2)?,
19967 wb16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
19968 y16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
19969 ssnap16: self.alloc_u8_uninit(nc * h * D * D * 2)?,
19970 qb16: self.alloc_u8_uninit(t * hk * D * 2)?,
19971 pb16: self.alloc_u8_uninit(nc * h * c * c * 2)?,
19972 o: self.uninit(D * h * t)?,
19973 t,
19974 nc,
19975 })
19976 }
19977
19978 pub fn f32_to_bf16_v(
19980 &self,
19981 x: &cudarc::driver::CudaView<f32>,
19982 dst: &mut CudaSlice<u8>,
19983 n: usize,
19984 ) -> Result<(), Box<dyn std::error::Error>> {
19985 let f = self.func("f32_to_bf16_bulk");
19986 let ni = n as i64;
19987 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
19988 let __s_b = self.gpu.stream();
19989 let mut b = __s_b.launch_builder(&f);
19990 b.arg(x).arg(dst).arg(&ni);
19991 unsafe {
19992 b.launch(cfg)?;
19993 }
19994 Ok(())
19995 }
19996
19997 pub fn f32_to_bf16_into(
19999 &self,
20000 x: &CudaSlice<f32>,
20001 dst: &mut CudaSlice<u8>,
20002 n: usize,
20003 ) -> Result<(), Box<dyn std::error::Error>> {
20004 let f = self.func("f32_to_bf16_bulk");
20005 let ni = n as i64;
20006 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
20007 let __s_b = self.gpu.stream();
20008 let mut b = __s_b.launch_builder(&f);
20009 b.arg(x).arg(dst).arg(&ni);
20010 unsafe {
20011 b.launch(cfg)?;
20012 }
20013 Ok(())
20014 }
20015
20016 pub fn gdn_chunk_k123_vl8(
20019 &self,
20020 seqs: &[GdnSeqVl],
20021 n_head: usize,
20022 hk: usize,
20023 wq: Option<&GdnWVl8>,
20024 ) -> Result<(), Box<dyn std::error::Error>> {
20025 let b = seqs.len();
20026 assert!(b >= 1 && b <= 8, "gdn_chunk_k123_vl8: 1..=8 sequences");
20027 let mut packed = [GdnSeqVl::default(); 8];
20028 packed[..b].copy_from_slice(seqs);
20029 let v = GdnVl8(packed);
20030 let (hi, ci) = (n_head as i32, 32i32);
20031 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
20032 {
20033 let f = self.func("gdn_chunk_cumgate_vl");
20034 let cfg = LaunchConfig {
20035 grid_dim: (max_nc, n_head as u32, b as u32),
20036 block_dim: (32, 1, 1),
20037 shared_mem_bytes: 0,
20038 };
20039 let __s_lb = self.gpu.stream();
20040 let mut lb = __s_lb.launch_builder(&f);
20041 lb.arg(&v).arg(&hi).arg(&ci);
20042 unsafe {
20043 lb.launch(cfg)?;
20044 }
20045 }
20046 let hki = hk as i32;
20047 if let Some(w) = wq {
20048 let f = self.func("gdn_k2_wgmma_vl");
20050 let cfg = LaunchConfig {
20051 grid_dim: (max_nc, n_head as u32, b as u32),
20052 block_dim: (128, 1, 1),
20053 shared_mem_bytes: 0,
20054 };
20055 let __s_lb = self.gpu.stream();
20056 let mut lb = __s_lb.launch_builder(&f);
20057 lb.arg(&v).arg(w).arg(&hi).arg(&ci).arg(&hki);
20058 unsafe {
20059 lb.launch(cfg)?;
20060 }
20061 } else {
20062 let f = self.func("gdn_chunk_attn_vl");
20063 let cfg = LaunchConfig {
20064 grid_dim: (max_nc, n_head as u32, b as u32),
20065 block_dim: (256, 1, 1),
20066 shared_mem_bytes: 0,
20067 };
20068 let __s_lb = self.gpu.stream();
20069 let mut lb = __s_lb.launch_builder(&f);
20070 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
20071 unsafe {
20072 lb.launch(cfg)?;
20073 }
20074 }
20075 {
20076 let f = self.func("gdn_chunk_solve32_vl");
20077 let cfg = LaunchConfig {
20078 grid_dim: (max_nc, n_head as u32, b as u32),
20079 block_dim: (256, 1, 1),
20080 shared_mem_bytes: 0,
20081 };
20082 let __s_lb = self.gpu.stream();
20083 let mut lb = __s_lb.launch_builder(&f);
20084 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
20085 unsafe {
20086 lb.launch(cfg)?;
20087 }
20088 }
20089 Ok(())
20090 }
20091
20092 #[allow(clippy::too_many_arguments)]
20096 pub fn gdn_prep_vl8(
20097 &self,
20098 seqs: &[GdnPrepVl],
20099 conv_w: &CudaSlice<f32>,
20100 dt_bias: &CudaSlice<f32>,
20101 a: &CudaSlice<f32>,
20102 conv_dim: usize,
20103 d_conv: usize,
20104 d_state: usize,
20105 num_v: usize,
20106 num_k: usize,
20107 key_dim: usize,
20108 hk: usize,
20109 eps: f32,
20110 ) -> Result<(), Box<dyn std::error::Error>> {
20111 let b = seqs.len();
20112 assert!(b >= 1 && b <= 8);
20113 let mut packed = [GdnPrepVl::default(); 8];
20114 packed[..b].copy_from_slice(seqs);
20115 let v = GdnPrepVl8(packed);
20116 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
20117 let (cdi, dci) = (conv_dim as i32, d_conv as i32);
20118 let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
20119 assert!(
20120 conv_fuse || hk == num_v,
20121 "de-broadcast requires the fused conv"
20122 );
20123 if conv_fuse {
20124 let f = self.func("ssm_conv1d_gdn_state_vl");
20125 let cfg = LaunchConfig {
20126 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
20127 block_dim: (256, 1, 1),
20128 shared_mem_bytes: 0,
20129 };
20130 let (dsi, nvi, nki, kdi, hki) = (
20131 d_state as i32,
20132 num_v as i32,
20133 num_k as i32,
20134 key_dim as i32,
20135 hk as i32,
20136 );
20137 let __s_lb = self.gpu.stream();
20138 let mut lb = __s_lb.launch_builder(&f);
20139 lb.arg(&v)
20140 .arg(conv_w)
20141 .arg(&cdi)
20142 .arg(&dci)
20143 .arg(&dsi)
20144 .arg(&nvi)
20145 .arg(&nki)
20146 .arg(&kdi)
20147 .arg(&hki);
20148 unsafe {
20149 lb.launch(cfg)?;
20150 }
20151 } else {
20152 let f = self.func("ssm_conv1d_tm_state_vl");
20153 let cfg = LaunchConfig {
20154 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
20155 block_dim: (256, 1, 1),
20156 shared_mem_bytes: 0,
20157 };
20158 let __s_lb = self.gpu.stream();
20159 let mut lb = __s_lb.launch_builder(&f);
20160 lb.arg(&v).arg(conv_w).arg(&cdi).arg(&dci);
20161 unsafe {
20162 lb.launch(cfg)?;
20163 }
20164 }
20165 {
20166 let f = self.func("ssm_conv_ring_update_vl");
20167 let n = (conv_dim * (d_conv - 1)) as u32;
20168 let cfg = LaunchConfig {
20169 grid_dim: (n.div_ceil(256), 1, b as u32),
20170 block_dim: (256, 1, 1),
20171 shared_mem_bytes: 0,
20172 };
20173 let __s_lb = self.gpu.stream();
20174 let mut lb = __s_lb.launch_builder(&f);
20175 lb.arg(&v).arg(&cdi).arg(&dci);
20176 unsafe {
20177 lb.launch(cfg)?;
20178 }
20179 }
20180 if !conv_fuse {
20181 let f = self.func("qkv_to_gdn_repack_vl");
20182 let n = max_t * (num_v * d_state) as u32;
20183 let cfg = LaunchConfig {
20184 grid_dim: (n.div_ceil(256), 1, b as u32),
20185 block_dim: (256, 1, 1),
20186 shared_mem_bytes: 0,
20187 };
20188 let (dsi, nvi, nki, kdi) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
20189 let __s_lb = self.gpu.stream();
20190 let mut lb = __s_lb.launch_builder(&f);
20191 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&nki).arg(&kdi);
20192 unsafe {
20193 lb.launch(cfg)?;
20194 }
20195 }
20196 if Self::l2_v2_on(d_state) {
20197 let f = self.func("gdn_l2_v2_vl");
20198 let cfg = LaunchConfig {
20199 grid_dim: ((max_t * hk as u32).div_ceil(8), 2, b as u32),
20200 block_dim: (256, 1, 1),
20201 shared_mem_bytes: 0,
20202 };
20203 let (dsi, nvi) = (d_state as i32, hk as i32);
20204 let __s_lb = self.gpu.stream();
20205 let mut lb = __s_lb.launch_builder(&f);
20206 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
20207 unsafe {
20208 lb.launch(cfg)?;
20209 }
20210 } else {
20211 let f = self.func("gdn_l2_vl");
20212 let cfg = LaunchConfig {
20213 grid_dim: (max_t * hk as u32, 2, b as u32),
20214 block_dim: (256, 1, 1),
20215 shared_mem_bytes: 0,
20216 };
20217 let (dsi, nvi) = (d_state as i32, hk as i32);
20218 let __s_lb = self.gpu.stream();
20219 let mut lb = __s_lb.launch_builder(&f);
20220 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
20221 unsafe {
20222 lb.launch(cfg)?;
20223 }
20224 }
20225 {
20226 let f = self.func("gdn_gate_prep_vl");
20227 let n = max_t * num_v as u32;
20228 let cfg = LaunchConfig {
20229 grid_dim: (n.div_ceil(256), 1, b as u32),
20230 block_dim: (256, 1, 1),
20231 shared_mem_bytes: 0,
20232 };
20233 let nvi = num_v as i32;
20234 let __s_lb = self.gpu.stream();
20235 let mut lb = __s_lb.launch_builder(&f);
20236 lb.arg(&v).arg(dt_bias).arg(a).arg(&nvi);
20237 unsafe {
20238 lb.launch(cfg)?;
20239 }
20240 }
20241 Ok(())
20242 }
20243
20244 pub fn gdn_mirror_vl8(
20246 &self,
20247 seqs: &[GdnSeqVl],
20248 n_head: usize,
20249 which: i32,
20250 hk: usize,
20251 ) -> Result<(), Box<dyn std::error::Error>> {
20252 let b = seqs.len();
20253 assert!(b >= 1 && b <= 8);
20254 let mut packed = [GdnSeqVl::default(); 8];
20255 packed[..b].copy_from_slice(seqs);
20256 let v = GdnVl8(packed);
20257 let ept = (if which == 0 { hk } else { n_head } * 128) as i32;
20258 let max_n = seqs
20259 .iter()
20260 .map(|s| {
20261 if which == 0 {
20262 s.t as i64 * ept as i64
20263 } else {
20264 s.nc as i64 * ept as i64 * 32
20265 }
20266 })
20267 .max()
20268 .unwrap();
20269 let f = self.func("gdn_mirror_vl");
20270 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
20271 let cfg = LaunchConfig {
20272 grid_dim: (blocks, 1, b as u32),
20273 block_dim: (256, 1, 1),
20274 shared_mem_bytes: 0,
20275 };
20276 let __s_lb = self.gpu.stream();
20277 let mut lb = __s_lb.launch_builder(&f);
20278 lb.arg(&v).arg(&ept).arg(&which);
20279 unsafe {
20280 lb.launch(cfg)?;
20281 }
20282 Ok(())
20283 }
20284
20285 pub fn gdn_tail_vl8(
20287 &self,
20288 seqs: &[GdnPrepVl],
20289 norm_w: &CudaSlice<f32>,
20290 d_state: usize,
20291 num_v: usize,
20292 eps: f32,
20293 ) -> Result<(), Box<dyn std::error::Error>> {
20294 let b = seqs.len();
20295 assert!(b >= 1 && b <= 8);
20296 let mut packed = [GdnPrepVl::default(); 8];
20297 packed[..b].copy_from_slice(seqs);
20298 let v = GdnPrepVl8(packed);
20299 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
20300 let f = self.func("gated_rmsnorm_f16out_vl");
20301 let cfg = LaunchConfig {
20303 grid_dim: (max_t * num_v as u32, 1, b as u32),
20304 block_dim: (128, 1, 1),
20305 shared_mem_bytes: 0,
20306 };
20307 let (dsi, nvi) = (d_state as i32, num_v as i32);
20308 let __s_lb = self.gpu.stream();
20309 let mut lb = __s_lb.launch_builder(&f);
20310 lb.arg(&v).arg(norm_w).arg(&dsi).arg(&nvi).arg(&eps);
20311 unsafe {
20312 lb.launch(cfg)?;
20313 }
20314 Ok(())
20315 }
20316
20317 pub fn addr_f32(&self, x: &CudaSlice<f32>) -> u64 {
20320 use cudarc::driver::DevicePtr;
20321 let s = self.gpu.stream();
20322 let (p, _g) = x.device_ptr(&s);
20323 p as u64
20324 }
20325 pub fn addr_f32_mut(&self, x: &mut CudaSlice<f32>) -> u64 {
20326 use cudarc::driver::DevicePtrMut;
20327 let s = self.gpu.stream();
20328 let (p, _g) = x.device_ptr_mut(&s);
20329 p as u64
20330 }
20331 pub fn addr_f32v(&self, x: &cudarc::driver::CudaView<f32>) -> u64 {
20332 use cudarc::driver::DevicePtr;
20333 let s = self.gpu.stream();
20334 let (p, _g) = x.device_ptr(&s);
20335 p as u64
20336 }
20337 pub fn addr_u8(&self, x: &CudaSlice<u8>) -> u64 {
20338 use cudarc::driver::DevicePtr;
20339 let s = self.gpu.stream();
20340 let (p, _g) = x.device_ptr(&s);
20341 p as u64
20342 }
20343
20344 pub fn gdn_chunk_vl8(
20348 &self,
20349 seqs: &[GdnSeqVl],
20350 n_head: usize,
20351 scale: f32,
20352 hk: usize,
20353 wq: Option<&GdnWVl8>,
20354 ) -> Result<(), Box<dyn std::error::Error>> {
20355 const NSPLIT: u32 = 4;
20356 let b = seqs.len();
20357 assert!(b >= 1 && b <= 8, "gdn_chunk_vl8: 1..=8 sequences");
20358 let mut packed = [GdnSeqVl::default(); 8];
20359 packed[..b].copy_from_slice(seqs);
20360 let v = GdnVl8(packed);
20361 let (hi, ci) = (n_head as i32, 32i32);
20362 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
20363 let hki = hk as i32;
20364 if let Some(w) = wq {
20365 let f = self.func("gdn_k45_wgmma_vl");
20367 let cfg = LaunchConfig {
20368 grid_dim: (n_head as u32, NSPLIT, b as u32),
20369 block_dim: (256, 1, 1),
20370 shared_mem_bytes: 0,
20371 };
20372 let __s_lb = self.gpu.stream();
20373 let mut lb = __s_lb.launch_builder(&f);
20374 lb.arg(&v).arg(w).arg(&scale).arg(&hi).arg(&ci).arg(&hki);
20375 unsafe {
20376 lb.launch(cfg)?;
20377 }
20378 let _ = max_nc;
20379 return Ok(());
20380 }
20381 {
20382 let f = self.func("gdn_chunk_state_mma_vl");
20383 let cfg = LaunchConfig {
20384 grid_dim: (n_head as u32, NSPLIT, b as u32),
20385 block_dim: (256, 1, 1),
20386 shared_mem_bytes: 0,
20387 };
20388 let __s_lb = self.gpu.stream();
20389 let mut lb = __s_lb.launch_builder(&f);
20390 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
20391 unsafe {
20392 lb.launch(cfg)?;
20393 }
20394 }
20395 {
20396 let f = self.func("gdn_chunk_output_mma_vl");
20397 let cfg = LaunchConfig {
20398 grid_dim: (max_nc, n_head as u32, b as u32),
20399 block_dim: (256, 1, 1),
20400 shared_mem_bytes: 0,
20401 };
20402 let __s_lb = self.gpu.stream();
20403 let mut lb = __s_lb.launch_builder(&f);
20404 lb.arg(&v).arg(&hi).arg(&ci).arg(&scale).arg(&hki);
20405 unsafe {
20406 lb.launch(cfg)?;
20407 }
20408 }
20409 Ok(())
20410 }
20411 pub fn gdn_scan_chunked(
20412 &self,
20413 q: &CudaSlice<f32>,
20414 k: &CudaSlice<f32>,
20415 v: &CudaSlice<f32>,
20416 g: &CudaSlice<f32>,
20417 beta: &CudaSlice<f32>,
20418 kb16_pre: Option<&CudaSlice<u8>>,
20419 qb16_pre: Option<&CudaSlice<u8>>,
20420 state_in: &CudaSlice<f32>,
20421 state_out: &mut CudaSlice<f32>,
20422 o: &mut CudaSlice<f32>,
20423 n_head: usize,
20424 t: usize,
20425 scale: f32,
20426 c: usize,
20427 hk: usize,
20428 ) -> Result<(), Box<dyn std::error::Error>> {
20429 const D: usize = 128;
20430 const NSPLIT: u32 = 4;
20431 assert!(c >= 1 && c <= 128, "gdn_scan_chunked: C must be in 1..=128");
20432 let h = n_head;
20433 let nc = (t + c - 1) / c;
20434 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
20435 let gdn_mma_pre = !portable_mma_gated()
20439 && c == 32
20440 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
20441 Ok("1") => true,
20442 Ok("0") => false,
20443 _ => cfg!(memra_hopper_mma),
20444 };
20445 let mut wb16_pre: Option<CudaSlice<u8>> = if gdn_mma_pre {
20446 Some(self.alloc_u8_uninit(nc * h * c * D * 2)?)
20447 } else {
20448 None
20449 };
20450 let gdn_wgmma_pre = gdn_mma_pre
20454 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
20455 Ok("0") => false,
20456 Ok("1") => true,
20457 _ => cfg!(memra_hopper_mma),
20458 };
20459 let nk = t * hk * D;
20460 let mut kb16_local: Option<CudaSlice<u8>> = None;
20461 if gdn_mma_pre && kb16_pre.is_none() {
20462 let mut kb = self.alloc_u8_uninit(nk * 2)?;
20463 let f = self.func("f32_to_bf16_bulk");
20464 let n2 = nk as i64;
20465 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
20466 let __s_b = self.gpu.stream();
20467 let mut b = __s_b.launch_builder(&f);
20468 b.arg(k).arg(&mut kb).arg(&n2);
20469 unsafe {
20470 b.launch(cfg2)?;
20471 }
20472 kb16_local = Some(kb);
20473 }
20474 let kb16_ref0: Option<&CudaSlice<u8>> = kb16_local.as_ref().or(kb16_pre);
20475 if let Some(kb) = kb16_pre {
20476 assert!(kb.len() >= nk * 2, "kb16_pre too small");
20477 }
20478 let mut qb16: Option<CudaSlice<u8>> = None;
20479 let mut pb16: Option<CudaSlice<u8>> = None;
20480 if gdn_wgmma_pre {
20481 if qb16_pre.is_none() {
20484 let mut qb = self.alloc_u8_uninit(nk * 2)?;
20485 let f = self.func("f32_to_bf16_bulk");
20486 let n2 = nk as i64;
20487 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
20488 let __s_b = self.gpu.stream();
20489 let mut b = __s_b.launch_builder(&f);
20490 b.arg(q).arg(&mut qb).arg(&n2);
20491 unsafe {
20492 b.launch(cfg2)?;
20493 }
20494 qb16 = Some(qb);
20495 } else if let Some(qb) = qb16_pre {
20496 assert!(qb.len() >= nk * 2, "qb16_pre too small");
20497 }
20498 pb16 = Some(self.alloc_u8_uninit(nc * h * c * c * 2)?);
20499 }
20500 let qb16_ref0: Option<&CudaSlice<u8>> = qb16.as_ref().or(qb16_pre);
20501 let k2w = if gdn_wgmma_pre {
20502 Some((
20503 *qb16_ref0.as_ref().unwrap(),
20504 *kb16_ref0.as_ref().unwrap(),
20505 pb16.as_mut().unwrap(),
20506 ))
20507 } else {
20508 None
20509 };
20510 let (gcum, p, u, w) =
20511 self.gdn_chunk_k123(q, k, v, g, beta, wb16_pre.as_mut(), n_head, t, c, hk, k2w)?;
20512 let _ = &w;
20513 let mut y = self.uninit(nc * h * c * D)?;
20514 let mut ssnap = self.uninit(nc * h * D * D)?; let gdn_mma = !portable_mma_gated()
20526 && c == 32
20527 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
20528 Ok("1") => true,
20529 Ok("0") => false,
20530 _ => cfg!(memra_hopper_mma),
20531 };
20532 if gdn_mma {
20533 let wb16 = wb16_pre
20534 .take()
20535 .expect("mma path pre-allocates wb16 (K3 store fold)");
20536 let kb16_ref: &CudaSlice<u8> = kb16_ref0.expect("mma path pre-builds kb16 above K123");
20537 if gdn_wgmma_pre {
20549 let qb16 = qb16_ref0.unwrap();
20551 let pb16 = pb16.as_ref().unwrap();
20552 {
20553 let f = self.func("gdn_k45_wgmma");
20554 let cfg = LaunchConfig {
20555 grid_dim: (h as u32, 4, 1),
20556 block_dim: (256, 1, 1),
20557 shared_mem_bytes: 0,
20558 };
20559 let hki = hk as i32;
20560 let __s_b = self.gpu.stream();
20561 let mut b = __s_b.launch_builder(&f);
20562 b.arg(kb16_ref)
20563 .arg(&gcum)
20564 .arg(beta)
20565 .arg(&u)
20566 .arg(&wb16)
20567 .arg(qb16)
20568 .arg(pb16)
20569 .arg(o)
20570 .arg(&scale)
20571 .arg(state_in)
20572 .arg(&mut *state_out)
20573 .arg(&hi)
20574 .arg(&ti)
20575 .arg(&ci)
20576 .arg(&hki);
20577 unsafe {
20578 b.launch(cfg)?;
20579 }
20580 }
20581 return Ok(());
20582 }
20583 let mut y16 = self.alloc_u8_uninit(nc * h * c * D * 2)?;
20587 let mut ssnap16 = self.alloc_u8_uninit(nc * h * D * D * 2)?;
20588 {
20589 let f = self.func("gdn_chunk_state_mma");
20590 let cfg = LaunchConfig {
20591 grid_dim: (h as u32, NSPLIT, 1),
20592 block_dim: (256, 1, 1),
20593 shared_mem_bytes: 0,
20594 };
20595 let hki = hk as i32;
20596 let __s_b = self.gpu.stream();
20597 let mut b = __s_b.launch_builder(&f);
20598 b.arg(kb16_ref)
20599 .arg(&gcum)
20600 .arg(beta)
20601 .arg(&u)
20602 .arg(&wb16)
20603 .arg(&mut y16)
20604 .arg(&mut ssnap16)
20605 .arg(state_in)
20606 .arg(&mut *state_out)
20607 .arg(&hi)
20608 .arg(&ti)
20609 .arg(&ci)
20610 .arg(&hki);
20611 unsafe {
20612 b.launch(cfg)?;
20613 }
20614 }
20615 {
20616 let f = self.func("gdn_chunk_output_mma");
20618 let jt = ((c + 31) / 32) as u32;
20619 let cfg = LaunchConfig {
20620 grid_dim: (nc as u32, h as u32, jt),
20621 block_dim: (256, 1, 1),
20622 shared_mem_bytes: 0,
20623 };
20624 let hki = hk as i32;
20625 let __s_b = self.gpu.stream();
20626 let mut b = __s_b.launch_builder(&f);
20627 b.arg(q)
20628 .arg(&gcum)
20629 .arg(&p)
20630 .arg(&y16)
20631 .arg(&ssnap16)
20632 .arg(o)
20633 .arg(&hi)
20634 .arg(&ti)
20635 .arg(&ci)
20636 .arg(&scale)
20637 .arg(&hki);
20638 unsafe {
20639 b.launch(cfg)?;
20640 }
20641 }
20642 return Ok(());
20643 }
20644 {
20645 let f = self.func("gdn_chunk_state_f32");
20647 let cfg = LaunchConfig {
20648 grid_dim: (h as u32, NSPLIT, 1),
20649 block_dim: (256, 1, 1),
20650 shared_mem_bytes: 0,
20651 };
20652 let __s_b = self.gpu.stream();
20653 let mut b = __s_b.launch_builder(&f);
20654 b.arg(k)
20655 .arg(&gcum)
20656 .arg(beta)
20657 .arg(&u)
20658 .arg(&w)
20659 .arg(&mut y)
20660 .arg(&mut ssnap)
20661 .arg(state_in)
20662 .arg(&mut *state_out)
20663 .arg(&hi)
20664 .arg(&ti)
20665 .arg(&ci);
20666 unsafe {
20667 b.launch(cfg)?;
20668 }
20669 }
20670 {
20671 let f = self.func("gdn_chunk_output_f32");
20673 let jt = ((c + 31) / 32) as u32;
20674 let cfg = LaunchConfig {
20675 grid_dim: (nc as u32, h as u32, jt),
20676 block_dim: (256, 1, 1),
20677 shared_mem_bytes: 0,
20678 };
20679 let __s_b = self.gpu.stream();
20680 let mut b = __s_b.launch_builder(&f);
20681 b.arg(q)
20682 .arg(&gcum)
20683 .arg(&p)
20684 .arg(&y)
20685 .arg(&ssnap)
20686 .arg(o)
20687 .arg(&hi)
20688 .arg(&ti)
20689 .arg(&ci)
20690 .arg(&scale);
20691 unsafe {
20692 b.launch(cfg)?;
20693 }
20694 }
20695 Ok(())
20696 }
20697
20698 #[allow(clippy::too_many_arguments)]
20707 #[allow(clippy::too_many_arguments)]
20708 pub fn gdn_scan_prefill(
20709 &self,
20710 q: &CudaSlice<f32>,
20711 k: &CudaSlice<f32>,
20712 v: &CudaSlice<f32>,
20713 g: &CudaSlice<f32>,
20714 beta: &CudaSlice<f32>,
20715 kb16_pre: Option<&CudaSlice<u8>>,
20716 qb16_pre: Option<&CudaSlice<u8>>,
20717 state_in: &CudaSlice<f32>,
20718 state_out: &mut CudaSlice<f32>,
20719 o: &mut CudaSlice<f32>,
20720 n_head: usize,
20721 t: usize,
20722 scale: f32,
20723 hk: usize,
20724 ) -> Result<(), Box<dyn std::error::Error>> {
20725 if std::env::var("MEMRA_GDN_DIFF").is_ok() && t >= 16 {
20726 assert!(hk == n_head, "GDN_DIFF oracle is broadcast-only");
20727 return self.gdn_scan_diff(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale);
20728 }
20729 if Self::gdn_chunked_enabled() && t >= 16 {
20730 self.gdn_scan_chunked(
20731 q,
20732 k,
20733 v,
20734 g,
20735 beta,
20736 kb16_pre,
20737 qb16_pre,
20738 state_in,
20739 state_out,
20740 o,
20741 n_head,
20742 t,
20743 scale,
20744 Self::gdn_chunk_size(),
20745 hk,
20746 )
20747 } else {
20748 assert!(
20749 hk == n_head,
20750 "s128 scan is broadcast-only (prep guarantees by predicate)"
20751 );
20752 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)
20753 }
20754 }
20755
20756 #[allow(clippy::too_many_arguments)]
20758 fn gdn_scan_diff(
20759 &self,
20760 q: &CudaSlice<f32>,
20761 k: &CudaSlice<f32>,
20762 v: &CudaSlice<f32>,
20763 g: &CudaSlice<f32>,
20764 beta: &CudaSlice<f32>,
20765 state_in: &CudaSlice<f32>,
20766 state_out: &mut CudaSlice<f32>,
20767 o: &mut CudaSlice<f32>,
20768 n_head: usize,
20769 t: usize,
20770 scale: f32,
20771 ) -> Result<(), Box<dyn std::error::Error>> {
20772 static CALL: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
20773 let call = CALL.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
20774 let mut o_c = self.uninit(o.len())?;
20775 let mut st_c = self.uninit(state_out.len())?;
20776 self.gdn_scan_chunked(
20777 q,
20778 k,
20779 v,
20780 g,
20781 beta,
20782 None,
20783 None,
20784 state_in,
20785 &mut st_c,
20786 &mut o_c,
20787 n_head,
20788 t,
20789 scale,
20790 Self::gdn_chunk_size(),
20791 n_head,
20792 )?;
20793 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)?;
20794 let (oh_s, oh_c) = (self.dtoh(o)?, self.dtoh(&o_c)?);
20795 let (sh_s, sh_c) = (self.dtoh(state_out)?, self.dtoh(&st_c)?);
20796 let stats = |a: &[f32], b: &[f32]| -> (f32, f32, f64) {
20797 let mut max_abs = 0f32;
20798 let mut max_rel = 0f32;
20799 let mut sum_rel = 0f64;
20800 for (x, y) in a.iter().zip(b) {
20801 let ad = (x - y).abs();
20802 let rel = ad / x.abs().max(y.abs()).max(1e-3);
20803 if ad > max_abs {
20804 max_abs = ad;
20805 }
20806 if rel > max_rel {
20807 max_rel = rel;
20808 }
20809 sum_rel += rel as f64;
20810 }
20811 (max_abs, max_rel, sum_rel / a.len() as f64)
20812 };
20813 let (o_ma, o_mr, o_mean) = stats(&oh_s, &oh_c);
20814 let (s_ma, s_mr, s_mean) = stats(&sh_s, &sh_c);
20815 println!(
20816 "[gdn-diff call {call:3} T={t} C={}] out: max_abs={o_ma:.3e} max_rel={o_mr:.3e} mean_rel={o_mean:.3e} | \
20817 state: max_abs={s_ma:.3e} max_rel={s_mr:.3e} mean_rel={s_mean:.3e}",
20818 Self::gdn_chunk_size()
20819 );
20820 Ok(())
20821 }
20822
20823 pub fn gdn_glog(
20825 &self,
20826 alpha: &CudaSlice<f32>,
20827 dt_bias: &CudaSlice<f32>,
20828 a: &CudaSlice<f32>,
20829 g_log: &mut CudaSlice<f32>,
20830 n_head: usize,
20831 t: usize,
20832 ) -> Result<(), Box<dyn std::error::Error>> {
20833 let f = self.func("gdn_glog_f32");
20834 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
20835 let (h, ti) = (n_head as i32, t as i32);
20836 let __s_b = self.gpu.stream();
20837 let mut b = __s_b.launch_builder(&f);
20838 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
20839 unsafe {
20840 b.launch(cfg)?;
20841 }
20842 Ok(())
20843 }
20844
20845 pub fn sigmoid_v(
20848 &self,
20849 x: &cudarc::driver::CudaView<f32>,
20850 y: &mut CudaSlice<f32>,
20851 n: usize,
20852 ) -> Result<(), Box<dyn std::error::Error>> {
20853 let f = self.func("sigmoid_f32");
20854 let cfg = LaunchConfig::for_num_elems(n as u32);
20855 let ni = n as i32;
20856 let __s_b = self.gpu.stream();
20857 let mut b = __s_b.launch_builder(&f);
20858 b.arg(x).arg(y).arg(&ni);
20859 unsafe {
20860 b.launch(cfg)?;
20861 }
20862 Ok(())
20863 }
20864
20865 pub fn gdn_glog_v(
20866 &self,
20867 alpha: &cudarc::driver::CudaView<f32>,
20868 dt_bias: &CudaSlice<f32>,
20869 a: &CudaSlice<f32>,
20870 g_log: &mut CudaSlice<f32>,
20871 n_head: usize,
20872 t: usize,
20873 ) -> Result<(), Box<dyn std::error::Error>> {
20874 let f = self.func("gdn_glog_f32");
20875 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
20876 let (h, ti) = (n_head as i32, t as i32);
20877 let __s_b = self.gpu.stream();
20878 let mut b = __s_b.launch_builder(&f);
20879 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
20880 unsafe {
20881 b.launch(cfg)?;
20882 }
20883 Ok(())
20884 }
20885
20886 pub fn sigmoid(
20887 &self,
20888 x: &CudaSlice<f32>,
20889 y: &mut CudaSlice<f32>,
20890 n: usize,
20891 ) -> Result<(), Box<dyn std::error::Error>> {
20892 let f = self.func("sigmoid_f32");
20893 let cfg = LaunchConfig::for_num_elems(n as u32);
20894 let ni = n as i32;
20895 let __s_b = self.gpu.stream();
20896 let mut b = __s_b.launch_builder(&f);
20897 b.arg(x).arg(y).arg(&ni);
20898 unsafe {
20899 b.launch(cfg)?;
20900 }
20901 Ok(())
20902 }
20903
20904 pub fn sig_mul_f16out(
20907 &self,
20908 a: &CudaSlice<f32>,
20909 g: &CudaSlice<f32>,
20910 dst: &mut CudaSlice<f32>,
20911 dst16: &mut CudaSlice<u8>,
20912 n: usize,
20913 ) -> Result<(), Box<dyn std::error::Error>> {
20914 let f = self.func("sig_mul_f16out_f32");
20915 let cfg = LaunchConfig::for_num_elems(n as u32);
20916 let ni = n as i32;
20917 let __s_b = self.gpu.stream();
20918 let mut b = __s_b.launch_builder(&f);
20919 b.arg(a).arg(g).arg(dst).arg(dst16).arg(&ni);
20920 unsafe {
20921 b.launch(cfg)?;
20922 }
20923 Ok(())
20924 }
20925
20926 #[allow(clippy::too_many_arguments)]
20935 pub fn attn_head_gate(
20936 &self,
20937 a: &CudaSlice<f32>,
20938 g: &CudaSlice<f32>,
20939 dst: &mut CudaSlice<f32>,
20940 dst16: Option<&mut CudaSlice<u8>>,
20941 head_dim: usize,
20942 n_head: usize,
20943 t: usize,
20944 ) -> Result<(), Box<dyn std::error::Error>> {
20945 let f = self.func("attn_head_gate_f32");
20946 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
20947 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
20948 let d16: u64 = match dst16 {
20950 Some(d) => self.addr_u8(d),
20951 None => 0,
20952 };
20953 let __s_b = self.gpu.stream();
20954 let mut b = __s_b.launch_builder(&f);
20955 b.arg(a)
20956 .arg(g)
20957 .arg(dst)
20958 .arg(&d16)
20959 .arg(&hd)
20960 .arg(&nh)
20961 .arg(&ti);
20962 unsafe {
20963 b.launch(cfg)?;
20964 }
20965 Ok(())
20966 }
20967
20968 #[allow(clippy::too_many_arguments)]
20977 pub fn swiglu_clamped_mul_scaled(
20978 &self,
20979 gate: &CudaSlice<f32>,
20980 up: &CudaSlice<f32>,
20981 gs: f32,
20982 us: f32,
20983 limit: f32,
20984 dst: &mut CudaSlice<f32>,
20985 n: usize,
20986 ) -> Result<(), Box<dyn std::error::Error>> {
20987 debug_assert!(
20988 limit > 1e-6,
20989 "swiglu_clamped needs a live limit; use silu_mul_scaled"
20990 );
20991 let f = self.func("swiglu_clamped_mul_scaled_f32");
20992 let cfg = LaunchConfig::for_num_elems(n as u32);
20993 let ni = n as i32;
20994 let __s_b = self.gpu.stream();
20995 let mut b = __s_b.launch_builder(&f);
20996 b.arg(gate)
20997 .arg(up)
20998 .arg(&gs)
20999 .arg(&us)
21000 .arg(&limit)
21001 .arg(dst)
21002 .arg(&ni);
21003 unsafe {
21004 b.launch(cfg)?;
21005 }
21006 Ok(())
21007 }
21008
21009 pub fn gated_rmsnorm(
21011 &self,
21012 o: &CudaSlice<f32>,
21013 w: &CudaSlice<f32>,
21014 z: &CudaSlice<f32>,
21015 dst: &mut CudaSlice<f32>,
21016 ncols: usize,
21017 nrows: usize,
21018 eps: f32,
21019 ) -> Result<(), Box<dyn std::error::Error>> {
21020 let f = self.func("gated_rmsnorm_f32");
21021 let cfg = LaunchConfig {
21022 grid_dim: (nrows as u32, 1, 1),
21023 block_dim: (128, 1, 1),
21024 shared_mem_bytes: 0,
21025 };
21026 let (nc, e) = (ncols as i32, eps);
21027 let __s_b = self.gpu.stream();
21028 let mut b = __s_b.launch_builder(&f);
21029 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
21030 unsafe {
21031 b.launch(cfg)?;
21032 }
21033 Ok(())
21034 }
21035
21036 pub fn gated_rmsnorm_f16out(
21039 &self,
21040 o: &CudaSlice<f32>,
21041 w: &CudaSlice<f32>,
21042 z: &CudaSlice<f32>,
21043 dst: &mut CudaSlice<f32>,
21044 dst16: &mut CudaSlice<u8>,
21045 ncols: usize,
21046 nrows: usize,
21047 eps: f32,
21048 ) -> Result<(), Box<dyn std::error::Error>> {
21049 let f = self.func("gated_rmsnorm_f16out_f32");
21050 let cfg = LaunchConfig {
21052 grid_dim: (nrows as u32, 1, 1),
21053 block_dim: (128, 1, 1),
21054 shared_mem_bytes: 0,
21055 };
21056 let (nc, e) = (ncols as i32, eps);
21057 let __s_b = self.gpu.stream();
21058 let mut b = __s_b.launch_builder(&f);
21059 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
21060 unsafe {
21061 b.launch(cfg)?;
21062 }
21063 Ok(())
21064 }
21065
21066 #[allow(clippy::too_many_arguments)]
21070 pub fn add_rms_norm_zq8(
21071 &self,
21072 a: &CudaSlice<f32>,
21073 b_in: &CudaSlice<f32>,
21074 w: &CudaSlice<f32>,
21075 res: &mut CudaSlice<f32>,
21076 z: &mut CudaSlice<f32>,
21077 ncols: usize,
21078 nrows: usize,
21079 eps: f32,
21080 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
21081 assert!(ncols % 32 == 0);
21082 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
21083 let mut d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
21084 let f = self.func("add_rms_norm_zq8");
21085 let cfg = LaunchConfig {
21086 grid_dim: (nrows as u32, 1, 1),
21087 block_dim: (1024, 1, 1),
21088 shared_mem_bytes: 0,
21089 };
21090 let (nc, ep) = (ncols as i32, eps);
21091 let __s_b = self.gpu.stream();
21092 let mut b = __s_b.launch_builder(&f);
21093 b.arg(a)
21094 .arg(b_in)
21095 .arg(w)
21096 .arg(res)
21097 .arg(z)
21098 .arg(&mut q)
21099 .arg(&mut d)
21100 .arg(&nc)
21101 .arg(&ep);
21102 unsafe {
21103 b.launch(cfg)?;
21104 }
21105 Ok((q, d))
21106 }
21107
21108 pub fn gated_rmsnorm_zv(
21113 &self,
21114 o: &CudaSlice<f32>,
21115 w: &CudaSlice<f32>,
21116 z: &cudarc::driver::CudaView<f32>,
21117 dst: &mut CudaSlice<f32>,
21118 ncols: usize,
21119 nrows: usize,
21120 eps: f32,
21121 ) -> Result<(), Box<dyn std::error::Error>> {
21122 let f = self.func("gated_rmsnorm_f32");
21123 let cfg = LaunchConfig {
21124 grid_dim: (nrows as u32, 1, 1),
21125 block_dim: (128, 1, 1),
21126 shared_mem_bytes: 0,
21127 };
21128 let (nc, e) = (ncols as i32, eps);
21129 let __s_b = self.gpu.stream();
21130 let mut b = __s_b.launch_builder(&f);
21131 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
21132 unsafe {
21133 b.launch(cfg)?;
21134 }
21135 Ok(())
21136 }
21137
21138 pub fn gated_rmsnorm_f16out_zv(
21139 &self,
21140 o: &CudaSlice<f32>,
21141 w: &CudaSlice<f32>,
21142 z: &cudarc::driver::CudaView<f32>,
21143 dst: &mut CudaSlice<f32>,
21144 dst16: &mut CudaSlice<u8>,
21145 ncols: usize,
21146 nrows: usize,
21147 eps: f32,
21148 ) -> Result<(), Box<dyn std::error::Error>> {
21149 let f = self.func("gated_rmsnorm_f16out_f32");
21150 let cfg = LaunchConfig {
21152 grid_dim: (nrows as u32, 1, 1),
21153 block_dim: (128, 1, 1),
21154 shared_mem_bytes: 0,
21155 };
21156 let (nc, e) = (ncols as i32, eps);
21157 let __s_b = self.gpu.stream();
21158 let mut b = __s_b.launch_builder(&f);
21159 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
21160 unsafe {
21161 b.launch(cfg)?;
21162 }
21163 Ok(())
21164 }
21165
21166 pub fn gated_rmsnorm_q8_1(
21167 &self,
21168 o: &CudaSlice<f32>,
21169 w: &CudaSlice<f32>,
21170 z: &CudaSlice<f32>,
21171 ncols: usize,
21172 nrows: usize,
21173 eps: f32,
21174 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
21175 assert!(ncols % 32 == 0);
21176 let f = self.func("gated_rmsnorm_q8_1");
21177 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
21178 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
21179 let cfg = LaunchConfig {
21180 grid_dim: (nrows as u32, 1, 1),
21181 block_dim: (128, 1, 1),
21182 shared_mem_bytes: 0,
21183 };
21184 let (nc, ep) = (ncols as i32, eps);
21185 let __s_b = self.gpu.stream();
21186 let mut b = __s_b.launch_builder(&f);
21187 b.arg(o)
21188 .arg(w)
21189 .arg(z)
21190 .arg(&mut out_q)
21191 .arg(&mut out_d)
21192 .arg(&nc)
21193 .arg(&ep);
21194 unsafe {
21195 b.launch(cfg)?;
21196 }
21197 Ok((out_q, out_d))
21198 }
21199
21200 pub fn transpose(
21202 &self,
21203 inp: &CudaSlice<f32>,
21204 rows: usize,
21205 cols: usize,
21206 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21207 let f = self.func("transpose_f32");
21208 let mut out = self.zeros(rows * cols)?;
21209 let cfg = LaunchConfig::for_num_elems((rows * cols) as u32);
21210 let (r, c) = (rows as i32, cols as i32);
21211 let __s_b = self.gpu.stream();
21212 let mut b = __s_b.launch_builder(&f);
21213 b.arg(inp).arg(&mut out).arg(&r).arg(&c);
21214 unsafe {
21215 b.launch(cfg)?;
21216 }
21217 Ok(out)
21218 }
21219
21220 pub fn repeat_heads(
21222 &self,
21223 inp: &CudaSlice<f32>,
21224 out: &mut CudaSlice<f32>,
21225 head_dim: usize,
21226 n_in: usize,
21227 n_out: usize,
21228 t: usize,
21229 ) -> Result<(), Box<dyn std::error::Error>> {
21230 let f = self.func("repeat_heads_f32");
21231 let cfg = LaunchConfig::for_num_elems((head_dim * n_out * t) as u32);
21232 let (hd, ni, no, ti) = (head_dim as i32, n_in as i32, n_out as i32, t as i32);
21233 let __s_b = self.gpu.stream();
21234 let mut b = __s_b.launch_builder(&f);
21235 b.arg(inp).arg(out).arg(&hd).arg(&ni).arg(&no).arg(&ti);
21236 unsafe {
21237 b.launch(cfg)?;
21238 }
21239 Ok(())
21240 }
21241
21242 pub fn q_gate_split(
21245 &self,
21246 qf: &CudaSlice<f32>,
21247 q_out: &mut CudaSlice<f32>,
21248 gate_out: &mut CudaSlice<f32>,
21249 head_dim: usize,
21250 n_head: usize,
21251 t: usize,
21252 ) -> Result<(), Box<dyn std::error::Error>> {
21253 let f = self.func("q_gate_split_f32");
21254 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
21255 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
21256 let __s_b = self.gpu.stream();
21257 let mut b = __s_b.launch_builder(&f);
21258 b.arg(qf)
21259 .arg(q_out)
21260 .arg(gate_out)
21261 .arg(&hd)
21262 .arg(&nh)
21263 .arg(&ti);
21264 unsafe {
21265 b.launch(cfg)?;
21266 }
21267 Ok(())
21268 }
21269
21270 pub fn qkv_to_gdn_repack(
21274 &self,
21275 conv_out: &CudaSlice<f32>,
21276 q_g: &mut CudaSlice<f32>,
21277 k_g: &mut CudaSlice<f32>,
21278 v_g: &mut CudaSlice<f32>,
21279 d_state: usize,
21280 num_v: usize,
21281 num_k: usize,
21282 key_dim: usize,
21283 t: usize,
21284 ) -> Result<(), Box<dyn std::error::Error>> {
21285 let f = self.func("qkv_to_gdn_repack_f32");
21286 let cfg = LaunchConfig::for_num_elems((d_state * num_v * t) as u32);
21287 let (ds, nv, nk, kd, ti) = (
21288 d_state as i32,
21289 num_v as i32,
21290 num_k as i32,
21291 key_dim as i32,
21292 t as i32,
21293 );
21294 let __s_b = self.gpu.stream();
21295 let mut b = __s_b.launch_builder(&f);
21296 b.arg(conv_out)
21297 .arg(q_g)
21298 .arg(k_g)
21299 .arg(v_g)
21300 .arg(&ds)
21301 .arg(&nv)
21302 .arg(&nk)
21303 .arg(&kd)
21304 .arg(&ti);
21305 unsafe {
21306 b.launch(cfg)?;
21307 }
21308 Ok(())
21309 }
21310
21311 pub fn conv_left_pad(
21314 &self,
21315 src: &CudaSlice<f32>,
21316 dst: &mut CudaSlice<f32>,
21317 conv_dim: usize,
21318 t: usize,
21319 pad: usize,
21320 ) -> Result<(), Box<dyn std::error::Error>> {
21321 let f = self.func("conv_left_pad_f32");
21322 let cfg = LaunchConfig::for_num_elems((conv_dim * t) as u32);
21323 let (cd, ti, p) = (conv_dim as i32, t as i32, pad as i32);
21324 let __s_b = self.gpu.stream();
21325 let mut b = __s_b.launch_builder(&f);
21326 b.arg(src).arg(dst).arg(&cd).arg(&ti).arg(&p);
21327 unsafe {
21328 b.launch(cfg)?;
21329 }
21330 Ok(())
21331 }
21332
21333 pub fn conv_assemble_and_roll(
21337 &self,
21338 qkv_col: &CudaSlice<f32>,
21339 conv_state: &mut CudaSlice<f32>,
21340 conv_in: &mut CudaSlice<f32>,
21341 conv_dim: usize,
21342 pad: usize,
21343 ) -> Result<(), Box<dyn std::error::Error>> {
21344 let f = self.func("conv_assemble_and_roll_f32");
21345 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
21346 let (cd, p) = (conv_dim as i32, pad as i32);
21347 let __s_b = self.gpu.stream();
21348 let mut b = __s_b.launch_builder(&f);
21349 b.arg(qkv_col).arg(conv_state).arg(conv_in).arg(&cd).arg(&p);
21350 unsafe {
21351 b.launch(cfg)?;
21352 }
21353 Ok(())
21354 }
21355
21356 pub fn ssm_conv1d_fused_decode(
21362 &self,
21363 qkv_col: &CudaSlice<f32>,
21364 conv_state: &mut CudaSlice<f32>,
21365 w: &CudaSlice<f32>,
21366 conv_out: &mut CudaSlice<f32>,
21367 conv_dim: usize,
21368 d_conv: usize,
21369 ) -> Result<(), Box<dyn std::error::Error>> {
21370 let f = self.func("ssm_conv1d_fused_decode_f32");
21371 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
21372 let (cd, dc) = (conv_dim as i32, d_conv as i32);
21373 let __s_b = self.gpu.stream();
21374 let mut b = __s_b.launch_builder(&f);
21375 b.arg(qkv_col)
21376 .arg(conv_state)
21377 .arg(w)
21378 .arg(conv_out)
21379 .arg(&cd)
21380 .arg(&dc);
21381 unsafe {
21382 b.launch(cfg)?;
21383 }
21384 Ok(())
21385 }
21386
21387 pub fn slice_range(
21390 &self,
21391 src: &CudaSlice<f32>,
21392 start: usize,
21393 len: usize,
21394 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21395 let host = self.gpu.stream().clone_dtoh(src)?;
21396 self.gpu.stream().synchronize()?;
21397 Ok(self.htod(&host[start..start + len])?)
21398 }
21399}
21400
21401#[cfg(test)]
21402mod target_dispatch_tests {
21403 use super::legacy_quant_gemm_allowed;
21404
21405 #[test]
21406 fn legacy_quant_gemm_arch_policy_honors_the_escape_hatch() {
21407 assert!(legacy_quant_gemm_allowed(false, false, false));
21409 assert!(!legacy_quant_gemm_allowed(false, false, true));
21410 assert!(!legacy_quant_gemm_allowed(true, false, false));
21412 assert!(!legacy_quant_gemm_allowed(true, false, true));
21413 assert!(legacy_quant_gemm_allowed(true, true, false));
21415 assert!(!legacy_quant_gemm_allowed(true, true, true));
21416 }
21417
21418 #[cfg(all(memra_portable_cuda, not(memra_hopper_mma)))]
21419 #[test]
21420 fn portable_build_disables_legacy_quant_gemm_without_an_env_override() {
21421 assert!(!legacy_quant_gemm_allowed(
21422 cfg!(memra_portable_cuda),
21423 cfg!(memra_hopper_mma),
21424 false
21425 ));
21426 }
21427
21428 #[cfg(memra_hopper_mma)]
21429 #[test]
21430 fn hopper_mma_build_re_admits_legacy_quant_gemm() {
21431 assert!(legacy_quant_gemm_allowed(
21432 cfg!(memra_portable_cuda),
21433 cfg!(memra_hopper_mma),
21434 false
21435 ));
21436 assert!(super::portable_mma_gated() == false);
21437 }
21438}
21439
21440impl memra_kv::KvDev for Engine {
21443 fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21444 Engine::zeros(self, n)
21445 }
21446 fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21447 Engine::uninit(self, n)
21448 }
21449 fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
21450 Engine::alloc_u8(self, n)
21451 }
21452 fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
21453 Engine::htod_i32(self, v)
21454 }
21455 fn clone_dtod(
21456 &self,
21457 src: &CudaSlice<f32>,
21458 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21459 Engine::clone_dtod(self, src)
21460 }
21461 fn copy_into(
21462 &self,
21463 dst: &mut CudaSlice<f32>,
21464 off: usize,
21465 src: &CudaSlice<f32>,
21466 len: usize,
21467 ) -> Result<(), Box<dyn std::error::Error>> {
21468 Engine::copy_into(self, dst, off, src, len)
21469 }
21470 fn set_i32_one(
21471 &self,
21472 d: &mut CudaSlice<i32>,
21473 v: i32,
21474 ) -> Result<(), Box<dyn std::error::Error>> {
21475 Engine::set_i32_one(self, d, v)
21476 }
21477}