1use cudarc::driver::{
4 CudaContext, CudaFunction, CudaModule, CudaSlice, CudaStream, LaunchConfig, PushKernelArg,
5};
6use cudarc::nvrtc::Ptx;
7use std::sync::{Arc, Mutex};
8
9#[cfg(debug_assertions)]
10pub(crate) fn debug_assert_tensor_stream_device<T>(
11 tensor: &CudaSlice<T>,
12 stream: &CudaStream,
13 site: &str,
14) {
15 let tensor_dev = tensor.ordinal();
16 let stream_dev = stream.context().ordinal();
17 assert_eq!(
18 tensor_dev, stream_dev,
19 "PP cross-device tensor read at {site}: tensor on dev{tensor_dev}, stream on dev{stream_dev}"
20 );
21}
22
23pub use memra_gguf;
24pub use memra_runtime;
25
26pub mod forward;
27pub mod hybrid;
28pub mod hybrid_forward;
29pub mod model;
30pub mod sigrouter_contract;
31pub mod vision;
32pub mod vision_pre;
33pub mod cache {
36 pub use memra_kv::*;
37}
38pub mod decode;
39pub mod decode_batch;
40pub mod dflash;
41pub mod eagle;
42pub mod gemma_spec;
43pub mod graph_update;
44pub mod mla;
48pub mod moesd;
49pub mod parallel;
50pub mod pp;
51pub mod round_stream;
52pub mod spec;
53pub use memra_sampling as sampler;
54
55pub fn moe_f16g_mode() -> u8 {
99 static M: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
100 *M.get_or_init(|| match std::env::var("MEMRA_MOE_F16G").as_deref() {
101 Ok("0") => 0,
102 Ok("2") => 2,
103 Ok("3") => 3,
104 Ok(_) => 1,
105 Err(_) => 2,
108 })
109}
110pub fn moe_f16g_sk_params() -> (i32, i32) {
124 static P: std::sync::OnceLock<(i32, i32)> = std::sync::OnceLock::new();
125 *P.get_or_init(|| match std::env::var("MEMRA_F16G_SK").as_deref() {
126 Ok("0") => (-1, 0),
127 Ok("32") => (0, i32::MAX),
128 Ok("128") => (0, 1),
129 _ => {
130 let cross = std::env::var("MEMRA_F16G_SK_CROSS")
131 .ok()
132 .and_then(|v| v.parse().ok())
133 .unwrap_or(64);
134 (0, cross)
135 }
136 })
137}
138pub fn moe_f16g_direct_on(qtype: i32) -> bool {
149 static M: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
150 let m = *M.get_or_init(|| match std::env::var("MEMRA_F16G_DIRECT").as_deref() {
151 Ok("0") => 0,
152 Ok("kq") => 1,
153 _ => 2,
154 });
155 match m {
156 0 => false,
157 1 => qtype == QT_Q4_K || qtype == QT_Q6_K,
158 _ => true,
159 }
160}
161pub fn moe_f16g_tail_on() -> bool {
170 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
171 *ON.get_or_init(|| std::env::var("MEMRA_F16G_TAIL").as_deref() != Ok("0"))
172}
173
174pub fn moe_f16g_gemma_on() -> bool {
181 static M: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
182 *M.get_or_init(|| !matches!(std::env::var("MEMRA_MOE_F16G").as_deref(), Ok("0") | Err(_)))
183}
184
185pub fn moe_fuse_actq_on() -> bool {
189 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
190 *ON.get_or_init(|| std::env::var("MEMRA_MOE_FUSE_ACTQ").as_deref() != Ok("0"))
191}
192
193pub fn router_prefill_exact_on() -> bool {
203 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
204 *ON.get_or_init(|| std::env::var("MEMRA_ROUTER_PREFILL_EXACT").as_deref() != Ok("0"))
205}
206
207pub fn router_kernel_on() -> bool {
208 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
209 *ON.get_or_init(|| {
210 let on = std::env::var("MEMRA_ROUTER_KERNEL").as_deref() != Ok("0");
211 if !on {
212 eprintln!("[memra] router kernel OFF (rollback: per-column cuBLAS gemv)");
213 }
214 on
215 })
216}
217
218pub const ROUTER_BATCH_MIN_T: usize = 8;
233pub fn router_batch_on() -> bool {
234 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
235 *ON.get_or_init(|| std::env::var("MEMRA_ROUTER_BATCH").as_deref() != Ok("0"))
236}
237mod cpu_experts;
238#[cfg(memra_cutlass)]
239pub mod cutlass_ffi;
240pub mod f16_ffi;
241pub mod fp8_ffi;
242pub mod mmq_ffi;
243pub mod moe_cache;
244pub mod prime_graph;
245pub mod spill;
246mod spill_pread;
247
248const FATBIN: &[u8] = include_bytes!(env!("MEMRA_ENGINE_FATBIN"));
255const HYBRID_FATBIN: &[u8] = include_bytes!(env!("MEMRA_HYBRID_FATBIN"));
256const QMATVEC_FATBIN: &[u8] = include_bytes!(env!("MEMRA_QMATVEC_FATBIN"));
257const FLASH_FATBIN: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN"));
258const GEMM_FATBIN: &[u8] = include_bytes!(env!("MEMRA_GEMM_FATBIN"));
259const ROUTER_FATBIN: &[u8] = include_bytes!(env!("MEMRA_ROUTER_FATBIN"));
260const SAMPLE_FATBIN: &[u8] = include_bytes!(env!("MEMRA_SAMPLE_FATBIN"));
262
263fn gemm_fatbin_bytes() -> std::borrow::Cow<'static, [u8]> {
269 assert!(
270 !(portable_mma_gated() && std::env::var_os("MEMRA_GEMM_FATBIN").is_some()),
271 "MEMRA_GEMM_FATBIN overrides are not allowed in the portable CUDA lane"
272 );
273 match std::env::var("MEMRA_GEMM_FATBIN") {
274 Ok(path) => std::borrow::Cow::Owned(
275 std::fs::read(&path).unwrap_or_else(|e| panic!("MEMRA_GEMM_FATBIN read {path}: {e}")),
276 ),
277 Err(_) => std::borrow::Cow::Borrowed(GEMM_FATBIN),
278 }
279}
280
281pub(crate) const fn portable_mma_gated() -> bool {
288 cfg!(memra_portable_cuda) && !cfg!(memra_hopper_mma)
289}
290
291const fn legacy_quant_gemm_allowed(portable_cuda: bool, hopper_mma: bool, no_gemm: bool) -> bool {
296 (!portable_cuda || hopper_mma) && !no_gemm
297}
298
299const FLASH_FATBIN_VQ4: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_VQ4"));
307const FLASH_FATBIN_VF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_VF8"));
308const FLASH_FATBIN_KF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8"));
309const FLASH_FATBIN_KF8VQ4: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8VQ4"));
310const FLASH_FATBIN_KF8VF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8VF8"));
311
312pub use memra_kv::{kv_blk_bytes, kv_cache_formats};
315
316fn flash_fatbin_bytes() -> &'static [u8] {
318 match kv_cache_formats() {
319 ("q8_0", "q5_1") => FLASH_FATBIN,
320 ("q8_0", "q4_0") => FLASH_FATBIN_VQ4,
321 ("q8_0", "fp8") => FLASH_FATBIN_VF8,
322 ("fp8", "q5_1") => FLASH_FATBIN_KF8,
323 ("fp8", "q4_0") => FLASH_FATBIN_KF8VQ4,
324 ("fp8", "fp8") => FLASH_FATBIN_KF8VF8,
325 other => unreachable!("kv_cache_formats returned {other:?}"),
326 }
327}
328
329fn k1_launch_override() -> Option<(u32, u32, u32)> {
336 static K1: std::sync::OnceLock<Option<(u32, u32, u32)>> = std::sync::OnceLock::new();
337 *K1.get_or_init(|| {
338 let v = std::env::var("MEMRA_GEMM_K1_LAUNCH").ok()?;
339 let p: Vec<u32> = v.split(',').filter_map(|s| s.trim().parse().ok()).collect();
340 match p.as_slice() {
341 [bm, bn, w] => Some((*bm, *bn, *w)),
342 _ => None,
343 }
344 })
345}
346
347pub(crate) fn wgmma_gemm_enabled() -> bool {
354 static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
355 *V.get_or_init(|| std::env::var("MEMRA_WGMMA").as_deref() == Ok("1"))
356}
357
358pub const FA_VEC_MIN_TKV: usize = 96;
373pub fn fa_vec_min_tkv() -> usize {
377 static V: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
378 *V.get_or_init(|| {
379 std::env::var("MEMRA_FA_VEC_MIN")
380 .ok()
381 .and_then(|v| v.parse().ok())
382 .unwrap_or_else(|| FA_VEC_MIN_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
383 })
384}
385
386pub fn fa_f16pv_on() -> bool {
397 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
398 *ON.get_or_init(|| {
399 std::env::var("MEMRA_FA_F16PV")
400 .map(|v| v != "0")
401 .unwrap_or_else(|_| std::env::var("MEMRA_DRAFT").is_err())
402 })
403}
404
405pub fn fa512_hp_on() -> bool {
409 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
410 *ON.get_or_init(|| std::env::var("MEMRA_FA512_HP").as_deref() != Ok("0"))
411}
412
413pub fn faw_hp_on() -> bool {
417 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
418 *ON.get_or_init(|| std::env::var("MEMRA_FAW_HP").as_deref() != Ok("0"))
419}
420
421pub fn fa512_wide_warps() -> usize {
425 static N: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
426 *N.get_or_init(|| match std::env::var("MEMRA_FA512_W4").as_deref() {
427 Ok("1") => 4,
428 _ => 2,
429 })
430}
431
432pub fn fa512_min_tkv() -> usize {
435 static FA512_MIN: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
436 *FA512_MIN.get_or_init(|| {
437 std::env::var("MEMRA_FA512_MIN")
438 .ok()
439 .and_then(|v| v.parse().ok())
440 .unwrap_or(512)
441 })
442}
443pub static FA_VEC_MIN_DEFAULT: std::sync::atomic::AtomicUsize =
447 std::sync::atomic::AtomicUsize::new(FA_VEC_MIN_TKV);
448pub static FA_SPW_DEFAULT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(32);
452pub static FUSED_MR1_DEFAULT: std::sync::atomic::AtomicBool =
458 std::sync::atomic::AtomicBool::new(false);
459pub static ROUTER_W8_DEFAULT: std::sync::atomic::AtomicBool =
466 std::sync::atomic::AtomicBool::new(true);
467pub static FA_SP512_DEFAULT: std::sync::atomic::AtomicUsize =
468 std::sync::atomic::AtomicUsize::new(16);
469pub static RMS_BLOCK_DEFAULT: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(256);
474pub static FA_SP_GEMMA: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
476pub static MMQ_SK_FORCE: std::sync::atomic::AtomicI8 = std::sync::atomic::AtomicI8::new(-1);
481pub use memra_kv::KV_FP8_FORCE;
484pub(crate) fn rms_block() -> u32 {
485 static V: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
486 *V.get_or_init(|| {
487 std::env::var("MEMRA_RMS_BLOCK")
488 .ok()
489 .and_then(|v| v.parse().ok())
490 .unwrap_or_else(|| RMS_BLOCK_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
491 })
492}
493
494pub(crate) fn fa_split_keys(t_kv: usize, n_head_kv: usize) -> usize {
495 static S: std::sync::OnceLock<Option<usize>> = std::sync::OnceLock::new();
496 if let Some(forced) = *S.get_or_init(|| {
497 std::env::var("MEMRA_FA_SPLIT")
498 .ok()
499 .and_then(|v| v.parse().ok())
500 .filter(|&s: &usize| s >= 8 && s % 8 == 0)
501 }) {
502 return forced;
503 }
504 if FA_SP_GEMMA.load(std::sync::atomic::Ordering::Relaxed)
522 && std::env::var("MEMRA_FA_SP16").as_deref() == Ok("1")
523 {
524 return if t_kv <= 8192 {
525 16
526 } else if t_kv <= 16384 {
527 64
528 } else {
529 128
530 };
531 }
532 let big_rig = fa_sm_count() >= 128;
533 if big_rig {
534 let _ = n_head_kv;
535 if t_kv <= 2048 {
536 16
537 } else if t_kv <= 16384 {
538 64
539 } else {
540 128
541 }
542 } else if n_head_kv <= 4 {
543 if t_kv <= 512 {
564 8
565 } else if t_kv <= 16384 {
566 64
567 } else {
568 128
569 }
570 } else {
571 if t_kv <= 8192 {
572 32
573 } else if t_kv <= 16384 {
574 64
575 } else {
576 128
577 }
578 }
579}
580
581fn fa_sm_count() -> i32 {
584 static N: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
585 *N.get_or_init(|| {
586 cudarc::driver::result::init().ok();
587 cudarc::driver::result::device::get(0)
588 .and_then(|d| unsafe { cudarc::driver::result::device::get_attribute(
589 d, cudarc::driver::sys::CUdevice_attribute_enum::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT) })
590 .unwrap_or(82)
591 })
592}
593
594fn fa_hd_suffix(head_dim: usize) -> Result<&'static str, Box<dyn std::error::Error>> {
598 match head_dim {
599 256 => Ok(""),
600 128 => Ok("_hd128"),
601 d => Err(format!(
602 "fa_prefill: no kernel stamped for head_dim={d} (only 256/128); \
603 callers must gate to sdpa_naive"
604 )
605 .into()),
606 }
607}
608
609pub const QT_Q8_0: i32 = 0;
611pub const QT_Q4_K: i32 = 1;
612pub const QT_Q6_K: i32 = 2;
613pub const QT_Q5_K: i32 = 3;
614pub const QT_Q3_K: i32 = 4;
615pub const QT_IQ4_XS: i32 = 5;
616pub const QT_IQ3_S: i32 = 6;
617pub const QT_NVFP4: i32 = 7;
618pub const QT_F8_E4M3: i32 = 10;
624pub const QT_NVFP4_RP: i32 = 9;
627pub const QT_F32: i32 = 8;
629pub const QT_BF16: i32 = 11;
630pub const QT_Q4_0: i32 = 12; pub const QT_Q2_K: i32 = 13;
635pub const QT_F8_E4M3_BLK: i32 = 14;
651
652pub struct Engine {
654 pub gpu: memra_runtime::Gpu,
655 module: Arc<CudaModule>,
656 hybrid: Arc<CudaModule>,
657 qmatvec: Arc<CudaModule>,
658 flash: Arc<CudaModule>,
659 flash_g: std::sync::OnceLock<Arc<CudaModule>>,
663 gemm: Arc<CudaModule>,
664 router: Arc<CudaModule>,
665 sample: Arc<CudaModule>,
667 moe_cache: Mutex<Option<crate::moe_cache::MoeSlotCache>>,
671 moe_cache_layout: Mutex<Option<Vec<usize>>>,
675 capture_keep_on: std::sync::atomic::AtomicBool,
681 verify_exact: std::sync::atomic::AtomicBool,
686 capture_keep: Mutex<Vec<Box<dyn std::any::Any + Send>>>,
687 pub copy_stream: Arc<CudaStream>,
689 #[cfg(memra_cutlass)]
696 cutlass_scratch: Mutex<Option<crate::cutlass_ffi::CutlassScratch>>,
697 fp8_scratch: Mutex<Option<crate::fp8_ffi::Fp8Scratch>>,
701 fa_vf16_scratch: Mutex<Option<CudaSlice<u8>>>,
704 fa_part_pool: Mutex<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>>,
708 fa_part_retired: Mutex<Vec<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>>,
712 fn_cache: Mutex<std::collections::HashMap<String, CudaFunction>>,
714 f16_scratch: Mutex<Option<crate::f16_ffi::F16Scratch>>,
715 argmax_partials: Mutex<Option<(CudaSlice<f32>, CudaSlice<i32>)>>,
720 prime_deqw_ws: Mutex<Option<(CudaSlice<u8>, CudaSlice<u8>)>>,
725 router_stage: Mutex<Option<PinnedStage>>,
729}
730
731fn fa_v2_on() -> bool {
741 std::env::var("MEMRA_FA_V2")
747 .map(|v| v != "0")
748 .unwrap_or(true)
749}
750
751fn fa_v3_on() -> bool {
759 std::env::var("MEMRA_FA_V3")
763 .map(|v| v != "0")
764 .unwrap_or(true)
765}
766
767fn fa_v4_mode() -> &'static str {
772 static M: std::sync::OnceLock<String> = std::sync::OnceLock::new();
773 M.get_or_init(|| std::env::var("MEMRA_FA_V4").unwrap_or_default())
774}
775fn fa_v4_on() -> bool {
776 fa_v4_mode() != "0"
777} pub static FA_SMEM_TKV_DEFAULT: std::sync::atomic::AtomicUsize =
786 std::sync::atomic::AtomicUsize::new(1024);
787pub static FA_V4_MAX_DEFAULT: std::sync::atomic::AtomicUsize =
788 std::sync::atomic::AtomicUsize::new(usize::MAX);
789pub fn fa_v4_at_pub(t_kv: usize) -> bool {
790 fa_v4_at(t_kv)
791}
792fn fa_v4_at(t_kv: usize) -> bool {
793 static M: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
794 let mx = *M.get_or_init(|| {
795 std::env::var("MEMRA_FA_V4_MAX")
796 .ok()
797 .and_then(|v| v.parse().ok())
798 .unwrap_or_else(|| FA_V4_MAX_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
799 });
800 fa_v4_on() && t_kv < mx
801}
802pub const FA_DEEP_MIN_DEFAULT: usize = 0;
816fn fa_deep_at(t_kv: usize) -> bool {
817 if std::env::var("MEMRA_FA_DEEP").as_deref() == Ok("0") {
818 return false;
819 }
820 let min = std::env::var("MEMRA_FA_DEEP_MIN")
821 .ok()
822 .and_then(|v| v.parse().ok())
823 .unwrap_or(FA_DEEP_MIN_DEFAULT);
824 t_kv >= min
825}
826pub fn fa_deep_at_pub(t_kv: usize) -> bool {
828 fa_deep_at(t_kv)
829}
830
831fn fa_v3_active(head_dim: usize) -> bool {
832 fa_v3_on()
835 && head_dim % 128 == 0
836 && kv_cache_formats() == ("q8_0", "q5_1")
837 && !Engine::kv_fp8_on()
838}
839
840pub fn fa_seqs_eligible(t_kv: usize, head_dim: usize) -> bool {
848 std::env::var("MEMRA_NO_FA_VEC").is_err()
849 && t_kv >= fa_vec_min_tkv()
850 && head_dim == 256
851 && fa_v4_at(t_kv)
852 && !matches!(fa_v4_mode(), "noB3" | "stage")
853 && !Engine::kv_fp8_on()
854}
855pub fn fa_split_keys_pub(t_kv: usize, n_head_kv: usize) -> usize {
857 fa_split_keys(t_kv, n_head_kv)
858}
859
860struct PinnedStage {
865 ptr: *mut u8,
866 cap: usize,
867}
868unsafe impl Send for PinnedStage {}
869impl PinnedStage {
870 fn new(cap: usize) -> Result<Self, Box<dyn std::error::Error>> {
871 let ptr = unsafe { cudarc::driver::result::malloc_host(cap, 0)? } as *mut u8;
872 Ok(PinnedStage { ptr, cap })
873 }
874}
875impl Drop for PinnedStage {
876 fn drop(&mut self) {
877 let _ = unsafe { cudarc::driver::result::free_host(self.ptr as _) };
878 }
879}
880
881pub const ARGMAX_NB: usize = 256;
884
885pub(crate) use memra_fa3_vl as fa3_vl_raw;
887
888unsafe extern "C" {
889 fn memra_fa3_prefill(
891 q16: *const core::ffi::c_void,
892 k16: *const core::ffi::c_void,
893 v16: *const core::ffi::c_void,
894 o: *mut f32,
895 t: i32,
896 h: i32,
897 hkv: i32,
898 d: i32,
899 scale: f32,
900 stream: *mut core::ffi::c_void,
901 ) -> i32;
902 pub(crate) fn memra_fa3_vl(
904 q16s: *const *const core::ffi::c_void,
905 k16s: *const *const core::ffi::c_void,
906 v16s: *const *const core::ffi::c_void,
907 os: *const *mut f32,
908 ts: *const i32,
909 b: i32,
910 h: i32,
911 hkv: i32,
912 d: i32,
913 scale: f32,
914 stream: *mut core::ffi::c_void,
915 ) -> i32;
916}
917
918#[repr(C)]
923#[derive(Clone, Copy)]
924pub struct WPtr8(pub [u64; 8]);
925unsafe impl cudarc::driver::DeviceRepr for WPtr8 {}
926
927#[repr(C)]
932#[derive(Clone, Copy, Default)]
933pub struct GdnSeqVl {
934 pub kb16: u64,
935 pub gcum: u64,
936 pub beta: u64,
937 pub u: u64,
938 pub wb16: u64,
939 pub y: u64,
940 pub ssnap: u64,
941 pub state_in: u64,
942 pub state_out: u64,
943 pub q: u64,
944 pub p: u64,
945 pub o: u64,
946 pub k: u64,
947 pub v: u64,
948 pub g: u64,
949 pub a: u64,
950 pub w: u64,
951 pub t: i32,
952 pub nc: i32,
953}
954unsafe impl cudarc::driver::DeviceRepr for GdnSeqVl {}
955#[repr(C)]
956#[derive(Clone, Copy)]
957pub struct GdnVl8(pub [GdnSeqVl; 8]);
958unsafe impl cudarc::driver::DeviceRepr for GdnVl8 {}
959
960#[repr(C)]
963#[derive(Clone, Copy, Default)]
964pub struct GdnWVl {
965 pub qb16: u64,
966 pub pb16: u64,
967}
968unsafe impl cudarc::driver::DeviceRepr for GdnWVl {}
969#[repr(C)]
970#[derive(Clone, Copy)]
971pub struct GdnWVl8(pub [GdnWVl; 8]);
972unsafe impl cudarc::driver::DeviceRepr for GdnWVl8 {}
973
974#[repr(C)]
976#[derive(Clone, Copy, Default)]
977pub struct GdnPrepVl {
978 pub qkv: u64,
979 pub conv_state: u64,
980 pub conv_out: u64,
981 pub q_g: u64,
982 pub k_g: u64,
983 pub v_g: u64,
984 pub q_l2: u64,
985 pub k_l2: u64,
986 pub beta_raw: u64,
987 pub alpha: u64,
988 pub beta: u64,
989 pub g_log: u64,
990 pub o: u64,
991 pub z: u64,
992 pub gn: u64,
993 pub gn16: u64,
994 pub kb16: u64,
995 pub qb16: u64,
996 pub t: i32,
997 pub pad: i32,
998}
999unsafe impl cudarc::driver::DeviceRepr for GdnPrepVl {}
1000#[repr(C)]
1001#[derive(Clone, Copy)]
1002pub struct GdnPrepVl8(pub [GdnPrepVl; 8]);
1003unsafe impl cudarc::driver::DeviceRepr for GdnPrepVl8 {}
1004
1005#[repr(C)]
1007#[derive(Clone, Copy, Default)]
1008pub struct FaSeqVl {
1009 pub q: u64,
1010 pub k16: u64,
1011 pub v16: u64,
1012 pub o: u64,
1013 pub kf: u64,
1014 pub vf: u64,
1015 pub t: i32,
1016 pub pad: i32,
1017}
1018unsafe impl cudarc::driver::DeviceRepr for FaSeqVl {}
1019#[repr(C)]
1020#[derive(Clone, Copy)]
1021pub struct FaVl8(pub [FaSeqVl; 8]);
1022unsafe impl cudarc::driver::DeviceRepr for FaVl8 {}
1023
1024#[repr(C)]
1026#[derive(Clone, Copy, Default)]
1027pub struct AttnPreVl {
1028 pub qf: u64,
1029 pub kf: u64,
1030 pub vf: u64,
1031 pub q: u64,
1032 pub gate: u64,
1033 pub qn: u64,
1034 pub kn: u64,
1035 pub kc: u64,
1036 pub vc: u64,
1037 pub t: i32,
1038 pub pad: i32,
1039}
1040unsafe impl cudarc::driver::DeviceRepr for AttnPreVl {}
1041#[repr(C)]
1042#[derive(Clone, Copy)]
1043pub struct AttnPreVl8(pub [AttnPreVl; 8]);
1044unsafe impl cudarc::driver::DeviceRepr for AttnPreVl8 {}
1045
1046pub struct GdnChunkBufs {
1049 pub gcum: CudaSlice<f32>,
1050 pub a: CudaSlice<f32>,
1051 pub p: CudaSlice<f32>,
1052 pub u: CudaSlice<f32>,
1053 pub w: CudaSlice<f32>,
1054 pub kb16: CudaSlice<u8>,
1055 pub wb16: CudaSlice<u8>,
1056 pub y16: CudaSlice<u8>,
1057 pub ssnap16: CudaSlice<u8>,
1058 pub qb16: CudaSlice<u8>,
1059 pub pb16: CudaSlice<u8>,
1060 pub o: CudaSlice<f32>,
1061 pub t: usize,
1062 pub nc: usize,
1063}
1064
1065#[repr(C)]
1067#[derive(Clone, Copy)]
1068pub struct F32x8(pub [f32; 8]);
1069unsafe impl cudarc::driver::DeviceRepr for F32x8 {}
1070
1071pub static PRIME_NANOS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1075
1076impl Engine {
1077 pub fn new(ordinal: usize) -> Result<Self, Box<dyn std::error::Error>> {
1078 let gpu = memra_runtime::Gpu::new(ordinal)?;
1079 if std::env::var("MEMRA_ARCH_CHECK").as_deref() != Ok("0") {
1083 use cudarc::driver::sys::CUdevice_attribute_enum as A;
1084 let (maj, min) = cudarc::driver::result::device::get(ordinal as i32)
1085 .and_then(|d| unsafe {
1086 Ok((
1087 cudarc::driver::result::device::get_attribute(
1088 d,
1089 A::CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR,
1090 )?,
1091 cudarc::driver::result::device::get_attribute(
1092 d,
1093 A::CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR,
1094 )?,
1095 ))
1096 })
1097 .unwrap_or((0, 0));
1098 let built = env!("MEMRA_BUILT_CUDA_ARCH");
1099 let ok = matches!(
1100 (built, maj, min),
1101 ("120a", 12, 0) | ("120a", 12, 1) | ("100a", 10, 0) | ("90a", 9, 0) | ("89", 8, 9)
1102 );
1103 if !ok {
1104 return Err(format!(
1105 "memra was built for sm_{built} but device {ordinal} reports compute \
1106 capability {maj}.{min}. Rebuild on this machine (MEMRA_CUDA_ARCH \
1107 auto-detects the GPU) or set MEMRA_ARCH_CHECK=0 to bypass."
1108 )
1109 .into());
1110 }
1111 }
1112 unsafe {
1117 use cudarc::driver::sys;
1118 let dev: sys::CUdevice = ordinal as sys::CUdevice;
1119 let mut pool: sys::CUmemoryPool = std::ptr::null_mut();
1120 if sys::cuDeviceGetDefaultMemPool(&mut pool, dev) == sys::CUresult::CUDA_SUCCESS {
1121 let mut thresh: u64 = u64::MAX;
1122 let _ = sys::cuMemPoolSetAttribute(
1123 pool,
1124 sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_RELEASE_THRESHOLD,
1125 &mut thresh as *mut u64 as *mut core::ffi::c_void,
1126 );
1127 }
1128 }
1129 let module = gpu.ctx.load_module(Ptx::from_binary(FATBIN.to_vec()))?;
1130 let hybrid = gpu
1131 .ctx
1132 .load_module(Ptx::from_binary(HYBRID_FATBIN.to_vec()))?;
1133 let qmatvec = gpu
1134 .ctx
1135 .load_module(Ptx::from_binary(QMATVEC_FATBIN.to_vec()))?;
1136 let flash = gpu
1137 .ctx
1138 .load_module(Ptx::from_binary(flash_fatbin_bytes().to_vec()))?;
1139 let gemm = gpu
1140 .ctx
1141 .load_module(Ptx::from_binary(gemm_fatbin_bytes().into_owned()))?;
1142 let router = gpu
1143 .ctx
1144 .load_module(Ptx::from_binary(ROUTER_FATBIN.to_vec()))?;
1145 let sample = gpu
1146 .ctx
1147 .load_module(Ptx::from_binary(SAMPLE_FATBIN.to_vec()))?;
1148 let copy_stream = gpu.ctx.new_stream()?;
1149 if std::env::var("MEMRA_EVT")
1165 .map(|v| v == "1")
1166 .unwrap_or(false)
1167 {
1168 } else {
1170 unsafe {
1171 gpu.ctx.disable_event_tracking();
1172 }
1173 }
1174 Ok(Self {
1175 gpu,
1176 module,
1177 hybrid,
1178 qmatvec,
1179 flash,
1180 flash_g: std::sync::OnceLock::new(),
1181 gemm,
1182 router,
1183 sample,
1184 moe_cache: Mutex::new(None),
1185 moe_cache_layout: Mutex::new(None),
1186 copy_stream,
1187 capture_keep_on: std::sync::atomic::AtomicBool::new(false),
1188 verify_exact: std::sync::atomic::AtomicBool::new(false),
1189 capture_keep: Mutex::new(Vec::new()),
1190 argmax_partials: Mutex::new(None),
1191 prime_deqw_ws: Mutex::new(None),
1192 router_stage: Mutex::new(None),
1193 fp8_scratch: Mutex::new(None),
1194 fa_vf16_scratch: Mutex::new(None),
1195 fa_part_pool: Mutex::new(None),
1196 fa_part_retired: Mutex::new(Vec::new()),
1197 fn_cache: Mutex::new(Default::default()),
1198 f16_scratch: Mutex::new(None),
1199 #[cfg(memra_cutlass)]
1200 cutlass_scratch: Mutex::new(None),
1201 })
1202 }
1203
1204 pub fn ctx(&self) -> &Arc<CudaContext> {
1205 &self.gpu.ctx
1206 }
1207
1208 pub fn pool_cached_bytes(&self) -> usize {
1226 let (reserved, used) = self.pool_reserved_used();
1227 reserved.saturating_sub(used)
1228 }
1229
1230 pub fn pool_reserved_used(&self) -> (usize, usize) {
1237 use cudarc::driver::sys;
1238 unsafe {
1239 let mut pool: sys::CUmemoryPool = std::ptr::null_mut();
1240 if sys::cuDeviceGetDefaultMemPool(&mut pool, self.gpu.ctx.ordinal() as sys::CUdevice)
1241 != sys::CUresult::CUDA_SUCCESS
1242 {
1243 return (0, 0);
1244 }
1245 let (mut reserved, mut used) = (0u64, 0u64);
1246 if sys::cuMemPoolGetAttribute(
1247 pool,
1248 sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_RESERVED_MEM_CURRENT,
1249 &mut reserved as *mut u64 as *mut core::ffi::c_void,
1250 ) != sys::CUresult::CUDA_SUCCESS
1251 {
1252 return (0, 0);
1253 }
1254 if sys::cuMemPoolGetAttribute(
1255 pool,
1256 sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_USED_MEM_CURRENT,
1257 &mut used as *mut u64 as *mut core::ffi::c_void,
1258 ) != sys::CUresult::CUDA_SUCCESS
1259 {
1260 return (0, 0);
1261 }
1262 (reserved as usize, used as usize)
1263 }
1264 }
1265
1266 pub fn stream(&self) -> Arc<CudaStream> {
1269 self.gpu.stream()
1270 }
1271 pub fn gkv_on() -> bool {
1274 memra_kv::gkv_on()
1275 }
1276
1277 pub fn wkv_on() -> bool {
1289 memra_kv::wkv_on()
1290 }
1291
1292 pub fn kv_fp8_on() -> bool {
1298 memra_kv::kv_fp8_on()
1299 }
1300
1301 fn fa_func(&self, name: &str, head_dim: usize) -> CudaFunction {
1304 if head_dim == 512 && Self::gkv_on() {
1305 self.func_g(name)
1306 } else {
1307 self.func(name)
1308 }
1309 }
1310
1311 fn func_g(&self, name: &str) -> CudaFunction {
1315 let m = self.flash_g.get_or_init(|| {
1316 self.gpu
1317 .ctx
1318 .load_module(cudarc::nvrtc::Ptx::from_binary(
1319 FLASH_FATBIN_KF8VF8.to_vec(),
1320 ))
1321 .expect("load kf8vf8 flash fatbin (fp8-globals arm)")
1322 });
1323 let key = format!("g:{name}");
1324 if let Some(f) = self.fn_cache.lock().unwrap().get(&key) {
1325 return f.clone();
1326 }
1327 let f = match m.load_function(name) {
1328 Ok(f) => f,
1329 Err(_) => self.func(name),
1330 };
1331 self.fn_cache.lock().unwrap().insert(key, f.clone());
1332 f
1333 }
1334
1335 fn func(&self, name: &str) -> CudaFunction {
1336 if let Some(f) = self.fn_cache.lock().unwrap().get(name) {
1339 return f.clone();
1340 }
1341 let f = self
1342 .module
1343 .load_function(name)
1344 .or_else(|_| self.hybrid.load_function(name))
1345 .or_else(|_| self.qmatvec.load_function(name))
1346 .or_else(|_| self.flash.load_function(name))
1347 .or_else(|_| self.gemm.load_function(name))
1348 .or_else(|_| self.router.load_function(name))
1349 .or_else(|_| self.sample.load_function(name))
1350 .unwrap_or_else(|_| panic!("kernel {name} not in any fatbin"));
1351 self.fn_cache
1352 .lock()
1353 .unwrap()
1354 .insert(name.to_string(), f.clone());
1355 f
1356 }
1357
1358 pub fn scatter_trim_logits(
1361 &self,
1362 src: &CudaSlice<f32>,
1363 d2t: &CudaSlice<u32>,
1364 dst: &mut CudaSlice<f32>,
1365 d_vocab: usize,
1366 n_vocab: usize,
1367 ) -> Result<(), Box<dyn std::error::Error>> {
1368 let f1 = self.func("scatter_trim_logits_f32");
1369 let f2 = self.func("scatter_trim_logits_pass2_f32");
1370 let (dv, nv) = (d_vocab as i32, n_vocab as i32);
1371 let cfg1 = LaunchConfig {
1372 grid_dim: (256, 1, 1),
1373 block_dim: (256, 1, 1),
1374 shared_mem_bytes: 0,
1375 };
1376 let __s_b1 = self.gpu.stream();
1377 let mut b1 = __s_b1.launch_builder(&f1);
1378 b1.arg(src).arg(d2t).arg(&mut *dst).arg(&dv).arg(&nv);
1379 unsafe {
1380 b1.launch(cfg1)?;
1381 }
1382 let cfg2 = LaunchConfig {
1383 grid_dim: (d_vocab.div_ceil(256) as u32, 1, 1),
1384 block_dim: (256, 1, 1),
1385 shared_mem_bytes: 0,
1386 };
1387 let __s_b2 = self.gpu.stream();
1388 let mut b2 = __s_b2.launch_builder(&f2);
1389 b2.arg(src).arg(d2t).arg(&mut *dst).arg(&dv);
1390 unsafe {
1391 b2.launch(cfg2)?;
1392 }
1393 Ok(())
1394 }
1395
1396 #[allow(clippy::too_many_arguments)]
1402 pub fn filter_stats(
1403 &self,
1404 x: &CudaSlice<f32>,
1405 row_stride: usize,
1406 rows: &CudaSlice<i32>,
1407 out_th: &mut CudaSlice<f32>,
1408 out_z: &mut CudaSlice<f32>,
1409 out_max: &mut CudaSlice<f32>,
1410 n: usize,
1411 nrow: usize,
1412 temp: f32,
1413 top_k: i32,
1414 top_p: f32,
1415 min_p: f32,
1416 ) -> Result<(), Box<dyn std::error::Error>> {
1417 let f = self.func("filter_stats_f32");
1418 let (ni, nr, rs) = (n as i32, nrow as i32, row_stride as i64);
1419 let cfg = LaunchConfig {
1420 grid_dim: (nrow as u32, 1, 1),
1421 block_dim: (1024, 1, 1),
1422 shared_mem_bytes: 0,
1423 };
1424 let __s_b = self.gpu.stream();
1425 let mut b = __s_b.launch_builder(&f);
1426 b.arg(x)
1427 .arg(&rs)
1428 .arg(rows)
1429 .arg(&mut *out_th)
1430 .arg(&mut *out_z)
1431 .arg(&mut *out_max)
1432 .arg(&ni)
1433 .arg(&nr)
1434 .arg(&temp)
1435 .arg(&top_k)
1436 .arg(&top_p)
1437 .arg(&min_p);
1438 unsafe {
1439 b.launch(cfg)?;
1440 }
1441 Ok(())
1442 }
1443
1444 #[allow(clippy::too_many_arguments)]
1446 pub fn softmax_gather_filtered(
1447 &self,
1448 x: &CudaSlice<f32>,
1449 row_stride: usize,
1450 ids: &CudaSlice<u32>,
1451 rows: &CudaSlice<i32>,
1452 th: &CudaSlice<f32>,
1453 z: &CudaSlice<f32>,
1454 out: &mut CudaSlice<f32>,
1455 n: usize,
1456 npair: usize,
1457 temp: f32,
1458 ) -> Result<(), Box<dyn std::error::Error>> {
1459 let f = self.func("softmax_gather_filtered_f32");
1460 let (ni, np, rs) = (n as i32, npair as i32, row_stride as i64);
1461 let cfg = LaunchConfig {
1462 grid_dim: (npair as u32, 1, 1),
1463 block_dim: (256, 1, 1),
1464 shared_mem_bytes: 0,
1465 };
1466 let __s_b = self.gpu.stream();
1467 let mut b = __s_b.launch_builder(&f);
1468 b.arg(x)
1469 .arg(&rs)
1470 .arg(ids)
1471 .arg(rows)
1472 .arg(th)
1473 .arg(z)
1474 .arg(&mut *out)
1475 .arg(&ni)
1476 .arg(&np)
1477 .arg(&temp);
1478 unsafe {
1479 b.launch(cfg)?;
1480 }
1481 Ok(())
1482 }
1483
1484 #[allow(clippy::too_many_arguments)]
1486 pub fn residual_sample_filtered(
1487 &self,
1488 p: &CudaSlice<f32>,
1489 q: Option<&CudaSlice<f32>>,
1490 n: usize,
1491 temp: f32,
1492 seed: u64,
1493 stream_pos: u32,
1494 p_stats: (f32, f32, f32),
1495 q_stats: (f32, f32, f32),
1496 out_tok: &mut CudaSlice<u32>,
1497 ) -> Result<(), Box<dyn std::error::Error>> {
1498 let f = self.func("residual_sample_filtered_f32");
1499 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
1500 let has_q: i32 = q.is_some() as i32;
1501 let qbuf = q.unwrap_or(p);
1502 let (pm, pth, pz) = p_stats;
1503 let (qm, qth, qz) = q_stats;
1504 let cfg = LaunchConfig {
1505 grid_dim: (1, 1, 1),
1506 block_dim: (1024, 1, 1),
1507 shared_mem_bytes: 0,
1508 };
1509 let __s_b = self.gpu.stream();
1510 let mut b = __s_b.launch_builder(&f);
1511 b.arg(p)
1512 .arg(qbuf)
1513 .arg(&has_q)
1514 .arg(&ni)
1515 .arg(&temp)
1516 .arg(&slo)
1517 .arg(&shi)
1518 .arg(&stream_pos)
1519 .arg(&pm)
1520 .arg(&pth)
1521 .arg(&pz)
1522 .arg(&qm)
1523 .arg(&qth)
1524 .arg(&qz)
1525 .arg(&mut *out_tok);
1526 unsafe {
1527 b.launch(cfg)?;
1528 }
1529 Ok(())
1530 }
1531
1532 #[allow(clippy::too_many_arguments)]
1534 pub fn gumbel_perturb_filtered(
1535 &self,
1536 x: &CudaSlice<f32>,
1537 y: &mut CudaSlice<f32>,
1538 n: usize,
1539 seed: u64,
1540 stream_pos: u32,
1541 temp: f32,
1542 row_max: f32,
1543 th: f32,
1544 ) -> Result<(), Box<dyn std::error::Error>> {
1545 let f = self.func("gumbel_perturb_filtered_f32");
1546 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
1547 let cfg = LaunchConfig {
1548 grid_dim: (n.div_ceil(256) as u32, 1, 1),
1549 block_dim: (256, 1, 1),
1550 shared_mem_bytes: 0,
1551 };
1552 let __s_b = self.gpu.stream();
1553 let mut b = __s_b.launch_builder(&f);
1554 b.arg(x)
1555 .arg(&mut *y)
1556 .arg(&ni)
1557 .arg(&slo)
1558 .arg(&shi)
1559 .arg(&stream_pos)
1560 .arg(&temp)
1561 .arg(&row_max)
1562 .arg(&th);
1563 unsafe {
1564 b.launch(cfg)?;
1565 }
1566 Ok(())
1567 }
1568
1569 #[allow(clippy::too_many_arguments)]
1573 pub fn penalize_logits(
1574 &self,
1575 x: &mut CudaSlice<f32>,
1576 hist: &CudaSlice<u32>,
1577 n_hist: usize,
1578 rep: f32,
1579 freq: f32,
1580 present: f32,
1581 n: usize,
1582 ) -> Result<(), Box<dyn std::error::Error>> {
1583 if n_hist == 0 {
1584 return Ok(());
1585 }
1586 let f = self.func("penalize_logits_f32");
1587 let (nh, ni) = (n_hist as i32, n as i32);
1588 let cfg = LaunchConfig {
1589 grid_dim: (n_hist.div_ceil(128) as u32, 1, 1),
1590 block_dim: (128, 1, 1),
1591 shared_mem_bytes: 0,
1592 };
1593 let __s_b = self.gpu.stream();
1594 let mut b = __s_b.launch_builder(&f);
1595 b.arg(&mut *x)
1596 .arg(hist)
1597 .arg(&nh)
1598 .arg(&rep)
1599 .arg(&freq)
1600 .arg(&present)
1601 .arg(&ni);
1602 unsafe {
1603 b.launch(cfg)?;
1604 }
1605 Ok(())
1606 }
1607
1608 #[allow(clippy::too_many_arguments)]
1610 pub fn penalize_logits_rows(
1611 &self,
1612 x: &mut CudaSlice<f32>,
1613 hist: &CudaSlice<u32>,
1614 n_hist: usize,
1615 rep: f32,
1616 freq: f32,
1617 present: f32,
1618 n: usize,
1619 nrow: usize,
1620 ) -> Result<(), Box<dyn std::error::Error>> {
1621 if n_hist == 0 || nrow == 0 {
1622 return Ok(());
1623 }
1624 let f = self.func("penalize_logits_rows_f32");
1625 let (nh, ni, nr) = (n_hist as i32, n as i32, nrow as i32);
1626 let cfg = LaunchConfig {
1627 grid_dim: (n_hist.div_ceil(128) as u32, nrow as u32, 1),
1628 block_dim: (128, 1, 1),
1629 shared_mem_bytes: 0,
1630 };
1631 let __s_b = self.gpu.stream();
1632 let mut b = __s_b.launch_builder(&f);
1633 b.arg(&mut *x)
1634 .arg(hist)
1635 .arg(&nh)
1636 .arg(&rep)
1637 .arg(&freq)
1638 .arg(&present)
1639 .arg(&ni)
1640 .arg(&nr);
1641 unsafe {
1642 b.launch(cfg)?;
1643 }
1644 Ok(())
1645 }
1646
1647 pub fn wpf_level() -> u32 {
1655 static ON: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
1656 *ON.get_or_init(|| {
1657 std::env::var("MEMRA_WPF")
1658 .ok()
1659 .and_then(|v| v.parse().ok())
1660 .unwrap_or(1)
1661 })
1662 }
1663
1664 pub fn set_verify_exact(&self, on: bool) {
1676 self.verify_exact
1677 .store(on, std::sync::atomic::Ordering::Relaxed);
1678 }
1679 pub(crate) fn verify_exact_on(&self) -> bool {
1680 self.verify_exact.load(std::sync::atomic::Ordering::Relaxed)
1681 }
1682
1683 pub fn qkv_append_on() -> bool {
1686 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1687 *ON.get_or_init(|| {
1688 std::env::var("MEMRA_QKV_APPEND")
1689 .map(|v| v != "0")
1690 .unwrap_or(true)
1691 })
1692 }
1693
1694 pub fn pdl_wb_on() -> bool {
1697 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1698 *ON.get_or_init(|| {
1699 std::env::var("MEMRA_PDL_WB")
1700 .map(|v| v != "0")
1701 .unwrap_or(true)
1702 })
1703 }
1704
1705 pub fn pdl_mmvq_on() -> bool {
1709 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1710 *ON.get_or_init(|| {
1711 std::env::var("MEMRA_PDL_MMVQ")
1712 .map(|v| v != "0")
1713 .unwrap_or(true)
1714 })
1715 }
1716
1717 pub fn pdl_on() -> bool {
1718 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1719 *ON.get_or_init(|| std::env::var("MEMRA_PDL").map(|v| v != "0").unwrap_or(true))
1720 }
1721
1722 fn q40_mr1_on() -> bool {
1728 static Q40MR: std::sync::OnceLock<Option<u32>> = std::sync::OnceLock::new();
1729 match *Q40MR.get_or_init(|| {
1730 std::env::var("MEMRA_Q40_MR")
1731 .ok()
1732 .and_then(|v| v.parse().ok())
1733 }) {
1734 Some(v) => v == 1,
1735 None => crate::FUSED_MR1_DEFAULT.load(std::sync::atomic::Ordering::Relaxed),
1736 }
1737 }
1738
1739 fn pdl_func_flash(
1744 &self,
1745 g: bool,
1746 name: &'static str,
1747 ) -> Result<cudarc::driver::sys::CUfunction, Box<dyn std::error::Error>> {
1748 use cudarc::driver::sys as cu;
1749 static MODS: std::sync::Mutex<Option<std::collections::HashMap<(usize, bool), usize>>> =
1756 std::sync::Mutex::new(None);
1757 static FNS: std::sync::Mutex<
1758 Option<std::collections::HashMap<(usize, bool, &'static str), usize>>,
1759 > = std::sync::Mutex::new(None);
1760 let ctx_key = self.ctx().cu_ctx() as usize;
1761 if let Some(&f) = FNS
1762 .lock()
1763 .unwrap()
1764 .get_or_insert_with(Default::default)
1765 .get(&(ctx_key, g, name))
1766 {
1767 return Ok(f as cu::CUfunction);
1768 }
1769 let module = {
1770 let mut mods = MODS.lock().unwrap();
1771 let map = mods.get_or_insert_with(Default::default);
1772 match map.get(&(ctx_key, g)) {
1773 Some(&m) => m,
1774 None => {
1775 let m = self.pdl_load_module_in_ctx(if g {
1776 FLASH_FATBIN_KF8VF8
1777 } else {
1778 FLASH_FATBIN
1779 })?;
1780 map.insert((ctx_key, g), m);
1781 m
1782 }
1783 }
1784 };
1785 let cname = std::ffi::CString::new(name)?;
1786 let mut f: cu::CUfunction = std::ptr::null_mut();
1787 let r = unsafe { cu::cuModuleGetFunction(&mut f, module as cu::CUmodule, cname.as_ptr()) };
1788 if r != cu::CUresult::CUDA_SUCCESS {
1789 return Err(format!("pdl_func_flash {name} (g={g}): {r:?}").into());
1790 }
1791 FNS.lock()
1792 .unwrap()
1793 .get_or_insert_with(Default::default)
1794 .insert((ctx_key, g, name), f as usize);
1795 Ok(f)
1796 }
1797
1798 fn pdl_load_module_in_ctx(&self, bytes: &[u8]) -> Result<usize, Box<dyn std::error::Error>> {
1803 use cudarc::driver::sys as cu;
1804 let mut prev: cu::CUcontext = std::ptr::null_mut();
1805 unsafe {
1806 cu::cuCtxGetCurrent(&mut prev).result()?;
1807 }
1808 self.ctx().bind_to_thread()?;
1809 let mut m: cu::CUmodule = std::ptr::null_mut();
1810 let r = unsafe { cu::cuModuleLoadData(&mut m, bytes.as_ptr() as *const std::ffi::c_void) };
1811 let restore = if prev.is_null() {
1812 cu::CUresult::CUDA_SUCCESS
1813 } else {
1814 unsafe { cu::cuCtxSetCurrent(prev) }
1815 };
1816 if r != cu::CUresult::CUDA_SUCCESS {
1817 return Err(format!("pdl module load: {r:?}").into());
1818 }
1819 if restore != cu::CUresult::CUDA_SUCCESS {
1820 return Err(format!("pdl module load: ctx restore {restore:?}").into());
1821 }
1822 Ok(m as usize)
1823 }
1824
1825 fn pdl_func(
1826 &self,
1827 name: &'static str,
1828 ) -> Result<cudarc::driver::sys::CUfunction, Box<dyn std::error::Error>> {
1829 use cudarc::driver::sys as cu;
1830 static MODULES: std::sync::Mutex<Option<std::collections::HashMap<usize, usize>>> =
1833 std::sync::Mutex::new(None);
1834 static QMODULES: std::sync::Mutex<Option<std::collections::HashMap<usize, usize>>> =
1837 std::sync::Mutex::new(None);
1838 static FNS: std::sync::Mutex<
1839 Option<std::collections::HashMap<(usize, &'static str), usize>>,
1840 > = std::sync::Mutex::new(None);
1841 let ctx_key = self.ctx().cu_ctx() as usize;
1842 if let Some(&f) = FNS
1843 .lock()
1844 .unwrap()
1845 .get_or_insert_with(Default::default)
1846 .get(&(ctx_key, name))
1847 {
1848 return Ok(f as cu::CUfunction);
1849 }
1850 let module = {
1851 let mut mods = MODULES.lock().unwrap();
1852 let map = mods.get_or_insert_with(Default::default);
1853 match map.get(&ctx_key) {
1854 Some(&m) => m,
1855 None => {
1856 let m = self.pdl_load_module_in_ctx(FATBIN)?;
1857 map.insert(ctx_key, m);
1858 m
1859 }
1860 }
1861 };
1862 let cname = std::ffi::CString::new(name)?;
1863 let mut f: cu::CUfunction = std::ptr::null_mut();
1864 let mut r =
1865 unsafe { cu::cuModuleGetFunction(&mut f, module as cu::CUmodule, cname.as_ptr()) };
1866 if r == cu::CUresult::CUDA_ERROR_NOT_FOUND {
1867 let qmodule = {
1868 let mut mods = QMODULES.lock().unwrap();
1869 let map = mods.get_or_insert_with(Default::default);
1870 match map.get(&ctx_key) {
1871 Some(&m) => m,
1872 None => {
1873 let m = self.pdl_load_module_in_ctx(QMATVEC_FATBIN)?;
1874 map.insert(ctx_key, m);
1875 m
1876 }
1877 }
1878 };
1879 r = unsafe { cu::cuModuleGetFunction(&mut f, qmodule as cu::CUmodule, cname.as_ptr()) };
1880 }
1881 if r != cu::CUresult::CUDA_SUCCESS {
1882 return Err(format!("pdl_func {name}: {r:?}").into());
1883 }
1884 FNS.lock()
1885 .unwrap()
1886 .get_or_insert_with(Default::default)
1887 .insert((ctx_key, name), f as usize);
1888 Ok(f)
1889 }
1890
1891 unsafe fn launch_pdl_flash(
1903 &self,
1904 g: bool,
1905 name: &'static str,
1906 grid: (u32, u32, u32),
1907 block: (u32, u32, u32),
1908 smem: u32,
1909 params: &mut [*mut std::ffi::c_void],
1910 ) -> Result<(), Box<dyn std::error::Error>> {
1911 use cudarc::driver::sys as cu;
1912 let f = self.pdl_func_flash(g, name)?;
1913 if smem > 0 {
1914 let r =
1916 unsafe {
1917 cu::cuFuncSetAttribute(f,
1918 cu::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
1919 smem as i32)
1920 };
1921 if r != cu::CUresult::CUDA_SUCCESS {
1922 return Err(format!("pdl smem attr {name}: {r:?}").into());
1923 }
1924 }
1925 let mut attr = cu::CUlaunchAttribute {
1926 id: cu::CUlaunchAttributeID::CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION,
1927 pad: [0; 4],
1928 value: cu::CUlaunchAttributeValue {
1929 programmaticStreamSerializationAllowed: 1,
1930 },
1931 };
1932 let cfg = cu::CUlaunchConfig {
1933 gridDimX: grid.0,
1934 gridDimY: grid.1,
1935 gridDimZ: grid.2,
1936 blockDimX: block.0,
1937 blockDimY: block.1,
1938 blockDimZ: block.2,
1939 sharedMemBytes: smem,
1940 hStream: self.gpu.stream().cu_stream(),
1941 attrs: &mut attr,
1942 numAttrs: 1,
1943 };
1944 let r = unsafe { cu::cuLaunchKernelEx(&cfg, f, params.as_mut_ptr(), std::ptr::null_mut()) };
1945 if r != cu::CUresult::CUDA_SUCCESS {
1946 return Err(format!("launch_pdl_flash {name}: {r:?}").into());
1947 }
1948 Ok(())
1949 }
1950
1951 unsafe fn launch_pdl(
1952 &self,
1953 name: &'static str,
1954 grid: (u32, u32, u32),
1955 block: (u32, u32, u32),
1956 params: &mut [*mut std::ffi::c_void],
1957 ) -> Result<(), Box<dyn std::error::Error>> {
1958 use cudarc::driver::sys as cu;
1959 let f = self.pdl_func(name)?;
1960 let mut attr = cu::CUlaunchAttribute {
1961 id: cu::CUlaunchAttributeID::CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION,
1962 pad: [0; 4],
1963 value: cu::CUlaunchAttributeValue {
1964 programmaticStreamSerializationAllowed: 1,
1965 },
1966 };
1967 let cfg = cu::CUlaunchConfig {
1968 gridDimX: grid.0,
1969 gridDimY: grid.1,
1970 gridDimZ: grid.2,
1971 blockDimX: block.0,
1972 blockDimY: block.1,
1973 blockDimZ: block.2,
1974 sharedMemBytes: 0,
1975 hStream: self.gpu.stream().cu_stream(),
1976 attrs: &mut attr,
1977 numAttrs: 1,
1978 };
1979 let r = unsafe { cu::cuLaunchKernelEx(&cfg, f, params.as_mut_ptr(), std::ptr::null_mut()) };
1980 if r != cu::CUresult::CUDA_SUCCESS {
1981 return Err(format!("launch_pdl {name}: {r:?}").into());
1982 }
1983 Ok(())
1984 }
1985
1986 pub fn prefetch_weight_l2(
1989 &self,
1990 w: &crate::model::GpuTensor,
1991 ) -> Result<(), Box<dyn std::error::Error>> {
1992 if let crate::model::GpuTensor::Quant { bytes, rp4, .. } = w {
1993 let p = rp4.as_ref().unwrap_or(bytes);
1994 self.prefetch_l2(p, p.len())?;
1995 }
1996 Ok(())
1997 }
1998
1999 pub fn gather_row_bf16(
2002 &self,
2003 table: &CudaSlice<u8>,
2004 tok: &CudaSlice<u32>,
2005 idx: usize,
2006 dst: &mut CudaSlice<f32>,
2007 ncols: usize,
2008 ) -> Result<(), Box<dyn std::error::Error>> {
2009 let f = self.func("gather_row_bf16_f32");
2010 let cfg = LaunchConfig {
2011 grid_dim: (ncols.div_ceil(256) as u32, 1, 1),
2012 block_dim: (256, 1, 1),
2013 shared_mem_bytes: 0,
2014 };
2015 let (nc, ix) = (ncols as i32, idx as i32);
2016 let __s_b = self.gpu.stream();
2017 let mut b = __s_b.launch_builder(&f);
2018 b.arg(table).arg(tok).arg(&ix).arg(dst).arg(&nc);
2019 unsafe {
2020 b.launch(cfg)?;
2021 }
2022 Ok(())
2023 }
2024
2025 pub fn add_row_inplace(
2027 &self,
2028 logits: &mut CudaSlice<f32>,
2029 bias: &CudaSlice<f32>,
2030 n: usize,
2031 row_off: usize,
2032 ) -> Result<(), Box<dyn std::error::Error>> {
2033 let f = self.func("add_row_inplace_f32");
2034 let cfg = LaunchConfig {
2035 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2036 block_dim: (256, 1, 1),
2037 shared_mem_bytes: 0,
2038 };
2039 let (ni, off) = (n as i32, row_off as i64);
2040 let __s_b = self.gpu.stream();
2041 let mut b = __s_b.launch_builder(&f);
2042 b.arg(logits).arg(bias).arg(&ni).arg(&off);
2043 unsafe {
2044 b.launch(cfg)?;
2045 }
2046 Ok(())
2047 }
2048
2049 pub fn prefetch_l2(
2051 &self,
2052 p: &CudaSlice<u8>,
2053 n: usize,
2054 ) -> Result<(), Box<dyn std::error::Error>> {
2055 let f = self.func("prefetch_l2_bytes");
2056 let lines = n.div_ceil(128);
2057 let ni = n as i64;
2058 let cfg = LaunchConfig {
2059 grid_dim: (lines.div_ceil(256) as u32, 1, 1),
2060 block_dim: (256, 1, 1),
2061 shared_mem_bytes: 0,
2062 };
2063 let __s_b = self.gpu.stream();
2064 let mut b = __s_b.launch_builder(&f);
2065 b.arg(p).arg(&ni);
2066 unsafe {
2067 b.launch(cfg)?;
2068 }
2069 Ok(())
2070 }
2071
2072 pub fn router_gemv(
2075 &self,
2076 w: &CudaSlice<f32>,
2077 x: &CudaSlice<f32>,
2078 n_embd: usize,
2079 n_experts: usize,
2080 t: usize,
2081 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2082 let w8 = match std::env::var("MEMRA_ROUTER_V2").as_deref() {
2088 Ok("0") => false,
2089 Ok(_) => true,
2090 Err(_) => ROUTER_W8_DEFAULT.load(std::sync::atomic::Ordering::Relaxed),
2091 };
2092 let batch = w8 && t >= ROUTER_BATCH_MIN_T && router_batch_on();
2102 self.router_gemv_form(w, x, n_embd, n_experts, t, w8, batch)
2103 }
2104
2105 pub fn router_gemv_form(
2108 &self,
2109 w: &CudaSlice<f32>,
2110 x: &CudaSlice<f32>,
2111 n_embd: usize,
2112 n_experts: usize,
2113 t: usize,
2114 w8: bool,
2115 batch: bool,
2116 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2117 debug_assert!(!batch || w8, "batch twin exists for the w8 form only");
2118 let mut y = self.alloc_uninit::<f32>(t * n_experts)?;
2119 let f = if batch {
2120 self.func("router_gemv_f32_w8_batch")
2121 } else if w8 {
2122 self.func("router_gemv_f32_w8")
2123 } else {
2124 self.func("router_gemv_f32")
2125 };
2126 let (ne, nx, ti) = (n_embd as i32, n_experts as i32, t as i32);
2127 let cfg = if batch {
2128 LaunchConfig {
2129 grid_dim: (n_experts.div_ceil(8) as u32, t.div_ceil(8) as u32, 1),
2130 block_dim: (32, 8, 1),
2131 shared_mem_bytes: 0,
2132 }
2133 } else {
2134 LaunchConfig {
2135 grid_dim: (n_experts as u32, t as u32, 1),
2136 block_dim: (32, if w8 { 8 } else { 1 }, 1),
2137 shared_mem_bytes: 0,
2138 }
2139 };
2140 let __s_b = self.gpu.stream();
2141 let mut b = __s_b.launch_builder(&f);
2142 b.arg(w).arg(x).arg(&mut y).arg(&ne).arg(&nx).arg(&ti);
2143 unsafe {
2144 b.launch(cfg)?;
2145 }
2146 Ok(y)
2147 }
2148
2149 pub fn rows_permute(
2151 &self,
2152 src: &CudaSlice<f32>,
2153 idx: &CudaSlice<i32>,
2154 nrows: usize,
2155 ncols: usize,
2156 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2157 let mut dst = self.alloc_uninit::<f32>(nrows * ncols)?;
2158 let f = self.func("rows_permute_f32");
2159 let (nc, nr) = (ncols as i32, nrows as i32);
2160 let cfg = LaunchConfig {
2161 grid_dim: (nrows as u32, 1, 1),
2162 block_dim: (256, 1, 1),
2163 shared_mem_bytes: 0,
2164 };
2165 let __s_b = self.gpu.stream();
2166 let mut b = __s_b.launch_builder(&f);
2167 b.arg(src).arg(idx).arg(&mut dst).arg(&nc).arg(&nr);
2168 unsafe {
2169 b.launch(cfg)?;
2170 }
2171 Ok(dst)
2172 }
2173
2174 pub fn sigmoid_dot_rows(
2179 &self,
2180 x: &CudaSlice<f32>,
2181 w: &CudaSlice<f32>,
2182 n_embd: usize,
2183 t: usize,
2184 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2185 static OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2188 if *OFF.get_or_init(|| std::env::var("MEMRA_SHEXP_DOT").as_deref() == Ok("0")) {
2189 let gs = self.linear(x, w, t, n_embd, 1)?;
2190 let mut g = self.uninit(t)?;
2191 self.sigmoid(&gs, &mut g, t)?;
2192 return Ok(g);
2193 }
2194 let mut g = self.alloc_uninit::<f32>(t)?;
2200 let f = self.func("sigmoid_dot_rows_f32");
2201 let (ne, ti) = (n_embd as i32, t as i32);
2202 let cfg = LaunchConfig {
2203 grid_dim: (t as u32, 1, 1),
2204 block_dim: (32, 8, 1),
2205 shared_mem_bytes: 0,
2206 };
2207 let __s_b = self.gpu.stream();
2208 let mut b = __s_b.launch_builder(&f);
2209 b.arg(x).arg(w).arg(&mut g).arg(&ne).arg(&ti);
2210 unsafe {
2211 b.launch(cfg)?;
2212 }
2213 Ok(g)
2214 }
2215
2216 pub fn spec_rollback_stream(
2218 &self,
2219 len_ptrs: &CudaSlice<u64>,
2220 pos_start: &CudaSlice<i32>,
2221 acc: &CudaSlice<u32>,
2222 base: usize,
2223 n_rows: usize,
2224 ) -> Result<(), Box<dyn std::error::Error>> {
2225 let f = self.func("spec_rollback_stream");
2226 let (b, nr) = (base as i32, n_rows as i32);
2227 let cfg = LaunchConfig {
2228 grid_dim: (n_rows.div_ceil(64) as u32, 1, 1),
2229 block_dim: (64, 1, 1),
2230 shared_mem_bytes: 0,
2231 };
2232 let __s_bl = self.gpu.stream();
2233 let mut bl = __s_bl.launch_builder(&f);
2234 bl.arg(len_ptrs).arg(pos_start).arg(acc).arg(&b).arg(&nr);
2235 unsafe {
2236 bl.launch(cfg)?;
2237 }
2238 Ok(())
2239 }
2240
2241 pub fn plain_tok_ring(
2243 &self,
2244 vam: &CudaSlice<u32>,
2245 pos_start: &CudaSlice<i32>,
2246 base: usize,
2247 ring: &mut CudaSlice<u32>,
2248 ) -> Result<(), Box<dyn std::error::Error>> {
2249 let f = self.func("plain_tok_ring");
2250 let (b, cap) = (base as i32, ring.len() as i32);
2251 let cfg = LaunchConfig {
2252 grid_dim: (1, 1, 1),
2253 block_dim: (32, 1, 1),
2254 shared_mem_bytes: 0,
2255 };
2256 let __s_bl = self.gpu.stream();
2257 let mut bl = __s_bl.launch_builder(&f);
2258 bl.arg(vam).arg(pos_start).arg(&b).arg(&mut *ring).arg(&cap);
2259 unsafe {
2260 bl.launch(cfg)?;
2261 }
2262 Ok(())
2263 }
2264
2265 pub fn spec_ring_commit(
2267 &self,
2268 vtok: &CudaSlice<u32>,
2269 acc: &CudaSlice<u32>,
2270 brk: &CudaSlice<u32>,
2271 ring: &mut CudaSlice<u32>,
2272 pend: &mut CudaSlice<u32>,
2273 ) -> Result<(), Box<dyn std::error::Error>> {
2274 let f = self.func("spec_ring_commit");
2275 let cfg = LaunchConfig {
2276 grid_dim: (1, 1, 1),
2277 block_dim: (32, 1, 1),
2278 shared_mem_bytes: 0,
2279 };
2280 let __s_b = self.gpu.stream();
2281 let mut b = __s_b.launch_builder(&f);
2282 b.arg(vtok).arg(acc).arg(brk).arg(ring).arg(pend);
2283 unsafe {
2284 b.launch(cfg)?;
2285 }
2286 Ok(())
2287 }
2288 pub fn i32_copy_add(
2289 &self,
2290 src: &CudaSlice<i32>,
2291 dst: &mut CudaSlice<i32>,
2292 delta: i32,
2293 ) -> Result<(), Box<dyn std::error::Error>> {
2294 let f = self.func("i32_copy_add");
2295 let cfg = LaunchConfig {
2296 grid_dim: (1, 1, 1),
2297 block_dim: (32, 1, 1),
2298 shared_mem_bytes: 0,
2299 };
2300 let __s_b = self.gpu.stream();
2301 let mut b = __s_b.launch_builder(&f);
2302 b.arg(src).arg(dst).arg(&delta);
2303 unsafe {
2304 b.launch(cfg)?;
2305 }
2306 Ok(())
2307 }
2308 pub fn u32_copy(
2309 &self,
2310 src: &CudaSlice<u32>,
2311 dst: &mut CudaSlice<u32>,
2312 ) -> Result<(), Box<dyn std::error::Error>> {
2313 let f = self.func("u32_copy");
2314 let cfg = LaunchConfig {
2315 grid_dim: (1, 1, 1),
2316 block_dim: (32, 1, 1),
2317 shared_mem_bytes: 0,
2318 };
2319 let __s_b = self.gpu.stream();
2320 let mut b = __s_b.launch_builder(&f);
2321 b.arg(src).arg(dst);
2322 unsafe {
2323 b.launch(cfg)?;
2324 }
2325 Ok(())
2326 }
2327
2328 pub fn spec_adapt_k(
2332 &self,
2333 acc: &CudaSlice<u32>,
2334 brk: &mut CudaSlice<u32>,
2335 floor: usize,
2336 cap: usize,
2337 ) -> Result<(), Box<dyn std::error::Error>> {
2338 let f = self.func("spec_adapt_k");
2339 let (fl, cp) = (floor as i32, cap as i32);
2340 let cfg = LaunchConfig {
2341 grid_dim: (1, 1, 1),
2342 block_dim: (32, 1, 1),
2343 shared_mem_bytes: 0,
2344 };
2345 let __s_b = self.gpu.stream();
2346 let mut b = __s_b.launch_builder(&f);
2347 b.arg(acc).arg(brk).arg(&fl).arg(&cp);
2348 unsafe {
2349 b.launch(cfg)?;
2350 }
2351 Ok(())
2352 }
2353
2354 pub fn spec_accept_greedy_dc(
2356 &self,
2357 preds: &CudaSlice<u32>,
2358 vtok: &CudaSlice<u32>,
2359 last_pred: &CudaSlice<u32>,
2360 brk: &CudaSlice<u32>,
2361 out: &mut CudaSlice<u32>,
2362 ) -> Result<(), Box<dyn std::error::Error>> {
2363 let f = self.func("spec_accept_greedy_dc");
2364 let cfg = LaunchConfig {
2365 grid_dim: (1, 1, 1),
2366 block_dim: (32, 1, 1),
2367 shared_mem_bytes: 0,
2368 };
2369 let __s_b = self.gpu.stream();
2370 let mut b = __s_b.launch_builder(&f);
2371 b.arg(preds).arg(vtok).arg(last_pred).arg(brk).arg(out);
2372 unsafe {
2373 b.launch(cfg)?;
2374 }
2375 Ok(())
2376 }
2377
2378 pub fn pos_iota(
2380 &self,
2381 pos0: &CudaSlice<i32>,
2382 out: &mut CudaSlice<i32>,
2383 t: usize,
2384 ) -> Result<(), Box<dyn std::error::Error>> {
2385 let f = self.func("pos_iota_i32");
2386 let ti = t as i32;
2387 let cfg = LaunchConfig {
2388 grid_dim: (1, 1, 1),
2389 block_dim: (t.max(1) as u32, 1, 1),
2390 shared_mem_bytes: 0,
2391 };
2392 let __s_b = self.gpu.stream();
2393 let mut b = __s_b.launch_builder(&f);
2394 b.arg(pos0).arg(out).arg(&ti);
2395 unsafe {
2396 b.launch(cfg)?;
2397 }
2398 Ok(())
2399 }
2400 #[allow(clippy::too_many_arguments)]
2401 pub fn append_kv_quantized_rows_dc(
2402 &self,
2403 k_rows: &CudaSlice<f32>,
2404 v_rows: &CudaSlice<f32>,
2405 kc: &mut CudaSlice<u8>,
2406 vc: &mut CudaSlice<u8>,
2407 t0_dev: &CudaSlice<i32>,
2408 t: usize,
2409 kv_dim_k: usize,
2410 kv_dim_v: usize,
2411 k_tok_bytes: usize,
2412 v_tok_bytes: usize,
2413 g: bool,
2414 ) -> Result<(), Box<dyn std::error::Error>> {
2415 let f = if g {
2416 self.func_g("append_quantize_kv_q8_0_q5_1_rows_dc")
2417 } else {
2418 self.func("append_quantize_kv_q8_0_q5_1_rows_dc")
2419 };
2420 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
2421 let cfg = LaunchConfig {
2422 grid_dim: (nblk, t as u32, 1),
2423 block_dim: (32, 1, 1),
2424 shared_mem_bytes: 0,
2425 };
2426 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
2427 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
2428 let __s_b = self.gpu.stream();
2429 let mut b = __s_b.launch_builder(&f);
2430 b.arg(k_rows)
2431 .arg(v_rows)
2432 .arg(kc)
2433 .arg(vc)
2434 .arg(t0_dev)
2435 .arg(&kdk)
2436 .arg(&kdv)
2437 .arg(&ktb)
2438 .arg(&vtb);
2439 unsafe {
2440 b.launch(cfg)?;
2441 }
2442 Ok(())
2443 }
2444
2445 #[allow(clippy::too_many_arguments)]
2448 pub fn append_kv_quantized_row_dc_inc(
2449 &self,
2450 k_row: &CudaSlice<f32>,
2451 v_row: &CudaSlice<f32>,
2452 kc: &mut CudaSlice<u8>,
2453 vc: &mut CudaSlice<u8>,
2454 t0_dev: &mut CudaSlice<i32>,
2455 kv_dim_k: usize,
2456 kv_dim_v: usize,
2457 k_tok_bytes: usize,
2458 v_tok_bytes: usize,
2459 g: bool,
2460 ) -> Result<(), Box<dyn std::error::Error>> {
2461 let f = if g {
2462 self.func_g("append_quantize_kv_q8_0_q5_1_dc_inc")
2463 } else {
2464 self.func("append_quantize_kv_q8_0_q5_1_dc_inc")
2465 };
2466 let nthreads = ((kv_dim_k.max(kv_dim_v) / 32) * 32).min(1024) as u32;
2467 let cfg = LaunchConfig {
2468 grid_dim: (1, 1, 1),
2469 block_dim: (nthreads, 1, 1),
2470 shared_mem_bytes: 0,
2471 };
2472 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
2473 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
2474 let __s_b = self.gpu.stream();
2475 let mut b = __s_b.launch_builder(&f);
2476 b.arg(k_row)
2477 .arg(v_row)
2478 .arg(kc)
2479 .arg(vc)
2480 .arg(t0_dev)
2481 .arg(&kdk)
2482 .arg(&kdv)
2483 .arg(&ktb)
2484 .arg(&vtb);
2485 unsafe {
2486 b.launch(cfg)?;
2487 }
2488 Ok(())
2489 }
2490
2491 pub fn pack_tok_p(
2493 &self,
2494 tok: &CudaSlice<u32>,
2495 p: &CudaSlice<f32>,
2496 out: &mut CudaSlice<u32>,
2497 slot: usize,
2498 ) -> Result<(), Box<dyn std::error::Error>> {
2499 let f = self.func("pack_tok_p");
2500 let sl = slot as i32;
2501 let cfg = LaunchConfig {
2502 grid_dim: (1, 1, 1),
2503 block_dim: (32, 1, 1),
2504 shared_mem_bytes: 0,
2505 };
2506 let __s_b = self.gpu.stream();
2507 let mut b = __s_b.launch_builder(&f);
2508 b.arg(tok).arg(p).arg(out).arg(&sl);
2509 unsafe {
2510 b.launch(cfg)?;
2511 }
2512 Ok(())
2513 }
2514 pub fn tok_map_u32(
2515 &self,
2516 tok: &mut CudaSlice<u32>,
2517 map: &CudaSlice<u32>,
2518 ) -> Result<(), Box<dyn std::error::Error>> {
2519 let f = self.func("tok_map_u32");
2520 let cfg = LaunchConfig {
2521 grid_dim: (1, 1, 1),
2522 block_dim: (32, 1, 1),
2523 shared_mem_bytes: 0,
2524 };
2525 let __s_b = self.gpu.stream();
2526 let mut b = __s_b.launch_builder(&f);
2527 b.arg(tok).arg(map);
2528 unsafe {
2529 b.launch(cfg)?;
2530 }
2531 Ok(())
2532 }
2533
2534 #[allow(clippy::too_many_arguments)]
2536 pub fn spec_assemble_verify(
2537 &self,
2538 tokp: &CudaSlice<u32>,
2539 pend: &CudaSlice<u32>,
2540 d2t: Option<&CudaSlice<u32>>,
2541 vtok: &mut CudaSlice<u32>,
2542 brk: &mut CudaSlice<u32>,
2543 p_min: f32,
2544 k: usize,
2545 pmin0: bool,
2546 ) -> Result<(), Box<dyn std::error::Error>> {
2547 let f = self.func("spec_assemble_verify");
2548 let (ki, pm) = (k as i32, if pmin0 { 1i32 } else { 0i32 });
2549 let cfg = LaunchConfig {
2550 grid_dim: (1, 1, 1),
2551 block_dim: (32, 1, 1),
2552 shared_mem_bytes: 0,
2553 };
2554 let __s_b = self.gpu.stream();
2555 let mut b = __s_b.launch_builder(&f);
2556 match d2t {
2557 Some(m) => {
2558 b.arg(tokp)
2559 .arg(pend)
2560 .arg(m)
2561 .arg(vtok)
2562 .arg(brk)
2563 .arg(&p_min)
2564 .arg(&ki)
2565 .arg(&pm);
2566 unsafe {
2567 b.launch(cfg)?;
2568 }
2569 }
2570 None => {
2571 let null: u64 = 0;
2572 b.arg(tokp)
2573 .arg(pend)
2574 .arg(&null)
2575 .arg(vtok)
2576 .arg(brk)
2577 .arg(&p_min)
2578 .arg(&ki)
2579 .arg(&pm);
2580 unsafe {
2581 b.launch(cfg)?;
2582 }
2583 }
2584 }
2585 Ok(())
2586 }
2587
2588 #[allow(clippy::too_many_arguments)]
2590 pub fn ssm_conv_ring_rebuild_dc(
2591 &self,
2592 qkv_tm: &CudaSlice<f32>,
2593 ring_old: &CudaSlice<f32>,
2594 conv_state: &mut CudaSlice<f32>,
2595 conv_dim: usize,
2596 acc: &CudaSlice<u32>,
2597 base: usize,
2598 t_v: usize,
2599 d_conv: usize,
2600 ) -> Result<(), Box<dyn std::error::Error>> {
2601 let f = self.func("ssm_conv_ring_rebuild_f32_dc");
2602 let n = conv_dim * (d_conv - 1);
2603 let cfg = LaunchConfig::for_num_elems(n as u32);
2604 let (cd, b0, tv, dc) = (conv_dim as i32, base as i32, t_v as i32, d_conv as i32);
2605 let __s_b = self.gpu.stream();
2606 let mut b = __s_b.launch_builder(&f);
2607 b.arg(qkv_tm)
2608 .arg(ring_old)
2609 .arg(conv_state)
2610 .arg(&cd)
2611 .arg(acc)
2612 .arg(&b0)
2613 .arg(&tv)
2614 .arg(&dc);
2615 unsafe {
2616 b.launch(cfg)?;
2617 }
2618 Ok(())
2619 }
2620 #[allow(clippy::too_many_arguments)]
2621 pub fn gdn_scan_s128_dc(
2622 &self,
2623 q: &CudaSlice<f32>,
2624 k: &CudaSlice<f32>,
2625 v: &CudaSlice<f32>,
2626 g: &CudaSlice<f32>,
2627 beta: &CudaSlice<f32>,
2628 state_in: &CudaSlice<f32>,
2629 state_out: &mut CudaSlice<f32>,
2630 o: &mut CudaSlice<f32>,
2631 n_head: usize,
2632 acc: &CudaSlice<u32>,
2633 base: usize,
2634 t_v: usize,
2635 scale: f32,
2636 ) -> Result<(), Box<dyn std::error::Error>> {
2637 let f = self.func("gdn_scan_s128_dc");
2638 const S_V: u32 = 128;
2639 const WARP: u32 = 32;
2640 const COLS_PER_BLOCK: u32 = 4;
2641 let cfg = LaunchConfig {
2642 grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
2643 block_dim: (WARP, COLS_PER_BLOCK, 1),
2644 shared_mem_bytes: 0,
2645 };
2646 let (h, b0, tv) = (n_head as i32, base as i32, t_v as i32);
2647 let __s_b = self.gpu.stream();
2648 let mut b = __s_b.launch_builder(&f);
2649 b.arg(q)
2650 .arg(k)
2651 .arg(v)
2652 .arg(g)
2653 .arg(beta)
2654 .arg(state_in)
2655 .arg(state_out)
2656 .arg(o)
2657 .arg(&h)
2658 .arg(acc)
2659 .arg(&b0)
2660 .arg(&tv)
2661 .arg(&scale);
2662 unsafe {
2663 b.launch(cfg)?;
2664 }
2665 Ok(())
2666 }
2667
2668 pub fn spec_rollback_kv(
2670 &self,
2671 len_ptrs: &CudaSlice<u64>,
2672 saved: &CudaSlice<i32>,
2673 acc: &CudaSlice<u32>,
2674 base: usize,
2675 n_layer: usize,
2676 ) -> Result<(), Box<dyn std::error::Error>> {
2677 let f = self.func("spec_rollback_kv");
2678 let (b, nl) = (base as i32, n_layer as i32);
2679 let cfg = LaunchConfig {
2680 grid_dim: (n_layer.div_ceil(64) as u32, 1, 1),
2681 block_dim: (64, 1, 1),
2682 shared_mem_bytes: 0,
2683 };
2684 let __s_bl = self.gpu.stream();
2685 let mut bl = __s_bl.launch_builder(&f);
2686 bl.arg(len_ptrs).arg(saved).arg(acc).arg(&b).arg(&nl);
2687 unsafe {
2688 bl.launch(cfg)?;
2689 }
2690 Ok(())
2691 }
2692
2693 pub fn spec_fork_valid(
2695 &self,
2696 acc: &CudaSlice<u32>,
2697 optimistic_pending: u32,
2698 valid: &mut CudaSlice<u32>,
2699 ) -> Result<(), Box<dyn std::error::Error>> {
2700 let f = self.func("spec_fork_valid");
2701 let cfg = LaunchConfig {
2702 grid_dim: (1, 1, 1),
2703 block_dim: (1, 1, 1),
2704 shared_mem_bytes: 0,
2705 };
2706 let __s_bl = self.gpu.stream();
2707 let mut bl = __s_bl.launch_builder(&f);
2708 bl.arg(acc).arg(&optimistic_pending).arg(valid);
2709 unsafe {
2710 bl.launch(cfg)?;
2711 }
2712 Ok(())
2713 }
2714
2715 pub fn spec_fork_reconcile_kv(
2717 &self,
2718 len_ptrs: &CudaSlice<u64>,
2719 saved: &CudaSlice<i32>,
2720 acc: &CudaSlice<u32>,
2721 valid: &CudaSlice<u32>,
2722 base: usize,
2723 n_layer: usize,
2724 ) -> Result<(), Box<dyn std::error::Error>> {
2725 let f = self.func("spec_fork_reconcile_kv");
2726 let (b, nl) = (base as i32, n_layer as i32);
2727 let cfg = LaunchConfig {
2728 grid_dim: (n_layer.div_ceil(64) as u32, 1, 1),
2729 block_dim: (64, 1, 1),
2730 shared_mem_bytes: 0,
2731 };
2732 let __s_bl = self.gpu.stream();
2733 let mut bl = __s_bl.launch_builder(&f);
2734 bl.arg(len_ptrs)
2735 .arg(saved)
2736 .arg(acc)
2737 .arg(valid)
2738 .arg(&b)
2739 .arg(&nl);
2740 unsafe {
2741 bl.launch(cfg)?;
2742 }
2743 Ok(())
2744 }
2745
2746 pub fn spec_fork_restore_f32(
2748 &self,
2749 snapshot: &CudaSlice<f32>,
2750 state: &mut CudaSlice<f32>,
2751 valid: &CudaSlice<u32>,
2752 ) -> Result<(), Box<dyn std::error::Error>> {
2753 assert_eq!(
2754 snapshot.len(),
2755 state.len(),
2756 "fork recurrent snapshot shape mismatch"
2757 );
2758 let f = self.func("spec_fork_restore_f32");
2759 let n = state.len() as i32;
2760 let blocks = state.len().div_ceil(256).min(65535).max(1) as u32;
2761 let cfg = LaunchConfig {
2762 grid_dim: (blocks, 1, 1),
2763 block_dim: (256, 1, 1),
2764 shared_mem_bytes: 0,
2765 };
2766 let __s_bl = self.gpu.stream();
2767 let mut bl = __s_bl.launch_builder(&f);
2768 bl.arg(snapshot).arg(state).arg(valid).arg(&n);
2769 unsafe {
2770 bl.launch(cfg)?;
2771 }
2772 Ok(())
2773 }
2774
2775 pub fn spec_seed_gather(
2778 &self,
2779 vx: &CudaSlice<f32>,
2780 fill_prev: &CudaSlice<f32>,
2781 acc: &CudaSlice<u32>,
2782 h_seed: &mut CudaSlice<f32>,
2783 base: usize,
2784 n_embd: usize,
2785 ) -> Result<(), Box<dyn std::error::Error>> {
2786 let f = self.func("spec_seed_gather");
2787 let (b, ne) = (base as i32, n_embd as i32);
2788 let cfg = LaunchConfig {
2789 grid_dim: (n_embd.div_ceil(256) as u32, 1, 1),
2790 block_dim: (256, 1, 1),
2791 shared_mem_bytes: 0,
2792 };
2793 let __s_bl = self.gpu.stream();
2794 let mut bl = __s_bl.launch_builder(&f);
2795 bl.arg(vx)
2796 .arg(fill_prev)
2797 .arg(acc)
2798 .arg(h_seed)
2799 .arg(&b)
2800 .arg(&ne);
2801 unsafe {
2802 bl.launch(cfg)?;
2803 }
2804 Ok(())
2805 }
2806
2807 pub fn spec_accept_greedy(
2809 &self,
2810 preds: &CudaSlice<u32>,
2811 draft: &CudaSlice<u32>,
2812 last_pred: u32,
2813 base: usize,
2814 k_round: usize,
2815 out: &mut CudaSlice<u32>,
2816 ) -> Result<(), Box<dyn std::error::Error>> {
2817 let f = self.func("spec_accept_greedy");
2818 let (b, k) = (base as i32, k_round as i32);
2819 let cfg = LaunchConfig {
2820 grid_dim: (1, 1, 1),
2821 block_dim: (32, 1, 1),
2822 shared_mem_bytes: 0,
2823 };
2824 let __s_bl = self.gpu.stream();
2825 let mut bl = __s_bl.launch_builder(&f);
2826 bl.arg(preds)
2827 .arg(draft)
2828 .arg(&last_pred)
2829 .arg(&b)
2830 .arg(&k)
2831 .arg(out);
2832 unsafe {
2833 bl.launch(cfg)?;
2834 }
2835 Ok(())
2836 }
2837
2838 pub fn gumbel_perturb(
2845 &self,
2846 x: &CudaSlice<f32>,
2847 y: &mut CudaSlice<f32>,
2848 n: usize,
2849 seed: u64,
2850 stream_pos: u32,
2851 temp: f32,
2852 ) -> Result<(), Box<dyn std::error::Error>> {
2853 let f = self.func("gumbel_perturb_f32");
2854 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2855 let cfg = LaunchConfig {
2856 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2857 block_dim: (256, 1, 1),
2858 shared_mem_bytes: 0,
2859 };
2860 let __s_b = self.gpu.stream();
2861 let mut b = __s_b.launch_builder(&f);
2862 b.arg(x)
2863 .arg(&mut *y)
2864 .arg(&ni)
2865 .arg(&slo)
2866 .arg(&shi)
2867 .arg(&stream_pos)
2868 .arg(&temp);
2869 unsafe {
2870 b.launch(cfg)?;
2871 }
2872 Ok(())
2873 }
2874
2875 pub fn mask_logits_col(
2883 &self,
2884 logits: &mut CudaSlice<f32>,
2885 mask: &CudaSlice<u32>,
2886 col: usize,
2887 n: usize,
2888 mask_words: usize,
2889 ) -> Result<(), Box<dyn std::error::Error>> {
2890 let f = self.func("mask_logits_f32");
2891 let (ci, ni, mw) = (col as i32, n as i32, mask_words as i32);
2892 let cfg = LaunchConfig {
2893 grid_dim: (n.div_ceil(256).min(1024) as u32, 1, 1),
2894 block_dim: (256, 1, 1),
2895 shared_mem_bytes: 0,
2896 };
2897 let __s_b = self.gpu.stream();
2898 let mut b = __s_b.launch_builder(&f);
2899 b.arg(&mut *logits).arg(mask).arg(&ci).arg(&ni).arg(&mw);
2900 unsafe {
2901 b.launch(cfg)?;
2902 }
2903 Ok(())
2904 }
2905
2906 pub fn gumbel_perturb_col(
2913 &self,
2914 x: &CudaSlice<f32>,
2915 col: usize,
2916 y: &mut CudaSlice<f32>,
2917 n: usize,
2918 seed: u64,
2919 stream_pos: u32,
2920 temp: f32,
2921 ) -> Result<(), Box<dyn std::error::Error>> {
2922 let f = self.func("gumbel_perturb_f32");
2923 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2924 let col_view = x.slice(col * n..(col + 1) * n);
2925 let cfg = LaunchConfig {
2926 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2927 block_dim: (256, 1, 1),
2928 shared_mem_bytes: 0,
2929 };
2930 let __s_b = self.gpu.stream();
2931 let mut b = __s_b.launch_builder(&f);
2932 b.arg(&col_view)
2933 .arg(&mut *y)
2934 .arg(&ni)
2935 .arg(&slo)
2936 .arg(&shi)
2937 .arg(&stream_pos)
2938 .arg(&temp);
2939 unsafe {
2940 b.launch(cfg)?;
2941 }
2942 Ok(())
2943 }
2944
2945 #[allow(clippy::too_many_arguments)]
2951 pub fn gumbel_perturb_filtered_col(
2952 &self,
2953 x: &CudaSlice<f32>,
2954 col: usize,
2955 y: &mut CudaSlice<f32>,
2956 n: usize,
2957 seed: u64,
2958 stream_pos: u32,
2959 temp: f32,
2960 stat_max: &CudaSlice<f32>,
2961 stat_th: &CudaSlice<f32>,
2962 stat_idx: usize,
2963 ) -> Result<(), Box<dyn std::error::Error>> {
2964 let f = self.func("gumbel_perturb_filtered_col_f32");
2965 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2966 let (ci, si) = (col as i32, stat_idx as i32);
2967 let cfg = LaunchConfig {
2968 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2969 block_dim: (256, 1, 1),
2970 shared_mem_bytes: 0,
2971 };
2972 let __s_b = self.gpu.stream();
2973 let mut b = __s_b.launch_builder(&f);
2974 b.arg(x)
2975 .arg(&ci)
2976 .arg(&mut *y)
2977 .arg(&ni)
2978 .arg(&slo)
2979 .arg(&shi)
2980 .arg(&stream_pos)
2981 .arg(&temp)
2982 .arg(stat_max)
2983 .arg(stat_th)
2984 .arg(&si);
2985 unsafe {
2986 b.launch(cfg)?;
2987 }
2988 Ok(())
2989 }
2990
2991 pub fn sctr_inc(&self, ctr: &mut CudaSlice<u32>) -> Result<(), Box<dyn std::error::Error>> {
2996 let f = self.func("memra_sctr_inc");
2997 let cfg = LaunchConfig {
2998 grid_dim: (1, 1, 1),
2999 block_dim: (1, 1, 1),
3000 shared_mem_bytes: 0,
3001 };
3002 let __s_b = self.gpu.stream();
3003 let mut b = __s_b.launch_builder(&f);
3004 b.arg(&mut *ctr);
3005 unsafe {
3006 b.launch(cfg)?;
3007 }
3008 Ok(())
3009 }
3010
3011 pub fn gumbel_perturb_ctr(
3016 &self,
3017 x: &CudaSlice<f32>,
3018 y: &mut CudaSlice<f32>,
3019 n: usize,
3020 seed: u64,
3021 ctr: &CudaSlice<u32>,
3022 temp: f32,
3023 ) -> Result<(), Box<dyn std::error::Error>> {
3024 let f = self.func("gumbel_perturb_ctr_f32");
3025 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3026 let cfg = LaunchConfig {
3027 grid_dim: (n.div_ceil(256) as u32, 1, 1),
3028 block_dim: (256, 1, 1),
3029 shared_mem_bytes: 0,
3030 };
3031 let __s_b = self.gpu.stream();
3032 let mut b = __s_b.launch_builder(&f);
3033 b.arg(x)
3034 .arg(&mut *y)
3035 .arg(&ni)
3036 .arg(&slo)
3037 .arg(&shi)
3038 .arg(ctr)
3039 .arg(&temp);
3040 unsafe {
3041 b.launch(cfg)?;
3042 }
3043 Ok(())
3044 }
3045
3046 pub fn softmax_gather(
3050 &self,
3051 x: &CudaSlice<f32>,
3052 row_stride: usize,
3053 ids: &CudaSlice<u32>,
3054 rows: &CudaSlice<i32>,
3055 out: &mut CudaSlice<f32>,
3056 n: usize,
3057 npair: usize,
3058 temp: f32,
3059 ) -> Result<(), Box<dyn std::error::Error>> {
3060 let f = self.func("softmax_gather_f32");
3061 let (ni, rs) = (n as i32, row_stride as i64);
3062 let np = npair as i32;
3063 let cfg = LaunchConfig {
3064 grid_dim: (npair as u32, 1, 1),
3065 block_dim: (256, 1, 1),
3066 shared_mem_bytes: 0,
3067 };
3068 let __s_b = self.gpu.stream();
3069 let mut b = __s_b.launch_builder(&f);
3070 b.arg(x)
3071 .arg(&rs)
3072 .arg(ids)
3073 .arg(rows)
3074 .arg(&mut *out)
3075 .arg(&ni)
3076 .arg(&np)
3077 .arg(&temp);
3078 unsafe {
3079 b.launch(cfg)?;
3080 }
3081 Ok(())
3082 }
3083
3084 pub fn residual_sample(
3088 &self,
3089 p: &CudaSlice<f32>,
3090 q: Option<&CudaSlice<f32>>,
3091 n: usize,
3092 temp: f32,
3093 seed: u64,
3094 stream_pos: u32,
3095 out_tok: &mut CudaSlice<u32>,
3096 ) -> Result<(), Box<dyn std::error::Error>> {
3097 let f = self.func("residual_sample_f32");
3098 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3099 let nth = 1024u32;
3100 let cfg = LaunchConfig {
3101 grid_dim: (1, 1, 1),
3102 block_dim: (nth, 1, 1),
3103 shared_mem_bytes: 0,
3104 };
3105 let has_q: i32 = q.is_some() as i32;
3106 let qbuf = q.unwrap_or(p); let __s_b = self.gpu.stream();
3108 let mut b = __s_b.launch_builder(&f);
3109 b.arg(p)
3110 .arg(qbuf)
3111 .arg(&has_q)
3112 .arg(&ni)
3113 .arg(&temp)
3114 .arg(&slo)
3115 .arg(&shi)
3116 .arg(&stream_pos)
3117 .arg(&mut *out_tok);
3118 unsafe {
3119 b.launch(cfg)?;
3120 }
3121 Ok(())
3122 }
3123
3124 pub fn with_moe_cache<R>(
3129 &self,
3130 max_block_bytes: usize,
3131 f: impl FnOnce(
3132 &mut crate::moe_cache::MoeSlotCache,
3133 &Engine,
3134 ) -> Result<R, Box<dyn std::error::Error>>,
3135 ) -> Result<R, Box<dyn std::error::Error>> {
3136 let mut guard = self.moe_cache.lock().unwrap();
3137 if guard.is_none() {
3138 *guard = Some(crate::moe_cache::MoeSlotCache::new(self, max_block_bytes)?);
3139 }
3140 let cache = guard.as_mut().unwrap();
3141 f(cache, self)
3142 }
3143
3144 pub fn freeze_moe_cache(&self) {
3147 if let Some(cache) = self.moe_cache.lock().unwrap().as_mut() {
3148 cache.freeze();
3149 }
3150 }
3151
3152 pub fn export_moe_residency(&self) -> Option<Vec<(u16, u8, u16)>> {
3155 self.moe_cache
3156 .lock()
3157 .unwrap()
3158 .as_ref()
3159 .map(crate::moe_cache::MoeSlotCache::export_residency)
3160 }
3161
3162 pub(crate) fn moe_cache_frozen(&self) -> bool {
3163 self.moe_cache
3164 .lock()
3165 .unwrap()
3166 .as_ref()
3167 .is_some_and(crate::moe_cache::MoeSlotCache::is_frozen)
3168 }
3169
3170 pub fn frozen_cpu_experts_prefer_tokenwise_prime(&self) -> bool {
3177 crate::cpu_experts::configured()
3178 && self.moe_cache_frozen()
3179 && std::env::var("MEMRA_CPU_EXPERT_BATCHED_PRIME").as_deref() != Ok("1")
3180 }
3181
3182 pub(crate) fn configure_moe_cache_layout(&self, block_bytes: Vec<usize>) {
3184 assert!(
3185 self.moe_cache.lock().unwrap().is_none(),
3186 "MoE cache layout configured after cache construction"
3187 );
3188 *self.moe_cache_layout.lock().unwrap() = Some(block_bytes);
3189 }
3190
3191 pub(crate) fn moe_cache_layout(&self) -> Option<Vec<usize>> {
3192 self.moe_cache_layout.lock().unwrap().clone()
3193 }
3194
3195 pub fn moe_cache_enabled() -> bool {
3197 std::env::var("MEMRA_MOE_CACHE").as_deref() != Ok("0")
3198 }
3199
3200 pub fn moe_cache_stats(&self) -> Option<(u64, u64, u64, usize)> {
3203 let guard = self.moe_cache.lock().unwrap();
3204 guard
3205 .as_ref()
3206 .map(|c| (c.hits, c.misses, c.staged_bytes, c.n_slots()))
3207 }
3208
3209 pub fn cpu_expert_stats(
3213 &self,
3214 ) -> Option<(u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64)> {
3215 crate::cpu_experts::configured().then(crate::cpu_experts::stats)
3216 }
3217
3218 pub fn cpu_expert_predictor_stats(&self) -> (u64, u64) {
3221 crate::cpu_experts::predictor_stats()
3222 }
3223
3224 pub fn cpu_expert_exposed_wait_ns(&self) -> Option<u64> {
3225 crate::cpu_experts::configured().then(crate::cpu_experts::exposed_wait_ns)
3226 }
3227
3228 pub fn cpu_expert_gpu_residency_stats(&self) -> Option<(u64, u64, u64)> {
3231 crate::cpu_experts::configured().then(crate::cpu_experts::incomplete_gpu_residency_stats)
3232 }
3233
3234 pub fn moe_pread_stats(&self) -> Option<(u64, u64, u64, u64, u64, u64, u64)> {
3237 let guard = self.moe_cache.lock().unwrap();
3238 guard
3239 .as_ref()
3240 .and_then(|cache| cache.pread_stats())
3241 .map(|stats| {
3242 (
3243 stats.reads,
3244 stats.bytes,
3245 stats.read_errors,
3246 stats.short_reads,
3247 stats.fallbacks,
3248 stats.buffer_waits,
3249 stats.ring_full,
3250 )
3251 })
3252 }
3253
3254 pub fn spill_config_fallbacks(&self) -> u64 {
3256 crate::spill_pread::config_fallbacks()
3257 }
3258
3259 pub fn moe_cache_reset_counters(&self) {
3261 if let Some(c) = self.moe_cache.lock().unwrap().as_mut() {
3262 c.reset_counters();
3263 }
3264 }
3265
3266 pub fn htod_bytes(&self, v: &[u8]) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3267 Ok(self.gpu.stream().clone_htod(v)?)
3268 }
3269
3270 pub fn htod_bytes_padded(
3274 &self,
3275 v: &[u8],
3276 pad: usize,
3277 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3278 let mut d = self.alloc_u8_uninit(v.len() + pad)?;
3279 {
3280 let mut view = d.slice_mut(0..v.len());
3281 self.gpu.stream().memcpy_htod(v, &mut view)?;
3282 }
3283 Ok(d)
3284 }
3285
3286 pub fn copy_into(
3288 &self,
3289 dst: &mut CudaSlice<f32>,
3290 off: usize,
3291 src: &CudaSlice<f32>,
3292 len: usize,
3293 ) -> Result<(), Box<dyn std::error::Error>> {
3294 let mut view = dst.slice_mut(off..off + len);
3295 self.gpu
3296 .stream()
3297 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3298 Ok(())
3299 }
3300
3301 pub fn copy_u8_into(
3304 &self,
3305 dst: &mut CudaSlice<u8>,
3306 off: usize,
3307 src: &CudaSlice<u8>,
3308 len: usize,
3309 ) -> Result<(), Box<dyn std::error::Error>> {
3310 let mut view = dst.slice_mut(off..off + len);
3311 self.gpu
3312 .stream()
3313 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3314 Ok(())
3315 }
3316
3317 pub fn copy_u8_range_into(
3319 &self,
3320 dst: &mut CudaSlice<u8>,
3321 dst_off: usize,
3322 src: &CudaSlice<u8>,
3323 src_off: usize,
3324 len: usize,
3325 ) -> Result<(), Box<dyn std::error::Error>> {
3326 let mut dst_view = dst.slice_mut(dst_off..dst_off + len);
3327 self.gpu
3328 .stream()
3329 .memcpy_dtod(&src.slice(src_off..src_off + len), &mut dst_view)?;
3330 Ok(())
3331 }
3332
3333 pub fn prepare_kv_append(
3337 &self,
3338 kv: &mut crate::cache::KvLayer,
3339 retain_from: usize,
3340 append_rows: usize,
3341 ) -> Result<usize, Box<dyn std::error::Error>> {
3342 let Some(plan) = kv
3343 .ring
3344 .as_ref()
3345 .map(|ring| ring.append_plan(kv.len, retain_from, append_rows))
3346 .transpose()?
3347 else {
3348 return Ok(kv.len);
3349 };
3350 match plan {
3351 crate::cache::KvRingAppend::Contiguous { write_row } => Ok(write_row),
3352 crate::cache::KvRingAppend::Rebase {
3353 src_row,
3354 keep_rows,
3355 new_base,
3356 write_row,
3357 } => {
3358 if keep_rows > 0 {
3359 let k_len = keep_rows * kv.k_tok_bytes;
3360 let v_len = keep_rows * kv.v_tok_bytes;
3361 let mut k_tmp = self.alloc_u8_uninit(k_len)?;
3362 let mut v_tmp = self.alloc_u8_uninit(v_len)?;
3363 self.copy_u8_range_into(&mut k_tmp, 0, &kv.k, src_row * kv.k_tok_bytes, k_len)?;
3364 self.copy_u8_range_into(&mut v_tmp, 0, &kv.v, src_row * kv.v_tok_bytes, v_len)?;
3365 self.copy_u8_into(&mut kv.k, 0, &k_tmp, k_len)?;
3366 self.copy_u8_into(&mut kv.v, 0, &v_tmp, v_len)?;
3367 }
3368 kv.ring.as_mut().unwrap().apply_rebase(new_base);
3369 Ok(write_row)
3370 }
3371 }
3372 }
3373
3374 pub fn htod_u8_into(
3377 &self,
3378 dst: &mut CudaSlice<u8>,
3379 off: usize,
3380 src: &[u8],
3381 ) -> Result<(), Box<dyn std::error::Error>> {
3382 let mut view = dst.slice_mut(off..off + src.len());
3383 self.gpu.stream().memcpy_htod(src, &mut view)?;
3384 Ok(())
3385 }
3386
3387 pub fn view<'a>(&self, b: &'a CudaSlice<f32>, len: usize) -> cudarc::driver::CudaView<'a, f32> {
3388 b.slice(0..len)
3389 }
3390
3391 pub fn view_u8_range<'a>(
3394 &self,
3395 b: &'a CudaSlice<u8>,
3396 start: usize,
3397 end: usize,
3398 ) -> cudarc::driver::CudaView<'a, u8> {
3399 b.slice(start..end)
3400 }
3401 pub fn view_u8<'a>(
3402 &self,
3403 b: &'a CudaSlice<u8>,
3404 len: usize,
3405 ) -> cudarc::driver::CudaView<'a, u8> {
3406 b.slice(0..len)
3407 }
3408
3409 pub fn append_kv_quantized(
3413 &self,
3414 k_row: &CudaSlice<f32>,
3415 v_row: &CudaSlice<f32>,
3416 kc: &mut CudaSlice<u8>,
3417 vc: &mut CudaSlice<u8>,
3418 t: usize,
3419 kv_dim_k: usize,
3420 kv_dim_v: usize,
3421 k_tok_bytes: usize,
3422 v_tok_bytes: usize,
3423 g: bool,
3424 ) -> Result<(), Box<dyn std::error::Error>> {
3425 let f = if g {
3426 self.func_g("append_quantize_kv_q8_0_q5_1")
3427 } else {
3428 self.func("append_quantize_kv_q8_0_q5_1")
3429 };
3430 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3431 let cfg = LaunchConfig {
3432 grid_dim: (nblk, 1, 1),
3433 block_dim: (32, 1, 1),
3434 shared_mem_bytes: 0,
3435 };
3436 let (ti, kdk, kdv) = (t as i32, kv_dim_k as i32, kv_dim_v as i32);
3437 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3438 let __s_b = self.gpu.stream();
3439 let mut b = __s_b.launch_builder(&f);
3440 b.arg(k_row)
3441 .arg(v_row)
3442 .arg(kc)
3443 .arg(vc)
3444 .arg(&ti)
3445 .arg(&kdk)
3446 .arg(&kdv)
3447 .arg(&ktb)
3448 .arg(&vtb);
3449 unsafe {
3450 b.launch(cfg)?;
3451 }
3452 Ok(())
3453 }
3454
3455 pub fn append_kv_quantized_dc(
3459 &self,
3460 k_row: &CudaSlice<f32>,
3461 v_row: &CudaSlice<f32>,
3462 kc: &mut CudaSlice<u8>,
3463 vc: &mut CudaSlice<u8>,
3464 t_dev: &CudaSlice<i32>,
3465 kv_dim_k: usize,
3466 kv_dim_v: usize,
3467 k_tok_bytes: usize,
3468 v_tok_bytes: usize,
3469 g: bool,
3470 ) -> Result<(), Box<dyn std::error::Error>> {
3471 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3472 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
3473 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3474 if Self::pdl_on() && Self::pdl_wb_on() {
3476 use cudarc::driver::{DevicePtr, DevicePtrMut};
3477 let s = &self.gpu.stream();
3478 let (pk, _g0) = k_row.device_ptr(s);
3479 let (pv, _g1) = v_row.device_ptr(s);
3480 let (pkc, _g2) = kc.device_ptr_mut(s);
3481 let (pvc, _g3) = vc.device_ptr_mut(s);
3482 let (pt, _g4) = t_dev.device_ptr(s);
3483 let mut ps = [
3484 &pk as *const _ as *mut std::ffi::c_void,
3485 &pv as *const _ as *mut _,
3486 &pkc as *const _ as *mut _,
3487 &pvc as *const _ as *mut _,
3488 &pt as *const _ as *mut _,
3489 &kdk as *const _ as *mut _,
3490 &kdv as *const _ as *mut _,
3491 &ktb as *const _ as *mut _,
3492 &vtb as *const _ as *mut _,
3493 ];
3494 unsafe {
3495 self.launch_pdl_flash(
3496 g,
3497 "append_quantize_kv_q8_0_q5_1_dc",
3498 (nblk, 1, 1),
3499 (32, 1, 1),
3500 0,
3501 &mut ps,
3502 )?;
3503 }
3504 return Ok(());
3505 }
3506 let f = if g {
3507 self.func_g("append_quantize_kv_q8_0_q5_1_dc")
3508 } else {
3509 self.func("append_quantize_kv_q8_0_q5_1_dc")
3510 };
3511 let cfg = LaunchConfig {
3512 grid_dim: (nblk, 1, 1),
3513 block_dim: (32, 1, 1),
3514 shared_mem_bytes: 0,
3515 };
3516 let __s_b = self.gpu.stream();
3517 let mut b = __s_b.launch_builder(&f);
3518 b.arg(k_row)
3519 .arg(v_row)
3520 .arg(kc)
3521 .arg(vc)
3522 .arg(t_dev)
3523 .arg(&kdk)
3524 .arg(&kdv)
3525 .arg(&ktb)
3526 .arg(&vtb);
3527 unsafe {
3528 b.launch(cfg)?;
3529 }
3530 Ok(())
3531 }
3532
3533 #[allow(clippy::too_many_arguments)]
3540 pub fn append_kv_quantized_rows(
3541 &self,
3542 k_rows: &CudaSlice<f32>,
3543 v_rows: &CudaSlice<f32>,
3544 kc: &mut CudaSlice<u8>,
3545 vc: &mut CudaSlice<u8>,
3546 t0: usize,
3547 t: usize,
3548 kv_dim_k: usize,
3549 kv_dim_v: usize,
3550 k_tok_bytes: usize,
3551 v_tok_bytes: usize,
3552 g: bool,
3553 ) -> Result<(), Box<dyn std::error::Error>> {
3554 if std::env::var("MEMRA_PRIME_APPEND_LOOP").is_ok() {
3555 for i in 0..t {
3556 let k_row = k_rows.slice(i * kv_dim_k..(i + 1) * kv_dim_k);
3557 let v_row = v_rows.slice(i * kv_dim_v..(i + 1) * kv_dim_v);
3558 self.append_kv_quantized_view(
3559 &k_row,
3560 &v_row,
3561 kc,
3562 vc,
3563 t0 + i,
3564 kv_dim_k,
3565 kv_dim_v,
3566 k_tok_bytes,
3567 v_tok_bytes,
3568 g,
3569 )?;
3570 }
3571 return Ok(());
3572 }
3573 let f = if g {
3574 self.func_g("append_quantize_kv_q8_0_q5_1_rows")
3575 } else {
3576 self.func("append_quantize_kv_q8_0_q5_1_rows")
3577 };
3578 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3579 let cfg = LaunchConfig {
3580 grid_dim: (nblk, t as u32, 1),
3581 block_dim: (32, 1, 1),
3582 shared_mem_bytes: 0,
3583 };
3584 let (t0i, kdk, kdv) = (t0 as i32, kv_dim_k as i32, kv_dim_v as i32);
3585 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3586 let __s_b = self.gpu.stream();
3587 let mut b = __s_b.launch_builder(&f);
3588 b.arg(k_rows)
3589 .arg(v_rows)
3590 .arg(kc)
3591 .arg(vc)
3592 .arg(&t0i)
3593 .arg(&kdk)
3594 .arg(&kdv)
3595 .arg(&ktb)
3596 .arg(&vtb);
3597 unsafe {
3598 b.launch(cfg)?;
3599 }
3600 Ok(())
3601 }
3602
3603 pub fn inc_seqlen(&self, p: &mut CudaSlice<i32>) -> Result<(), Box<dyn std::error::Error>> {
3607 let f = self.func("inc_i32");
3608 let cfg = LaunchConfig {
3609 grid_dim: (1, 1, 1),
3610 block_dim: (1, 1, 1),
3611 shared_mem_bytes: 0,
3612 };
3613 let __s_b = self.gpu.stream();
3614 let mut b = __s_b.launch_builder(&f);
3615 b.arg(p);
3616 unsafe {
3617 b.launch(cfg)?;
3618 }
3619 Ok(())
3620 }
3621
3622 pub fn append_kv_quantized_view(
3625 &self,
3626 k_row: &cudarc::driver::CudaView<f32>,
3627 v_row: &cudarc::driver::CudaView<f32>,
3628 kc: &mut CudaSlice<u8>,
3629 vc: &mut CudaSlice<u8>,
3630 t: usize,
3631 kv_dim_k: usize,
3632 kv_dim_v: usize,
3633 k_tok_bytes: usize,
3634 v_tok_bytes: usize,
3635 g: bool,
3636 ) -> Result<(), Box<dyn std::error::Error>> {
3637 let f = if g {
3638 self.func_g("append_quantize_kv_q8_0_q5_1")
3639 } else {
3640 self.func("append_quantize_kv_q8_0_q5_1")
3641 };
3642 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3643 let cfg = LaunchConfig {
3644 grid_dim: (nblk, 1, 1),
3645 block_dim: (32, 1, 1),
3646 shared_mem_bytes: 0,
3647 };
3648 let (ti, kdk, kdv) = (t as i32, kv_dim_k as i32, kv_dim_v as i32);
3649 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3650 let __s_b = self.gpu.stream();
3651 let mut b = __s_b.launch_builder(&f);
3652 b.arg(k_row)
3653 .arg(v_row)
3654 .arg(kc)
3655 .arg(vc)
3656 .arg(&ti)
3657 .arg(&kdk)
3658 .arg(&kdv)
3659 .arg(&ktb)
3660 .arg(&vtb);
3661 unsafe {
3662 b.launch(cfg)?;
3663 }
3664 Ok(())
3665 }
3666
3667 pub fn copy_view_into(
3670 &self,
3671 dst: &mut CudaSlice<f32>,
3672 off: usize,
3673 src: &cudarc::driver::CudaView<f32>,
3674 len: usize,
3675 ) -> Result<(), Box<dyn std::error::Error>> {
3676 let mut view = dst.slice_mut(off..off + len);
3677 self.gpu
3678 .stream()
3679 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3680 Ok(())
3681 }
3682
3683 pub fn clone_dtod(
3687 &self,
3688 src: &CudaSlice<f32>,
3689 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3690 let mut dst = self.gpu.stream().alloc_zeros::<f32>(src.len())?;
3691 self.gpu.stream().memcpy_dtod(src, &mut dst)?;
3692 Ok(dst)
3693 }
3694
3695 pub fn dtod_copy_view(
3698 &self,
3699 src: &cudarc::driver::CudaView<f32>,
3700 dst: &mut CudaSlice<f32>,
3701 ) -> Result<(), Box<dyn std::error::Error>> {
3702 self.gpu.stream().memcpy_dtod(src, dst)?;
3703 Ok(())
3704 }
3705
3706 pub fn dtod_copy_view_i8(
3708 &self,
3709 src: &cudarc::driver::CudaView<i8>,
3710 dst: &mut CudaSlice<i8>,
3711 ) -> Result<(), Box<dyn std::error::Error>> {
3712 self.gpu.stream().memcpy_dtod(src, dst)?;
3713 Ok(())
3714 }
3715
3716 pub fn dtod_copy_into(
3718 &self,
3719 src: &CudaSlice<f32>,
3720 dst: &mut CudaSlice<f32>,
3721 offset: usize,
3722 ) -> Result<(), Box<dyn std::error::Error>> {
3723 let n = src.len();
3724 let mut dv = dst.slice_mut(offset..offset + n);
3725 self.gpu.stream().memcpy_dtod(src, &mut dv)?;
3726 Ok(())
3727 }
3728
3729 pub fn uninit_i8(&self, n: usize) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
3731 self.alloc_uninit::<i8>(n)
3732 }
3733
3734 pub fn qmatvec(
3736 &self,
3737 w: &CudaSlice<u8>,
3738 x: &CudaSlice<f32>,
3739 m: usize,
3740 in_f: usize,
3741 out_f: usize,
3742 qtype: i32,
3743 row_bytes: usize,
3744 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3745 let f = self.func("qmatvec_f32");
3746 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
3748 grid_dim: (out_f as u32, m as u32, 1),
3749 block_dim: (256, 1, 1),
3750 shared_mem_bytes: 0,
3751 };
3752 let (inf, outf, mi, qt, rb) =
3753 (in_f as i32, out_f as i32, m as i32, qtype, row_bytes as i64);
3754 let __s_b = self.gpu.stream();
3755 let mut b = __s_b.launch_builder(&f);
3756 b.arg(w)
3757 .arg(x)
3758 .arg(&mut y)
3759 .arg(&inf)
3760 .arg(&outf)
3761 .arg(&mi)
3762 .arg(&qt)
3763 .arg(&rb);
3764 unsafe {
3765 b.launch(cfg)?;
3766 }
3767 Ok(y)
3768 }
3769
3770 pub fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3772 let s = self.gpu.stream().alloc_zeros::<u8>(n)?;
3773 self.keep_if_capturing(&s);
3774 Ok(s)
3775 }
3776
3777 pub fn alloc_u8_uninit(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3781 let s = unsafe { self.gpu.stream().alloc::<u8>(n)? };
3782 self.keep_if_capturing(&s);
3783 Ok(s)
3784 }
3785
3786 pub fn memset_zeros_view(
3789 &self,
3790 dst: &mut cudarc::driver::CudaViewMut<f32>,
3791 ) -> Result<(), Box<dyn std::error::Error>> {
3792 self.gpu.stream().memset_zeros(dst)?;
3793 Ok(())
3794 }
3795
3796 pub fn stage_expert(
3802 &self,
3803 host_bytes: &[u8],
3804 scratch: &mut CudaSlice<u8>,
3805 off: usize,
3806 ) -> Result<(), Box<dyn std::error::Error>> {
3807 let mut dst = scratch.slice_mut(off..off + host_bytes.len()); self.gpu.stream().memcpy_htod(host_bytes, &mut dst)?; Ok(())
3810 }
3811
3812 pub fn moe_router_topk(
3818 &self,
3819 logits: &CudaSlice<f32>,
3820 t: usize,
3821 n_expert: usize,
3822 n_used: usize,
3823 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
3824 let f = self.func("moe_router_topk_f32");
3825 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 {
3828 grid_dim: (t as u32, 1, 1),
3829 block_dim: (n_expert as u32, 1, 1),
3830 shared_mem_bytes: 0,
3831 };
3832 let (ne, nu) = (n_expert as i32, n_used as i32);
3833 let __s_b = self.gpu.stream();
3834 let mut b = __s_b.launch_builder(&f);
3835 b.arg(logits)
3836 .arg(&mut sel_idx)
3837 .arg(&mut sel_w)
3838 .arg(&ne)
3839 .arg(&nu);
3840 unsafe {
3841 b.launch(cfg)?;
3842 }
3843 Ok((sel_idx, sel_w))
3844 }
3845
3846 pub fn moe_router_topk_scaled(
3849 &self,
3850 logits: &CudaSlice<f32>,
3851 t: usize,
3852 n_expert: usize,
3853 n_used: usize,
3854 ex_scale: &CudaSlice<f32>,
3855 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
3856 let f = self.func("moe_router_topk_scaled_f32");
3861 let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?;
3862 let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?;
3863 let cfg = LaunchConfig {
3864 grid_dim: (t as u32, 1, 1),
3865 block_dim: (n_expert as u32, 1, 1),
3866 shared_mem_bytes: 0,
3867 };
3868 let (ne, nu) = (n_expert as i32, n_used as i32);
3869 let __s_b = self.gpu.stream();
3870 let mut b = __s_b.launch_builder(&f);
3871 b.arg(logits)
3872 .arg(&mut sel_idx)
3873 .arg(&mut sel_w)
3874 .arg(&ne)
3875 .arg(&nu)
3876 .arg(ex_scale);
3877 unsafe {
3878 b.launch(cfg)?;
3879 }
3880 Ok((sel_idx, sel_w))
3881 }
3882
3883 pub fn moe_router_topk_host(
3891 &self,
3892 logits: &CudaSlice<f32>,
3893 t: usize,
3894 n_expert: usize,
3895 n_used: usize,
3896 ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
3897 let f = self.func("moe_router_topk_f32");
3898 let n = t * n_used;
3899 let mut sel_idx = self.alloc_uninit::<i32>(n)?;
3900 let mut sel_w = self.alloc_uninit::<f32>(n)?;
3901 let cfg = LaunchConfig {
3902 grid_dim: (t as u32, 1, 1),
3903 block_dim: (n_expert as u32, 1, 1),
3904 shared_mem_bytes: 0,
3905 };
3906 let (ne, nu) = (n_expert as i32, n_used as i32);
3907 let __s_b = self.gpu.stream();
3908 let mut b = __s_b.launch_builder(&f);
3909 b.arg(logits)
3910 .arg(&mut sel_idx)
3911 .arg(&mut sel_w)
3912 .arg(&ne)
3913 .arg(&nu);
3914 unsafe {
3915 b.launch(cfg)?;
3916 }
3917 let bytes = n * 8;
3919 let mut guard = self.router_stage.lock().unwrap();
3920 if guard.as_ref().map(|p| p.cap < bytes).unwrap_or(true) {
3921 *guard = Some(PinnedStage::new(bytes.max(4096))?);
3922 }
3923 let stage = guard.as_mut().unwrap();
3924 let (si, sw) = unsafe {
3925 (
3926 std::slice::from_raw_parts_mut(stage.ptr as *mut i32, n),
3927 std::slice::from_raw_parts_mut(stage.ptr.add(n * 4) as *mut f32, n),
3928 )
3929 };
3930 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()))
3934 }
3935
3936 #[allow(clippy::too_many_arguments)]
3940 pub fn moe_router_sigmoid_topk(
3941 &self,
3942 logits: &CudaSlice<f32>,
3943 t: usize,
3944 n_expert: usize,
3945 n_used: usize,
3946 active_count: usize,
3947 correction_bias: &CudaSlice<f32>,
3948 active: &CudaSlice<u8>,
3949 scaling_factor: f32,
3950 route_norm: bool,
3951 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
3952 crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
3953 if n_expert == 0 || n_expert > 1024 || n_used == 0 || n_used > n_expert {
3954 return Err(format!(
3955 "sigmoid router shape unsupported: n_expert={n_expert}, n_used={n_used}",
3956 )
3957 .into());
3958 }
3959 if logits.len() < t * n_expert
3960 || correction_bias.len() != n_expert
3961 || active.len() != n_expert
3962 {
3963 return Err(format!(
3964 "sigmoid router buffer mismatch: logits={} bias={} active={} expected logits>={} row={}",
3965 logits.len(), correction_bias.len(), active.len(), t * n_expert, n_expert,
3966 ).into());
3967 }
3968 let f = self.func("moe_router_sigmoid_topk_f32");
3969 let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?;
3970 let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?;
3971 let threads = n_expert.div_ceil(32) * 32;
3972 let cfg = LaunchConfig {
3973 grid_dim: (t as u32, 1, 1),
3974 block_dim: (threads as u32, 1, 1),
3975 shared_mem_bytes: 0,
3976 };
3977 let (ne, nu, rn) = (n_expert as i32, n_used as i32, i32::from(route_norm));
3978 let __s_b = self.gpu.stream();
3979 let mut b = __s_b.launch_builder(&f);
3980 b.arg(logits)
3981 .arg(correction_bias)
3982 .arg(active)
3983 .arg(&mut sel_idx)
3984 .arg(&mut sel_w)
3985 .arg(&ne)
3986 .arg(&nu)
3987 .arg(&scaling_factor)
3988 .arg(&rn);
3989 unsafe {
3990 b.launch(cfg)?;
3991 }
3992 Ok((sel_idx, sel_w))
3993 }
3994
3995 #[allow(clippy::too_many_arguments)]
3998 pub fn moe_router_sigmoid_topk_host(
3999 &self,
4000 logits: &CudaSlice<f32>,
4001 t: usize,
4002 n_expert: usize,
4003 n_used: usize,
4004 active_count: usize,
4005 correction_bias: &CudaSlice<f32>,
4006 active: &CudaSlice<u8>,
4007 scaling_factor: f32,
4008 route_norm: bool,
4009 ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
4010 let (sel_idx, sel_w) = self.moe_router_sigmoid_topk(
4011 logits,
4012 t,
4013 n_expert,
4014 n_used,
4015 active_count,
4016 correction_bias,
4017 active,
4018 scaling_factor,
4019 route_norm,
4020 )?;
4021 let n = t * n_used;
4022 let bytes = n * 8;
4023 let mut guard = self.router_stage.lock().unwrap();
4024 if guard.as_ref().map(|p| p.cap < bytes).unwrap_or(true) {
4025 *guard = Some(PinnedStage::new(bytes.max(4096))?);
4026 }
4027 let stage = guard.as_mut().unwrap();
4028 let (si, sw) = unsafe {
4029 (
4030 std::slice::from_raw_parts_mut(stage.ptr as *mut i32, n),
4031 std::slice::from_raw_parts_mut(stage.ptr.add(n * 4) as *mut f32, n),
4032 )
4033 };
4034 self.gpu.stream().memcpy_dtoh(&sel_idx, si)?;
4035 self.gpu.stream().memcpy_dtoh(&sel_w, sw)?;
4036 self.gpu.stream().synchronize()?;
4037 Ok((si.iter().map(|&i| i as u32).collect(), sw.to_vec()))
4038 }
4039
4040 pub fn stage_expert_async(
4044 &self,
4045 host_bytes: &[u8],
4046 scratch: &mut CudaSlice<u8>,
4047 off: usize,
4048 ) -> Result<cudarc::driver::CudaEvent, Box<dyn std::error::Error>> {
4049 let mut dst = scratch.slice_mut(off..off + host_bytes.len());
4050 self.copy_stream.memcpy_htod(host_bytes, &mut dst)?;
4051 Ok(self.copy_stream.record_event(None)?)
4052 }
4053
4054 pub fn compute_wait(
4056 &self,
4057 ev: &cudarc::driver::CudaEvent,
4058 ) -> Result<(), Box<dyn std::error::Error>> {
4059 self.gpu.stream().wait(ev)?;
4060 Ok(())
4061 }
4062
4063 pub fn qmatvec_view(
4068 &self,
4069 w: &CudaSlice<u8>,
4070 range: std::ops::Range<usize>,
4071 x: &cudarc::driver::CudaView<f32>,
4072 m: usize,
4073 in_f: usize,
4074 out_f: usize,
4075 qtype: i32,
4076 row_bytes: usize,
4077 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4078 let f = self.func("qmatvec_f32");
4079 let wv = w.slice(range); let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
4082 grid_dim: (out_f as u32, m as u32, 1),
4083 block_dim: (256, 1, 1),
4084 shared_mem_bytes: 0,
4085 };
4086 let (inf, outf, mi, qt, rb) =
4087 (in_f as i32, out_f as i32, m as i32, qtype, row_bytes as i64);
4088 let __s_b = self.gpu.stream();
4089 let mut b = __s_b.launch_builder(&f);
4090 b.arg(&wv)
4091 .arg(x)
4092 .arg(&mut y)
4093 .arg(&inf)
4094 .arg(&outf)
4095 .arg(&mi)
4096 .arg(&qt)
4097 .arg(&rb);
4098 unsafe {
4099 b.launch(cfg)?;
4100 }
4101 Ok(y)
4102 }
4103
4104 #[allow(clippy::too_many_arguments)]
4111 pub fn moe_gate_up_silu8_q8(
4115 &self,
4116 gp: WPtr8,
4117 up: WPtr8,
4118 aq: &CudaSlice<i8>,
4119 ad: &CudaSlice<f32>,
4120 in_f: usize,
4121 n_ff: usize,
4122 n_used: usize,
4123 qt_g: i32,
4124 qt_u: i32,
4125 rb_g: usize,
4126 rb_u: usize,
4127 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4128 let f = self.func("moe_gate_up_silu8_q8");
4129 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
4130 let cfg = LaunchConfig {
4131 grid_dim: (n_ff as u32, n_used as u32, 1),
4132 block_dim: (32, 1, 1),
4133 shared_mem_bytes: 0,
4134 };
4135 let (inf, nff, rbg, rbu) = (in_f as i32, n_ff as i32, rb_g as i64, rb_u as i64);
4136 let __s_b = self.gpu.stream();
4137 let mut b = __s_b.launch_builder(&f);
4138 b.arg(&gp)
4139 .arg(&up)
4140 .arg(aq)
4141 .arg(ad)
4142 .arg(&mut act)
4143 .arg(&inf)
4144 .arg(&nff)
4145 .arg(&qt_g)
4146 .arg(&qt_u)
4147 .arg(&rbg)
4148 .arg(&rbu);
4149 unsafe {
4150 b.launch(cfg)?;
4151 }
4152 Ok(act)
4153 }
4154
4155 #[allow(clippy::too_many_arguments)]
4156 pub fn moe_down8_fma_q8(
4157 &self,
4158 dp: WPtr8,
4159 w: F32x8,
4160 aq2: &CudaSlice<i8>,
4161 ad2: &CudaSlice<f32>,
4162 dst: &mut cudarc::driver::CudaViewMut<f32>,
4163 in_f: usize,
4164 out_f: usize,
4165 n_used: usize,
4166 qt: i32,
4167 rb: usize,
4168 ) -> Result<(), Box<dyn std::error::Error>> {
4169 let f = self.func("moe_down8_fma_q8");
4170 let cfg = LaunchConfig {
4171 grid_dim: (out_f as u32, 1, 1),
4172 block_dim: (32, 1, 1),
4173 shared_mem_bytes: 0,
4174 };
4175 let (inf, outf, nu, rbi) = (in_f as i32, out_f as i32, n_used as i32, rb as i64);
4176 let __s_b = self.gpu.stream();
4177 let mut b = __s_b.launch_builder(&f);
4178 b.arg(&dp)
4179 .arg(&w)
4180 .arg(aq2)
4181 .arg(ad2)
4182 .arg(dst)
4183 .arg(&inf)
4184 .arg(&outf)
4185 .arg(&nu)
4186 .arg(&qt)
4187 .arg(&rbi);
4188 unsafe {
4189 b.launch(cfg)?;
4190 }
4191 Ok(())
4192 }
4193
4194 pub fn qmatvec_expert_q8(
4196 &self,
4197 w: &CudaSlice<u8>,
4198 range: std::ops::Range<usize>,
4199 aq: &CudaSlice<i8>,
4200 ad: &CudaSlice<f32>,
4201 m: usize,
4202 in_f: usize,
4203 out_f: usize,
4204 qtype: i32,
4205 row_bytes: usize,
4206 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4207 let f = self.func("qmatvec_expert_q8");
4208 let wv = w.slice(range);
4209 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
4210 const ROWS: u32 = 4; let cfg = LaunchConfig {
4212 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, m as u32, 1),
4213 block_dim: (32, ROWS, 1),
4214 shared_mem_bytes: 0,
4215 };
4216 let (inf, outf, mi, rbi) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
4217 let __s_b = self.gpu.stream();
4218 let mut b = __s_b.launch_builder(&f);
4219 b.arg(&wv)
4220 .arg(aq)
4221 .arg(ad)
4222 .arg(&mut y)
4223 .arg(&inf)
4224 .arg(&outf)
4225 .arg(&mi)
4226 .arg(&qtype)
4227 .arg(&rbi);
4228 unsafe {
4229 b.launch(cfg)?;
4230 }
4231 Ok(y)
4232 }
4233
4234 pub fn moe_gate_up_silu8(
4235 &self,
4236 gp: WPtr8,
4237 up: WPtr8,
4238 x: &cudarc::driver::CudaView<f32>,
4239 in_f: usize,
4240 n_ff: usize,
4241 n_used: usize,
4242 qt_g: i32,
4243 qt_u: i32,
4244 rb_g: usize,
4245 rb_u: usize,
4246 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4247 let f = self.func("moe_gate_up_silu8_f32");
4248 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?; let cfg = LaunchConfig {
4250 grid_dim: (n_ff as u32, n_used as u32, 1),
4251 block_dim: (256, 1, 1),
4252 shared_mem_bytes: 0,
4253 };
4254 let (inf, nff, rbg, rbu) = (in_f as i32, n_ff as i32, rb_g as i64, rb_u as i64);
4255 let __s_b = self.gpu.stream();
4256 let mut b = __s_b.launch_builder(&f);
4257 b.arg(&gp)
4258 .arg(&up)
4259 .arg(x)
4260 .arg(&mut act)
4261 .arg(&inf)
4262 .arg(&nff)
4263 .arg(&qt_g)
4264 .arg(&qt_u)
4265 .arg(&rbg)
4266 .arg(&rbu);
4267 unsafe {
4268 b.launch(cfg)?;
4269 }
4270 Ok(act)
4271 }
4272
4273 #[allow(clippy::too_many_arguments)]
4279 pub fn moe_down8_fma_into(
4280 &self,
4281 dp: WPtr8,
4282 w: F32x8,
4283 act: &CudaSlice<f32>,
4284 dst: &mut cudarc::driver::CudaViewMut<f32>,
4285 in_f: usize,
4286 out_f: usize,
4287 n_used: usize,
4288 qt: i32,
4289 rb: usize,
4290 ) -> Result<(), Box<dyn std::error::Error>> {
4291 let f = self.func("moe_down8_fma_f32");
4292 let cfg = LaunchConfig {
4293 grid_dim: (out_f as u32, 1, 1),
4294 block_dim: (256, 1, 1),
4295 shared_mem_bytes: 0,
4296 };
4297 let (inf, outf, nu, rbv) = (in_f as i32, out_f as i32, n_used as i32, rb as i64);
4298 let __s_b = self.gpu.stream();
4299 let mut b = __s_b.launch_builder(&f);
4300 b.arg(&dp)
4301 .arg(&w)
4302 .arg(act)
4303 .arg(dst)
4304 .arg(&inf)
4305 .arg(&outf)
4306 .arg(&nu)
4307 .arg(&qt)
4308 .arg(&rbv);
4309 unsafe {
4310 b.launch(cfg)?;
4311 }
4312 Ok(())
4313 }
4314
4315 #[allow(clippy::too_many_arguments)]
4320 #[allow(clippy::too_many_arguments)]
4335 #[allow(clippy::too_many_arguments)]
4337 pub fn moe_pairs_matvec_q8(
4338 &self,
4339 table: &CudaSlice<u64>,
4340 proj: i32,
4341 pair_tok: &CudaSlice<i32>,
4342 pair_ex: &CudaSlice<i32>,
4343 aq: &CudaSlice<i8>,
4344 ad: &CudaSlice<f32>,
4345 in_f: usize,
4346 out_f: usize,
4347 n_expert: usize,
4348 n_pairs: usize,
4349 qtype: i32,
4350 row_bytes: usize,
4351 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4352 let f = self.func("moe_pairs_matvec_q8");
4353 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4354 const ROWS: u32 = 4;
4355 let cfg = LaunchConfig {
4356 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_pairs as u32, 1),
4357 block_dim: (32, ROWS, 1),
4358 shared_mem_bytes: 0,
4359 };
4360 let (inf, outf, ne, np, rbi) = (
4361 in_f as i32,
4362 out_f as i32,
4363 n_expert as i32,
4364 n_pairs as i32,
4365 row_bytes as i64,
4366 );
4367 let __s_b = self.gpu.stream();
4368 let mut b = __s_b.launch_builder(&f);
4369 b.arg(table)
4370 .arg(&proj)
4371 .arg(pair_tok)
4372 .arg(pair_ex)
4373 .arg(aq)
4374 .arg(ad)
4375 .arg(&mut y)
4376 .arg(&inf)
4377 .arg(&outf)
4378 .arg(&ne)
4379 .arg(&np)
4380 .arg(&qtype)
4381 .arg(&rbi);
4382 unsafe {
4383 b.launch(cfg)?;
4384 }
4385 Ok(y)
4386 }
4387
4388 #[allow(clippy::too_many_arguments)]
4390 pub fn moe_pairs_matvec_q8_em(
4391 &self,
4392 table: &CudaSlice<u64>,
4393 proj: i32,
4394 ex_ids: &CudaSlice<i32>,
4395 ex_off: &CudaSlice<i32>,
4396 ex_pairs: &CudaSlice<i32>,
4397 pair_tok: &CudaSlice<i32>,
4398 aq: &CudaSlice<i8>,
4399 ad: &CudaSlice<f32>,
4400 in_f: usize,
4401 out_f: usize,
4402 n_expert: usize,
4403 n_active: usize,
4404 n_pairs: usize,
4405 qtype: i32,
4406 row_bytes: usize,
4407 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4408 let f = self.func("moe_pairs_matvec_q8_em");
4409 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4410 const ROWS: u32 = 4;
4411 let cfg = LaunchConfig {
4412 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_active as u32, 1),
4413 block_dim: (32, ROWS, 1),
4414 shared_mem_bytes: 0,
4415 };
4416 let (inf, outf, ne, na, rbi) = (
4417 in_f as i32,
4418 out_f as i32,
4419 n_expert as i32,
4420 n_active as i32,
4421 row_bytes as i64,
4422 );
4423 let __s_b = self.gpu.stream();
4424 let mut b = __s_b.launch_builder(&f);
4425 b.arg(table)
4426 .arg(&proj)
4427 .arg(ex_ids)
4428 .arg(ex_off)
4429 .arg(ex_pairs)
4430 .arg(pair_tok)
4431 .arg(aq)
4432 .arg(ad)
4433 .arg(&mut y)
4434 .arg(&inf)
4435 .arg(&outf)
4436 .arg(&ne)
4437 .arg(&na)
4438 .arg(&qtype)
4439 .arg(&rbi);
4440 unsafe {
4441 b.launch(cfg)?;
4442 }
4443 Ok(y)
4444 }
4445
4446 #[allow(clippy::too_many_arguments)]
4449 pub fn moe_pairs_matvec_q8_dec(
4450 &self,
4451 table: &CudaSlice<u64>,
4452 proj: i32,
4453 ex_ids: &CudaSlice<i32>,
4454 ex_off: &CudaSlice<i32>,
4455 ex_pairs: &CudaSlice<i32>,
4456 pair_tok: &CudaSlice<i32>,
4457 aq: &CudaSlice<i8>,
4458 ad: &CudaSlice<f32>,
4459 in_f: usize,
4460 out_f: usize,
4461 n_expert: usize,
4462 n_active: usize,
4463 n_pairs: usize,
4464 qtype: i32,
4465 row_bytes: usize,
4466 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4467 let f = self.func("moe_pairs_matvec_q8_dec");
4468 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4469 const ROWS: u32 = 4;
4470 let cfg = LaunchConfig {
4471 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_active as u32, 1),
4472 block_dim: (32, ROWS, 1),
4473 shared_mem_bytes: 0,
4474 };
4475 let (inf, outf, ne, na, rbi) = (
4476 in_f as i32,
4477 out_f as i32,
4478 n_expert as i32,
4479 n_active as i32,
4480 row_bytes as i64,
4481 );
4482 let __s_b = self.gpu.stream();
4483 let mut b = __s_b.launch_builder(&f);
4484 b.arg(table)
4485 .arg(&proj)
4486 .arg(ex_ids)
4487 .arg(ex_off)
4488 .arg(ex_pairs)
4489 .arg(pair_tok)
4490 .arg(aq)
4491 .arg(ad)
4492 .arg(&mut y)
4493 .arg(&inf)
4494 .arg(&outf)
4495 .arg(&ne)
4496 .arg(&na)
4497 .arg(&qtype)
4498 .arg(&rbi);
4499 unsafe {
4500 b.launch(cfg)?;
4501 }
4502 Ok(y)
4503 }
4504
4505 pub fn moe_pairs_gelu_mul(
4506 &self,
4507 gate: &CudaSlice<f32>,
4508 up: &CudaSlice<f32>,
4509 n: usize,
4510 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4511 let f = self.func("moe_pairs_gelu_mul");
4512 let mut act = self.alloc_uninit::<f32>(n)?;
4513 let cfg = LaunchConfig::for_num_elems(n as u32);
4514 let nl = n as i64;
4515 let __s_b = self.gpu.stream();
4516 let mut b = __s_b.launch_builder(&f);
4517 b.arg(gate).arg(up).arg(&mut act).arg(&nl);
4518 unsafe {
4519 b.launch(cfg)?;
4520 }
4521 Ok(act)
4522 }
4523
4524 pub fn moe_pairs_silu_mul(
4525 &self,
4526 gate: &CudaSlice<f32>,
4527 up: &CudaSlice<f32>,
4528 n: usize,
4529 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4530 let f = self.func("moe_pairs_silu_mul");
4531 let mut act = self.alloc_uninit::<f32>(n)?;
4532 let cfg = LaunchConfig::for_num_elems(n as u32);
4533 let nl = n as i64;
4534 let __s_b = self.gpu.stream();
4535 let mut b = __s_b.launch_builder(&f);
4536 b.arg(gate).arg(up).arg(&mut act).arg(&nl);
4537 unsafe {
4538 b.launch(cfg)?;
4539 }
4540 Ok(act)
4541 }
4542
4543 #[allow(clippy::too_many_arguments)]
4544 pub fn moe_pairs_scatter(
4545 &self,
4546 y_down: &CudaSlice<f32>,
4547 pair_w: &CudaSlice<f32>,
4548 tok_pair_off: &CudaSlice<i32>,
4549 tok_pair_ids: &CudaSlice<i32>,
4550 moe_out: &mut CudaSlice<f32>,
4551 t: usize,
4552 n_embd: usize,
4553 ) -> Result<(), Box<dyn std::error::Error>> {
4554 let f = self.func("moe_pairs_scatter");
4555 let cfg = LaunchConfig {
4556 grid_dim: (((n_embd + 255) / 256) as u32, t as u32, 1),
4557 block_dim: (256, 1, 1),
4558 shared_mem_bytes: 0,
4559 };
4560 let ne = n_embd as i32;
4561 let __s_b = self.gpu.stream();
4562 let mut b = __s_b.launch_builder(&f);
4563 b.arg(y_down)
4564 .arg(pair_w)
4565 .arg(tok_pair_off)
4566 .arg(tok_pair_ids)
4567 .arg(moe_out)
4568 .arg(&ne);
4569 unsafe {
4570 b.launch(cfg)?;
4571 }
4572 Ok(())
4573 }
4574
4575 #[allow(clippy::too_many_arguments)]
4579 pub fn moe_gate_up_gelu8_dev_q8(
4580 &self,
4581 table: &CudaSlice<u64>,
4582 sel: &cudarc::driver::CudaView<i32>,
4583 aq: &CudaSlice<i8>,
4584 ad: &CudaSlice<f32>,
4585 in_f: usize,
4586 n_ff: usize,
4587 n_used: usize,
4588 n_expert: usize,
4589 qt_g: i32,
4590 qt_u: i32,
4591 rb_g: usize,
4592 rb_u: usize,
4593 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4594 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
4595 let (inf, nff, ne, rbg, rbu) = (
4596 in_f as i32,
4597 n_ff as i32,
4598 n_expert as i32,
4599 rb_g as i64,
4600 rb_u as i64,
4601 );
4602 let f = self.func("moe_gate_up_gelu8_dev_q8");
4603 let cfg = LaunchConfig {
4604 grid_dim: (n_ff as u32, n_used as u32, 1),
4605 block_dim: (32, 1, 1),
4606 shared_mem_bytes: 0,
4607 };
4608 let __s_b = self.gpu.stream();
4609 let mut b = __s_b.launch_builder(&f);
4610 b.arg(table)
4611 .arg(sel)
4612 .arg(aq)
4613 .arg(ad)
4614 .arg(&mut act)
4615 .arg(&inf)
4616 .arg(&nff)
4617 .arg(&ne)
4618 .arg(&qt_g)
4619 .arg(&qt_u)
4620 .arg(&rbg)
4621 .arg(&rbu);
4622 unsafe {
4623 b.launch(cfg)?;
4624 }
4625 Ok(act)
4626 }
4627
4628 #[allow(clippy::too_many_arguments)]
4630 pub fn moe_gate_up_gelu8_dev_q8_rows(
4631 &self,
4632 table: &CudaSlice<u64>,
4633 sel: &CudaSlice<i32>,
4634 aq: &CudaSlice<i8>,
4635 ad: &CudaSlice<f32>,
4636 t: usize,
4637 in_f: usize,
4638 n_ff: usize,
4639 n_used: usize,
4640 n_expert: usize,
4641 qt_g: i32,
4642 qt_u: i32,
4643 rb_g: usize,
4644 rb_u: usize,
4645 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4646 let mut act = self.alloc_uninit::<f32>(t * n_used * n_ff)?;
4647 let (inf, nff, ne, rbg, rbu, nu) = (
4648 in_f as i32,
4649 n_ff as i32,
4650 n_expert as i32,
4651 rb_g as i64,
4652 rb_u as i64,
4653 n_used as i32,
4654 );
4655 let f = self.func("moe_gate_up_gelu8_dev_q8_rows");
4656 let cfg = LaunchConfig {
4657 grid_dim: (n_ff as u32, n_used as u32, t as u32),
4658 block_dim: (32, 1, 1),
4659 shared_mem_bytes: 0,
4660 };
4661 let __s_b = self.gpu.stream();
4662 let mut b = __s_b.launch_builder(&f);
4663 b.arg(table)
4664 .arg(sel)
4665 .arg(aq)
4666 .arg(ad)
4667 .arg(&mut act)
4668 .arg(&inf)
4669 .arg(&nff)
4670 .arg(&ne)
4671 .arg(&qt_g)
4672 .arg(&qt_u)
4673 .arg(&rbg)
4674 .arg(&rbu)
4675 .arg(&nu);
4676 unsafe {
4677 b.launch(cfg)?;
4678 }
4679 Ok(act)
4680 }
4681
4682 #[allow(clippy::too_many_arguments)]
4684 pub fn moe_gate_up_gelu8_dev_q8_csr(
4685 &self,
4686 table: &CudaSlice<u64>,
4687 sel: &CudaSlice<i32>,
4688 aq: &CudaSlice<i8>,
4689 ad: &CudaSlice<f32>,
4690 n_pairs: usize,
4691 in_f: usize,
4692 n_ff: usize,
4693 n_used: usize,
4694 n_expert: usize,
4695 qt_g: i32,
4696 qt_u: i32,
4697 rb_g: usize,
4698 rb_u: usize,
4699 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4700 let mut act = self.alloc_uninit::<f32>(n_pairs * n_ff)?;
4701 let (inf, nff, ne, rbg, rbu, nu, npi) = (
4702 in_f as i32,
4703 n_ff as i32,
4704 n_expert as i32,
4705 rb_g as i64,
4706 rb_u as i64,
4707 n_used as i32,
4708 n_pairs as i32,
4709 );
4710 let f = self.func("moe_gate_up_gelu8_dev_q8_csr");
4711 let cfg = LaunchConfig {
4712 grid_dim: (n_ff as u32, n_pairs as u32, 1),
4713 block_dim: (32, 1, 1),
4714 shared_mem_bytes: 0,
4715 };
4716 let __s_b = self.gpu.stream();
4717 let mut b = __s_b.launch_builder(&f);
4718 b.arg(table)
4719 .arg(sel)
4720 .arg(aq)
4721 .arg(ad)
4722 .arg(&mut act)
4723 .arg(&inf)
4724 .arg(&nff)
4725 .arg(&ne)
4726 .arg(&qt_g)
4727 .arg(&qt_u)
4728 .arg(&rbg)
4729 .arg(&rbu)
4730 .arg(&nu)
4731 .arg(&npi);
4732 unsafe {
4733 b.launch(cfg)?;
4734 }
4735 Ok(act)
4736 }
4737
4738 #[allow(clippy::too_many_arguments)]
4740 pub fn moe_down8_fma_dev_q8_rows_g(
4741 &self,
4742 table: &CudaSlice<u64>,
4743 sel: &CudaSlice<i32>,
4744 w: &CudaSlice<f32>,
4745 aq2: &CudaSlice<i8>,
4746 ad2: &CudaSlice<f32>,
4747 dst: &mut CudaSlice<f32>,
4748 t: usize,
4749 in_f: usize,
4750 out_f: usize,
4751 n_used: usize,
4752 n_expert: usize,
4753 qt: i32,
4754 rb: usize,
4755 ) -> Result<(), Box<dyn std::error::Error>> {
4756 let (inf, outf, nu, ne, rbi) = (
4757 in_f as i32,
4758 out_f as i32,
4759 n_used as i32,
4760 n_expert as i32,
4761 rb as i64,
4762 );
4763 let step_b1_w8 = t == 1 && in_f == 1280 && out_f == 4096 && n_used == 8 && qt == QT_IQ4_XS;
4767 let f = self.func(if step_b1_w8 {
4768 "moe_down8_fma_dev_q8_rows_w8"
4769 } else {
4770 "moe_down8_fma_dev_q8_rows_g"
4771 });
4772 let cfg = LaunchConfig {
4773 grid_dim: (out_f as u32, 1, t as u32),
4774 block_dim: (32, if step_b1_w8 { 8 } else { 1 }, 1),
4775 shared_mem_bytes: 0,
4776 };
4777 let __s_b = self.gpu.stream();
4778 let mut b = __s_b.launch_builder(&f);
4779 b.arg(table)
4780 .arg(sel)
4781 .arg(w)
4782 .arg(aq2)
4783 .arg(ad2)
4784 .arg(dst)
4785 .arg(&inf)
4786 .arg(&outf)
4787 .arg(&nu)
4788 .arg(&ne)
4789 .arg(&qt)
4790 .arg(&rbi);
4791 unsafe {
4792 b.launch(cfg)?;
4793 }
4794 Ok(())
4795 }
4796
4797 pub fn rp_probe_q4(&self, m: usize) -> Result<(f64, f64), Box<dyn std::error::Error>> {
4801 let (out_f, in_f) = (2048usize, 2816usize);
4802 let nblk = in_f / 32;
4803 let mut seed = 0x9E3779B97F4A7C15u64;
4804 let mut rng = move || {
4805 seed = seed
4806 .wrapping_mul(6364136223846793005)
4807 .wrapping_add(1442695040888963407);
4808 (seed >> 33) as u8
4809 };
4810 let mut w = vec![0u8; out_f * nblk * 18];
4811 for b in w.iter_mut() {
4812 *b = rng();
4813 }
4814 for r in 0..out_f {
4815 for g in 0..nblk {
4816 let off = (r * nblk + g) * 18;
4817 w[off] = 0x00;
4818 w[off + 1] = 0x2C; }
4820 }
4821 let qplane = out_f * nblk * 16;
4822 let mut wrp = vec![0u8; w.len()];
4823 for r in 0..out_f {
4824 for g in 0..nblk {
4825 let src = &w[(r * nblk + g) * 18..(r * nblk + g) * 18 + 18];
4826 wrp[qplane + (r * nblk + g) * 2..qplane + (r * nblk + g) * 2 + 2]
4827 .copy_from_slice(&src[0..2]);
4828 wrp[(r * nblk + g) * 16..(r * nblk + g) * 16 + 16].copy_from_slice(&src[2..18]);
4829 }
4830 }
4831 let w_d = self.htod_bytes(&w)?;
4832 let wrp_d = self.htod_bytes(&wrp)?;
4833 let mut aq = vec![0i8; m * in_f];
4834 for v in aq.iter_mut() {
4835 *v = rng() as i8;
4836 }
4837 let aq_d = self.htod_i8(&aq)?;
4838 let ad_d = self.htod(&vec![0.03125f32; m * nblk])?;
4839 let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
4840 let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
4841 const RPB: u32 = 4;
4842 let cfg = LaunchConfig {
4843 grid_dim: ((out_f as u32).div_ceil(RPB), 1, 1),
4844 block_dim: (32, RPB, 1),
4845 shared_mem_bytes: 0,
4846 };
4847 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
4848 let (rb, qp) = ((nblk * 18) as i64, qplane as i64);
4849 let fb = self.func("qmatvec_q4_0_mmvq_b4");
4850 let fr = self.func("qmatvec_q4_0_mmvq_b4_rp");
4851 {
4852 let __s_b = self.gpu.stream();
4853 let mut b = __s_b.launch_builder(&fb);
4854 b.arg(&w_d)
4855 .arg(&aq_d)
4856 .arg(&ad_d)
4857 .arg(&mut y0)
4858 .arg(&inf)
4859 .arg(&outf)
4860 .arg(&mi)
4861 .arg(&rb);
4862 unsafe {
4863 b.launch(cfg)?;
4864 }
4865 let __s_b = self.gpu.stream();
4866 let mut b = __s_b.launch_builder(&fr);
4867 b.arg(&wrp_d)
4868 .arg(&aq_d)
4869 .arg(&ad_d)
4870 .arg(&mut y1)
4871 .arg(&inf)
4872 .arg(&outf)
4873 .arg(&mi)
4874 .arg(&qp);
4875 unsafe {
4876 b.launch(cfg)?;
4877 }
4878 }
4879 self.gpu.stream().synchronize()?;
4880 let (h0, h1) = (self.dtoh(&y0)?, self.dtoh(&y1)?);
4881 let nd = h0
4882 .iter()
4883 .zip(&h1)
4884 .filter(|(a, b)| a.to_bits() != b.to_bits())
4885 .count();
4886 if nd != 0 {
4887 return Err(format!("rp twin not bitwise: {nd}/{} diffs", h0.len()).into());
4888 }
4889 let mut time = |rp: bool| -> Result<f64, Box<dyn std::error::Error>> {
4890 self.gpu.stream().synchronize()?;
4891 let t0 = std::time::Instant::now();
4892 for _ in 0..500 {
4893 if rp {
4894 let __s_b = self.gpu.stream();
4895 let mut b = __s_b.launch_builder(&fr);
4896 b.arg(&wrp_d)
4897 .arg(&aq_d)
4898 .arg(&ad_d)
4899 .arg(&mut y1)
4900 .arg(&inf)
4901 .arg(&outf)
4902 .arg(&mi)
4903 .arg(&qp);
4904 unsafe {
4905 b.launch(cfg)?;
4906 }
4907 } else {
4908 let __s_b = self.gpu.stream();
4909 let mut b = __s_b.launch_builder(&fb);
4910 b.arg(&w_d)
4911 .arg(&aq_d)
4912 .arg(&ad_d)
4913 .arg(&mut y0)
4914 .arg(&inf)
4915 .arg(&outf)
4916 .arg(&mi)
4917 .arg(&rb);
4918 unsafe {
4919 b.launch(cfg)?;
4920 }
4921 }
4922 }
4923 self.gpu.stream().synchronize()?;
4924 Ok(t0.elapsed().as_secs_f64() * 1e6 / 500.0)
4925 };
4926 let _ = time(false)?;
4927 let _ = time(true)?; Ok((time(false)?, time(true)?))
4929 }
4930
4931 pub fn build_q4_rp4(
4936 &self,
4937 t: &mut crate::model::GpuTensor,
4938 ) -> Result<(), Box<dyn std::error::Error>> {
4939 use crate::model::GpuTensor;
4940 let GpuTensor::Quant {
4941 bytes,
4942 qtype,
4943 row_bytes,
4944 ne,
4945 rp4,
4946 ..
4947 } = t
4948 else {
4949 return Ok(());
4950 };
4951 if *qtype != QT_Q4_0 || rp4.is_some() || ne.len() != 2 {
4952 return Ok(());
4953 }
4954 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
4955 if in_f % 32 != 0 || *row_bytes != (in_f / 32) * 18 {
4956 return Ok(());
4957 }
4958 let nblk = in_f / 32;
4959 let mut dst = self.alloc_uninit::<u8>(out_f * nblk * 18)?;
4960 let f = self.func("q4_0_split_rp_build");
4961 let n = (out_f * nblk) as i32;
4962 let cfg = LaunchConfig {
4963 grid_dim: (((out_f * nblk) as u32).div_ceil(256), 1, 1),
4964 block_dim: (256, 1, 1),
4965 shared_mem_bytes: 0,
4966 };
4967 let (of, nb) = (out_f as i32, nblk as i32);
4968 let _ = n;
4969 let __s_b = self.gpu.stream();
4970 let mut b = __s_b.launch_builder(&f);
4971 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
4972 unsafe {
4973 b.launch(cfg)?;
4974 }
4975 *rp4 = Some(dst);
4976 Ok(())
4977 }
4978
4979 pub fn build_q8_rp4(
4984 &self,
4985 t: &mut crate::model::GpuTensor,
4986 ) -> Result<(), Box<dyn std::error::Error>> {
4987 use crate::model::GpuTensor;
4988 let GpuTensor::Quant {
4989 bytes,
4990 qtype,
4991 row_bytes,
4992 ne,
4993 rp4,
4994 ..
4995 } = t
4996 else {
4997 return Ok(());
4998 };
4999 if *qtype != QT_Q8_0 || rp4.is_some() || ne.len() != 2 {
5000 return Ok(());
5001 }
5002 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5003 if in_f % 32 != 0 || *row_bytes != (in_f / 32) * 34 {
5004 return Ok(());
5005 }
5006 *rp4 = Some(self.build_q8_rp4_raw(bytes, in_f, out_f)?);
5007 Ok(())
5008 }
5009
5010 pub fn build_q8_rp4_raw(
5013 &self,
5014 bytes: &CudaSlice<u8>,
5015 in_f: usize,
5016 out_f: usize,
5017 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
5018 assert!(in_f % 32 == 0);
5019 let nblk = in_f / 32;
5020 let mut dst = self.alloc_uninit::<u8>(out_f * nblk * 34)?;
5021 let f = self.func("q8_0_split_rp_build");
5022 let cfg = LaunchConfig {
5023 grid_dim: (((out_f * nblk) as u32).div_ceil(256), 1, 1),
5024 block_dim: (256, 1, 1),
5025 shared_mem_bytes: 0,
5026 };
5027 let (of, nb) = (out_f as i32, nblk as i32);
5028 let __s_b = self.gpu.stream();
5029 let mut b = __s_b.launch_builder(&f);
5030 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
5031 unsafe {
5032 b.launch(cfg)?;
5033 }
5034 Ok(dst)
5035 }
5036
5037 pub fn build_q4k_rp4(
5045 &self,
5046 t: &mut crate::model::GpuTensor,
5047 ) -> Result<(), Box<dyn std::error::Error>> {
5048 use crate::model::GpuTensor;
5049 let GpuTensor::Quant {
5050 bytes,
5051 qtype,
5052 row_bytes,
5053 ne,
5054 rp4,
5055 ..
5056 } = t
5057 else {
5058 return Ok(());
5059 };
5060 if *qtype != QT_Q4_K || rp4.is_some() || ne.len() != 2 {
5061 return Ok(());
5062 }
5063 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5064 if in_f % 256 != 0 || *row_bytes != (in_f / 256) * 144 {
5065 return Ok(());
5066 }
5067 *rp4 = Some(self.build_kq_rp4_raw(bytes, in_f, out_f, QT_Q4_K)?);
5068 Ok(())
5069 }
5070
5071 pub fn build_q6k_rp4(
5072 &self,
5073 t: &mut crate::model::GpuTensor,
5074 ) -> Result<(), Box<dyn std::error::Error>> {
5075 use crate::model::GpuTensor;
5076 let GpuTensor::Quant {
5077 bytes,
5078 qtype,
5079 row_bytes,
5080 ne,
5081 rp4,
5082 ..
5083 } = t
5084 else {
5085 return Ok(());
5086 };
5087 if *qtype != QT_Q6_K || rp4.is_some() || ne.len() != 2 {
5088 return Ok(());
5089 }
5090 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5091 if in_f % 256 != 0 || *row_bytes != (in_f / 256) * 210 {
5092 return Ok(());
5093 }
5094 *rp4 = Some(self.build_kq_rp4_raw(bytes, in_f, out_f, QT_Q6_K)?);
5095 Ok(())
5096 }
5097
5098 pub fn build_kq_rp4_raw(
5100 &self,
5101 bytes: &CudaSlice<u8>,
5102 in_f: usize,
5103 out_f: usize,
5104 qtype: i32,
5105 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
5106 assert!(in_f % 256 == 0);
5107 let nsbk = in_f / 256;
5108 let (sb_bytes, kname) = match qtype {
5109 QT_Q4_K => (144usize, "q4_K_split_rp_build"),
5110 QT_Q6_K => (210usize, "q6_K_split_rp_build"),
5111 _ => return Err(format!("build_kq_rp4_raw: qtype {qtype} has no rp mirror").into()),
5112 };
5113 let mut dst = self.alloc_uninit::<u8>(out_f * nsbk * sb_bytes)?;
5114 let f = self.func(kname);
5115 let cfg = LaunchConfig {
5116 grid_dim: (((out_f * nsbk) as u32).div_ceil(256), 1, 1),
5117 block_dim: (256, 1, 1),
5118 shared_mem_bytes: 0,
5119 };
5120 let (of, nb) = (out_f as i32, nsbk as i32);
5121 let __s_b = self.gpu.stream();
5122 let mut b = __s_b.launch_builder(&f);
5123 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
5124 unsafe {
5125 b.launch(cfg)?;
5126 }
5127 Ok(dst)
5128 }
5129
5130 pub fn kqrp_enabled() -> bool {
5134 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5135 *ON.get_or_init(|| match std::env::var("MEMRA_KQRP").as_deref() {
5136 Ok("0") => false,
5137 Ok(_) => true,
5138 Err(_) => cfg!(memra_hopper_mma),
5139 })
5140 }
5141
5142 pub fn build_q4_rp_swap(
5148 &self,
5149 t: &mut crate::model::GpuTensor,
5150 ) -> Result<bool, Box<dyn std::error::Error>> {
5151 self.build_q4_rp4(t)?;
5152 self.gpu.stream().synchronize()?; use crate::model::GpuTensor;
5154 let GpuTensor::Quant { bytes, rp4, rp, .. } = t else {
5155 return Ok(false);
5156 };
5157 match rp4.take() {
5158 Some(split) => {
5159 *bytes = split; *rp = true;
5161 Ok(true)
5162 }
5163 None => Ok(false),
5164 }
5165 }
5166
5167 pub fn q4rp_enabled() -> bool {
5169 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5170 *ON.get_or_init(|| {
5171 std::env::var("MEMRA_Q4RP")
5172 .map(|v| v != "0")
5173 .unwrap_or(true)
5174 })
5175 }
5176
5177 pub fn copy_rows_strided(
5180 &self,
5181 src: &CudaSlice<f32>,
5182 dst: &mut CudaSlice<f32>,
5183 row_elems: usize,
5184 n_rows: usize,
5185 src_stride: usize,
5186 src_off: usize,
5187 ) -> Result<(), Box<dyn std::error::Error>> {
5188 let f = self.func("copy_rows_strided_f32");
5189 let cfg = LaunchConfig {
5190 grid_dim: (((row_elems as u32 + 255) / 256).max(1), n_rows as u32, 1),
5191 block_dim: (256, 1, 1),
5192 shared_mem_bytes: 0,
5193 };
5194 let (re, nr) = (row_elems as i32, n_rows as i32);
5195 let (st, off) = (src_stride as i64, src_off as i64);
5196 let __s_b = self.gpu.stream();
5197 let mut b = __s_b.launch_builder(&f);
5198 b.arg(src)
5199 .arg(&mut *dst)
5200 .arg(&re)
5201 .arg(&nr)
5202 .arg(&st)
5203 .arg(&off);
5204 unsafe {
5205 b.launch(cfg)?;
5206 }
5207 Ok(())
5208 }
5209
5210 pub fn u32_set_k(
5212 &self,
5213 dst: &mut CudaSlice<u32>,
5214 v: u32,
5215 idx: usize,
5216 ) -> Result<(), Box<dyn std::error::Error>> {
5217 let f = self.func("u32_set_k");
5218 let cfg = LaunchConfig {
5219 grid_dim: (1, 1, 1),
5220 block_dim: (1, 1, 1),
5221 shared_mem_bytes: 0,
5222 };
5223 let ii = idx as i32;
5224 let __s_b = self.gpu.stream();
5225 let mut b = __s_b.launch_builder(&f);
5226 b.arg(dst).arg(&v).arg(&ii);
5227 unsafe {
5228 b.launch(cfg)?;
5229 }
5230 Ok(())
5231 }
5232
5233 pub fn i32_add_k(
5235 &self,
5236 d: &mut CudaSlice<i32>,
5237 v: i32,
5238 ) -> Result<(), Box<dyn std::error::Error>> {
5239 let f = self.func("i32_add_k");
5240 let cfg = LaunchConfig {
5241 grid_dim: (1, 1, 1),
5242 block_dim: (32, 1, 1),
5243 shared_mem_bytes: 0,
5244 };
5245 let __s_b = self.gpu.stream();
5246 let mut b = __s_b.launch_builder(&f);
5247 b.arg(d).arg(&v);
5248 unsafe {
5249 b.launch(cfg)?;
5250 }
5251 Ok(())
5252 }
5253
5254 pub fn i32_iota_from(
5256 &self,
5257 ctr: &CudaSlice<i32>,
5258 dst: &mut CudaSlice<i32>,
5259 n: usize,
5260 ) -> Result<(), Box<dyn std::error::Error>> {
5261 let f = self.func("i32_iota_from");
5262 let cfg = LaunchConfig::for_num_elems(n as u32);
5263 let ni = n as i32;
5264 let __s_b = self.gpu.stream();
5265 let mut b = __s_b.launch_builder(&f);
5266 b.arg(ctr).arg(dst).arg(&ni);
5267 unsafe {
5268 b.launch(cfg)?;
5269 }
5270 Ok(())
5271 }
5272
5273 pub fn u32_map_k(
5275 &self,
5276 buf: &mut CudaSlice<u32>,
5277 map: &CudaSlice<u32>,
5278 idx: usize,
5279 ) -> Result<(), Box<dyn std::error::Error>> {
5280 let f = self.func("u32_map_k");
5281 let cfg = LaunchConfig {
5282 grid_dim: (1, 1, 1),
5283 block_dim: (1, 1, 1),
5284 shared_mem_bytes: 0,
5285 };
5286 let ii = idx as i32;
5287 let __s_b = self.gpu.stream();
5288 let mut b = __s_b.launch_builder(&f);
5289 b.arg(buf).arg(map).arg(&ii);
5290 unsafe {
5291 b.launch(cfg)?;
5292 }
5293 Ok(())
5294 }
5295
5296 #[allow(clippy::too_many_arguments)]
5298 pub fn u32_pack2(
5299 &self,
5300 a: &CudaSlice<u32>,
5301 off_a: usize,
5302 n1: usize,
5303 b_in: &CudaSlice<u32>,
5304 n2: usize,
5305 out: &mut CudaSlice<u32>,
5306 ) -> Result<(), Box<dyn std::error::Error>> {
5307 let f = self.func("u32_pack2");
5308 let cfg = LaunchConfig::for_num_elems((n1 + n2) as u32);
5309 let (oa, i1, i2) = (off_a as i32, n1 as i32, n2 as i32);
5310 let __s_b = self.gpu.stream();
5311 let mut b = __s_b.launch_builder(&f);
5312 b.arg(a).arg(&oa).arg(&i1).arg(b_in).arg(&i2).arg(out);
5313 unsafe {
5314 b.launch(cfg)?;
5315 }
5316 Ok(())
5317 }
5318
5319 pub fn moe_w_exscale(
5321 &self,
5322 w: &mut CudaSlice<f32>,
5323 sel: &CudaSlice<i32>,
5324 s: &CudaSlice<f32>,
5325 n: usize,
5326 ) -> Result<(), Box<dyn std::error::Error>> {
5327 let f = self.func("moe_w_exscale");
5328 let cfg = LaunchConfig::for_num_elems(n as u32);
5329 let ni = n as i32;
5330 let __s_b = self.gpu.stream();
5331 let mut b = __s_b.launch_builder(&f);
5332 b.arg(w).arg(sel).arg(s).arg(&ni);
5333 unsafe {
5334 b.launch(cfg)?;
5335 }
5336 Ok(())
5337 }
5338
5339 pub fn moe_w_scale_by_expert(
5342 &self,
5343 w: &mut CudaSlice<f32>,
5344 sel: &CudaSlice<i32>,
5345 macros: &CudaSlice<f32>,
5346 n_expert: usize,
5347 n: usize,
5348 ) -> Result<(), Box<dyn std::error::Error>> {
5349 let f = self.func("moe_w_scale_by_expert");
5350 let cfg = LaunchConfig {
5351 grid_dim: (n.div_ceil(64) as u32, 1, 1),
5352 block_dim: (64, 1, 1),
5353 shared_mem_bytes: 0,
5354 };
5355 let (ne, nn) = (n_expert as i32, n as i32);
5356 let __s_b = self.gpu.stream();
5357 let mut b = __s_b.launch_builder(&f);
5358 b.arg(w).arg(sel).arg(macros).arg(&ne).arg(&nn);
5359 unsafe {
5360 b.launch(cfg)?;
5361 }
5362 Ok(())
5363 }
5364
5365 pub fn moe_gate_up_silu8_dev_q8(
5366 &self,
5367 table: &CudaSlice<u64>,
5368 sel: &cudarc::driver::CudaView<i32>,
5369 aq: &CudaSlice<i8>,
5370 ad: &CudaSlice<f32>,
5371 in_f: usize,
5372 n_ff: usize,
5373 n_used: usize,
5374 n_expert: usize,
5375 qt_g: i32,
5376 qt_u: i32,
5377 rb_g: usize,
5378 rb_u: usize,
5379 macros: &CudaSlice<f32>,
5380 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5381 static GU: std::sync::OnceLock<(String, u32)> = std::sync::OnceLock::new();
5382 let (mode, wpb) = GU.get_or_init(|| {
5383 let mode = std::env::var("MEMRA_MOE_DEVQ8_GU").unwrap_or_default();
5384 let wpb = std::env::var("MEMRA_MOE_DEVQ8_WPB")
5385 .ok()
5386 .and_then(|v| v.parse().ok())
5387 .unwrap_or(4u32)
5388 .clamp(1, 16);
5389 (mode, wpb)
5390 });
5391 let (mode, wpb) = (mode.as_str(), *wpb);
5392 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
5393 let (inf, nff, ne, rbg, rbu) = (
5394 in_f as i32,
5395 n_ff as i32,
5396 n_expert as i32,
5397 rb_g as i64,
5398 rb_u as i64,
5399 );
5400 let (f, cfg) = match mode {
5401 "1" | "2" | "4" => {
5402 let rpw: u32 = mode.parse().unwrap();
5403 let f = self.func(match rpw {
5404 1 => "moe_gate_up_silu8_dev_q8_r1",
5405 2 => "moe_gate_up_silu8_dev_q8_r2",
5406 _ => "moe_gate_up_silu8_dev_q8_r4",
5407 });
5408 let rows_per_block = (rpw * wpb) as usize;
5409 let gx = n_ff.div_ceil(rows_per_block) as u32;
5410 (
5411 f,
5412 LaunchConfig {
5413 grid_dim: (gx, n_used as u32, 1),
5414 block_dim: (32, wpb, 1),
5415 shared_mem_bytes: 0,
5416 },
5417 )
5418 }
5419 "j8" if n_used <= 32 => (
5420 self.func("moe_gate_up_silu8_dev_q8_j8"),
5421 LaunchConfig {
5422 grid_dim: (n_ff as u32, 1, 1),
5423 block_dim: (32, n_used as u32, 1),
5424 shared_mem_bytes: 0,
5425 },
5426 ),
5427 "vsm2" => {
5429 let f = self.func("moe_gate_up_silu8_dev_q8_vsm2");
5430 let sh = (rb_g + rb_u) as u32;
5431 use cudarc::driver::sys::CUfunction_attribute_enum as A;
5432 f.set_attribute(
5433 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
5434 sh as i32,
5435 )?;
5436 (
5437 f,
5438 LaunchConfig {
5439 grid_dim: (n_ff as u32, n_used as u32, 1),
5440 block_dim: (32, 1, 1),
5441 shared_mem_bytes: sh,
5442 },
5443 )
5444 }
5445 "vsm" => {
5446 let f = self.func("moe_gate_up_silu8_dev_q8_vsm");
5447 let sh = (rb_g + rb_u) as u32;
5448 use cudarc::driver::sys::CUfunction_attribute_enum as A;
5449 f.set_attribute(
5450 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
5451 sh as i32,
5452 )?;
5453 (
5454 f,
5455 LaunchConfig {
5456 grid_dim: (n_ff as u32, n_used as u32, 1),
5457 block_dim: (32, 1, 1),
5458 shared_mem_bytes: sh,
5459 },
5460 )
5461 }
5462 "sg" => (
5463 self.func("moe_gate_up_silu8_dev_q8_sg"),
5464 LaunchConfig {
5465 grid_dim: (n_ff as u32, n_used as u32, 1),
5466 block_dim: (32, 1, 1),
5467 shared_mem_bytes: 0,
5468 },
5469 ),
5470 "j8sg" if n_used <= 32 => (
5471 self.func("moe_gate_up_silu8_dev_q8_j8sg"),
5472 LaunchConfig {
5473 grid_dim: (n_ff as u32, 1, 1),
5474 block_dim: (32, n_used as u32, 1),
5475 shared_mem_bytes: 0,
5476 },
5477 ),
5478 "u64" if in_f == 2048 => (
5479 self.func("moe_gate_up_silu8_dev_q8_u64"),
5480 LaunchConfig {
5481 grid_dim: (n_ff as u32, n_used as u32, 1),
5482 block_dim: (32, 1, 1),
5483 shared_mem_bytes: 0,
5484 },
5485 ),
5486 "gs4" if in_f == 2048 => (
5487 self.func("moe_gate_up_silu8_dev_q8_gs4"),
5488 LaunchConfig {
5489 grid_dim: (n_ff as u32, n_used as u32, 1),
5490 block_dim: (32, 4, 1),
5491 shared_mem_bytes: 0,
5492 },
5493 ),
5494 "v" | "" => (
5496 self.func("moe_gate_up_silu8_dev_q8_v"),
5497 LaunchConfig {
5498 grid_dim: (n_ff as u32, n_used as u32, 1),
5499 block_dim: (32, 1, 1),
5500 shared_mem_bytes: 0,
5501 },
5502 ),
5503 "s2" => (
5504 self.func("moe_gate_up_silu8_dev_q8_s2"),
5505 LaunchConfig {
5506 grid_dim: (n_ff as u32, n_used as u32, 1),
5507 block_dim: (32, 2, 1),
5508 shared_mem_bytes: 0,
5509 },
5510 ),
5511 "s2z" => {
5512 let rz = wpb.min(16); (
5514 self.func("moe_gate_up_silu8_dev_q8_s2z"),
5515 LaunchConfig {
5516 grid_dim: (n_ff.div_ceil(rz as usize) as u32, n_used as u32, 1),
5517 block_dim: (32, 2, rz),
5518 shared_mem_bytes: 0,
5519 },
5520 )
5521 }
5522 _ => (
5523 self.func("moe_gate_up_silu8_dev_q8"),
5524 LaunchConfig {
5525 grid_dim: (n_ff as u32, n_used as u32, 1),
5526 block_dim: (32, 1, 1),
5527 shared_mem_bytes: 0,
5528 },
5529 ),
5530 };
5531 let __s_b = self.gpu.stream();
5532 let mut b = __s_b.launch_builder(&f);
5533 b.arg(table)
5534 .arg(sel)
5535 .arg(aq)
5536 .arg(ad)
5537 .arg(&mut act)
5538 .arg(&inf)
5539 .arg(&nff)
5540 .arg(&ne)
5541 .arg(&qt_g)
5542 .arg(&qt_u)
5543 .arg(&rbg)
5544 .arg(&rbu)
5545 .arg(macros);
5546 unsafe {
5547 b.launch(cfg)?;
5548 }
5549 Ok(act)
5550 }
5551
5552 #[allow(clippy::too_many_arguments)]
5553 pub fn moe_down8_fma_dev_q8(
5554 &self,
5555 table: &CudaSlice<u64>,
5556 sel: &cudarc::driver::CudaView<i32>,
5557 w: &cudarc::driver::CudaView<f32>,
5558 aq2: &CudaSlice<i8>,
5559 ad2: &CudaSlice<f32>,
5560 dst: &mut cudarc::driver::CudaViewMut<f32>,
5561 in_f: usize,
5562 out_f: usize,
5563 n_used: usize,
5564 n_expert: usize,
5565 qt: i32,
5566 rb: usize,
5567 ) -> Result<(), Box<dyn std::error::Error>> {
5568 static DOWN: std::sync::OnceLock<String> = std::sync::OnceLock::new();
5569 let mode = DOWN.get_or_init(|| std::env::var("MEMRA_MOE_DEVQ8_DOWN").unwrap_or_default());
5570 let (inf, outf, nu, ne, rbi) = (
5571 in_f as i32,
5572 out_f as i32,
5573 n_used as i32,
5574 n_expert as i32,
5575 rb as i64,
5576 );
5577 let (f, cfg) = match mode.as_str() {
5580 m @ ("1" | "2" | "4") if n_used <= 8 => {
5581 let rpw: usize = m.parse().unwrap();
5582 let f = self.func(match rpw {
5583 1 => "moe_down8_fma_dev_q8_w8r1",
5584 2 => "moe_down8_fma_dev_q8_w8r2",
5585 _ => "moe_down8_fma_dev_q8_w8r4",
5586 });
5587 (
5588 f,
5589 LaunchConfig {
5590 grid_dim: (out_f.div_ceil(rpw) as u32, 1, 1),
5591 block_dim: (32, n_used as u32, 1),
5592 shared_mem_bytes: 0,
5593 },
5594 )
5595 }
5596 "h2" if in_f == 512 => (
5597 self.func("moe_down8_fma_dev_q8_h2"),
5598 LaunchConfig {
5599 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5600 block_dim: (32, 1, 1),
5601 shared_mem_bytes: 0,
5602 },
5603 ),
5604 "" if in_f == 704 && n_used <= 8 => (
5607 self.func("moe_down8_fma_dev_q8_w8r2"),
5608 LaunchConfig {
5609 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5610 block_dim: (32, n_used as u32, 1),
5611 shared_mem_bytes: 0,
5612 },
5613 ),
5614 "w8h2v" | "" if in_f == 512 && n_used <= 8 => (
5618 self.func("moe_down8_fma_dev_q8_w8h2v"),
5619 LaunchConfig {
5620 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5621 block_dim: (32, n_used as u32, 1),
5622 shared_mem_bytes: 0,
5623 },
5624 ),
5625 "w8h2r2v" if in_f == 512 && n_used <= 8 => (
5626 self.func("moe_down8_fma_dev_q8_w8h2r2v"),
5627 LaunchConfig {
5628 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
5629 block_dim: (32, n_used as u32, 1),
5630 shared_mem_bytes: 0,
5631 },
5632 ),
5633 "w8h2r2" if in_f == 512 && n_used <= 8 => (
5634 self.func("moe_down8_fma_dev_q8_w8h2r2"),
5635 LaunchConfig {
5636 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
5637 block_dim: (32, n_used as u32, 1),
5638 shared_mem_bytes: 0,
5639 },
5640 ),
5641 "w8h2" if in_f == 512 && n_used <= 8 => (
5642 self.func("moe_down8_fma_dev_q8_w8h2"),
5643 LaunchConfig {
5644 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5645 block_dim: (32, n_used as u32, 1),
5646 shared_mem_bytes: 0,
5647 },
5648 ),
5649 _ => (
5650 self.func("moe_down8_fma_dev_q8"),
5651 LaunchConfig {
5652 grid_dim: (out_f as u32, 1, 1),
5653 block_dim: (32, 1, 1),
5654 shared_mem_bytes: 0,
5655 },
5656 ),
5657 };
5658 let __s_b = self.gpu.stream();
5659 let mut b = __s_b.launch_builder(&f);
5660 b.arg(table)
5661 .arg(sel)
5662 .arg(w)
5663 .arg(aq2)
5664 .arg(ad2)
5665 .arg(dst)
5666 .arg(&inf)
5667 .arg(&outf)
5668 .arg(&nu)
5669 .arg(&ne)
5670 .arg(&qt)
5671 .arg(&rbi);
5672 unsafe {
5673 b.launch(cfg)?;
5674 }
5675 Ok(())
5676 }
5677
5678 #[allow(clippy::too_many_arguments)]
5685 pub fn moe_gate_up_silu8_dev_q8_rows(
5686 &self,
5687 table: &CudaSlice<u64>,
5688 sel: &CudaSlice<i32>,
5689 aq: &CudaSlice<i8>,
5690 ad: &CudaSlice<f32>,
5691 t: usize,
5692 in_f: usize,
5693 n_ff: usize,
5694 n_used: usize,
5695 n_expert: usize,
5696 qt_g: i32,
5697 qt_u: i32,
5698 rb_g: usize,
5699 rb_u: usize,
5700 macros: &CudaSlice<f32>,
5701 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5702 let f = self.func("moe_gate_up_silu8_dev_q8_v_rows");
5703 let mut act = self.alloc_uninit::<f32>(t * n_used * n_ff)?;
5704 let cfg = LaunchConfig {
5705 grid_dim: (n_ff as u32, n_used as u32, t as u32),
5706 block_dim: (32, 1, 1),
5707 shared_mem_bytes: 0,
5708 };
5709 let (inf, nff, ne, nu, rbg, rbu) = (
5710 in_f as i32,
5711 n_ff as i32,
5712 n_expert as i32,
5713 n_used as i32,
5714 rb_g as i64,
5715 rb_u as i64,
5716 );
5717 let __s_b = self.gpu.stream();
5718 let mut b = __s_b.launch_builder(&f);
5719 b.arg(table)
5720 .arg(sel)
5721 .arg(aq)
5722 .arg(ad)
5723 .arg(&mut act)
5724 .arg(&inf)
5725 .arg(&nff)
5726 .arg(&ne)
5727 .arg(&qt_g)
5728 .arg(&qt_u)
5729 .arg(&rbg)
5730 .arg(&rbu)
5731 .arg(&nu)
5732 .arg(macros);
5733 unsafe {
5734 b.launch(cfg)?;
5735 }
5736 Ok(act)
5737 }
5738
5739 #[allow(clippy::too_many_arguments)]
5744 pub fn moe_down8_fma_dev_q8_rows(
5745 &self,
5746 table: &CudaSlice<u64>,
5747 sel: &CudaSlice<i32>,
5748 w: &CudaSlice<f32>,
5749 aq2: &CudaSlice<i8>,
5750 ad2: &CudaSlice<f32>,
5751 dst: &mut CudaSlice<f32>,
5752 t: usize,
5753 in_f: usize,
5754 out_f: usize,
5755 n_used: usize,
5756 n_expert: usize,
5757 qt: i32,
5758 rb: usize,
5759 ) -> Result<(), Box<dyn std::error::Error>> {
5760 assert!(
5761 in_f == 512 && n_used <= 8,
5762 "down rows twin is w8h2v shape-gated"
5763 );
5764 let f = self.func("moe_down8_fma_dev_q8_w8h2v_rows");
5765 let cfg = LaunchConfig {
5766 grid_dim: (out_f.div_ceil(2) as u32, 1, t as u32),
5767 block_dim: (32, n_used as u32, 1),
5768 shared_mem_bytes: 0,
5769 };
5770 let (inf, outf, nu, ne, rbi) = (
5771 in_f as i32,
5772 out_f as i32,
5773 n_used as i32,
5774 n_expert as i32,
5775 rb as i64,
5776 );
5777 let __s_b = self.gpu.stream();
5778 let mut b = __s_b.launch_builder(&f);
5779 b.arg(table)
5780 .arg(sel)
5781 .arg(w)
5782 .arg(aq2)
5783 .arg(ad2)
5784 .arg(dst)
5785 .arg(&inf)
5786 .arg(&outf)
5787 .arg(&nu)
5788 .arg(&ne)
5789 .arg(&qt)
5790 .arg(&rbi);
5791 unsafe {
5792 b.launch(cfg)?;
5793 }
5794 Ok(())
5795 }
5796
5797 #[allow(clippy::too_many_arguments)]
5801 pub fn moe_gate_up_silu8_dev_q8_csr(
5802 &self,
5803 table: &CudaSlice<u64>,
5804 sel: &CudaSlice<i32>,
5805 aq: &CudaSlice<i8>,
5806 ad: &CudaSlice<f32>,
5807 n_pairs: usize,
5808 in_f: usize,
5809 n_ff: usize,
5810 n_used: usize,
5811 n_expert: usize,
5812 qt_g: i32,
5813 qt_u: i32,
5814 rb_g: usize,
5815 rb_u: usize,
5816 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5817 let f = self.func("moe_gate_up_silu8_dev_q8_csr_iq4");
5818 let mut act = self.alloc_uninit::<f32>(n_pairs * n_ff)?;
5819 let cfg = LaunchConfig {
5820 grid_dim: (n_ff as u32, n_pairs as u32, 1),
5821 block_dim: (32, 1, 1),
5822 shared_mem_bytes: 0,
5823 };
5824 let (inf, nff, ne, nu, npi, rbg, rbu) = (
5825 in_f as i32,
5826 n_ff as i32,
5827 n_expert as i32,
5828 n_used as i32,
5829 n_pairs as i32,
5830 rb_g as i64,
5831 rb_u as i64,
5832 );
5833 let __s_b = self.gpu.stream();
5834 let mut b = __s_b.launch_builder(&f);
5835 b.arg(table)
5836 .arg(sel)
5837 .arg(aq)
5838 .arg(ad)
5839 .arg(&mut act)
5840 .arg(&inf)
5841 .arg(&nff)
5842 .arg(&ne)
5843 .arg(&qt_g)
5844 .arg(&qt_u)
5845 .arg(&rbg)
5846 .arg(&rbu)
5847 .arg(&nu)
5848 .arg(&npi);
5849 unsafe {
5850 b.launch(cfg)?;
5851 }
5852 Ok(act)
5853 }
5854
5855 #[allow(clippy::too_many_arguments)]
5859 pub fn moe_down8_fma_dev_q8_variant(
5860 &self,
5861 variant: &str,
5862 table: &CudaSlice<u64>,
5863 sel: &cudarc::driver::CudaView<i32>,
5864 w: &cudarc::driver::CudaView<f32>,
5865 aq2: &CudaSlice<i8>,
5866 ad2: &CudaSlice<f32>,
5867 dst: &mut cudarc::driver::CudaViewMut<f32>,
5868 in_f: usize,
5869 out_f: usize,
5870 n_used: usize,
5871 n_expert: usize,
5872 qt: i32,
5873 rb: usize,
5874 ) -> Result<(), Box<dyn std::error::Error>> {
5875 let (inf, outf, nu, ne, rbi) = (
5876 in_f as i32,
5877 out_f as i32,
5878 n_used as i32,
5879 n_expert as i32,
5880 rb as i64,
5881 );
5882 let (f, cfg) = match variant {
5883 "w8h2" | "w8h2v" => (
5884 self.func(if variant == "w8h2" {
5885 "moe_down8_fma_dev_q8_w8h2"
5886 } else {
5887 "moe_down8_fma_dev_q8_w8h2v"
5888 }),
5889 LaunchConfig {
5890 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5891 block_dim: (32, n_used as u32, 1),
5892 shared_mem_bytes: 0,
5893 },
5894 ),
5895 "w8h2r2" | "w8h2r2v" => (
5896 self.func(if variant == "w8h2r2" {
5897 "moe_down8_fma_dev_q8_w8h2r2"
5898 } else {
5899 "moe_down8_fma_dev_q8_w8h2r2v"
5900 }),
5901 LaunchConfig {
5902 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
5903 block_dim: (32, n_used as u32, 1),
5904 shared_mem_bytes: 0,
5905 },
5906 ),
5907 _ => (
5908 self.func("moe_down8_fma_dev_q8"),
5909 LaunchConfig {
5910 grid_dim: (out_f as u32, 1, 1),
5911 block_dim: (32, 1, 1),
5912 shared_mem_bytes: 0,
5913 },
5914 ),
5915 };
5916 let __s_b = self.gpu.stream();
5917 let mut b = __s_b.launch_builder(&f);
5918 b.arg(table)
5919 .arg(sel)
5920 .arg(w)
5921 .arg(aq2)
5922 .arg(ad2)
5923 .arg(dst)
5924 .arg(&inf)
5925 .arg(&outf)
5926 .arg(&nu)
5927 .arg(&ne)
5928 .arg(&qt)
5929 .arg(&rbi);
5930 unsafe {
5931 b.launch(cfg)?;
5932 }
5933 Ok(())
5934 }
5935
5936 #[allow(clippy::too_many_arguments)]
5938 pub fn moe_gate_up_silu8_dev_q8_variant(
5939 &self,
5940 variant: &str,
5941 table: &CudaSlice<u64>,
5942 sel: &cudarc::driver::CudaView<i32>,
5943 aq: &CudaSlice<i8>,
5944 ad: &CudaSlice<f32>,
5945 in_f: usize,
5946 n_ff: usize,
5947 n_used: usize,
5948 n_expert: usize,
5949 qt_g: i32,
5950 qt_u: i32,
5951 rb_g: usize,
5952 rb_u: usize,
5953 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5954 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
5955 let (inf, nff, ne, rbg, rbu) = (
5956 in_f as i32,
5957 n_ff as i32,
5958 n_expert as i32,
5959 rb_g as i64,
5960 rb_u as i64,
5961 );
5962 let f = self.func(if variant == "v" {
5963 "moe_gate_up_silu8_dev_q8_v"
5964 } else {
5965 "moe_gate_up_silu8_dev_q8"
5966 });
5967 let cfg = LaunchConfig {
5968 grid_dim: (n_ff as u32, n_used as u32, 1),
5969 block_dim: (32, 1, 1),
5970 shared_mem_bytes: 0,
5971 };
5972 let __s_b = self.gpu.stream();
5973 let mut b = __s_b.launch_builder(&f);
5974 b.arg(table)
5975 .arg(sel)
5976 .arg(aq)
5977 .arg(ad)
5978 .arg(&mut act)
5979 .arg(&inf)
5980 .arg(&nff)
5981 .arg(&ne)
5982 .arg(&qt_g)
5983 .arg(&qt_u)
5984 .arg(&rbg)
5985 .arg(&rbu);
5986 unsafe {
5987 b.launch(cfg)?;
5988 }
5989 Ok(act)
5990 }
5991
5992 pub fn moe_gate_up_silu8_dev(
5993 &self,
5994 table: &CudaSlice<u64>,
5995 sel: &cudarc::driver::CudaView<i32>,
5996 x: &cudarc::driver::CudaView<f32>,
5997 in_f: usize,
5998 n_ff: usize,
5999 n_used: usize,
6000 n_expert: usize,
6001 qt_g: i32,
6002 qt_u: i32,
6003 rb_g: usize,
6004 rb_u: usize,
6005 macros: &CudaSlice<f32>,
6006 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6007 let f = self.func("moe_gate_up_silu8_dev");
6008 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?; let cfg = LaunchConfig {
6010 grid_dim: (n_ff as u32, n_used as u32, 1),
6011 block_dim: (256, 1, 1),
6012 shared_mem_bytes: 0,
6013 };
6014 let (inf, nff, ne, rbg, rbu) = (
6015 in_f as i32,
6016 n_ff as i32,
6017 n_expert as i32,
6018 rb_g as i64,
6019 rb_u as i64,
6020 );
6021 let __s_b = self.gpu.stream();
6022 let mut b = __s_b.launch_builder(&f);
6023 b.arg(table)
6024 .arg(sel)
6025 .arg(x)
6026 .arg(&mut act)
6027 .arg(&inf)
6028 .arg(&nff)
6029 .arg(&ne)
6030 .arg(&qt_g)
6031 .arg(&qt_u)
6032 .arg(&rbg)
6033 .arg(&rbu)
6034 .arg(macros);
6035 unsafe {
6036 b.launch(cfg)?;
6037 }
6038 Ok(act)
6039 }
6040
6041 #[allow(clippy::too_many_arguments)]
6044 pub fn moe_down8_fma_dev(
6045 &self,
6046 table: &CudaSlice<u64>,
6047 sel: &cudarc::driver::CudaView<i32>,
6048 w: &cudarc::driver::CudaView<f32>,
6049 act: &CudaSlice<f32>,
6050 dst: &mut cudarc::driver::CudaViewMut<f32>,
6051 in_f: usize,
6052 out_f: usize,
6053 n_used: usize,
6054 n_expert: usize,
6055 qt: i32,
6056 rb: usize,
6057 ) -> Result<(), Box<dyn std::error::Error>> {
6058 let f = self.func("moe_down8_fma_dev");
6059 let cfg = LaunchConfig {
6060 grid_dim: (out_f as u32, 1, 1),
6061 block_dim: (256, 1, 1),
6062 shared_mem_bytes: 0,
6063 };
6064 let (inf, outf, nu, ne, rbv) = (
6065 in_f as i32,
6066 out_f as i32,
6067 n_used as i32,
6068 n_expert as i32,
6069 rb as i64,
6070 );
6071 let __s_b = self.gpu.stream();
6072 let mut b = __s_b.launch_builder(&f);
6073 b.arg(table)
6074 .arg(sel)
6075 .arg(w)
6076 .arg(act)
6077 .arg(dst)
6078 .arg(&inf)
6079 .arg(&outf)
6080 .arg(&nu)
6081 .arg(&ne)
6082 .arg(&qt)
6083 .arg(&rbv);
6084 unsafe {
6085 b.launch(cfg)?;
6086 }
6087 Ok(())
6088 }
6089
6090 pub fn axpy_into(
6092 &self,
6093 src: &CudaSlice<f32>,
6094 alpha: f32,
6095 dst: &mut cudarc::driver::CudaViewMut<f32>,
6096 n: usize,
6097 ) -> Result<(), Box<dyn std::error::Error>> {
6098 let f = self.func("axpy_f32");
6099 let cfg = LaunchConfig::for_num_elems(n as u32);
6100 let (a, ni) = (alpha, n as i32);
6101 let __s_b = self.gpu.stream();
6102 let mut b = __s_b.launch_builder(&f);
6103 b.arg(src).arg(dst).arg(&a).arg(&ni);
6104 unsafe {
6105 b.launch(cfg)?;
6106 }
6107 Ok(())
6108 }
6109
6110 pub fn add_scaled_rows(
6112 &self,
6113 src: &CudaSlice<f32>,
6114 scale: &CudaSlice<f32>,
6115 dst: &mut CudaSlice<f32>,
6116 ncols: usize,
6117 nrows: usize,
6118 ) -> Result<(), Box<dyn std::error::Error>> {
6119 let f = self.func("add_scaled_rows_f32");
6120 let cfg = LaunchConfig::for_num_elems((ncols * nrows) as u32);
6121 let (nc, nr) = (ncols as i32, nrows as i32);
6122 let __s_b = self.gpu.stream();
6123 let mut b = __s_b.launch_builder(&f);
6124 b.arg(src).arg(scale).arg(dst).arg(&nc).arg(&nr);
6125 unsafe {
6126 b.launch(cfg)?;
6127 }
6128 Ok(())
6129 }
6130
6131 pub fn gather_rows(
6135 &self,
6136 src: &CudaSlice<f32>,
6137 idx: &CudaSlice<i32>,
6138 dst: &mut CudaSlice<f32>,
6139 ncols: usize,
6140 m_e: usize,
6141 ) -> Result<(), Box<dyn std::error::Error>> {
6142 let f = self.func("gather_rows_f32");
6143 let cfg = LaunchConfig::for_num_elems((m_e * ncols) as u32);
6144 let (nc, me) = (ncols as i32, m_e as i32);
6145 let __s_b = self.gpu.stream();
6146 let mut b = __s_b.launch_builder(&f);
6147 b.arg(src).arg(idx).arg(dst).arg(&nc).arg(&me);
6148 unsafe {
6149 b.launch(cfg)?;
6150 }
6151 Ok(())
6152 }
6153
6154 pub fn scatter_slot(
6159 &self,
6160 src: &CudaSlice<f32>,
6161 tok_idx: &CudaSlice<i32>,
6162 slot_idx: &CudaSlice<i32>,
6163 weight: &CudaSlice<f32>,
6164 dst: &mut CudaSlice<f32>,
6165 wbuf: &mut CudaSlice<f32>,
6166 ncols: usize,
6167 n_used: usize,
6168 m_e: usize,
6169 ) -> Result<(), Box<dyn std::error::Error>> {
6170 let f = self.func("scatter_add_slot_f32");
6171 let cfg = LaunchConfig::for_num_elems((m_e * ncols) as u32);
6172 let (nc, nu, me) = (ncols as i32, n_used as i32, m_e as i32);
6173 let __s_b = self.gpu.stream();
6174 let mut b = __s_b.launch_builder(&f);
6175 b.arg(src)
6176 .arg(tok_idx)
6177 .arg(slot_idx)
6178 .arg(weight)
6179 .arg(dst)
6180 .arg(wbuf)
6181 .arg(&nc)
6182 .arg(&nu)
6183 .arg(&me);
6184 unsafe {
6185 b.launch(cfg)?;
6186 }
6187 Ok(())
6188 }
6189
6190 pub fn reduce_slots(
6194 &self,
6195 slots: &CudaSlice<f32>,
6196 wbuf: &CudaSlice<f32>,
6197 dst: &mut CudaSlice<f32>,
6198 ncols: usize,
6199 n_used: usize,
6200 t: usize,
6201 ) -> Result<(), Box<dyn std::error::Error>> {
6202 let f = self.func("reduce_slots_f32");
6203 let cfg = LaunchConfig::for_num_elems((t * ncols) as u32);
6204 let (nc, nu, ti) = (ncols as i32, n_used as i32, t as i32);
6205 let __s_b = self.gpu.stream();
6206 let mut b = __s_b.launch_builder(&f);
6207 b.arg(slots).arg(wbuf).arg(dst).arg(&nc).arg(&nu).arg(&ti);
6208 unsafe {
6209 b.launch(cfg)?;
6210 }
6211 Ok(())
6212 }
6213
6214 pub fn quantize_q8_1_view(
6221 &self,
6222 x: &cudarc::driver::CudaView<f32>,
6223 m: usize,
6224 in_f: usize,
6225 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
6226 let f = self.func("quantize_q8_1");
6227 let nblk = in_f / 32;
6228 let mut q = self.alloc_uninit::<i8>(m * in_f)?;
6229 let mut d = self.alloc_uninit::<f32>(m * nblk)?;
6230 let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
6231 let (inf, mi) = (in_f as i32, m as i32);
6232 let __s_b = self.gpu.stream();
6233 let mut b = __s_b.launch_builder(&f);
6234 b.arg(x).arg(&mut q).arg(&mut d).arg(&inf).arg(&mi);
6235 unsafe {
6236 b.launch(cfg)?;
6237 }
6238 Ok((q, d))
6239 }
6240
6241 pub fn quantize_q8_1(
6242 &self,
6243 x: &CudaSlice<f32>,
6244 m: usize,
6245 in_f: usize,
6246 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
6247 let nblk = in_f / 32;
6248 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);
6252 let (inf, mi) = (in_f as i32, m as i32);
6253 if Self::pdl_on() && Self::pdl_wb_on() {
6254 {
6255 use cudarc::driver::{DevicePtr, DevicePtrMut};
6256 let s = &self.gpu.stream();
6257 let (px, _g0) = x.device_ptr(s);
6258 let (pq, _g1) = q.device_ptr_mut(s);
6259 let (pd, _g2) = d.device_ptr_mut(s);
6260 let mut ps = [
6261 &px as *const _ as *mut std::ffi::c_void,
6262 &pq as *const _ as *mut _,
6263 &pd as *const _ as *mut _,
6264 &inf as *const _ as *mut _,
6265 &mi as *const _ as *mut _,
6266 ];
6267 unsafe {
6268 self.launch_pdl("quantize_q8_1", cfg.grid_dim, cfg.block_dim, &mut ps)?;
6269 }
6270 }
6271 return Ok((q, d));
6272 }
6273 let f = self.func("quantize_q8_1");
6274 let __s_b = self.gpu.stream();
6275 let mut b = __s_b.launch_builder(&f);
6276 b.arg(x).arg(&mut q).arg(&mut d).arg(&inf).arg(&mi);
6277 unsafe {
6278 b.launch(cfg)?;
6279 }
6280 Ok((q, d))
6281 }
6282
6283 pub fn quantize_fp4_act(
6287 &self,
6288 x: &CudaSlice<f32>,
6289 m: usize,
6290 in_f: usize,
6291 ) -> Result<(CudaSlice<u32>, CudaSlice<u8>), Box<dyn std::error::Error>> {
6292 let f = self.func("quantize_fp4_act");
6293 let nb16 = in_f / 16;
6294 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);
6297 let (inf, mi) = (in_f as i32, m as i32);
6298 let __s_b = self.gpu.stream();
6299 let mut b = __s_b.launch_builder(&f);
6300 b.arg(x).arg(&mut aq4).arg(&mut ad4).arg(&inf).arg(&mi);
6301 unsafe {
6302 b.launch(cfg)?;
6303 }
6304 Ok((aq4, ad4))
6305 }
6306
6307 pub fn qmatvec_gemm_nvfp4_fp4(
6312 &self,
6313 bytes: &CudaSlice<u8>,
6314 x: &CudaSlice<f32>,
6315 m: usize,
6316 in_f: usize,
6317 out_f: usize,
6318 row_bytes: usize,
6319 scale: f32,
6320 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6321 assert!(
6322 in_f % 64 == 0,
6323 "FP4 GEMM requires in_f % 64 == 0, got {in_f}"
6324 );
6325 let (aq4, ad4) = self.quantize_fp4_act(x, m, in_f)?;
6326 let mut y = self.fp4_gemm_launch(bytes, &aq4, &ad4, m, in_f, out_f, row_bytes)?;
6327 if scale != 1.0 {
6328 self.scale_inplace(&mut y, scale, m * out_f)?;
6329 }
6330 Ok(y)
6331 }
6332
6333 fn fp4_gemm_launch(
6336 &self,
6337 bytes: &CudaSlice<u8>,
6338 aq4: &CudaSlice<u32>,
6339 ad4: &CudaSlice<u8>,
6340 m: usize,
6341 in_f: usize,
6342 out_f: usize,
6343 row_bytes: usize,
6344 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6345 let f = self.func("qmatvec_gemm_nvfp4_fp4");
6346 let mut y = self.alloc_uninit::<f32>(m * out_f)?; const BM: u32 = 64;
6348 const BN: u32 = 256;
6349 let cfg = LaunchConfig {
6350 grid_dim: ((out_f as u32 + BM - 1) / BM, (m as u32 + BN - 1) / BN, 1),
6351 block_dim: (32, 4, 1),
6352 shared_mem_bytes: 0,
6353 };
6354 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6355 let __s_b = self.gpu.stream();
6356 let mut b = __s_b.launch_builder(&f);
6357 b.arg(bytes)
6358 .arg(aq4)
6359 .arg(ad4)
6360 .arg(&mut y)
6361 .arg(&inf)
6362 .arg(&outf)
6363 .arg(&mi)
6364 .arg(&rb);
6365 unsafe {
6366 b.launch(cfg)?;
6367 }
6368 Ok(y)
6369 }
6370
6371 pub fn qmatvec_gemm_nvfp4_fp4_raw(
6373 &self,
6374 bytes: &CudaSlice<u8>,
6375 x: &CudaSlice<f32>,
6376 m: usize,
6377 in_f: usize,
6378 out_f: usize,
6379 row_bytes: usize,
6380 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6381 assert!(
6382 in_f % 64 == 0,
6383 "FP4 GEMM requires in_f % 64 == 0, got {in_f}"
6384 );
6385 let (aq4, ad4) = self.quantize_fp4_act(x, m, in_f)?;
6386 self.fp4_gemm_launch(bytes, &aq4, &ad4, m, in_f, out_f, row_bytes)
6387 }
6388
6389 pub fn qmatvec_q8_0_fast(
6391 &self,
6392 w: &CudaSlice<u8>,
6393 x: &CudaSlice<f32>,
6394 m: usize,
6395 in_f: usize,
6396 out_f: usize,
6397 row_bytes: usize,
6398 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6399 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6400 let f = self.func("qmatvec_q8_0_dp4a");
6401 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6403 grid_dim: (out_f as u32, m as u32, 1),
6404 block_dim: (128, 1, 1),
6405 shared_mem_bytes: 0,
6406 };
6407 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6408 let __s_b = self.gpu.stream();
6409 let mut b = __s_b.launch_builder(&f);
6410 b.arg(w)
6411 .arg(&aq)
6412 .arg(&ad)
6413 .arg(&mut y)
6414 .arg(&inf)
6415 .arg(&outf)
6416 .arg(&mi)
6417 .arg(&rb);
6418 unsafe {
6419 b.launch(cfg)?;
6420 }
6421 Ok(y)
6422 }
6423
6424 #[allow(non_snake_case)] pub fn qmatvec_q4_K_fast(
6427 &self,
6428 w: &CudaSlice<u8>,
6429 x: &CudaSlice<f32>,
6430 m: usize,
6431 in_f: usize,
6432 out_f: usize,
6433 row_bytes: usize,
6434 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6435 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6436 let f = self.func("qmatvec_q4_K_dp4a");
6437 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6439 grid_dim: (out_f as u32, m as u32, 1),
6440 block_dim: (128, 1, 1),
6441 shared_mem_bytes: 0,
6442 };
6443 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6444 let __s_b = self.gpu.stream();
6445 let mut b = __s_b.launch_builder(&f);
6446 b.arg(w)
6447 .arg(&aq)
6448 .arg(&ad)
6449 .arg(&mut y)
6450 .arg(&inf)
6451 .arg(&outf)
6452 .arg(&mi)
6453 .arg(&rb);
6454 unsafe {
6455 b.launch(cfg)?;
6456 }
6457 Ok(y)
6458 }
6459
6460 #[allow(non_snake_case)] pub fn qmatvec_q6_K_fast(
6463 &self,
6464 w: &CudaSlice<u8>,
6465 x: &CudaSlice<f32>,
6466 m: usize,
6467 in_f: usize,
6468 out_f: usize,
6469 row_bytes: usize,
6470 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6471 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6472 let f = self.func("qmatvec_q6_K_dp4a");
6473 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6475 grid_dim: (out_f as u32, m as u32, 1),
6476 block_dim: (128, 1, 1),
6477 shared_mem_bytes: 0,
6478 };
6479 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6480 let __s_b = self.gpu.stream();
6481 let mut b = __s_b.launch_builder(&f);
6482 b.arg(w)
6483 .arg(&aq)
6484 .arg(&ad)
6485 .arg(&mut y)
6486 .arg(&inf)
6487 .arg(&outf)
6488 .arg(&mi)
6489 .arg(&rb);
6490 unsafe {
6491 b.launch(cfg)?;
6492 }
6493 Ok(y)
6494 }
6495
6496 #[allow(non_snake_case)] pub fn qmatvec_q5_K_fast(
6499 &self,
6500 w: &CudaSlice<u8>,
6501 x: &CudaSlice<f32>,
6502 m: usize,
6503 in_f: usize,
6504 out_f: usize,
6505 row_bytes: usize,
6506 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6507 self.qmatvec_dp4a_named("qmatvec_q5_K_dp4a", w, x, m, in_f, out_f, row_bytes)
6508 }
6509 #[allow(non_snake_case)] pub fn qmatvec_q3_K_fast(
6512 &self,
6513 w: &CudaSlice<u8>,
6514 x: &CudaSlice<f32>,
6515 m: usize,
6516 in_f: usize,
6517 out_f: usize,
6518 row_bytes: usize,
6519 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6520 self.qmatvec_dp4a_named("qmatvec_q3_K_dp4a", w, x, m, in_f, out_f, row_bytes)
6521 }
6522 pub fn qmatvec_nvfp4_fast_rp(
6524 &self,
6525 w: &CudaSlice<u8>,
6526 x: &CudaSlice<f32>,
6527 m: usize,
6528 in_f: usize,
6529 out_f: usize,
6530 row_bytes: usize,
6531 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6532 assert!(
6533 in_f % 64 == 0,
6534 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
6535 );
6536 self.qmatvec_dp4a_named("qmatvec_nvfp4_dp4a_rp", w, x, m, in_f, out_f, row_bytes)
6537 }
6538 pub fn qmatvec_nvfp4_fast(
6540 &self,
6541 w: &CudaSlice<u8>,
6542 x: &CudaSlice<f32>,
6543 m: usize,
6544 in_f: usize,
6545 out_f: usize,
6546 row_bytes: usize,
6547 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6548 assert!(
6551 in_f % 64 == 0,
6552 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
6553 );
6554 self.qmatvec_dp4a_named("qmatvec_nvfp4_dp4a", w, x, m, in_f, out_f, row_bytes)
6555 }
6556 #[allow(non_snake_case)] pub fn qmatvec_iq4_XS_fast(
6559 &self,
6560 w: &CudaSlice<u8>,
6561 x: &CudaSlice<f32>,
6562 m: usize,
6563 in_f: usize,
6564 out_f: usize,
6565 row_bytes: usize,
6566 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6567 self.qmatvec_dp4a_named("qmatvec_iq4_XS_dp4a", w, x, m, in_f, out_f, row_bytes)
6568 }
6569
6570 fn qmatvec_dp4a_named(
6572 &self,
6573 name: &str,
6574 w: &CudaSlice<u8>,
6575 x: &CudaSlice<f32>,
6576 m: usize,
6577 in_f: usize,
6578 out_f: usize,
6579 row_bytes: usize,
6580 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6581 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6582 let f = self.func(name);
6583 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6585 grid_dim: (out_f as u32, m as u32, 1),
6586 block_dim: (128, 1, 1),
6587 shared_mem_bytes: 0,
6588 };
6589 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6590 let __s_b = self.gpu.stream();
6591 let mut b = __s_b.launch_builder(&f);
6592 b.arg(w)
6593 .arg(&aq)
6594 .arg(&ad)
6595 .arg(&mut y)
6596 .arg(&inf)
6597 .arg(&outf)
6598 .arg(&mi)
6599 .arg(&rb);
6600 unsafe {
6601 b.launch(cfg)?;
6602 }
6603 Ok(y)
6604 }
6605
6606 pub fn htod(&self, v: &[f32]) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6607 Ok(self.gpu.stream().clone_htod(v)?)
6608 }
6609 pub fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
6610 Ok(self.gpu.stream().clone_htod(v)?)
6611 }
6612 pub fn htod_i8(&self, v: &[i8]) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
6614 Ok(self.gpu.stream().clone_htod(v)?)
6615 }
6616 pub fn htod_u64(&self, v: &[u64]) -> Result<CudaSlice<u64>, Box<dyn std::error::Error>> {
6617 Ok(self.gpu.stream().clone_htod(v)?)
6618 }
6619 pub fn dtoh_view(
6621 &self,
6622 d: &cudarc::driver::CudaView<f32>,
6623 ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6624 let v = self.gpu.stream().clone_dtoh(d)?;
6625 self.gpu.stream().synchronize()?;
6626 Ok(v)
6627 }
6628 pub fn dtoh(&self, d: &CudaSlice<f32>) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6629 let v = self.gpu.stream().clone_dtoh(d)?;
6630 self.gpu.stream().synchronize()?;
6631 Ok(v)
6632 }
6633 pub fn dtoh_pair(
6637 &self,
6638 a: &CudaSlice<f32>,
6639 b: &CudaSlice<f32>,
6640 ) -> Result<(Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
6641 let av = self.gpu.stream().clone_dtoh(a)?;
6642 let bv = self.gpu.stream().clone_dtoh(b)?;
6643 self.gpu.stream().synchronize()?;
6644 Ok((av, bv))
6645 }
6646 pub fn dtoh_i32(&self, d: &CudaSlice<i32>) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
6648 let v = self.gpu.stream().clone_dtoh(d)?;
6649 self.gpu.stream().synchronize()?;
6650 Ok(v)
6651 }
6652 pub fn dtoh_u8(&self, d: &CudaSlice<u8>) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
6654 let v = self.gpu.stream().clone_dtoh(d)?;
6655 self.gpu.stream().synchronize()?;
6656 Ok(v)
6657 }
6658 pub fn dtoh_u8_view(
6659 &self,
6660 d: &cudarc::driver::CudaView<u8>,
6661 ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
6662 let v = self.gpu.stream().clone_dtoh(d)?;
6663 self.gpu.stream().synchronize()?;
6664 Ok(v)
6665 }
6666 pub fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6667 let s = self.gpu.stream().alloc_zeros::<f32>(n)?;
6668 self.keep_if_capturing(&s);
6669 Ok(s)
6670 }
6671
6672 pub fn prob_of_token_device(
6681 &self,
6682 logits: &CudaSlice<f32>,
6683 tok: &CudaSlice<u32>,
6684 n_vocab: usize,
6685 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6686 let nb = ARGMAX_NB;
6687 let mut part = self.alloc_uninit::<f32>(nb)?;
6688 let mut p = self.alloc_uninit::<f32>(1)?;
6689 let f1 = self.func("prob_of_token_partial_f32");
6690 let cfg1 = LaunchConfig {
6691 grid_dim: (nb as u32, 1, 1),
6692 block_dim: (256, 1, 1),
6693 shared_mem_bytes: 0,
6694 };
6695 let nv = n_vocab as i32;
6696 let __s_b1 = self.gpu.stream();
6697 let mut b1 = __s_b1.launch_builder(&f1);
6698 b1.arg(logits).arg(tok).arg(&mut part).arg(&nv);
6699 unsafe {
6700 b1.launch(cfg1)?;
6701 }
6702 let f2 = self.func("prob_of_token_final_f32");
6703 let cfg2 = LaunchConfig {
6704 grid_dim: (1, 1, 1),
6705 block_dim: (256, 1, 1),
6706 shared_mem_bytes: 0,
6707 };
6708 let nbi = nb as i32;
6709 let __s_b2 = self.gpu.stream();
6710 let mut b2 = __s_b2.launch_builder(&f2);
6711 b2.arg(&part).arg(&mut p).arg(&nbi);
6712 unsafe {
6713 b2.launch(cfg2)?;
6714 }
6715 Ok(p)
6716 }
6717
6718 pub fn prob_of_token_device_col(
6725 &self,
6726 logits: &CudaSlice<f32>,
6727 tok_all: &CudaSlice<u32>,
6728 tok_idx: usize,
6729 p_out: &mut CudaSlice<f32>,
6730 p_idx: usize,
6731 n_vocab: usize,
6732 ) -> Result<(), Box<dyn std::error::Error>> {
6733 let tok_v = tok_all.slice(tok_idx..tok_idx + 1);
6734 let mut p_v = p_out.slice_mut(p_idx..p_idx + 1);
6735 let nb = ARGMAX_NB;
6736 let mut part = self.alloc_uninit::<f32>(nb)?;
6737 let f1 = self.func("prob_of_token_partial_f32");
6738 let cfg1 = LaunchConfig {
6739 grid_dim: (nb as u32, 1, 1),
6740 block_dim: (256, 1, 1),
6741 shared_mem_bytes: 0,
6742 };
6743 let nv = n_vocab as i32;
6744 let __s_b1 = self.gpu.stream();
6745 let mut b1 = __s_b1.launch_builder(&f1);
6746 b1.arg(logits).arg(&tok_v).arg(&mut part).arg(&nv);
6747 unsafe {
6748 b1.launch(cfg1)?;
6749 }
6750 let f2 = self.func("prob_of_token_final_f32");
6751 let cfg2 = LaunchConfig {
6752 grid_dim: (1, 1, 1),
6753 block_dim: (256, 1, 1),
6754 shared_mem_bytes: 0,
6755 };
6756 let nbi = nb as i32;
6757 let __s_b2 = self.gpu.stream();
6758 let mut b2 = __s_b2.launch_builder(&f2);
6759 b2.arg(&part).arg(&mut p_v).arg(&nbi);
6760 unsafe {
6761 b2.launch(cfg2)?;
6762 }
6763 Ok(())
6764 }
6765
6766 pub fn prob_of_token_device_into(
6767 &self,
6768 logits: &CudaSlice<f32>,
6769 tok: &CudaSlice<u32>,
6770 p_out: &mut CudaSlice<f32>,
6771 n_vocab: usize,
6772 ) -> Result<(), Box<dyn std::error::Error>> {
6773 let nb = ARGMAX_NB;
6774 let mut part = self.alloc_uninit::<f32>(nb)?;
6775 let f1 = self.func("prob_of_token_partial_f32");
6776 let cfg1 = LaunchConfig {
6777 grid_dim: (nb as u32, 1, 1),
6778 block_dim: (256, 1, 1),
6779 shared_mem_bytes: 0,
6780 };
6781 let nv = n_vocab as i32;
6782 let __s_b1 = self.gpu.stream();
6783 let mut b1 = __s_b1.launch_builder(&f1);
6784 b1.arg(logits).arg(tok).arg(&mut part).arg(&nv);
6785 unsafe {
6786 b1.launch(cfg1)?;
6787 }
6788 let f2 = self.func("prob_of_token_final_f32");
6789 let cfg2 = LaunchConfig {
6790 grid_dim: (1, 1, 1),
6791 block_dim: (256, 1, 1),
6792 shared_mem_bytes: 0,
6793 };
6794 let nbi = nb as i32;
6795 let __s_b2 = self.gpu.stream();
6796 let mut b2 = __s_b2.launch_builder(&f2);
6797 b2.arg(&part).arg(p_out).arg(&nbi);
6798 unsafe {
6799 b2.launch(cfg2)?;
6800 }
6801 Ok(())
6802 }
6803
6804 pub fn argmax_token_device(
6805 &self,
6806 logits: &CudaSlice<f32>,
6807 n_vocab: usize,
6808 ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
6809 let mut tok = unsafe { self.gpu.stream().alloc::<u32>(1)? };
6810 self.argmax_token_device_into(logits, &mut tok, n_vocab)?;
6811 Ok(tok)
6812 }
6813 pub fn argmax_token_device_into(
6820 &self,
6821 logits: &CudaSlice<f32>,
6822 tok: &mut CudaSlice<u32>,
6823 n_vocab: usize,
6824 ) -> Result<(), Box<dyn std::error::Error>> {
6825 let nb = ARGMAX_NB;
6826 let f1 = self.func("argmax_partial_f32");
6827 let f2 = self.func("argmax_final_f32");
6828 let mut guard = self.argmax_partials.lock().unwrap();
6829 if guard.is_none() {
6830 let pv = self.gpu.stream().alloc_zeros::<f32>(nb)?;
6833 let pi = self.gpu.stream().alloc_zeros::<i32>(nb)?;
6834 *guard = Some((pv, pi));
6835 }
6836 let (part_v, part_i) = guard.as_mut().unwrap();
6837 let nv = n_vocab as i32;
6838 let nbi = nb as i32;
6839 let cfg1 = LaunchConfig {
6841 grid_dim: (nb as u32, 1, 1),
6842 block_dim: (256, 1, 1),
6843 shared_mem_bytes: 0,
6844 };
6845 let __s_b1 = self.gpu.stream();
6846 let mut b1 = __s_b1.launch_builder(&f1);
6847 b1.arg(logits).arg(&mut *part_v).arg(&mut *part_i).arg(&nv);
6848 unsafe {
6849 b1.launch(cfg1)?;
6850 }
6851 let cfg2 = LaunchConfig {
6853 grid_dim: (1, 1, 1),
6854 block_dim: (256, 1, 1),
6855 shared_mem_bytes: 0,
6856 };
6857 let __s_b2 = self.gpu.stream();
6858 let mut b2 = __s_b2.launch_builder(&f2);
6859 b2.arg(&*part_v).arg(&*part_i).arg(tok).arg(&nbi);
6860 unsafe {
6861 b2.launch(cfg2)?;
6862 }
6863 Ok(())
6864 }
6865 pub fn argmax_token_device_col(
6871 &self,
6872 logits: &CudaSlice<f32>,
6873 col: usize,
6874 n_vocab: usize,
6875 toks: &mut CudaSlice<u32>,
6876 out_idx: usize,
6877 ) -> Result<(), Box<dyn std::error::Error>> {
6878 let nb = ARGMAX_NB;
6879 let f1 = self.func("argmax_partial_f32");
6880 let f2 = self.func("argmax_final_f32");
6881 let mut guard = self.argmax_partials.lock().unwrap();
6882 if guard.is_none() {
6883 let pv = self.gpu.stream().alloc_zeros::<f32>(nb)?;
6884 let pi = self.gpu.stream().alloc_zeros::<i32>(nb)?;
6885 *guard = Some((pv, pi));
6886 }
6887 let (part_v, part_i) = guard.as_mut().unwrap();
6888 let col_view = logits.slice(col * n_vocab..(col + 1) * n_vocab);
6889 let nv = n_vocab as i32;
6890 let nbi = nb as i32;
6891 let cfg1 = LaunchConfig {
6892 grid_dim: (nb as u32, 1, 1),
6893 block_dim: (256, 1, 1),
6894 shared_mem_bytes: 0,
6895 };
6896 let __s_b1 = self.gpu.stream();
6897 let mut b1 = __s_b1.launch_builder(&f1);
6898 b1.arg(&col_view)
6899 .arg(&mut *part_v)
6900 .arg(&mut *part_i)
6901 .arg(&nv);
6902 unsafe {
6903 b1.launch(cfg1)?;
6904 }
6905 let mut tok_view = toks.slice_mut(out_idx..out_idx + 1);
6906 let cfg2 = LaunchConfig {
6907 grid_dim: (1, 1, 1),
6908 block_dim: (256, 1, 1),
6909 shared_mem_bytes: 0,
6910 };
6911 let __s_b2 = self.gpu.stream();
6912 let mut b2 = __s_b2.launch_builder(&f2);
6913 b2.arg(&*part_v).arg(&*part_i).arg(&mut tok_view).arg(&nbi);
6914 unsafe {
6915 b2.launch(cfg2)?;
6916 }
6917 Ok(())
6918 }
6919 pub fn htod_u32_v(&self, v: &[u32]) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
6921 Ok(self.gpu.stream().clone_htod(v)?)
6922 }
6923 pub fn dtoh_u32(&self, d: &CudaSlice<u32>) -> Result<Vec<u32>, Box<dyn std::error::Error>> {
6924 let v = self.gpu.stream().clone_dtoh(d)?;
6925 self.gpu.stream().synchronize()?;
6926 Ok(v)
6927 }
6928 pub fn htod_u32_into(
6932 &self,
6933 dst: &mut CudaSlice<u32>,
6934 src: &[u32],
6935 ) -> Result<(), Box<dyn std::error::Error>> {
6936 let mut view = dst.slice_mut(0..src.len());
6937 self.gpu.stream().memcpy_htod(src, &mut view)?;
6938 Ok(())
6939 }
6940
6941 pub fn htod_i32_into(
6944 &self,
6945 dst: &mut CudaSlice<i32>,
6946 src: &[i32],
6947 ) -> Result<(), Box<dyn std::error::Error>> {
6948 let mut view = dst.slice_mut(0..src.len());
6949 self.gpu.stream().memcpy_htod(src, &mut view)?;
6950 Ok(())
6951 }
6952
6953 pub fn alloc_u32_zeroed(&self, n: usize) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
6954 let s = self.gpu.stream().alloc_zeros::<u32>(n)?;
6955 self.keep_if_capturing(&s);
6956 Ok(s)
6957 }
6958 pub fn embed_gather_device_into(
6961 &self,
6962 embd: &CudaSlice<u8>,
6963 token_d: &CudaSlice<u32>,
6964 x_out: &mut CudaSlice<f32>,
6965 n_embd: usize,
6966 qtype: i32,
6967 row_bytes: usize,
6968 ) -> Result<(), Box<dyn std::error::Error>> {
6969 let f = self.func("embed_gather_u32");
6970 let cfg = LaunchConfig {
6971 grid_dim: (((n_embd as u32 + 255) / 256).max(1), 1, 1),
6972 block_dim: (256, 1, 1),
6973 shared_mem_bytes: 0,
6974 };
6975 let (ne, qt, rb) = (n_embd as i32, qtype, row_bytes as i64);
6976 let __s_b = self.gpu.stream();
6977 let mut b = __s_b.launch_builder(&f);
6978 b.arg(embd)
6979 .arg(token_d)
6980 .arg(x_out)
6981 .arg(&ne)
6982 .arg(&qt)
6983 .arg(&rb);
6984 unsafe {
6985 b.launch(cfg)?;
6986 }
6987 Ok(())
6988 }
6989 pub fn dtoh_i32_one(&self, d: &CudaSlice<i32>) -> Result<i32, Box<dyn std::error::Error>> {
6991 let v = self.gpu.stream().clone_dtoh(d)?;
6992 self.gpu.stream().synchronize()?;
6993 Ok(v[0])
6994 }
6995 pub fn i32_set_k(
7002 &self,
7003 dst: &mut CudaSlice<i32>,
7004 v: i32,
7005 ) -> Result<(), Box<dyn std::error::Error>> {
7006 let f = self.func("i32_set_k");
7007 let cfg = LaunchConfig {
7008 grid_dim: (1, 1, 1),
7009 block_dim: (1, 1, 1),
7010 shared_mem_bytes: 0,
7011 };
7012 let idx = 0i32;
7013 let __s_b = self.gpu.stream();
7014 let mut b = __s_b.launch_builder(&f);
7015 b.arg(dst).arg(&v).arg(&idx);
7016 unsafe {
7017 b.launch(cfg)?;
7018 }
7019 Ok(())
7020 }
7021
7022 pub fn set_i32_one(
7023 &self,
7024 d: &mut CudaSlice<i32>,
7025 v: i32,
7026 ) -> Result<(), Box<dyn std::error::Error>> {
7027 self.gpu.stream().memcpy_htod(&[v], d)?;
7028 Ok(())
7029 }
7030 pub fn set_u32_one(
7033 &self,
7034 d: &mut CudaSlice<u32>,
7035 v: u32,
7036 ) -> Result<(), Box<dyn std::error::Error>> {
7037 self.gpu.stream().memcpy_htod(&[v], d)?;
7038 Ok(())
7039 }
7040 pub fn dtoh_u32_one(&self, d: &CudaSlice<u32>) -> Result<u32, Box<dyn std::error::Error>> {
7042 let v = self.gpu.stream().clone_dtoh(d)?;
7043 self.gpu.stream().synchronize()?;
7044 Ok(v[0])
7045 }
7046 pub fn upload_u8(&self, bytes: &[u8]) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
7048 Ok(self.gpu.stream().clone_htod(bytes)?)
7049 }
7050 pub fn embed_gather_device(
7054 &self,
7055 embd: &CudaSlice<u8>,
7056 token_d: &CudaSlice<u32>,
7057 n_embd: usize,
7058 qtype: i32,
7059 row_bytes: usize,
7060 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7061 let f = self.func("embed_gather_u32");
7062 let mut x = self.alloc_uninit::<f32>(n_embd)?;
7063 let cfg = LaunchConfig {
7064 grid_dim: (((n_embd as u32 + 255) / 256).max(1), 1, 1),
7065 block_dim: (256, 1, 1),
7066 shared_mem_bytes: 0,
7067 };
7068 let (ne, qt, rb) = (n_embd as i32, qtype, row_bytes as i64);
7069 let __s_b = self.gpu.stream();
7070 let mut b = __s_b.launch_builder(&f);
7071 b.arg(embd)
7072 .arg(token_d)
7073 .arg(&mut x)
7074 .arg(&ne)
7075 .arg(&qt)
7076 .arg(&rb);
7077 unsafe {
7078 b.launch(cfg)?;
7079 }
7080 Ok(x)
7081 }
7082
7083 pub fn embed_gather_device_t(
7087 &self,
7088 embd: &CudaSlice<u8>,
7089 tokens: &[u32],
7090 n_embd: usize,
7091 qtype: i32,
7092 row_bytes: usize,
7093 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7094 let t = tokens.len();
7095 let tok_d = self.gpu.stream().clone_htod(tokens)?;
7096 let f = self.func("embed_gather_u32_t");
7097 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7098 let cfg = LaunchConfig {
7099 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7100 block_dim: (256, 1, 1),
7101 shared_mem_bytes: 0,
7102 };
7103 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7104 let __s_b = self.gpu.stream();
7105 let mut b = __s_b.launch_builder(&f);
7106 b.arg(embd)
7107 .arg(&tok_d)
7108 .arg(&mut x)
7109 .arg(&ne)
7110 .arg(&qt)
7111 .arg(&rb)
7112 .arg(&ti);
7113 unsafe {
7114 b.launch(cfg)?;
7115 }
7116 Ok(x)
7117 }
7118
7119 pub fn embed_gather_device_tv(
7124 &self,
7125 embd: &CudaSlice<u8>,
7126 tok_v: &cudarc::driver::CudaView<u32>,
7127 t: usize,
7128 n_embd: usize,
7129 qtype: i32,
7130 row_bytes: usize,
7131 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7132 let f = self.func("embed_gather_u32_t");
7133 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7134 let cfg = LaunchConfig {
7135 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7136 block_dim: (256, 1, 1),
7137 shared_mem_bytes: 0,
7138 };
7139 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7140 let __s_b = self.gpu.stream();
7141 let mut b = __s_b.launch_builder(&f);
7142 b.arg(embd)
7143 .arg(tok_v)
7144 .arg(&mut x)
7145 .arg(&ne)
7146 .arg(&qt)
7147 .arg(&rb)
7148 .arg(&ti);
7149 unsafe {
7150 b.launch(cfg)?;
7151 }
7152 Ok(x)
7153 }
7154
7155 pub fn embed_gather_device_td(
7156 &self,
7157 embd: &CudaSlice<u8>,
7158 tok_d: &CudaSlice<u32>,
7159 t: usize,
7160 n_embd: usize,
7161 qtype: i32,
7162 row_bytes: usize,
7163 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7164 let f = self.func("embed_gather_u32_t");
7165 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7166 let cfg = LaunchConfig {
7167 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7168 block_dim: (256, 1, 1),
7169 shared_mem_bytes: 0,
7170 };
7171 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7172 let __s_b = self.gpu.stream();
7173 let mut b = __s_b.launch_builder(&f);
7174 b.arg(embd)
7175 .arg(tok_d)
7176 .arg(&mut x)
7177 .arg(&ne)
7178 .arg(&qt)
7179 .arg(&rb)
7180 .arg(&ti);
7181 unsafe {
7182 b.launch(cfg)?;
7183 }
7184 Ok(x)
7185 }
7186
7187 #[inline]
7193 fn keep_if_capturing<T: cudarc::driver::DeviceRepr + Send + 'static>(&self, s: &CudaSlice<T>) {
7195 if self
7196 .capture_keep_on
7197 .load(std::sync::atomic::Ordering::Relaxed)
7198 {
7199 self.capture_keep.lock().unwrap().push(Box::new(s.clone()));
7200 }
7201 }
7202
7203 fn alloc_uninit<T: cudarc::driver::DeviceRepr + Send + 'static>(
7204 &self,
7205 n: usize,
7206 ) -> Result<CudaSlice<T>, Box<dyn std::error::Error>> {
7207 let mut s = unsafe { self.gpu.stream().alloc::<T>(n)? };
7208 {
7212 static Z: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7213 if *Z.get_or_init(|| std::env::var("MEMRA_DEBUG_ZERO_ALLOCS").as_deref() == Ok("1")) {
7214 use cudarc::driver::DevicePtrMut;
7216 let n_bytes = s.len() * std::mem::size_of::<T>();
7217 let stream = self.gpu.stream();
7218 let (p_, _g) = s.device_ptr_mut(&stream);
7219 unsafe {
7220 cudarc::driver::sys::cuMemsetD8Async(p_, 0, n_bytes, stream.cu_stream())
7221 .result()?;
7222 }
7223 }
7224 }
7225 self.keep_if_capturing(&s);
7226 Ok(s)
7227 }
7228
7229 pub fn uninit_q8_pair(
7234 &self,
7235 n: usize,
7236 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7237 Ok((
7238 self.alloc_uninit::<i8>(n)?,
7239 self.alloc_uninit::<f32>(n / 32)?,
7240 ))
7241 }
7242
7243 pub fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7244 self.alloc_uninit::<f32>(n)
7245 }
7246
7247 pub fn alloc_i8_uninit(&self, n: usize) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
7249 self.alloc_uninit::<i8>(n)
7250 }
7251
7252 #[allow(clippy::too_many_arguments)]
7256 pub fn rms_norm3(
7257 &self,
7258 x: &CudaSlice<f32>,
7259 w0: &CudaSlice<f32>,
7260 w1: &CudaSlice<f32>,
7261 w2: &CudaSlice<f32>,
7262 d0: &mut CudaSlice<f32>,
7263 d1: &mut CudaSlice<f32>,
7264 d2: &mut CudaSlice<f32>,
7265 ncols: usize,
7266 nrows: usize,
7267 eps: f32,
7268 ) -> Result<(), Box<dyn std::error::Error>> {
7269 let f = self.func("rms_norm3_f32");
7270 let cfg = LaunchConfig {
7271 grid_dim: (nrows as u32, 1, 1),
7272 block_dim: (rms_block(), 1, 1),
7273 shared_mem_bytes: 0,
7274 };
7275 let (nc, e) = (ncols as i32, eps);
7276 let __s_b = self.gpu.stream();
7277 let mut b = __s_b.launch_builder(&f);
7278 b.arg(x)
7279 .arg(w0)
7280 .arg(w1)
7281 .arg(w2)
7282 .arg(d0)
7283 .arg(d1)
7284 .arg(d2)
7285 .arg(&nc)
7286 .arg(&e);
7287 unsafe {
7288 b.launch(cfg)?;
7289 }
7290 Ok(())
7291 }
7292
7293 #[allow(clippy::too_many_arguments)]
7295 pub fn qkvnorm_w_on_prefill(rows: usize, ncols: usize) -> bool {
7298 static WARP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7299 *WARP_ON.get_or_init(|| {
7300 std::env::var("MEMRA_QKVNORM_W")
7301 .map(|v| v != "0")
7302 .unwrap_or(true)
7303 }) && ncols % 4 == 0
7304 && rows >= 64
7305 }
7306
7307 #[allow(clippy::too_many_arguments)]
7310 pub fn rms_norm_qkv_w4b(
7311 &self,
7312 q: &CudaSlice<f32>,
7313 k: &CudaSlice<f32>,
7314 v: &CudaSlice<f32>,
7315 wq: &CudaSlice<f32>,
7316 wk: &CudaSlice<f32>,
7317 wv: &CudaSlice<f32>,
7318 dq: &mut CudaSlice<f32>,
7319 dk: &mut CudaSlice<f32>,
7320 dv: &mut CudaSlice<f32>,
7321 dvb: &mut CudaSlice<u8>,
7322 ncols: usize,
7323 rq: usize,
7324 rk: usize,
7325 eps: f32,
7326 vf16: bool,
7327 ) -> Result<(), Box<dyn std::error::Error>> {
7328 assert!(ncols % 4 == 0 && rq + 2 * rk >= 64);
7329 let f = self.func("rms_norm_qkv_w4b_f32");
7330 let rows = (rq + 2 * rk) as u32;
7331 let cfg = LaunchConfig {
7332 grid_dim: (rows.div_ceil(8), 1, 1),
7333 block_dim: (256, 1, 1),
7334 shared_mem_bytes: 0,
7335 };
7336 let (nc, rqi, rki, rvi, e) = (ncols as i32, rq as i32, rk as i32, rk as i32, eps);
7337 let vf = vf16 as i32;
7338 let __s_b = self.gpu.stream();
7339 let mut b = __s_b.launch_builder(&f);
7340 b.arg(q)
7341 .arg(k)
7342 .arg(v)
7343 .arg(wq)
7344 .arg(wk)
7345 .arg(wv)
7346 .arg(dq)
7347 .arg(dk)
7348 .arg(dv)
7349 .arg(&mut *dvb)
7350 .arg(&nc)
7351 .arg(&rqi)
7352 .arg(&rki)
7353 .arg(&rvi)
7354 .arg(&e)
7355 .arg(&vf);
7356 unsafe {
7357 b.launch(cfg)?;
7358 }
7359 Ok(())
7360 }
7361
7362 pub fn rms_norm_qkv(
7363 &self,
7364 q: &CudaSlice<f32>,
7365 k: &CudaSlice<f32>,
7366 v: &CudaSlice<f32>,
7367 wq: &CudaSlice<f32>,
7368 wk: &CudaSlice<f32>,
7369 wv: &CudaSlice<f32>,
7370 dq: &mut CudaSlice<f32>,
7371 dk: &mut CudaSlice<f32>,
7372 dv: &mut CudaSlice<f32>,
7373 ncols: usize,
7374 rq: usize,
7375 rk: usize,
7376 eps: f32,
7377 ) -> Result<(), Box<dyn std::error::Error>> {
7378 static WARP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7382 let warp_on = *WARP_ON.get_or_init(|| {
7383 std::env::var("MEMRA_QKVNORM_W")
7384 .map(|v| v != "0")
7385 .unwrap_or(true)
7386 });
7387 if warp_on && ncols % 4 == 0 && rq + 2 * rk >= 64 {
7390 let f = self.func("rms_norm_qkv_w4_f32");
7391 let rows = (rq + 2 * rk) as u32;
7392 let cfg = LaunchConfig {
7393 grid_dim: (rows.div_ceil(8), 1, 1),
7394 block_dim: (256, 1, 1),
7395 shared_mem_bytes: 0,
7396 };
7397 let (nc, rqi, rki, rvi, e) = (ncols as i32, rq as i32, rk as i32, rk as i32, eps);
7398 let __s_b = self.gpu.stream();
7399 let mut b = __s_b.launch_builder(&f);
7400 b.arg(q)
7401 .arg(k)
7402 .arg(v)
7403 .arg(wq)
7404 .arg(wk)
7405 .arg(wv)
7406 .arg(dq)
7407 .arg(dk)
7408 .arg(dv)
7409 .arg(&nc)
7410 .arg(&rqi)
7411 .arg(&rki)
7412 .arg(&rvi)
7413 .arg(&e);
7414 unsafe {
7415 b.launch(cfg)?;
7416 }
7417 return Ok(());
7418 }
7419 let f = self.func("rms_norm_qkv_f32");
7420 let grid = (rq + 2 * rk) as u32;
7421 let cfg = LaunchConfig {
7422 grid_dim: (grid, 1, 1),
7423 block_dim: (rms_block(), 1, 1),
7424 shared_mem_bytes: 0,
7425 };
7426 let (nc, rqi, rki, e) = (ncols as i32, rq as i32, rk as i32, eps);
7427 let __s_b = self.gpu.stream();
7428 let mut b = __s_b.launch_builder(&f);
7429 b.arg(q)
7430 .arg(k)
7431 .arg(v)
7432 .arg(wq)
7433 .arg(wk)
7434 .arg(wv)
7435 .arg(dq)
7436 .arg(dk)
7437 .arg(dv)
7438 .arg(&nc)
7439 .arg(&rqi)
7440 .arg(&rki)
7441 .arg(&e);
7442 unsafe {
7443 b.launch(cfg)?;
7444 }
7445 Ok(())
7446 }
7447
7448 #[allow(clippy::too_many_arguments)]
7450 pub fn rms_norm2x(
7451 &self,
7452 a: &CudaSlice<f32>,
7453 bb: &CudaSlice<f32>,
7454 wa: &CudaSlice<f32>,
7455 wb: &CudaSlice<f32>,
7456 da: &mut CudaSlice<f32>,
7457 db: &mut CudaSlice<f32>,
7458 ncols: usize,
7459 nrows: usize,
7460 eps: f32,
7461 ) -> Result<(), Box<dyn std::error::Error>> {
7462 let f = self.func("rms_norm2x_f32");
7463 let cfg = LaunchConfig {
7464 grid_dim: (2 * nrows as u32, 1, 1),
7465 block_dim: (rms_block(), 1, 1),
7466 shared_mem_bytes: 0,
7467 };
7468 let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
7469 let __s_b = self.gpu.stream();
7470 let mut b = __s_b.launch_builder(&f);
7471 b.arg(a)
7472 .arg(bb)
7473 .arg(wa)
7474 .arg(wb)
7475 .arg(da)
7476 .arg(db)
7477 .arg(&nc)
7478 .arg(&nr)
7479 .arg(&e);
7480 unsafe {
7481 b.launch(cfg)?;
7482 }
7483 Ok(())
7484 }
7485
7486 pub fn softcap(
7488 &self,
7489 y: &mut CudaSlice<f32>,
7490 cap: f32,
7491 n: usize,
7492 ) -> Result<(), Box<dyn std::error::Error>> {
7493 let f = self.func("softcap_f32");
7494 let cfg = LaunchConfig::for_num_elems(n as u32);
7495 let ni = n as i32;
7496 let __s_b = self.gpu.stream();
7497 let mut b = __s_b.launch_builder(&f);
7498 b.arg(y).arg(&cap).arg(&ni);
7499 unsafe {
7500 b.launch(cfg)?;
7501 }
7502 Ok(())
7503 }
7504
7505 pub fn mask_ids_rows(
7508 &self,
7509 y: &mut CudaSlice<f32>,
7510 ids: &CudaSlice<i32>,
7511 n_ids: usize,
7512 n_vocab: usize,
7513 t: usize,
7514 ) -> Result<(), Box<dyn std::error::Error>> {
7515 let f = self.func("mask_ids_rows_f32");
7516 let cfg = LaunchConfig::for_num_elems((n_ids * t) as u32);
7517 let (ni, nv, ti) = (n_ids as i32, n_vocab as i32, t as i32);
7518 let __s_b = self.gpu.stream();
7519 let mut b = __s_b.launch_builder(&f);
7520 b.arg(y).arg(ids).arg(&ni).arg(&nv).arg(&ti);
7521 unsafe {
7522 b.launch(cfg)?;
7523 }
7524 Ok(())
7525 }
7526
7527 #[allow(clippy::too_many_arguments)]
7529 pub fn add_scale_rms_norm(
7530 &self,
7531 a: &CudaSlice<f32>,
7532 b_in: &CudaSlice<f32>,
7533 c: f32,
7534 w: &CudaSlice<f32>,
7535 res: &mut CudaSlice<f32>,
7536 dst: &mut CudaSlice<f32>,
7537 ncols: usize,
7538 nrows: usize,
7539 eps: f32,
7540 ) -> Result<(), Box<dyn std::error::Error>> {
7541 let f = self.func("add_scale_rms_norm_f32");
7542 let cfg = LaunchConfig {
7543 grid_dim: (nrows as u32, 1, 1),
7544 block_dim: (rms_block(), 1, 1),
7545 shared_mem_bytes: 0,
7546 };
7547 let (nc, e2) = (ncols as i32, eps);
7548 let __s_b = self.gpu.stream();
7549 let mut b = __s_b.launch_builder(&f);
7550 b.arg(a)
7551 .arg(b_in)
7552 .arg(&c)
7553 .arg(w)
7554 .arg(res)
7555 .arg(dst)
7556 .arg(&nc)
7557 .arg(&e2);
7558 unsafe {
7559 b.launch(cfg)?;
7560 }
7561 Ok(())
7562 }
7563
7564 #[allow(clippy::too_many_arguments)]
7567 pub fn add_scale_rms_norm_q8_1(
7568 &self,
7569 a: &CudaSlice<f32>,
7570 b_in: &CudaSlice<f32>,
7571 c: f32,
7572 w: &CudaSlice<f32>,
7573 res: &mut CudaSlice<f32>,
7574 ncols: usize,
7575 nrows: usize,
7576 eps: f32,
7577 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7578 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7579 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7580 let (nc, e2) = (ncols as i32, eps);
7581 if Self::pdl_on() && Self::pdl_wb_on() {
7582 {
7583 use cudarc::driver::{DevicePtr, DevicePtrMut};
7584 let s = &self.gpu.stream();
7585 let (pa, _g0) = a.device_ptr(s);
7586 let (pb, _g1) = b_in.device_ptr(s);
7587 let (pw, _g2) = w.device_ptr(s);
7588 let (pr, _g3) = res.device_ptr_mut(s);
7589 let (pq, _g4) = out_q.device_ptr_mut(s);
7590 let (pd, _g5) = out_d.device_ptr_mut(s);
7591 let mut ps = [
7592 &pa as *const _ as *mut std::ffi::c_void,
7593 &pb as *const _ as *mut _,
7594 &c as *const _ as *mut _,
7595 &pw as *const _ as *mut _,
7596 &pr as *const _ as *mut _,
7597 &pq as *const _ as *mut _,
7598 &pd as *const _ as *mut _,
7599 &nc as *const _ as *mut _,
7600 &e2 as *const _ as *mut _,
7601 ];
7602 unsafe {
7603 self.launch_pdl(
7604 "add_scale_rms_norm_q8_1",
7605 (nrows as u32, 1, 1),
7606 (rms_block(), 1, 1),
7607 &mut ps,
7608 )?;
7609 }
7610 }
7611 return Ok((out_q, out_d));
7612 }
7613 let f = self.func("add_scale_rms_norm_q8_1");
7614 let cfg = LaunchConfig {
7615 grid_dim: (nrows as u32, 1, 1),
7616 block_dim: (rms_block(), 1, 1),
7617 shared_mem_bytes: 0,
7618 };
7619 let __s_b = self.gpu.stream();
7620 let mut b = __s_b.launch_builder(&f);
7621 b.arg(a)
7622 .arg(b_in)
7623 .arg(&c)
7624 .arg(w)
7625 .arg(res)
7626 .arg(&mut out_q)
7627 .arg(&mut out_d)
7628 .arg(&nc)
7629 .arg(&e2);
7630 unsafe {
7631 b.launch(cfg)?;
7632 }
7633 Ok((out_q, out_d))
7634 }
7635
7636 #[allow(clippy::too_many_arguments)]
7638 pub fn add_scale_rms_norm_q8_1_into(
7639 &self,
7640 a: &CudaSlice<f32>,
7641 b_in: &CudaSlice<f32>,
7642 c: f32,
7643 w: &CudaSlice<f32>,
7644 res: &mut CudaSlice<f32>,
7645 ncols: usize,
7646 nrows: usize,
7647 eps: f32,
7648 out_q: &mut CudaSlice<i8>,
7649 out_d: &mut CudaSlice<f32>,
7650 ) -> Result<(), Box<dyn std::error::Error>> {
7651 debug_assert!(out_q.len() >= nrows * ncols && out_d.len() >= nrows * (ncols / 32));
7652 let (nc, e2) = (ncols as i32, eps);
7653 if Self::pdl_on() && Self::pdl_wb_on() {
7654 use cudarc::driver::{DevicePtr, DevicePtrMut};
7655 let s = &self.gpu.stream();
7656 let (pa, _g0) = a.device_ptr(s);
7657 let (pb, _g1) = b_in.device_ptr(s);
7658 let (pw, _g2) = w.device_ptr(s);
7659 let (pr, _g3) = res.device_ptr_mut(s);
7660 let (pq, _g4) = out_q.device_ptr_mut(s);
7661 let (pd, _g5) = out_d.device_ptr_mut(s);
7662 let mut ps = [
7663 &pa as *const _ as *mut std::ffi::c_void,
7664 &pb as *const _ as *mut _,
7665 &c as *const _ as *mut _,
7666 &pw as *const _ as *mut _,
7667 &pr as *const _ as *mut _,
7668 &pq as *const _ as *mut _,
7669 &pd as *const _ as *mut _,
7670 &nc as *const _ as *mut _,
7671 &e2 as *const _ as *mut _,
7672 ];
7673 unsafe {
7674 self.launch_pdl(
7675 "add_scale_rms_norm_q8_1",
7676 (nrows as u32, 1, 1),
7677 (rms_block(), 1, 1),
7678 &mut ps,
7679 )?;
7680 }
7681 return Ok(());
7682 }
7683 let f = self.func("add_scale_rms_norm_q8_1");
7684 let cfg = LaunchConfig {
7685 grid_dim: (nrows as u32, 1, 1),
7686 block_dim: (rms_block(), 1, 1),
7687 shared_mem_bytes: 0,
7688 };
7689 let __s_b = self.gpu.stream();
7690 let mut b = __s_b.launch_builder(&f);
7691 b.arg(a)
7692 .arg(b_in)
7693 .arg(&c)
7694 .arg(w)
7695 .arg(res)
7696 .arg(&mut *out_q)
7697 .arg(&mut *out_d)
7698 .arg(&nc)
7699 .arg(&e2);
7700 unsafe {
7701 b.launch(cfg)?;
7702 }
7703 Ok(())
7704 }
7705
7706 #[allow(clippy::too_many_arguments)]
7709 pub fn rms_pre_add_scale_rms_norm_q8_1(
7710 &self,
7711 a: &CudaSlice<f32>,
7712 wa: &CudaSlice<f32>,
7713 b_in: &CudaSlice<f32>,
7714 c: f32,
7715 w: &CudaSlice<f32>,
7716 res: &mut CudaSlice<f32>,
7717 ncols: usize,
7718 nrows: usize,
7719 eps: f32,
7720 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7721 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7722 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7723 let (nc, e2) = (ncols as i32, eps);
7724 if Self::pdl_on() {
7725 {
7726 use cudarc::driver::{DevicePtr, DevicePtrMut};
7727 let s = &self.gpu.stream();
7728 let (pa, _g0) = a.device_ptr(s);
7729 let (pwa, _g1) = wa.device_ptr(s);
7730 let (pb, _g2) = b_in.device_ptr(s);
7731 let (pw, _g3) = w.device_ptr(s);
7732 let (pr, _g4) = res.device_ptr_mut(s);
7733 let (pq, _g5) = out_q.device_ptr_mut(s);
7734 let (pd, _g6) = out_d.device_ptr_mut(s);
7735 let mut ps = [
7736 &pa as *const _ as *mut std::ffi::c_void,
7737 &pwa as *const _ as *mut _,
7738 &pb as *const _ as *mut _,
7739 &c as *const _ as *mut _,
7740 &pw as *const _ as *mut _,
7741 &pr as *const _ as *mut _,
7742 &pq as *const _ as *mut _,
7743 &pd as *const _ as *mut _,
7744 &nc as *const _ as *mut _,
7745 &e2 as *const _ as *mut _,
7746 ];
7747 unsafe {
7748 self.launch_pdl(
7749 "rms_pre_add_scale_rms_norm_q8_1",
7750 (nrows as u32, 1, 1),
7751 (rms_block(), 1, 1),
7752 &mut ps,
7753 )?;
7754 }
7755 }
7756 return Ok((out_q, out_d));
7757 }
7758 let f = self.func("rms_pre_add_scale_rms_norm_q8_1");
7759 let cfg = LaunchConfig {
7760 grid_dim: (nrows as u32, 1, 1),
7761 block_dim: (rms_block(), 1, 1),
7762 shared_mem_bytes: 0,
7763 };
7764 let __s_b = self.gpu.stream();
7765 let mut b = __s_b.launch_builder(&f);
7766 b.arg(a)
7767 .arg(wa)
7768 .arg(b_in)
7769 .arg(&c)
7770 .arg(w)
7771 .arg(res)
7772 .arg(&mut out_q)
7773 .arg(&mut out_d)
7774 .arg(&nc)
7775 .arg(&e2);
7776 unsafe {
7777 b.launch(cfg)?;
7778 }
7779 Ok((out_q, out_d))
7780 }
7781
7782 pub fn gelu_tanh_mul_q8_1(
7785 &self,
7786 gate: &CudaSlice<f32>,
7787 up: &cudarc::driver::CudaView<f32>,
7788 act: &mut CudaSlice<f32>,
7789 ncols: usize,
7790 nrows: usize,
7791 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7792 debug_assert!(ncols % 128 == 0);
7793 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7794 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7795 let nc = ncols as i32;
7796 if Self::pdl_on() {
7797 {
7798 use cudarc::driver::{DevicePtr, DevicePtrMut};
7799 let s = &self.gpu.stream();
7800 let (pg, _g0) = gate.device_ptr(s);
7801 let (pu, _g1) = up.device_ptr(s);
7802 let (pact, _g2) = act.device_ptr_mut(s);
7803 let (pq, _g3) = out_q.device_ptr_mut(s);
7804 let (pd, _g4) = out_d.device_ptr_mut(s);
7805 let mut ps = [
7806 &pg as *const _ as *mut std::ffi::c_void,
7807 &pu as *const _ as *mut _,
7808 &pact as *const _ as *mut _,
7809 &pq as *const _ as *mut _,
7810 &pd as *const _ as *mut _,
7811 &nc as *const _ as *mut _,
7812 ];
7813 unsafe {
7814 self.launch_pdl(
7815 "gelu_tanh_mul_q8_1",
7816 (nrows as u32, 1, 1),
7817 (rms_block(), 1, 1),
7818 &mut ps,
7819 )?;
7820 }
7821 }
7822 return Ok((out_q, out_d));
7823 }
7824 let f = self.func("gelu_tanh_mul_q8_1");
7825 let cfg = LaunchConfig {
7826 grid_dim: (nrows as u32, 1, 1),
7827 block_dim: (rms_block(), 1, 1),
7828 shared_mem_bytes: 0,
7829 };
7830 let __s_b = self.gpu.stream();
7831 let mut b = __s_b.launch_builder(&f);
7832 b.arg(gate)
7833 .arg(up)
7834 .arg(act)
7835 .arg(&mut out_q)
7836 .arg(&mut out_d)
7837 .arg(&nc);
7838 unsafe {
7839 b.launch(cfg)?;
7840 }
7841 Ok((out_q, out_d))
7842 }
7843
7844 #[allow(clippy::too_many_arguments)]
7846 pub fn gelu_tanh_mul_q8_1_into(
7847 &self,
7848 gate: &CudaSlice<f32>,
7849 up: &cudarc::driver::CudaView<f32>,
7850 act: &mut CudaSlice<f32>,
7851 ncols: usize,
7852 nrows: usize,
7853 out_q: &mut CudaSlice<i8>,
7854 out_d: &mut CudaSlice<f32>,
7855 ) -> Result<(), Box<dyn std::error::Error>> {
7856 debug_assert!(ncols % 128 == 0);
7857 debug_assert!(out_q.len() >= nrows * ncols && out_d.len() >= nrows * (ncols / 32));
7858 let nc = ncols as i32;
7859 if Self::pdl_on() {
7860 use cudarc::driver::{DevicePtr, DevicePtrMut};
7861 let s = &self.gpu.stream();
7862 let (pg, _g0) = gate.device_ptr(s);
7863 let (pu, _g1) = up.device_ptr(s);
7864 let (pact, _g2) = act.device_ptr_mut(s);
7865 let (pq, _g3) = out_q.device_ptr_mut(s);
7866 let (pd, _g4) = out_d.device_ptr_mut(s);
7867 let mut ps = [
7868 &pg as *const _ as *mut std::ffi::c_void,
7869 &pu as *const _ as *mut _,
7870 &pact as *const _ as *mut _,
7871 &pq as *const _ as *mut _,
7872 &pd as *const _ as *mut _,
7873 &nc as *const _ as *mut _,
7874 ];
7875 unsafe {
7876 self.launch_pdl(
7877 "gelu_tanh_mul_q8_1",
7878 (nrows as u32, 1, 1),
7879 (rms_block(), 1, 1),
7880 &mut ps,
7881 )?;
7882 }
7883 return Ok(());
7884 }
7885 let f = self.func("gelu_tanh_mul_q8_1");
7886 let cfg = LaunchConfig {
7887 grid_dim: (nrows as u32, 1, 1),
7888 block_dim: (rms_block(), 1, 1),
7889 shared_mem_bytes: 0,
7890 };
7891 let __s_b = self.gpu.stream();
7892 let mut b = __s_b.launch_builder(&f);
7893 b.arg(gate)
7894 .arg(up)
7895 .arg(&mut *act)
7896 .arg(&mut *out_q)
7897 .arg(&mut *out_d)
7898 .arg(&nc);
7899 unsafe {
7900 b.launch(cfg)?;
7901 }
7902 Ok(())
7903 }
7904
7905 #[allow(clippy::too_many_arguments)]
7907 pub fn add_rms_norm3_q8z(
7908 &self,
7909 a: &CudaSlice<f32>,
7910 b_in: &CudaSlice<f32>,
7911 w0: &CudaSlice<f32>,
7912 w1: &CudaSlice<f32>,
7913 w2: &CudaSlice<f32>,
7914 res: &mut CudaSlice<f32>,
7915 out1: &mut CudaSlice<f32>,
7916 ncols: usize,
7917 nrows: usize,
7918 eps: f32,
7919 ) -> Result<
7920 (
7921 (CudaSlice<i8>, CudaSlice<f32>),
7922 (CudaSlice<i8>, CudaSlice<f32>),
7923 ),
7924 Box<dyn std::error::Error>,
7925 > {
7926 let mut q0 = self.alloc_uninit::<i8>(nrows * ncols)?;
7927 let mut d0 = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7928 let mut q2 = self.alloc_uninit::<i8>(nrows * ncols)?;
7929 let mut d2 = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7930 let f = self.func("add_rms_norm3_q8z_f32");
7931 let cfg = LaunchConfig {
7932 grid_dim: (nrows as u32, 1, 1),
7933 block_dim: (rms_block(), 1, 1),
7934 shared_mem_bytes: 0,
7935 };
7936 let (nc, e2) = (ncols as i32, eps);
7937 let __s_b = self.gpu.stream();
7938 let mut b = __s_b.launch_builder(&f);
7939 b.arg(a)
7940 .arg(b_in)
7941 .arg(w0)
7942 .arg(w1)
7943 .arg(w2)
7944 .arg(res)
7945 .arg(&mut q0)
7946 .arg(&mut d0)
7947 .arg(out1)
7948 .arg(&mut q2)
7949 .arg(&mut d2)
7950 .arg(&nc)
7951 .arg(&e2);
7952 unsafe {
7953 b.launch(cfg)?;
7954 }
7955 Ok(((q0, d0), (q2, d2)))
7956 }
7957
7958 #[allow(clippy::too_many_arguments)]
7960 pub fn add_rms_norm3(
7961 &self,
7962 a: &CudaSlice<f32>,
7963 b_in: &CudaSlice<f32>,
7964 w0: &CudaSlice<f32>,
7965 w1: &CudaSlice<f32>,
7966 w2: &CudaSlice<f32>,
7967 res: &mut CudaSlice<f32>,
7968 d0: &mut CudaSlice<f32>,
7969 d1: &mut CudaSlice<f32>,
7970 d2: &mut CudaSlice<f32>,
7971 ncols: usize,
7972 nrows: usize,
7973 eps: f32,
7974 ) -> Result<(), Box<dyn std::error::Error>> {
7975 let f = self.func("add_rms_norm3_f32");
7976 let cfg = LaunchConfig {
7977 grid_dim: (nrows as u32, 1, 1),
7978 block_dim: (rms_block(), 1, 1),
7979 shared_mem_bytes: 0,
7980 };
7981 let (nc, e2) = (ncols as i32, eps);
7982 let __s_b = self.gpu.stream();
7983 let mut b = __s_b.launch_builder(&f);
7984 b.arg(a)
7985 .arg(b_in)
7986 .arg(w0)
7987 .arg(w1)
7988 .arg(w2)
7989 .arg(res)
7990 .arg(d0)
7991 .arg(d1)
7992 .arg(d2)
7993 .arg(&nc)
7994 .arg(&e2);
7995 unsafe {
7996 b.launch(cfg)?;
7997 }
7998 Ok(())
7999 }
8000
8001 pub fn add_scale(
8003 &self,
8004 a: &CudaSlice<f32>,
8005 b_in: &CudaSlice<f32>,
8006 c: f32,
8007 dst: &mut CudaSlice<f32>,
8008 n: usize,
8009 ) -> Result<(), Box<dyn std::error::Error>> {
8010 let f = self.func("add_scale_f32");
8011 let cfg = LaunchConfig::for_num_elems(n as u32);
8012 let ni = n as i32;
8013 let __s_b = self.gpu.stream();
8014 let mut b = __s_b.launch_builder(&f);
8015 b.arg(a).arg(b_in).arg(&c).arg(dst).arg(&ni);
8016 unsafe {
8017 b.launch(cfg)?;
8018 }
8019 Ok(())
8020 }
8021
8022 pub fn layer_norm_bias(
8024 &self,
8025 x: &CudaSlice<f32>,
8026 w: &CudaSlice<f32>,
8027 b: &CudaSlice<f32>,
8028 dst: &mut CudaSlice<f32>,
8029 ncols: usize,
8030 nrows: usize,
8031 eps: f32,
8032 ) -> Result<(), Box<dyn std::error::Error>> {
8033 let f = self.func("layer_norm_bias_f32");
8034 let (nc, e) = (ncols as i32, eps);
8035 let cfg = LaunchConfig {
8036 grid_dim: (nrows as u32, 1, 1),
8037 block_dim: (256, 1, 1),
8038 shared_mem_bytes: 0,
8039 };
8040 let __s_b = self.gpu.stream();
8041 let mut lb = __s_b.launch_builder(&f);
8042 lb.arg(x).arg(w).arg(b).arg(&mut *dst).arg(&nc).arg(&e);
8043 unsafe {
8044 lb.launch(cfg)?;
8045 }
8046 Ok(())
8047 }
8048
8049 pub fn gelu_tanh(
8051 &self,
8052 x: &CudaSlice<f32>,
8053 dst: &mut CudaSlice<f32>,
8054 n: usize,
8055 ) -> Result<(), Box<dyn std::error::Error>> {
8056 let f = self.func("gelu_tanh_f32");
8057 let ni = n as i64;
8058 let cfg = LaunchConfig {
8059 grid_dim: (n.div_ceil(256) as u32, 1, 1),
8060 block_dim: (256, 1, 1),
8061 shared_mem_bytes: 0,
8062 };
8063 let __s_b = self.gpu.stream();
8064 let mut lb = __s_b.launch_builder(&f);
8065 lb.arg(x).arg(&mut *dst).arg(&ni);
8066 unsafe {
8067 lb.launch(cfg)?;
8068 }
8069 Ok(())
8070 }
8071
8072 pub fn row_softmax(
8074 &self,
8075 x: &mut CudaSlice<f32>,
8076 ncols: usize,
8077 nrows: usize,
8078 ) -> Result<(), Box<dyn std::error::Error>> {
8079 let f = self.func("row_softmax_f32");
8080 let nc = ncols as i32;
8081 let cfg = LaunchConfig {
8082 grid_dim: (nrows as u32, 1, 1),
8083 block_dim: (256, 1, 1),
8084 shared_mem_bytes: 0,
8085 };
8086 let __s_b = self.gpu.stream();
8087 let mut lb = __s_b.launch_builder(&f);
8088 lb.arg(&mut *x).arg(&nc);
8089 unsafe {
8090 lb.launch(cfg)?;
8091 }
8092 Ok(())
8093 }
8094
8095 pub fn rms_norm(
8096 &self,
8097 x: &CudaSlice<f32>,
8098 w: &CudaSlice<f32>,
8099 dst: &mut CudaSlice<f32>,
8100 ncols: usize,
8101 nrows: usize,
8102 eps: f32,
8103 ) -> Result<(), Box<dyn std::error::Error>> {
8104 let (nc, e) = (ncols as i32, eps);
8105 if Self::pdl_on() && Self::pdl_wb_on() {
8106 use cudarc::driver::{DevicePtr, DevicePtrMut};
8107 let s = &self.gpu.stream();
8108 let (px, _g0) = x.device_ptr(s);
8109 let (pw, _g1) = w.device_ptr(s);
8110 let (pd, _g2) = dst.device_ptr_mut(s);
8111 let mut ps = [
8112 &px as *const _ as *mut std::ffi::c_void,
8113 &pw as *const _ as *mut _,
8114 &pd as *const _ as *mut _,
8115 &nc as *const _ as *mut _,
8116 &e as *const _ as *mut _,
8117 ];
8118 unsafe {
8119 self.launch_pdl(
8120 "rms_norm_f32",
8121 (nrows as u32, 1, 1),
8122 (rms_block(), 1, 1),
8123 &mut ps,
8124 )?;
8125 }
8126 return Ok(());
8127 }
8128 let f = self.func("rms_norm_f32");
8129 let cfg = LaunchConfig {
8130 grid_dim: (nrows as u32, 1, 1),
8131 block_dim: (rms_block(), 1, 1),
8132 shared_mem_bytes: 0,
8133 };
8134 let __s_b = self.gpu.stream();
8135 let mut b = __s_b.launch_builder(&f);
8136 b.arg(x).arg(w).arg(dst).arg(&nc).arg(&e);
8137 unsafe {
8138 b.launch(cfg)?;
8139 }
8140 Ok(())
8141 }
8142
8143 pub fn rms_norm_decode(
8151 &self,
8152 x: &CudaSlice<f32>,
8153 w: &CudaSlice<f32>,
8154 dst: &mut CudaSlice<f32>,
8155 ncols: usize,
8156 nrows: usize,
8157 eps: f32,
8158 ) -> Result<(), Box<dyn std::error::Error>> {
8159 let f = self.func("rms_norm_f32");
8160 let cfg = LaunchConfig {
8161 grid_dim: (nrows as u32, 1, 1),
8162 block_dim: (1024, 1, 1),
8163 shared_mem_bytes: 0,
8164 };
8165 let (nc, e) = (ncols as i32, eps);
8166 let __s_b = self.gpu.stream();
8167 let mut b = __s_b.launch_builder(&f);
8168 b.arg(x).arg(w).arg(dst).arg(&nc).arg(&e);
8169 unsafe {
8170 b.launch(cfg)?;
8171 }
8172 Ok(())
8173 }
8174
8175 pub fn rms_norm_q8_1(
8179 &self,
8180 x: &CudaSlice<f32>,
8181 w: &CudaSlice<f32>,
8182 ncols: usize,
8183 nrows: usize,
8184 eps: f32,
8185 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8186 let nblk = ncols / 32;
8187 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
8188 let mut d = self.alloc_uninit::<f32>(nrows * nblk)?;
8189 let (nc, e) = (ncols as i32, eps);
8190 if Self::pdl_on() {
8191 {
8192 use cudarc::driver::{DevicePtr, DevicePtrMut};
8193 let s = &self.gpu.stream();
8194 let (px, _g0) = x.device_ptr(s);
8195 let (pw, _g1) = w.device_ptr(s);
8196 let (pq, _g2) = q.device_ptr_mut(s);
8197 let (pd, _g3) = d.device_ptr_mut(s);
8198 let mut ps = [
8199 &px as *const _ as *mut std::ffi::c_void,
8200 &pw as *const _ as *mut _,
8201 &pq as *const _ as *mut _,
8202 &pd as *const _ as *mut _,
8203 &nc as *const _ as *mut _,
8204 &e as *const _ as *mut _,
8205 ];
8206 unsafe {
8207 self.launch_pdl("rms_norm_q8_1", (nrows as u32, 1, 1), (1024, 1, 1), &mut ps)?;
8208 }
8209 }
8210 return Ok((q, d));
8211 }
8212 let f = self.func("rms_norm_q8_1");
8213 let cfg = LaunchConfig {
8216 grid_dim: (nrows as u32, 1, 1),
8217 block_dim: (1024, 1, 1),
8218 shared_mem_bytes: 0,
8219 };
8220 let __s_b = self.gpu.stream();
8221 let mut b = __s_b.launch_builder(&f);
8222 b.arg(x).arg(w).arg(&mut q).arg(&mut d).arg(&nc).arg(&e);
8223 unsafe {
8224 b.launch(cfg)?;
8225 }
8226 Ok((q, d))
8227 }
8228
8229 pub fn rms_norm_q8_1_into(
8232 &self,
8233 x: &CudaSlice<f32>,
8234 w: &CudaSlice<f32>,
8235 ncols: usize,
8236 nrows: usize,
8237 eps: f32,
8238 q: &mut CudaSlice<i8>,
8239 d: &mut CudaSlice<f32>,
8240 ) -> Result<(), Box<dyn std::error::Error>> {
8241 let nblk = ncols / 32;
8242 debug_assert!(q.len() >= nrows * ncols && d.len() >= nrows * nblk);
8243 let (nc, e) = (ncols as i32, eps);
8244 if Self::pdl_on() {
8245 use cudarc::driver::{DevicePtr, DevicePtrMut};
8246 let s = &self.gpu.stream();
8247 let (px, _g0) = x.device_ptr(s);
8248 let (pw, _g1) = w.device_ptr(s);
8249 let (pq, _g2) = q.device_ptr_mut(s);
8250 let (pd, _g3) = d.device_ptr_mut(s);
8251 let mut ps = [
8252 &px as *const _ as *mut std::ffi::c_void,
8253 &pw as *const _ as *mut _,
8254 &pq as *const _ as *mut _,
8255 &pd as *const _ as *mut _,
8256 &nc as *const _ as *mut _,
8257 &e as *const _ as *mut _,
8258 ];
8259 unsafe {
8260 self.launch_pdl("rms_norm_q8_1", (nrows as u32, 1, 1), (1024, 1, 1), &mut ps)?;
8261 }
8262 return Ok(());
8263 }
8264 let f = self.func("rms_norm_q8_1");
8265 let cfg = LaunchConfig {
8266 grid_dim: (nrows as u32, 1, 1),
8267 block_dim: (1024, 1, 1),
8268 shared_mem_bytes: 0,
8269 };
8270 let __s_b = self.gpu.stream();
8271 let mut b = __s_b.launch_builder(&f);
8272 b.arg(x).arg(w).arg(&mut *q).arg(&mut *d).arg(&nc).arg(&e);
8273 unsafe {
8274 b.launch(cfg)?;
8275 }
8276 Ok(())
8277 }
8278
8279 pub fn quantize_q8_1_into(
8281 &self,
8282 x: &CudaSlice<f32>,
8283 m: usize,
8284 in_f: usize,
8285 q: &mut CudaSlice<i8>,
8286 d: &mut CudaSlice<f32>,
8287 ) -> Result<(), Box<dyn std::error::Error>> {
8288 let nblk = in_f / 32;
8289 debug_assert!(q.len() >= m * in_f && d.len() >= m * nblk);
8290 let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
8291 let (inf, mi) = (in_f as i32, m as i32);
8292 if Self::pdl_on() && Self::pdl_wb_on() {
8293 use cudarc::driver::{DevicePtr, DevicePtrMut};
8294 let s = &self.gpu.stream();
8295 let (px, _g0) = x.device_ptr(s);
8296 let (pq, _g1) = q.device_ptr_mut(s);
8297 let (pd, _g2) = d.device_ptr_mut(s);
8298 let mut ps = [
8299 &px as *const _ as *mut std::ffi::c_void,
8300 &pq as *const _ as *mut _,
8301 &pd as *const _ as *mut _,
8302 &inf as *const _ as *mut _,
8303 &mi as *const _ as *mut _,
8304 ];
8305 unsafe {
8306 self.launch_pdl("quantize_q8_1", cfg.grid_dim, cfg.block_dim, &mut ps)?;
8307 }
8308 return Ok(());
8309 }
8310 let f = self.func("quantize_q8_1");
8311 let __s_b = self.gpu.stream();
8312 let mut b = __s_b.launch_builder(&f);
8313 b.arg(x).arg(&mut *q).arg(&mut *d).arg(&inf).arg(&mi);
8314 unsafe {
8315 b.launch(cfg)?;
8316 }
8317 Ok(())
8318 }
8319
8320 pub fn add_rms_norm_q8_1(
8324 &self,
8325 a: &CudaSlice<f32>,
8326 b_in: &CudaSlice<f32>,
8327 w: &CudaSlice<f32>,
8328 res: &mut CudaSlice<f32>,
8329 ncols: usize,
8330 nrows: usize,
8331 eps: f32,
8332 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8333 let nblk = ncols / 32;
8334 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
8335 let mut d = self.alloc_uninit::<f32>(nrows * nblk)?;
8336 let f = self.func("add_rms_norm_q8_1");
8337 let cfg = LaunchConfig {
8339 grid_dim: (nrows as u32, 1, 1),
8340 block_dim: (1024, 1, 1),
8341 shared_mem_bytes: 0,
8342 };
8343 let (nc, e) = (ncols as i32, eps);
8344 let __s_bld = self.gpu.stream();
8345 let mut bld = __s_bld.launch_builder(&f);
8346 bld.arg(a)
8347 .arg(b_in)
8348 .arg(w)
8349 .arg(res)
8350 .arg(&mut q)
8351 .arg(&mut d)
8352 .arg(&nc)
8353 .arg(&e);
8354 unsafe {
8355 bld.launch(cfg)?;
8356 }
8357 Ok((q, d))
8358 }
8359
8360 pub fn add_rms_norm(
8364 &self,
8365 a: &CudaSlice<f32>,
8366 b: &CudaSlice<f32>,
8367 w: &CudaSlice<f32>,
8368 res: &mut CudaSlice<f32>,
8369 dst: &mut CudaSlice<f32>,
8370 ncols: usize,
8371 nrows: usize,
8372 eps: f32,
8373 ) -> Result<(), Box<dyn std::error::Error>> {
8374 let (nc, e) = (ncols as i32, eps);
8375 if Self::pdl_on() && Self::pdl_wb_on() {
8376 use cudarc::driver::{DevicePtr, DevicePtrMut};
8377 let s = &self.gpu.stream();
8378 let (pa, _g0) = a.device_ptr(s);
8379 let (pb, _g1) = b.device_ptr(s);
8380 let (pw, _g2) = w.device_ptr(s);
8381 let (pr, _g3) = res.device_ptr_mut(s);
8382 let (pd, _g4) = dst.device_ptr_mut(s);
8383 let mut ps = [
8384 &pa as *const _ as *mut std::ffi::c_void,
8385 &pb as *const _ as *mut _,
8386 &pw as *const _ as *mut _,
8387 &pr as *const _ as *mut _,
8388 &pd as *const _ as *mut _,
8389 &nc as *const _ as *mut _,
8390 &e as *const _ as *mut _,
8391 ];
8392 unsafe {
8393 self.launch_pdl(
8394 "add_rms_norm_f32",
8395 (nrows as u32, 1, 1),
8396 (rms_block(), 1, 1),
8397 &mut ps,
8398 )?;
8399 }
8400 return Ok(());
8401 }
8402 let f = self.func("add_rms_norm_f32");
8403 let cfg = LaunchConfig {
8404 grid_dim: (nrows as u32, 1, 1),
8405 block_dim: (rms_block(), 1, 1),
8406 shared_mem_bytes: 0,
8407 };
8408 let __s_b2 = self.gpu.stream();
8409 let mut b2 = __s_b2.launch_builder(&f);
8410 b2.arg(a)
8411 .arg(b)
8412 .arg(w)
8413 .arg(&mut *res)
8414 .arg(&mut *dst)
8415 .arg(&nc)
8416 .arg(&e);
8417 unsafe {
8418 b2.launch(cfg)?;
8419 }
8420 Ok(())
8421 }
8422
8423 #[allow(clippy::too_many_arguments)]
8426 pub fn rms_pre_add_rms_norm(
8427 &self,
8428 a: &CudaSlice<f32>,
8429 wa: &CudaSlice<f32>,
8430 b: &CudaSlice<f32>,
8431 w: &CudaSlice<f32>,
8432 res: &mut CudaSlice<f32>,
8433 dst: &mut CudaSlice<f32>,
8434 ncols: usize,
8435 nrows: usize,
8436 eps: f32,
8437 ) -> Result<(), Box<dyn std::error::Error>> {
8438 let f = self.func("rms_pre_add_rms_norm_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 (nc, e) = (ncols as i32, eps);
8445 let __s_b2 = self.gpu.stream();
8446 let mut b2 = __s_b2.launch_builder(&f);
8447 b2.arg(a)
8448 .arg(wa)
8449 .arg(b)
8450 .arg(w)
8451 .arg(&mut *res)
8452 .arg(&mut *dst)
8453 .arg(&nc)
8454 .arg(&e);
8455 unsafe {
8456 b2.launch(cfg)?;
8457 }
8458 Ok(())
8459 }
8460
8461 #[allow(clippy::too_many_arguments)]
8463 pub fn rms_pre_add_rms_norm_q8z(
8464 &self,
8465 a: &CudaSlice<f32>,
8466 wa: &CudaSlice<f32>,
8467 b: &CudaSlice<f32>,
8468 w: &CudaSlice<f32>,
8469 res: &mut CudaSlice<f32>,
8470 dst: &mut CudaSlice<f32>,
8471 ncols: usize,
8472 nrows: usize,
8473 eps: f32,
8474 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8475 debug_assert!(ncols % 128 == 0);
8476 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
8477 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
8478 let (nc, e) = (ncols as i32, eps);
8479 if Self::pdl_on() {
8480 {
8481 use cudarc::driver::{DevicePtr, DevicePtrMut};
8482 let s = &self.gpu.stream();
8483 let (pa, _g0) = a.device_ptr(s);
8484 let (pwa, _g1) = wa.device_ptr(s);
8485 let (pb, _g2) = b.device_ptr(s);
8486 let (pw, _g3) = w.device_ptr(s);
8487 let (pr, _g4) = res.device_ptr_mut(s);
8488 let (pdst, _g5) = dst.device_ptr_mut(s);
8489 let (pq, _g6) = out_q.device_ptr_mut(s);
8490 let (pd, _g7) = out_d.device_ptr_mut(s);
8491 let mut ps = [
8492 &pa as *const _ as *mut std::ffi::c_void,
8493 &pwa as *const _ as *mut _,
8494 &pb as *const _ as *mut _,
8495 &pw as *const _ as *mut _,
8496 &pr as *const _ as *mut _,
8497 &pdst as *const _ as *mut _,
8498 &pq as *const _ as *mut _,
8499 &pd as *const _ as *mut _,
8500 &nc as *const _ as *mut _,
8501 &e as *const _ as *mut _,
8502 ];
8503 unsafe {
8504 self.launch_pdl(
8505 "rms_pre_add_rms_norm_q8z_f32",
8506 (nrows as u32, 1, 1),
8507 (rms_block(), 1, 1),
8508 &mut ps,
8509 )?;
8510 }
8511 }
8512 return Ok((out_q, out_d));
8513 }
8514 let f = self.func("rms_pre_add_rms_norm_q8z_f32");
8515 let cfg = LaunchConfig {
8516 grid_dim: (nrows as u32, 1, 1),
8517 block_dim: (rms_block(), 1, 1),
8518 shared_mem_bytes: 0,
8519 };
8520 let __s_b2 = self.gpu.stream();
8521 let mut b2 = __s_b2.launch_builder(&f);
8522 b2.arg(a)
8523 .arg(wa)
8524 .arg(b)
8525 .arg(w)
8526 .arg(&mut *res)
8527 .arg(&mut *dst)
8528 .arg(&mut out_q)
8529 .arg(&mut out_d)
8530 .arg(&nc)
8531 .arg(&e);
8532 unsafe {
8533 b2.launch(cfg)?;
8534 }
8535 Ok((out_q, out_d))
8536 }
8537
8538 pub fn build_q4_out_concat3(
8542 &self,
8543 w0: &crate::model::GpuTensor,
8544 w1: &crate::model::GpuTensor,
8545 w2: &crate::model::GpuTensor,
8546 ) -> Result<Option<crate::model::GpuTensor>, Box<dyn std::error::Error>> {
8547 use crate::model::GpuTensor;
8548 let part = |w: &GpuTensor| -> Option<(usize, usize)> {
8549 match w {
8550 GpuTensor::Quant {
8551 qtype,
8552 row_bytes,
8553 rp,
8554 ..
8555 } if *qtype == QT_Q4_0 && !*rp => Some((*row_bytes, w.out_features())),
8556 _ => None,
8557 }
8558 };
8559 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (part(w0), part(w1), part(w2))
8560 else {
8561 return Ok(None);
8562 };
8563 if rb0 != rb1
8564 || rb0 != rb2
8565 || w0.in_features() != w1.in_features()
8566 || w0.in_features() != w2.in_features()
8567 {
8568 return Ok(None);
8569 }
8570 fn bytes_of(w: &crate::model::GpuTensor) -> &CudaSlice<u8> {
8571 match w {
8572 crate::model::GpuTensor::Quant { bytes, .. } => bytes,
8573 _ => unreachable!(),
8574 }
8575 }
8576 let (b0, b1, b2) = (bytes_of(w0), bytes_of(w1), bytes_of(w2));
8577 let total = rb0 * (o0 + o1 + o2);
8578 let mut cat = self.alloc_u8(total)?;
8579 self.copy_u8_into(&mut cat, 0, b0, rb0 * o0)?;
8580 self.copy_u8_into(&mut cat, rb0 * o0, b1, rb1 * o1)?;
8581 self.copy_u8_into(&mut cat, rb0 * (o0 + o1), b2, rb2 * o2)?;
8582 Ok(Some(GpuTensor::Quant {
8583 bytes: cat,
8584 qtype: QT_Q4_0,
8585 row_bytes: rb0,
8586 ne: vec![w0.in_features() as u64, (o0 + o1 + o2) as u64],
8587 scale: 1.0,
8588 rp: false,
8589 #[cfg(memra_cutlass)]
8590 cutlass: None,
8591 fp8: None,
8592 blk: None,
8593 rp4: None,
8594 f16: None,
8595 }))
8596 }
8597
8598 #[allow(clippy::too_many_arguments)]
8600 pub fn rms_norm_qkv_rope_cat(
8601 &self,
8602 qkv: &CudaSlice<f32>,
8603 wq: &CudaSlice<f32>,
8604 wk: &CudaSlice<f32>,
8605 wv: &CudaSlice<f32>,
8606 q: &mut CudaSlice<f32>,
8607 k: &mut CudaSlice<f32>,
8608 v: &mut CudaSlice<f32>,
8609 head_dim: usize,
8610 rq: usize,
8611 rk: usize,
8612 pos: &CudaSlice<i32>,
8613 nh_q: usize,
8614 nh_k: usize,
8615 base: f32,
8616 freq_scale: f32,
8617 ff: Option<&CudaSlice<f32>>,
8618 eps: f32,
8619 ) -> Result<(), Box<dyn std::error::Error>> {
8620 let rows = rq + rk + rk;
8621 let theta_scale = base.powf(-2.0 / head_dim as f32);
8622 let (nc, rqi, rki, nhq, nhk) = (
8623 head_dim as i32,
8624 rq as i32,
8625 rk as i32,
8626 nh_q as i32,
8627 nh_k as i32,
8628 );
8629 if Self::pdl_on() {
8630 use cudarc::driver::{DevicePtr, DevicePtrMut};
8631 let s = &self.gpu.stream();
8632 let (pqkv, _g0) = qkv.device_ptr(s);
8633 let (pwq, _g1) = wq.device_ptr(s);
8634 let (pwk, _g2) = wk.device_ptr(s);
8635 let (pwv, _g3) = wv.device_ptr(s);
8636 let (pq, _g4) = q.device_ptr_mut(s);
8637 let (pk, _g5) = k.device_ptr_mut(s);
8638 let (pv, _g6) = v.device_ptr_mut(s);
8639 let (ppos, _g7) = pos.device_ptr(s);
8640 let (pff, _g8) = match ff {
8641 Some(t) => {
8642 let (p, g) = t.device_ptr(s);
8643 (p, Some(g))
8644 }
8645 None => (0, None),
8646 };
8647 let mut ps = [
8648 &pqkv as *const _ as *mut std::ffi::c_void,
8649 &pwq as *const _ as *mut _,
8650 &pwk as *const _ as *mut _,
8651 &pwv as *const _ as *mut _,
8652 &pq as *const _ as *mut _,
8653 &pk as *const _ as *mut _,
8654 &pv as *const _ as *mut _,
8655 &nc as *const _ as *mut _,
8656 &rqi as *const _ as *mut _,
8657 &rki as *const _ as *mut _,
8658 &ppos as *const _ as *mut _,
8659 &nhq as *const _ as *mut _,
8660 &nhk as *const _ as *mut _,
8661 &theta_scale as *const _ as *mut _,
8662 &freq_scale as *const _ as *mut _,
8663 &pff as *const _ as *mut _,
8664 &eps as *const _ as *mut _,
8665 ];
8666 unsafe {
8667 self.launch_pdl(
8668 "rms_norm_qkv_rope_cat_f32",
8669 (rows as u32, 1, 1),
8670 (rms_block(), 1, 1),
8671 &mut ps,
8672 )?;
8673 }
8674 return Ok(());
8675 }
8676 let f = self.func("rms_norm_qkv_rope_cat_f32");
8677 let cfg = LaunchConfig {
8678 grid_dim: (rows as u32, 1, 1),
8679 block_dim: (rms_block(), 1, 1),
8680 shared_mem_bytes: 0,
8681 };
8682 let __s_b = self.gpu.stream();
8683 let mut b = __s_b.launch_builder(&f);
8684 match ff {
8685 Some(t) => {
8686 b.arg(qkv)
8687 .arg(wq)
8688 .arg(wk)
8689 .arg(wv)
8690 .arg(&mut *q)
8691 .arg(&mut *k)
8692 .arg(&mut *v)
8693 .arg(&nc)
8694 .arg(&rqi)
8695 .arg(&rki)
8696 .arg(pos)
8697 .arg(&nhq)
8698 .arg(&nhk)
8699 .arg(&theta_scale)
8700 .arg(&freq_scale)
8701 .arg(t)
8702 .arg(&eps);
8703 unsafe {
8704 b.launch(cfg)?;
8705 }
8706 }
8707 None => {
8708 let null: u64 = 0;
8709 b.arg(qkv)
8710 .arg(wq)
8711 .arg(wk)
8712 .arg(wv)
8713 .arg(&mut *q)
8714 .arg(&mut *k)
8715 .arg(&mut *v)
8716 .arg(&nc)
8717 .arg(&rqi)
8718 .arg(&rki)
8719 .arg(pos)
8720 .arg(&nhq)
8721 .arg(&nhk)
8722 .arg(&theta_scale)
8723 .arg(&freq_scale)
8724 .arg(&null)
8725 .arg(&eps);
8726 unsafe {
8727 b.launch(cfg)?;
8728 }
8729 }
8730 }
8731 Ok(())
8732 }
8733
8734 #[allow(clippy::too_many_arguments)]
8736 pub fn rms_norm_qkv_rope(
8737 &self,
8738 q0: &CudaSlice<f32>,
8739 k0: &CudaSlice<f32>,
8740 v0: &CudaSlice<f32>,
8741 wq: &CudaSlice<f32>,
8742 wk: &CudaSlice<f32>,
8743 wv: &CudaSlice<f32>,
8744 q: &mut CudaSlice<f32>,
8745 k: &mut CudaSlice<f32>,
8746 v: &mut CudaSlice<f32>,
8747 head_dim: usize,
8748 rq: usize,
8749 rk: usize,
8750 pos: &CudaSlice<i32>,
8751 nh_q: usize,
8752 nh_k: usize,
8753 base: f32,
8754 freq_scale: f32,
8755 ff: Option<&CudaSlice<f32>>,
8756 eps: f32,
8757 ) -> Result<(), Box<dyn std::error::Error>> {
8758 let f = self.func("rms_norm_qkv_rope_f32");
8759 let rows = rq + rk + rk; let cfg = LaunchConfig {
8761 grid_dim: (rows as u32, 1, 1),
8762 block_dim: (rms_block(), 1, 1),
8763 shared_mem_bytes: 0,
8764 };
8765 let theta_scale = base.powf(-2.0 / head_dim as f32);
8766 let (nc, rqi, rki, nhq, nhk) = (
8767 head_dim as i32,
8768 rq as i32,
8769 rk as i32,
8770 nh_q as i32,
8771 nh_k as i32,
8772 );
8773 let __s_b = self.gpu.stream();
8774 let mut b = __s_b.launch_builder(&f);
8775 match ff {
8776 Some(t) => {
8777 b.arg(q0)
8778 .arg(k0)
8779 .arg(v0)
8780 .arg(wq)
8781 .arg(wk)
8782 .arg(wv)
8783 .arg(&mut *q)
8784 .arg(&mut *k)
8785 .arg(&mut *v)
8786 .arg(&nc)
8787 .arg(&rqi)
8788 .arg(&rki)
8789 .arg(pos)
8790 .arg(&nhq)
8791 .arg(&nhk)
8792 .arg(&theta_scale)
8793 .arg(&freq_scale)
8794 .arg(t)
8795 .arg(&eps);
8796 unsafe {
8797 b.launch(cfg)?;
8798 }
8799 }
8800 None => {
8801 let null: u64 = 0;
8802 b.arg(q0)
8803 .arg(k0)
8804 .arg(v0)
8805 .arg(wq)
8806 .arg(wk)
8807 .arg(wv)
8808 .arg(&mut *q)
8809 .arg(&mut *k)
8810 .arg(&mut *v)
8811 .arg(&nc)
8812 .arg(&rqi)
8813 .arg(&rki)
8814 .arg(pos)
8815 .arg(&nhq)
8816 .arg(&nhk)
8817 .arg(&theta_scale)
8818 .arg(&freq_scale)
8819 .arg(&null)
8820 .arg(&eps);
8821 unsafe {
8822 b.launch(cfg)?;
8823 }
8824 }
8825 }
8826 Ok(())
8827 }
8828
8829 #[allow(clippy::too_many_arguments)]
8833 pub fn rms_norm_qkv_rope_append_dc(
8834 &self,
8835 q0: &CudaSlice<f32>,
8836 k0: &CudaSlice<f32>,
8837 v0: &CudaSlice<f32>,
8838 wq: &CudaSlice<f32>,
8839 wk: &CudaSlice<f32>,
8840 wv: &CudaSlice<f32>,
8841 q: &mut CudaSlice<f32>,
8842 k: &mut CudaSlice<f32>,
8843 v: &mut CudaSlice<f32>,
8844 head_dim: usize,
8845 rq: usize,
8846 rk: usize,
8847 pos: &CudaSlice<i32>,
8848 nh_q: usize,
8849 nh_k: usize,
8850 base: f32,
8851 freq_scale: f32,
8852 ff: Option<&CudaSlice<f32>>,
8853 eps: f32,
8854 kc: &mut CudaSlice<u8>,
8855 vc: &mut CudaSlice<u8>,
8856 t_dev: &CudaSlice<i32>,
8857 k_tok_bytes: usize,
8858 v_tok_bytes: usize,
8859 g: bool,
8860 ) -> Result<(), Box<dyn std::error::Error>> {
8861 let rows = rq + rk + rk;
8862 let theta_scale = base.powf(-2.0 / head_dim as f32);
8863 let (nc, rqi, rki, nhq, nhk) = (
8864 head_dim as i32,
8865 rq as i32,
8866 rk as i32,
8867 nh_q as i32,
8868 nh_k as i32,
8869 );
8870 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
8871 if Self::pdl_on() && Self::pdl_wb_on() {
8872 use cudarc::driver::{DevicePtr, DevicePtrMut};
8873 let s = &self.gpu.stream();
8874 let (p0, _a0) = q0.device_ptr(s);
8875 let (p1, _a1) = k0.device_ptr(s);
8876 let (p2, _a2) = v0.device_ptr(s);
8877 let (pwq, _a3) = wq.device_ptr(s);
8878 let (pwk, _a4) = wk.device_ptr(s);
8879 let (pwv, _a5) = wv.device_ptr(s);
8880 let (pq, _a6) = q.device_ptr_mut(s);
8881 let (pk, _a7) = k.device_ptr_mut(s);
8882 let (pv, _a8) = v.device_ptr_mut(s);
8883 let (pp, _a9) = pos.device_ptr(s);
8884 let pff: u64 = match ff {
8885 Some(t) => {
8886 let (p, _gg) = t.device_ptr(s);
8887 p as u64
8888 }
8889 None => 0,
8890 };
8891 let (pkc, _a10) = kc.device_ptr_mut(s);
8892 let (pvc, _a11) = vc.device_ptr_mut(s);
8893 let (pt, _a12) = t_dev.device_ptr(s);
8894 let mut ps = [
8895 &p0 as *const _ as *mut std::ffi::c_void,
8896 &p1 as *const _ as *mut _,
8897 &p2 as *const _ as *mut _,
8898 &pwq as *const _ as *mut _,
8899 &pwk as *const _ as *mut _,
8900 &pwv as *const _ as *mut _,
8901 &pq as *const _ as *mut _,
8902 &pk as *const _ as *mut _,
8903 &pv as *const _ as *mut _,
8904 &nc as *const _ as *mut _,
8905 &rqi as *const _ as *mut _,
8906 &rki as *const _ as *mut _,
8907 &pp as *const _ as *mut _,
8908 &nhq as *const _ as *mut _,
8909 &nhk as *const _ as *mut _,
8910 &theta_scale as *const _ as *mut _,
8911 &freq_scale as *const _ as *mut _,
8912 &pff as *const _ as *mut _,
8913 &eps as *const _ as *mut _,
8914 &pkc as *const _ as *mut _,
8915 &pvc as *const _ as *mut _,
8916 &pt as *const _ as *mut _,
8917 &ktb as *const _ as *mut _,
8918 &vtb as *const _ as *mut _,
8919 ];
8920 unsafe {
8921 self.launch_pdl_flash(
8922 g,
8923 "rms_norm_qkv_rope_append_dc_f32",
8924 (rows as u32, 1, 1),
8925 (rms_block(), 1, 1),
8926 0,
8927 &mut ps,
8928 )?;
8929 }
8930 return Ok(());
8931 }
8932 let f = if g {
8933 self.func_g("rms_norm_qkv_rope_append_dc_f32")
8934 } else {
8935 self.func("rms_norm_qkv_rope_append_dc_f32")
8936 };
8937 let cfg = LaunchConfig {
8938 grid_dim: (rows as u32, 1, 1),
8939 block_dim: (rms_block(), 1, 1),
8940 shared_mem_bytes: 0,
8941 };
8942 let __s_b = self.gpu.stream();
8943 let mut b = __s_b.launch_builder(&f);
8944 match ff {
8945 Some(t) => {
8946 b.arg(q0)
8947 .arg(k0)
8948 .arg(v0)
8949 .arg(wq)
8950 .arg(wk)
8951 .arg(wv)
8952 .arg(&mut *q)
8953 .arg(&mut *k)
8954 .arg(&mut *v)
8955 .arg(&nc)
8956 .arg(&rqi)
8957 .arg(&rki)
8958 .arg(pos)
8959 .arg(&nhq)
8960 .arg(&nhk)
8961 .arg(&theta_scale)
8962 .arg(&freq_scale)
8963 .arg(t)
8964 .arg(&eps)
8965 .arg(&mut *kc)
8966 .arg(&mut *vc)
8967 .arg(t_dev)
8968 .arg(&ktb)
8969 .arg(&vtb);
8970 unsafe {
8971 b.launch(cfg)?;
8972 }
8973 }
8974 None => {
8975 let null: u64 = 0;
8976 b.arg(q0)
8977 .arg(k0)
8978 .arg(v0)
8979 .arg(wq)
8980 .arg(wk)
8981 .arg(wv)
8982 .arg(&mut *q)
8983 .arg(&mut *k)
8984 .arg(&mut *v)
8985 .arg(&nc)
8986 .arg(&rqi)
8987 .arg(&rki)
8988 .arg(pos)
8989 .arg(&nhq)
8990 .arg(&nhk)
8991 .arg(&theta_scale)
8992 .arg(&freq_scale)
8993 .arg(&null)
8994 .arg(&eps)
8995 .arg(&mut *kc)
8996 .arg(&mut *vc)
8997 .arg(t_dev)
8998 .arg(&ktb)
8999 .arg(&vtb);
9000 unsafe {
9001 b.launch(cfg)?;
9002 }
9003 }
9004 }
9005 Ok(())
9006 }
9007
9008 pub fn add_q8_1(
9010 &self,
9011 a: &CudaSlice<f32>,
9012 b: &CudaSlice<f32>,
9013 res: &mut CudaSlice<f32>,
9014 ncols: usize,
9015 nrows: usize,
9016 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9017 debug_assert!(ncols % 128 == 0);
9018 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
9019 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9020 let f = self.func("add_q8_1_f32");
9021 let cfg = LaunchConfig {
9022 grid_dim: (nrows as u32, 1, 1),
9023 block_dim: (rms_block(), 1, 1),
9024 shared_mem_bytes: 0,
9025 };
9026 let nc = ncols as i32;
9027 let __s_b2 = self.gpu.stream();
9028 let mut b2 = __s_b2.launch_builder(&f);
9029 b2.arg(a)
9030 .arg(b)
9031 .arg(&mut *res)
9032 .arg(&mut out_q)
9033 .arg(&mut out_d)
9034 .arg(&nc);
9035 unsafe {
9036 b2.launch(cfg)?;
9037 }
9038 Ok((out_q, out_d))
9039 }
9040
9041 pub fn rms_pre_add_q8_1(
9045 &self,
9046 a: &CudaSlice<f32>,
9047 wa: &CudaSlice<f32>,
9048 b: &CudaSlice<f32>,
9049 res: &mut CudaSlice<f32>,
9050 ncols: usize,
9051 nrows: usize,
9052 eps: f32,
9053 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9054 debug_assert!(ncols % 128 == 0);
9055 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
9056 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9057 let f = self.func("rms_pre_add_q8_1_f32");
9058 let cfg = LaunchConfig {
9059 grid_dim: (nrows as u32, 1, 1),
9060 block_dim: (rms_block(), 1, 1),
9061 shared_mem_bytes: 0,
9062 };
9063 let (nc, ep) = (ncols as i32, eps);
9064 let __s_b2 = self.gpu.stream();
9065 let mut b2 = __s_b2.launch_builder(&f);
9066 b2.arg(a)
9067 .arg(wa)
9068 .arg(b)
9069 .arg(&mut *res)
9070 .arg(&mut out_q)
9071 .arg(&mut out_d)
9072 .arg(&nc)
9073 .arg(&ep);
9074 unsafe {
9075 b2.launch(cfg)?;
9076 }
9077 Ok((out_q, out_d))
9078 }
9079
9080 pub fn l2_v2_on(ncols: usize) -> bool {
9084 ncols == 128 && std::env::var("MEMRA_L2_V2").as_deref() != Ok("0")
9085 }
9086
9087 pub fn l2_norm_pp(
9088 &self,
9089 x: &CudaSlice<f32>,
9090 dst: &mut CudaSlice<f32>,
9091 dst16: Option<&mut CudaSlice<u8>>,
9092 ncols: usize,
9093 nrows: usize,
9094 eps: f32,
9095 ) -> Result<(), Box<dyn std::error::Error>> {
9096 if Self::l2_v2_on(ncols) {
9097 let f = self.func("l2_norm_pp_v2_f32");
9098 let rows_per_block = 8u32; let cfg = LaunchConfig {
9100 grid_dim: ((nrows as u32).div_ceil(rows_per_block), 1, 1),
9101 block_dim: (256, 1, 1),
9102 shared_mem_bytes: 0,
9103 };
9104 let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
9105 let d16: u64 = match dst16 {
9107 Some(d) => self.addr_u8(d),
9108 None => 0,
9109 };
9110 let __s_b = self.gpu.stream();
9111 let mut b = __s_b.launch_builder(&f);
9112 b.arg(x).arg(dst).arg(&d16).arg(&nc).arg(&nr).arg(&e);
9113 unsafe {
9114 b.launch(cfg)?;
9115 }
9116 return Ok(());
9117 }
9118 self.l2_norm(x, dst, ncols, nrows, eps)
9119 }
9120
9121 pub fn l2_norm(
9122 &self,
9123 x: &CudaSlice<f32>,
9124 dst: &mut CudaSlice<f32>,
9125 ncols: usize,
9126 nrows: usize,
9127 eps: f32,
9128 ) -> Result<(), Box<dyn std::error::Error>> {
9129 let f = self.func("l2_norm_f32");
9130 let cfg = LaunchConfig {
9131 grid_dim: (nrows as u32, 1, 1),
9132 block_dim: (256, 1, 1),
9133 shared_mem_bytes: 0,
9134 };
9135 let (nc, e) = (ncols as i32, eps);
9136 let __s_b = self.gpu.stream();
9137 let mut b = __s_b.launch_builder(&f);
9138 b.arg(x).arg(dst).arg(&nc).arg(&e);
9139 unsafe {
9140 b.launch(cfg)?;
9141 }
9142 Ok(())
9143 }
9144
9145 pub fn l2_norm_decode(
9151 &self,
9152 x: &CudaSlice<f32>,
9153 dst: &mut CudaSlice<f32>,
9154 ncols: usize,
9155 nrows: usize,
9156 eps: f32,
9157 ) -> Result<(), Box<dyn std::error::Error>> {
9158 let f = self.func("l2_norm_f32");
9159 let cfg = LaunchConfig {
9160 grid_dim: (nrows as u32, 1, 1),
9161 block_dim: (32, 1, 1),
9162 shared_mem_bytes: 0,
9163 };
9164 let (nc, e) = (ncols as i32, eps);
9165 let __s_b = self.gpu.stream();
9166 let mut b = __s_b.launch_builder(&f);
9167 b.arg(x).arg(dst).arg(&nc).arg(&e);
9168 unsafe {
9169 b.launch(cfg)?;
9170 }
9171 Ok(())
9172 }
9173
9174 pub fn rope_neox(
9176 &self,
9177 x: &mut CudaSlice<f32>,
9178 pos: &CudaSlice<i32>,
9179 head_dim: usize,
9180 n_dims: usize,
9181 n_heads: usize,
9182 n_tokens: usize,
9183 freq_base: f32,
9184 freq_scale: f32,
9185 ) -> Result<(), Box<dyn std::error::Error>> {
9186 let f = self.func("rope_neox_f32");
9187 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9188 let grid = (n_heads * n_tokens) as u32;
9189 let cfg = LaunchConfig {
9190 grid_dim: (grid, 1, 1),
9191 block_dim: ((head_dim / 2) as u32, 1, 1),
9192 shared_mem_bytes: 0,
9193 };
9194 let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
9195 let __s_b = self.gpu.stream();
9196 let mut b = __s_b.launch_builder(&f);
9197 b.arg(x)
9198 .arg(pos)
9199 .arg(&hd)
9200 .arg(&nd)
9201 .arg(&nh)
9202 .arg(&theta_scale)
9203 .arg(&freq_scale);
9204 unsafe {
9205 b.launch(cfg)?;
9206 }
9207 Ok(())
9208 }
9209
9210 pub fn rope_neox_ff(
9212 &self,
9213 x: &mut CudaSlice<f32>,
9214 pos: &CudaSlice<i32>,
9215 head_dim: usize,
9216 n_dims: usize,
9217 n_heads: usize,
9218 n_tokens: usize,
9219 freq_base: f32,
9220 freq_scale: f32,
9221 ff: &CudaSlice<f32>,
9222 ) -> Result<(), Box<dyn std::error::Error>> {
9223 let f = self.func("rope_neox_ff_f32");
9224 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9225 let grid = (n_heads * n_tokens) as u32;
9226 let cfg = LaunchConfig {
9227 grid_dim: (grid, 1, 1),
9228 block_dim: ((head_dim / 2) as u32, 1, 1),
9229 shared_mem_bytes: 0,
9230 };
9231 let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
9232 let __s_b = self.gpu.stream();
9233 let mut b = __s_b.launch_builder(&f);
9234 b.arg(x)
9235 .arg(pos)
9236 .arg(&hd)
9237 .arg(&nd)
9238 .arg(&nh)
9239 .arg(&theta_scale)
9240 .arg(&freq_scale)
9241 .arg(ff);
9242 unsafe {
9243 b.launch(cfg)?;
9244 }
9245 Ok(())
9246 }
9247
9248 #[allow(clippy::too_many_arguments)]
9250 pub fn rope_neox2(
9251 &self,
9252 q: &mut CudaSlice<f32>,
9253 k: &mut CudaSlice<f32>,
9254 pos: &CudaSlice<i32>,
9255 head_dim: usize,
9256 n_dims: usize,
9257 nh_q: usize,
9258 nh_k: usize,
9259 n_tokens: usize,
9260 freq_base: f32,
9261 freq_scale: f32,
9262 ff: Option<&CudaSlice<f32>>,
9263 ) -> Result<(), Box<dyn std::error::Error>> {
9264 let f = self.func("rope_neox2_f32");
9265 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9266 let grid = ((nh_q + nh_k) * n_tokens) as u32;
9267 let cfg = LaunchConfig {
9268 grid_dim: (grid, 1, 1),
9269 block_dim: ((head_dim / 2) as u32, 1, 1),
9270 shared_mem_bytes: 0,
9271 };
9272 let (hd, nd, nq, nk, nt) = (
9273 head_dim as i32,
9274 n_dims as i32,
9275 nh_q as i32,
9276 nh_k as i32,
9277 n_tokens as i32,
9278 );
9279 let __s_b = self.gpu.stream();
9280 let mut b = __s_b.launch_builder(&f);
9281 b.arg(q)
9282 .arg(k)
9283 .arg(pos)
9284 .arg(&hd)
9285 .arg(&nd)
9286 .arg(&nq)
9287 .arg(&nk)
9288 .arg(&nt)
9289 .arg(&theta_scale)
9290 .arg(&freq_scale);
9291 match ff {
9292 Some(ffv) => {
9293 b.arg(ffv);
9294 unsafe {
9295 b.launch(cfg)?;
9296 }
9297 }
9298 None => {
9299 let null: u64 = 0;
9300 b.arg(&null);
9301 unsafe {
9302 b.launch(cfg)?;
9303 }
9304 }
9305 }
9306 Ok(())
9307 }
9308
9309 pub fn gelu_tanh_mul(
9311 &self,
9312 gate: &CudaSlice<f32>,
9313 up: &CudaSlice<f32>,
9314 dst: &mut CudaSlice<f32>,
9315 n: usize,
9316 ) -> Result<(), Box<dyn std::error::Error>> {
9317 let f = self.func("gelu_tanh_mul_f32");
9318 let cfg = LaunchConfig::for_num_elems(n as u32);
9319 let ni = n as i32;
9320 let __s_b = self.gpu.stream();
9321 let mut b = __s_b.launch_builder(&f);
9322 b.arg(gate).arg(up).arg(dst).arg(&ni);
9323 unsafe {
9324 b.launch(cfg)?;
9325 }
9326 Ok(())
9327 }
9328
9329 pub fn silu_mul(
9330 &self,
9331 gate: &CudaSlice<f32>,
9332 up: &CudaSlice<f32>,
9333 dst: &mut CudaSlice<f32>,
9334 n: usize,
9335 ) -> Result<(), Box<dyn std::error::Error>> {
9336 let f = self.func("silu_mul_f32");
9337 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9339 let ni = n as i32;
9340 let __s_b = self.gpu.stream();
9341 let mut b = __s_b.launch_builder(&f);
9342 b.arg(gate).arg(up).arg(dst).arg(&ni);
9343 unsafe {
9344 b.launch(cfg)?;
9345 }
9346 Ok(())
9347 }
9348
9349 pub fn silu_mul_f16out(
9352 &self,
9353 gate: &CudaSlice<f32>,
9354 up: &CudaSlice<f32>,
9355 dst: &mut CudaSlice<f32>,
9356 dst16: &mut CudaSlice<u8>,
9357 n: usize,
9358 ) -> Result<(), Box<dyn std::error::Error>> {
9359 let f = self.func("silu_mul_f16out_f32");
9360 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9361 let ni = n as i32;
9362 let __s_b = self.gpu.stream();
9363 let mut b = __s_b.launch_builder(&f);
9364 b.arg(gate).arg(up).arg(dst).arg(dst16).arg(&ni);
9365 unsafe {
9366 b.launch(cfg)?;
9367 }
9368 Ok(())
9369 }
9370
9371 pub fn silu_mul_scaled(
9378 &self,
9379 gate: &CudaSlice<f32>,
9380 up: &CudaSlice<f32>,
9381 gs: f32,
9382 us: f32,
9383 dst: &mut CudaSlice<f32>,
9384 n: usize,
9385 ) -> Result<(), Box<dyn std::error::Error>> {
9386 let f = self.func("silu_mul_scaled_f32");
9387 let cfg = LaunchConfig::for_num_elems(n as u32);
9388 let ni = n as i32;
9389 let (gsf, usf) = (gs, us);
9390 let __s_b = self.gpu.stream();
9391 let mut b = __s_b.launch_builder(&f);
9392 b.arg(gate).arg(up).arg(&gsf).arg(&usf).arg(dst).arg(&ni);
9393 unsafe {
9394 b.launch(cfg)?;
9395 }
9396 Ok(())
9397 }
9398
9399 #[allow(clippy::too_many_arguments)]
9403 pub fn swigluoai_mul_scaled(
9404 &self,
9405 gate: &CudaSlice<f32>,
9406 up: &CudaSlice<f32>,
9407 gs: f32,
9408 us: f32,
9409 alpha: f32,
9410 limit: f32,
9411 dst: &mut CudaSlice<f32>,
9412 n: usize,
9413 ) -> Result<(), Box<dyn std::error::Error>> {
9414 let f = self.func("swigluoai_mul_scaled_f32");
9415 let cfg = LaunchConfig::for_num_elems(n as u32);
9416 let ni = n as i32;
9417 let __s_b = self.gpu.stream();
9418 let mut b = __s_b.launch_builder(&f);
9419 b.arg(gate)
9420 .arg(up)
9421 .arg(&gs)
9422 .arg(&us)
9423 .arg(&alpha)
9424 .arg(&limit)
9425 .arg(dst)
9426 .arg(&ni);
9427 unsafe {
9428 b.launch(cfg)?;
9429 }
9430 Ok(())
9431 }
9432
9433 pub fn silu_mul_scaled_q8_1(
9441 &self,
9442 gate: &CudaSlice<f32>,
9443 up: &CudaSlice<f32>,
9444 gs: f32,
9445 us: f32,
9446 n: usize,
9447 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9448 let f = self.func("silu_mul_scaled_q8_1");
9449 let nblk = n / 32;
9450 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);
9454 let (gsf, usf, ni) = (gs, us, n as i32);
9455 let __s_b = self.gpu.stream();
9456 let mut b = __s_b.launch_builder(&f);
9457 b.arg(gate)
9458 .arg(up)
9459 .arg(&gsf)
9460 .arg(&usf)
9461 .arg(&mut aq)
9462 .arg(&mut ad)
9463 .arg(&ni);
9464 unsafe {
9465 b.launch(cfg)?;
9466 }
9467 Ok((aq, ad))
9468 }
9469
9470 pub fn add(
9471 &self,
9472 a: &CudaSlice<f32>,
9473 b_in: &CudaSlice<f32>,
9474 dst: &mut CudaSlice<f32>,
9475 n: usize,
9476 ) -> Result<(), Box<dyn std::error::Error>> {
9477 let f = self.func("add_f32");
9478 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9480 let ni = n as i32;
9481 let __s_bld = self.gpu.stream();
9482 let mut bld = __s_bld.launch_builder(&f);
9483 bld.arg(a).arg(b_in).arg(dst).arg(&ni);
9484 unsafe {
9485 bld.launch(cfg)?;
9486 }
9487 Ok(())
9488 }
9489
9490 pub fn mul(
9491 &self,
9492 a: &CudaSlice<f32>,
9493 b_in: &CudaSlice<f32>,
9494 dst: &mut CudaSlice<f32>,
9495 n: usize,
9496 ) -> Result<(), Box<dyn std::error::Error>> {
9497 let f = self.func("mul_f32");
9498 let cfg = LaunchConfig::for_num_elems(n as u32);
9499 let ni = n as i32;
9500 let __s_bld = self.gpu.stream();
9501 let mut bld = __s_bld.launch_builder(&f);
9502 bld.arg(a).arg(b_in).arg(dst).arg(&ni);
9503 unsafe {
9504 bld.launch(cfg)?;
9505 }
9506 Ok(())
9507 }
9508
9509 pub fn matmul(
9512 &self,
9513 w: &crate::model::GpuTensor,
9514 x: &CudaSlice<f32>,
9515 m: usize,
9516 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9517 use crate::model::GpuTensor;
9518 let in_f = w.in_features();
9519 let out_f = w.out_features();
9520 #[allow(non_snake_case)]
9528 let GEMM_M_THRESHOLD = if self.verify_exact_on() {
9531 usize::MAX
9532 } else {
9533 16usize
9534 };
9535
9536 const GEMM_MIN_OUT_F: usize = 128; if m >= GEMM_M_THRESHOLD {
9561 if let Some(y) = self.try_fp8_gemm(w, x, m)? {
9562 return Ok(y);
9563 }
9564 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
9571 return Ok(y);
9572 }
9573 if let Some(y) = self.try_f16_gemm(w, x, m)? {
9576 return Ok(y);
9577 }
9578 }
9579 if let GpuTensor::Quant { qtype, .. } = w {
9594 if *qtype == QT_F8_E4M3_BLK {
9595 if m >= GEMM_M_THRESHOLD {
9596 if let Some(y) = self.try_e4m3_blk_prefill(w, x, m)? {
9597 return Ok(y);
9598 }
9599 }
9600 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9601 if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
9602 return Ok(y);
9603 }
9604 }
9605 }
9606 if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.mmq_supports(w) {
9607 return self.qmatvec_mmq(w, x, m);
9608 }
9609 if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.gemm_supports(w) {
9610 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9611 return self.qmatvec_gemm(w, &aq, &ad, m);
9612 }
9613 if m >= GEMM_M_THRESHOLD {
9616 if let Some(y) = self.try_fp4_gemm(w, x, m, in_f, out_f)? {
9617 return Ok(y);
9618 }
9619 }
9620 let fast = std::env::var("MEMRA_FAST").as_deref() != Ok("0");
9624 if m == 1 && fast {
9629 if let GpuTensor::Quant {
9630 bytes,
9631 qtype,
9632 row_bytes,
9633 rp,
9634 rp4,
9635 scale,
9636 ..
9637 } = w
9638 {
9639 if self.mmvq_supports(*qtype) {
9640 let (bytes, rp) = match rp4 {
9644 Some(m4) => (m4, true),
9645 None => (bytes, *rp),
9646 };
9647 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9648 return self.qmatvec_mmvq(
9649 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, rp,
9650 );
9651 }
9652 }
9653 }
9654 if (2..=16).contains(&m)
9670 && fast
9671 && std::env::var("MEMRA_NO_BATCHED").is_err()
9672 && (m <= 4 || Self::b8_enabled())
9673 {
9674 let m_ok = m <= 8
9684 || matches!(w, GpuTensor::Quant { qtype, .. }
9685 if *qtype == QT_Q4_0 || *qtype == QT_Q6_K || *qtype == QT_F8_E4M3
9686 || *qtype == QT_NVFP4 || *qtype == QT_Q4_K || *qtype == QT_Q5_K || *qtype == QT_Q8_0);
9687 if m_ok {
9688 if let GpuTensor::Quant {
9689 bytes,
9690 qtype,
9691 row_bytes,
9692 rp,
9693 rp4,
9694 ..
9695 } = w
9696 {
9697 if self.batched_supports(*qtype) && self.mmvq_supports(*qtype) {
9698 let (bytes, rp) = match rp4 {
9699 Some(m4) => (m4, true),
9700 None => (bytes, *rp),
9701 };
9702 let mcols = Self::batched_mcols(m);
9703 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9704 let mut y = self.qmatvec_mmvq_batched(
9705 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, mcols, 1.0, rp,
9706 )?;
9707 if let GpuTensor::Quant { scale, .. } = w {
9708 if *scale != 1.0 {
9709 self.scale_inplace(&mut y, *scale, m * out_f)?;
9710 }
9711 }
9712 return Ok(y);
9713 }
9714 }
9715 }
9716 }
9717 if fast {
9723 if let GpuTensor::Quant {
9724 bytes,
9725 qtype,
9726 row_bytes,
9727 scale,
9728 ..
9729 } = w
9730 {
9731 if *qtype == QT_F8_E4M3 {
9732 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
9733 return self.qmatvec_mmvq(
9734 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, false,
9735 );
9736 }
9737 }
9738 }
9739 let mut y = match w {
9740 GpuTensor::Quant {
9741 bytes,
9742 qtype,
9743 row_bytes,
9744 ..
9745 } if fast && *qtype == QT_Q8_0 => {
9746 self.qmatvec_q8_0_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9747 }
9748 GpuTensor::Quant {
9749 bytes,
9750 qtype,
9751 row_bytes,
9752 ..
9753 } if fast && *qtype == QT_Q4_K => {
9754 self.qmatvec_q4_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9755 }
9756 GpuTensor::Quant {
9757 bytes,
9758 qtype,
9759 row_bytes,
9760 ..
9761 } if fast && *qtype == QT_Q6_K => {
9762 self.qmatvec_q6_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9763 }
9764 GpuTensor::Quant {
9765 bytes,
9766 qtype,
9767 row_bytes,
9768 ..
9769 } if fast && *qtype == QT_Q5_K => {
9770 self.qmatvec_q5_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9771 }
9772 GpuTensor::Quant {
9773 bytes,
9774 qtype,
9775 row_bytes,
9776 ..
9777 } if fast && *qtype == QT_Q3_K => {
9778 self.qmatvec_q3_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9779 }
9780 GpuTensor::Quant {
9781 bytes,
9782 qtype,
9783 row_bytes,
9784 rp,
9785 ..
9786 } if fast && *qtype == QT_NVFP4 => self.qmatvec_dp4a_named(
9787 if *rp {
9788 "qmatvec_nvfp4_dp4a_rp"
9789 } else {
9790 "qmatvec_nvfp4_dp4a"
9791 },
9792 bytes,
9793 x,
9794 m,
9795 in_f,
9796 out_f,
9797 *row_bytes,
9798 )?,
9799 GpuTensor::Quant {
9803 bytes,
9804 qtype,
9805 row_bytes,
9806 ..
9807 } if fast && *qtype == QT_IQ4_XS && Self::iq_fast_enabled() => {
9808 self.qmatvec_iq4_XS_fast(bytes, x, m, in_f, out_f, *row_bytes)?
9809 }
9810 GpuTensor::Quant {
9815 bytes,
9816 qtype,
9817 row_bytes,
9818 rp,
9819 ..
9820 } =>
9821 {
9824 self.qmatvec(
9825 bytes,
9826 x,
9827 m,
9828 in_f,
9829 out_f,
9830 if *rp && *qtype == QT_NVFP4 {
9831 QT_NVFP4_RP
9832 } else {
9833 *qtype
9834 },
9835 *row_bytes,
9836 )?
9837 }
9838 GpuTensor::Float { data, .. } => self.linear(x, data, m, in_f, out_f)?,
9839 GpuTensor::FloatBf16 { data, .. } => {
9842 self.linear_bf16_chunked(x, data, m, in_f, out_f, false)?
9843 }
9844 };
9845 if let GpuTensor::Quant { scale, .. } = w {
9847 if *scale != 1.0 {
9848 self.scale_inplace(&mut y, *scale, m * out_f)?;
9849 }
9850 }
9851 Ok(y)
9852 }
9853
9854 pub fn uses_q8_1_fast(&self, w: &crate::model::GpuTensor) -> bool {
9857 use crate::model::GpuTensor;
9858 if std::env::var("MEMRA_FAST").as_deref() == Ok("0") {
9859 return false;
9860 }
9861 match w {
9862 GpuTensor::Quant { qtype, .. } => {
9869 matches!(
9870 *qtype,
9871 QT_Q8_0
9872 | QT_Q4_K
9873 | QT_Q6_K
9874 | QT_Q5_K
9875 | QT_Q3_K
9876 | QT_NVFP4
9877 | QT_F8_E4M3
9878 | QT_F8_E4M3_BLK
9879 | QT_Q4_0
9880 ) || (*qtype == QT_IQ4_XS && Self::iq_fast_enabled())
9881 }
9882 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
9883 }
9884 }
9885
9886 pub fn matmul_pre(
9891 &self,
9892 w: &crate::model::GpuTensor,
9893 aq: &CudaSlice<i8>,
9894 ad: &CudaSlice<f32>,
9895 x_fallback: &CudaSlice<f32>,
9896 m: usize,
9897 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9898 use crate::model::GpuTensor;
9899 let x_raw_ok = x_fallback.len() >= m * w.in_features();
9905 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
9908 if let Some(y) = self.try_fp8_gemm(w, x_fallback, m)? {
9909 return Ok(y);
9910 }
9911 if let Some(y) = self.try_fp8_blk_mmq(w, x_fallback, m)? {
9914 return Ok(y);
9915 }
9916 if let Some(y) = self.try_f16_gemm(w, x_fallback, m)? {
9918 return Ok(y);
9919 }
9920 }
9921 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
9927 if let Some(y) = self.try_e4m3_blk_prefill(w, x_fallback, m)? {
9928 return Ok(y);
9929 }
9930 }
9931 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
9932 return Ok(y);
9933 }
9934 if m >= 16
9939 && w.out_features() >= 128
9940 && self.mmq_supports(w)
9941 && !self.verify_exact_on()
9942 && x_raw_ok
9943 {
9944 return self.qmatvec_mmq(w, x_fallback, m);
9945 }
9946 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
9949 if let Some(y) =
9950 self.try_fp4_gemm(w, x_fallback, m, w.in_features(), w.out_features())?
9951 {
9952 return Ok(y);
9953 }
9954 }
9955 if m >= 16 && self.gemm_supports(w) && !self.verify_exact_on() {
9958 return self.qmatvec_gemm(w, aq, ad, m);
9959 }
9960 if !self.uses_q8_1_fast(w) {
9961 return self.matmul(w, x_fallback, m);
9962 }
9963 let in_f = w.in_features();
9964 let out_f = w.out_features();
9965 let (bytes, qtype, row_bytes, scale, rp) = match w {
9966 GpuTensor::Quant {
9967 bytes,
9968 qtype,
9969 row_bytes,
9970 scale,
9971 rp,
9972 ..
9973 } => (bytes, *qtype, *row_bytes, *scale, *rp),
9974 _ => unreachable!("uses_q8_1_fast guaranteed Quant"),
9975 };
9976 let (mbytes, mrp) = match w {
9979 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
9980 _ => (bytes, rp),
9981 };
9982 if m == 1 && self.mmvq_supports(qtype) {
9986 return self.qmatvec_mmvq(mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, mrp);
9987 }
9988 if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
10001 && std::env::var("MEMRA_NO_BATCHED").is_err()
10002 && (m <= 4 || Self::b8_enabled())
10003 && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_NVFP4
10007 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_F8_E4M3 || qtype == QT_Q8_0)
10008 {
10009 let mcols = Self::batched_mcols(m);
10010 return self.qmatvec_mmvq_batched(
10011 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, mrp,
10012 );
10013 }
10014 if qtype == QT_F8_E4M3 || qtype == QT_Q4_0 {
10020 let (b2, r2) = if qtype == QT_Q4_0 {
10021 (mbytes, mrp)
10022 } else {
10023 (bytes, rp)
10024 };
10025 return self.qmatvec_mmvq(b2, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, r2);
10026 }
10027 let name = match qtype {
10028 QT_Q8_0 => "qmatvec_q8_0_dp4a",
10029 QT_Q4_K => "qmatvec_q4_K_dp4a",
10030 QT_Q6_K => "qmatvec_q6_K_dp4a",
10031 QT_Q5_K => "qmatvec_q5_K_dp4a",
10032 QT_Q3_K => "qmatvec_q3_K_dp4a",
10033 QT_NVFP4 => {
10034 if rp {
10035 "qmatvec_nvfp4_dp4a_rp"
10036 } else {
10037 "qmatvec_nvfp4_dp4a"
10038 }
10039 }
10040 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
10041 _ => unreachable!(),
10042 };
10043 let f = self.func(name);
10044 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
10046 grid_dim: (out_f as u32, m as u32, 1),
10047 block_dim: (128, 1, 1),
10048 shared_mem_bytes: 0,
10049 };
10050 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
10051 let __s_b = self.gpu.stream();
10052 let mut b = __s_b.launch_builder(&f);
10053 b.arg(bytes)
10054 .arg(aq)
10055 .arg(ad)
10056 .arg(&mut y)
10057 .arg(&inf)
10058 .arg(&outf)
10059 .arg(&mi)
10060 .arg(&rb);
10061 unsafe {
10062 b.launch(cfg)?;
10063 }
10064 if scale != 1.0 {
10065 self.scale_inplace(&mut y, scale, m * out_f)?;
10066 }
10067 Ok(y)
10068 }
10069
10070 pub fn matmul_decode_exact(
10078 &self,
10079 w: &crate::model::GpuTensor,
10080 x: &CudaSlice<f32>,
10081 m: usize,
10082 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10083 use crate::model::GpuTensor;
10084 if let GpuTensor::Float { data, .. } = w {
10092 return self.linear_decode_exact(x, data, m, w.in_features(), w.out_features());
10093 }
10094 if let GpuTensor::FloatBf16 { data, .. } = w {
10097 let (in_f, out_f) = (w.in_features(), w.out_features());
10098 return self.linear_bf16_chunked(x, data, m, in_f, out_f, true);
10099 }
10100 if !self.uses_q8_1_fast(w) {
10101 return self.matmul(w, x, m);
10102 }
10103 let in_f = w.in_features();
10104 let out_f = w.out_features();
10105 let (bytes, qtype, row_bytes, scale, rp) = match w {
10106 GpuTensor::Quant {
10107 bytes,
10108 qtype,
10109 row_bytes,
10110 scale,
10111 rp,
10112 ..
10113 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10114 _ => return self.matmul(w, x, m),
10115 };
10116 let (bytes, rp) = match w {
10119 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10120 _ => (bytes, rp),
10121 };
10122 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10123 if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
10127 return Ok(y);
10128 }
10129 if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
10138 && std::env::var("MEMRA_NO_BATCHED").is_err()
10139 && (m <= 4 || Self::b8_enabled())
10140 && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_F8_E4M3
10143 || qtype == QT_NVFP4 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_Q8_0)
10144 {
10145 let mcols = Self::batched_mcols(m);
10146 return self.qmatvec_mmvq_batched(
10147 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
10148 );
10149 }
10150 if self.mmvq_supports(qtype) {
10151 return self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
10154 }
10155 self.matmul_pre(w, &aq, &ad, x, m)
10158 }
10159
10160 pub fn matmul_decode_exact_pre(
10170 &self,
10171 w: &crate::model::GpuTensor,
10172 aq: &CudaSlice<i8>,
10173 ad: &CudaSlice<f32>,
10174 m: usize,
10175 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10176 use crate::model::GpuTensor;
10177 debug_assert!(
10178 self.uses_q8_1_fast(w),
10179 "matmul_decode_exact_pre: caller must guarantee q8_1-fast"
10180 );
10181 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
10183 return Ok(y);
10184 }
10185 let in_f = w.in_features();
10186 let out_f = w.out_features();
10187 let (bytes, qtype, row_bytes, scale, rp) = match w {
10188 GpuTensor::Quant {
10189 bytes,
10190 qtype,
10191 row_bytes,
10192 scale,
10193 rp,
10194 ..
10195 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10196 _ => {
10197 return Err(
10198 "matmul_decode_exact_pre: Quant tensor required (q8_1-fast contract)".into(),
10199 );
10200 }
10201 };
10202 let (bytes, rp) = match w {
10204 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10205 _ => (bytes, rp),
10206 };
10207 if (2..=16).contains(&m)
10209 && self.batched_supports(qtype)
10210 && self.mmvq_supports(qtype)
10211 && std::env::var("MEMRA_NO_BATCHED").is_err()
10212 && (m <= 4 || Self::b8_enabled())
10213 && (m <= 8
10214 || qtype == QT_Q4_0
10215 || qtype == QT_Q6_K
10216 || qtype == QT_F8_E4M3
10217 || qtype == QT_NVFP4
10218 || qtype == QT_Q4_K
10219 || qtype == QT_Q5_K
10220 || qtype == QT_Q8_0)
10221 {
10222 let mcols = Self::batched_mcols(m);
10223 return self.qmatvec_mmvq_batched(
10224 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
10225 );
10226 }
10227 if self.mmvq_supports(qtype) {
10228 return self.qmatvec_mmvq(bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
10229 }
10230 let x0 = self.zeros(0)?;
10233 self.matmul_pre(w, aq, ad, &x0, m)
10234 }
10235
10236 pub fn matmul_decode_exact_dual_pre(
10245 &self,
10246 w0: &crate::model::GpuTensor,
10247 w1: &crate::model::GpuTensor,
10248 aq: &CudaSlice<i8>,
10249 ad: &CudaSlice<f32>,
10250 m: usize,
10251 ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
10252 {
10253 use crate::model::GpuTensor;
10254 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10255 let on = *ON.get_or_init(|| {
10256 std::env::var("MEMRA_SPEC_DUAL_T")
10257 .map(|v| v != "0")
10258 .unwrap_or(true)
10259 });
10260 if !on
10261 || !(2..=7).contains(&m)
10262 || std::env::var("MEMRA_NO_BATCHED").is_ok()
10263 || !self.uses_q8_1_fast(w0)
10264 || !self.uses_q8_1_fast(w1)
10265 {
10266 return Ok(None);
10267 }
10268 if !self.mmvq_supports(QT_NVFP4) {
10273 return Ok(None);
10274 }
10275 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10276 if w1.in_features() != in_f || w1.out_features() != out_f {
10277 return Ok(None);
10278 }
10279 let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
10280 (
10281 GpuTensor::Quant {
10282 bytes: b0,
10283 qtype: q0,
10284 row_bytes: rb0,
10285 scale: s0,
10286 rp: rp0,
10287 rp4: None,
10288 ..
10289 },
10290 GpuTensor::Quant {
10291 bytes: b1,
10292 qtype: q1,
10293 row_bytes: rb1,
10294 scale: s1,
10295 rp: rp1,
10296 rp4: None,
10297 ..
10298 },
10299 ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
10300 (b0, b1, *rb0, *s0, *s1, *rp0)
10301 }
10302 _ => return Ok(None),
10303 };
10304 if m > 4 && !(rp && Self::b8_enabled() && std::env::var("MEMRA_B567").as_deref() != Ok("0"))
10307 {
10308 return Ok(None);
10309 }
10310 let (y0, y1) =
10311 self.qmatvec_batched_dual_raw(b0, b1, aq, ad, m, in_f, out_f, row_bytes, rp)?;
10312 Ok(Some(((y0, s0), (y1, s1))))
10313 }
10314
10315 pub fn matmul_decode_exact_dual(
10331 &self,
10332 w0: &crate::model::GpuTensor,
10333 w1: &crate::model::GpuTensor,
10334 x: &CudaSlice<f32>,
10335 m: usize,
10336 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10337 use crate::model::GpuTensor;
10338 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10339 let on = *ON.get_or_init(|| {
10340 std::env::var("MEMRA_SPEC_DUAL_T")
10341 .map(|v| v != "0")
10342 .unwrap_or(true)
10343 });
10344 if !on
10345 || !(2..=4).contains(&m)
10346 || std::env::var("MEMRA_NO_BATCHED").is_ok()
10347 || !self.uses_q8_1_fast(w0)
10348 || !self.uses_q8_1_fast(w1)
10349 {
10350 return Ok(None);
10351 }
10352 if !self.mmvq_supports(QT_NVFP4) {
10357 return Ok(None);
10358 }
10359 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10360 if w1.in_features() != in_f || w1.out_features() != out_f {
10361 return Ok(None);
10362 }
10363 let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
10364 (
10365 GpuTensor::Quant {
10366 bytes: b0,
10367 qtype: q0,
10368 row_bytes: rb0,
10369 scale: s0,
10370 rp: rp0,
10371 rp4: None,
10372 ..
10373 },
10374 GpuTensor::Quant {
10375 bytes: b1,
10376 qtype: q1,
10377 row_bytes: rb1,
10378 scale: s1,
10379 rp: rp1,
10380 rp4: None,
10381 ..
10382 },
10383 ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
10384 (b0, b1, *rb0, *s0, *s1, *rp0)
10385 }
10386 _ => return Ok(None),
10387 };
10388 if std::env::var("MEMRA_DEBUG").is_ok() {
10391 static ONCE: std::sync::Once = std::sync::Once::new();
10392 ONCE.call_once(|| eprintln!("[memra] dual gate+up batched ENGAGED (m={m} rp={rp})"));
10393 }
10394 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10395 let (y0, y1) =
10396 self.qmatvec_batched_dual_raw(b0, b1, &aq, &ad, m, in_f, out_f, row_bytes, rp)?;
10397 let mut y0 = y0;
10398 let mut y1 = y1;
10399 if s0 != 1.0 {
10400 self.scale_inplace(&mut y0, s0, m * out_f)?;
10401 }
10402 if s1 != 1.0 {
10403 self.scale_inplace(&mut y1, s1, m * out_f)?;
10404 }
10405 Ok(Some((y0, y1)))
10406 }
10407
10408 #[allow(clippy::too_many_arguments)]
10413 pub fn qmatvec_batched_dual_raw(
10414 &self,
10415 b0: &CudaSlice<u8>,
10416 b1: &CudaSlice<u8>,
10417 aq: &CudaSlice<i8>,
10418 ad: &CudaSlice<f32>,
10419 m: usize,
10420 in_f: usize,
10421 out_f: usize,
10422 row_bytes: usize,
10423 rp: bool,
10424 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10425 const ROWS_PER_BLOCK: u32 = 4;
10426 let mcols = Self::batched_mcols(m);
10427 let tiny_rp1 = rp
10430 && mcols == 4
10431 && out_f <= 128
10432 && std::env::var("MEMRA_NVFP4_AUX_DUAL").as_deref() != Ok("0");
10433 let (name, rows_per_block) = if tiny_rp1 {
10434 ("qmatvec_nvfp4_mmvq_dual_b4_rp", ROWS_PER_BLOCK)
10435 } else {
10436 match (mcols, rp, m) {
10437 (2, false, _) => ("qmatvec_nvfp4_mmvq_dual_b2", ROWS_PER_BLOCK),
10438 (4, false, _) => ("qmatvec_nvfp4_mmvq_dual_b4_r2", ROWS_PER_BLOCK * 2),
10439 (2, true, _) => ("qmatvec_nvfp4_mmvq_dual_b2_rp", ROWS_PER_BLOCK),
10440 (4, true, _) => ("qmatvec_nvfp4_mmvq_dual_b4_rpr2", ROWS_PER_BLOCK * 2),
10441 (8, true, 5) => ("qmatvec_nvfp4_mmvq_dual_b5_rpr2", ROWS_PER_BLOCK * 2),
10442 (8, true, 6) => ("qmatvec_nvfp4_mmvq_dual_b6_rpr2", ROWS_PER_BLOCK * 2),
10443 (8, true, 7) => ("qmatvec_nvfp4_mmvq_dual_b7_rpr2", ROWS_PER_BLOCK * 2),
10444 _ => {
10445 return Err(
10446 format!("qmatvec_batched_dual_raw: no dual kernel for m {m}").into(),
10447 );
10448 }
10449 }
10450 };
10451 let f = self.func(name);
10452 let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
10453 let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
10454 let cfg = LaunchConfig {
10455 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
10456 block_dim: (32, ROWS_PER_BLOCK, 1),
10457 shared_mem_bytes: 0,
10458 };
10459 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
10460 let __s_b = self.gpu.stream();
10461 let mut b = __s_b.launch_builder(&f);
10462 b.arg(b0)
10463 .arg(b1)
10464 .arg(aq)
10465 .arg(ad)
10466 .arg(&mut y0)
10467 .arg(&mut y1)
10468 .arg(&inf)
10469 .arg(&outf)
10470 .arg(&mi)
10471 .arg(&rb);
10472 unsafe {
10473 b.launch(cfg)?;
10474 }
10475 Ok((y0, y1))
10476 }
10477
10478 pub fn matmul_pre_dual_noscale(
10490 &self,
10491 w0: &crate::model::GpuTensor,
10492 w1: &crate::model::GpuTensor,
10493 aq: &CudaSlice<i8>,
10494 ad: &CudaSlice<f32>,
10495 m: usize,
10496 ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
10497 {
10498 use crate::model::GpuTensor;
10499 if m != 1 || !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
10500 return Ok(None);
10501 }
10502 if !self.mmvq_supports(QT_NVFP4) {
10512 return Ok(None);
10513 }
10514 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10515 if w1.in_features() != in_f || w1.out_features() != out_f {
10516 return Ok(None);
10517 }
10518 let no_mirror =
10531 |w: &crate::model::GpuTensor| !matches!(w, GpuTensor::Quant { rp4: Some(_), .. });
10532 if self.q8_ffn_fuse2_on()
10533 && no_mirror(w0)
10534 && no_mirror(w1)
10535 && let Some([p0, p1]) = self.q8_fused_params(&[w0, w1])
10536 {
10537 let (y0, y1) = self.q8_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2)?;
10538 return Ok(Some(((y0, 1.0), (y1, 1.0))));
10539 }
10540 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
10550 let (y0, y1) =
10551 self.e4m3_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2, 1.0, 1.0)?;
10552 return Ok(Some(((y0, p0.3), (y1, p1.3))));
10553 }
10554 let (b0, q0, rb0, s0, rp0) = match w0 {
10555 GpuTensor::Quant {
10556 bytes,
10557 qtype,
10558 row_bytes,
10559 scale,
10560 rp,
10561 ..
10562 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10563 _ => return Ok(None),
10564 };
10565 let (b1, q1, rb1, s1, rp1) = match w1 {
10566 GpuTensor::Quant {
10567 bytes,
10568 qtype,
10569 row_bytes,
10570 scale,
10571 rp,
10572 ..
10573 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10574 _ => return Ok(None),
10575 };
10576 if q0 != QT_NVFP4 || q1 != QT_NVFP4 || rb0 != rb1 || rp0 != rp1 {
10577 return Ok(None);
10578 }
10579 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
10581 let rows_per_block = ROWS_PER_BLOCK * RPW;
10582 let f = self.func(if rp0 {
10583 "qmatvec_nvfp4_mmvq_dual_mr2_rp"
10584 } else {
10585 "qmatvec_nvfp4_mmvq_dual_mr2"
10586 });
10587 let mut y0 = self.alloc_uninit::<f32>(out_f)?;
10588 let mut y1 = self.alloc_uninit::<f32>(out_f)?;
10589 let cfg = LaunchConfig {
10590 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
10591 block_dim: (32, ROWS_PER_BLOCK, 1),
10592 shared_mem_bytes: 0,
10593 };
10594 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, rb0 as i64);
10595 let one = 1.0f32;
10598 let __s_b = self.gpu.stream();
10599 let mut b = __s_b.launch_builder(&f);
10600 b.arg(b0)
10601 .arg(b1)
10602 .arg(aq)
10603 .arg(ad)
10604 .arg(&mut y0)
10605 .arg(&mut y1)
10606 .arg(&inf)
10607 .arg(&outf)
10608 .arg(&mi)
10609 .arg(&rb)
10610 .arg(&one)
10611 .arg(&one);
10612 unsafe {
10613 b.launch(cfg)?;
10614 }
10615 Ok(Some(((y0, s0), (y1, s1))))
10616 }
10617
10618 #[allow(clippy::too_many_arguments)]
10626 pub fn matmul_nvfp4_fused3(
10627 &self,
10628 w0: &crate::model::GpuTensor,
10629 w1: &crate::model::GpuTensor,
10630 w2: &crate::model::GpuTensor,
10631 aq: &CudaSlice<i8>,
10632 ad: &CudaSlice<f32>,
10633 m: usize,
10634 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
10635 {
10636 use crate::model::GpuTensor;
10637 if m != 1
10641 || !self.mmvq_supports(QT_NVFP4)
10642 || !self.uses_q8_1_fast(w0)
10643 || !self.uses_q8_1_fast(w1)
10644 || !self.uses_q8_1_fast(w2)
10645 {
10646 return Ok(None);
10647 }
10648 let unpack = |w: &crate::model::GpuTensor| match w {
10649 GpuTensor::Quant {
10650 bytes,
10651 qtype,
10652 scale,
10653 rp,
10654 ..
10655 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
10656 _ => None,
10657 };
10658 let (Some(p0), Some(p1), Some(p2)) = (unpack(w0), unpack(w1), unpack(w2)) else {
10659 return Ok(None);
10660 };
10661 let in_f = w0.in_features();
10662 if w1.in_features() != in_f || w2.in_features() != in_f {
10663 return Ok(None);
10664 }
10665 let (o0, o1, o2) = (w0.out_features(), w1.out_features(), w2.out_features());
10666 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
10668 let rows_pb = ROWS_PER_BLOCK * RPW;
10669 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
10670 let f = self.func("qmatvec_nvfp4_mmvq_fused3_rp");
10671 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
10672 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
10673 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
10674 let cfg = LaunchConfig {
10675 grid_dim: (nb(o0) + nb(o1) + nb(o2), m as u32, 1),
10676 block_dim: (32, ROWS_PER_BLOCK, 1),
10677 shared_mem_bytes: 0,
10678 };
10679 let (inf, oi0, oi1, oi2, mi) = (in_f as i32, o0 as i32, o1 as i32, o2 as i32, m as i32);
10680 let (b0, b1, b2) = unsafe { (&*p0.0, &*p1.0, &*p2.0) };
10683 let __s_b = self.gpu.stream();
10684 let mut b = __s_b.launch_builder(&f);
10685 b.arg(b0)
10686 .arg(b1)
10687 .arg(b2)
10688 .arg(aq)
10689 .arg(ad)
10690 .arg(&mut y0)
10691 .arg(&mut y1)
10692 .arg(&mut y2)
10693 .arg(&inf)
10694 .arg(&oi0)
10695 .arg(&oi1)
10696 .arg(&oi2)
10697 .arg(&mi)
10698 .arg(&p0.1)
10699 .arg(&p1.1)
10700 .arg(&p2.1);
10701 unsafe {
10702 b.launch(cfg)?;
10703 }
10704 Ok(Some((y0, y1, y2)))
10705 }
10706
10707 pub fn matmul_q8_fused2(
10715 &self,
10716 w0: &crate::model::GpuTensor,
10717 w1: &crate::model::GpuTensor,
10718 aq: &CudaSlice<i8>,
10719 ad: &CudaSlice<f32>,
10720 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10721 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
10727 return Ok(Some(self.e4m3_fused2_core(
10728 p0.0,
10729 p1.0,
10730 aq,
10731 ad,
10732 w0.in_features(),
10733 p0.1,
10734 p1.1,
10735 p0.2,
10736 p0.3,
10737 p1.3,
10738 )?));
10739 }
10740 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
10741 return Ok(None);
10742 };
10743 Ok(Some(self.q8_fused2_core(
10744 p0.0,
10745 p1.0,
10746 aq,
10747 ad,
10748 w0.in_features(),
10749 p0.1,
10750 p1.1,
10751 p0.2,
10752 )?))
10753 }
10754
10755 #[allow(clippy::too_many_arguments)]
10756 fn q8_fused2_core(
10757 &self,
10758 b0: &CudaSlice<u8>,
10759 b1: &CudaSlice<u8>,
10760 aq: &CudaSlice<i8>,
10761 ad: &CudaSlice<f32>,
10762 in_f: usize,
10763 out0: usize,
10764 out1: usize,
10765 row_bytes: usize,
10766 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10767 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
10769 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
10770 let f = self.func("qmatvec_q8_0_mmvq_fused2");
10771 let mut y0 = self.alloc_uninit::<f32>(out0)?;
10772 let mut y1 = self.alloc_uninit::<f32>(out1)?;
10773 let cfg = LaunchConfig {
10774 grid_dim: (nb0 + nb1, 1, 1),
10775 block_dim: (32, ROWS_PER_BLOCK, 1),
10776 shared_mem_bytes: 0,
10777 };
10778 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
10779 let __s_b = self.gpu.stream();
10780 let mut b = __s_b.launch_builder(&f);
10781 b.arg(b0)
10782 .arg(b1)
10783 .arg(aq)
10784 .arg(ad)
10785 .arg(&mut y0)
10786 .arg(&mut y1)
10787 .arg(&inf)
10788 .arg(&o0)
10789 .arg(&o1)
10790 .arg(&rbl);
10791 unsafe {
10792 b.launch(cfg)?;
10793 }
10794 Ok((y0, y1))
10795 }
10796
10797 pub fn matmul_q8_fused2_x(
10803 &self,
10804 w0: &crate::model::GpuTensor,
10805 w1: &crate::model::GpuTensor,
10806 x: &CudaSlice<f32>,
10807 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10808 if !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
10809 return Ok(None);
10810 }
10811 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
10812 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
10813 return Ok(Some(self.e4m3_fused2_core(
10814 p0.0,
10815 p1.0,
10816 &aq,
10817 &ad,
10818 w0.in_features(),
10819 p0.1,
10820 p1.1,
10821 p0.2,
10822 p0.3,
10823 p1.3,
10824 )?));
10825 }
10826 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
10827 return Ok(None);
10828 };
10829 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
10830 Ok(Some(self.q8_fused2_core(
10831 p0.0,
10832 p1.0,
10833 &aq,
10834 &ad,
10835 w0.in_features(),
10836 p0.1,
10837 p1.1,
10838 p0.2,
10839 )?))
10840 }
10841
10842 #[allow(clippy::too_many_arguments)]
10845 pub fn qmatvec_q8_fused2_raw(
10846 &self,
10847 b0: &CudaSlice<u8>,
10848 b1: &CudaSlice<u8>,
10849 x: &CudaSlice<f32>,
10850 in_f: usize,
10851 out0: usize,
10852 out1: usize,
10853 row_bytes: usize,
10854 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10855 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
10856 self.q8_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes)
10857 }
10858
10859 pub fn matmul_q4_fused3(
10865 &self,
10866 w0: &crate::model::GpuTensor,
10867 w1: &crate::model::GpuTensor,
10868 w2: &crate::model::GpuTensor,
10869 aq: &CudaSlice<i8>,
10870 ad: &CudaSlice<f32>,
10871 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
10872 {
10873 use crate::model::GpuTensor;
10874 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
10875 match w {
10876 GpuTensor::Quant {
10877 qtype, row_bytes, ..
10878 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
10879 _ => None,
10880 }
10881 };
10882 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
10883 return Ok(None);
10884 };
10885 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
10886 return Ok(None);
10887 }
10888 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
10892 match w {
10893 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
10894 Some(m) => (m, true),
10895 None => (bytes, *rp),
10896 },
10897 _ => unreachable!(),
10898 }
10899 }
10900 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
10901 if rp0 != rp1 || rp1 != rp2 {
10902 return Ok(None);
10903 }
10904 let rp = rp0;
10905 let rpb: u32 = 4;
10906 let mr1 = rp && Self::q40_mr1_on();
10910 let nb = |o: usize| {
10911 if mr1 {
10912 (o as u32).div_ceil(rpb)
10913 } else {
10914 (o as u32).div_ceil(2).div_ceil(rpb)
10915 }
10916 };
10917 let grid = nb(o0) + nb(o1) + nb(o2);
10918 let mut y0 = self.alloc_uninit::<f32>(o0)?;
10919 let mut y1 = self.alloc_uninit::<f32>(o1)?;
10920 let mut y2 = self.alloc_uninit::<f32>(o2)?;
10921 let f = self.func(if mr1 {
10922 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
10923 } else if rp {
10924 "qmatvec_q4_0_mmvq_fused3_rp"
10925 } else {
10926 "qmatvec_q4_0_mmvq_fused3"
10927 });
10928 let cfg = LaunchConfig {
10929 grid_dim: (grid, 1, 1),
10930 block_dim: (32, rpb, 1),
10931 shared_mem_bytes: 0,
10932 };
10933 let inf = w0.in_features() as i32;
10934 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
10935 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
10936 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
10939 {
10940 use cudarc::driver::{DevicePtr, DevicePtrMut};
10941 let s = &self.gpu.stream();
10942 let (p0, _g0) = b0.device_ptr(s);
10943 let (p1, _g1) = b1.device_ptr(s);
10944 let (p2, _g2) = b2.device_ptr(s);
10945 let (paq, _g3) = aq.device_ptr(s);
10946 let (pad, _g4) = ad.device_ptr(s);
10947 let (py0, _g5) = y0.device_ptr_mut(s);
10948 let (py1, _g6) = y1.device_ptr_mut(s);
10949 let (py2, _g7) = y2.device_ptr_mut(s);
10950 let mut ps = [
10951 &p0 as *const _ as *mut std::ffi::c_void,
10952 &p1 as *const _ as *mut _,
10953 &p2 as *const _ as *mut _,
10954 &paq as *const _ as *mut _,
10955 &pad as *const _ as *mut _,
10956 &py0 as *const _ as *mut _,
10957 &py1 as *const _ as *mut _,
10958 &py2 as *const _ as *mut _,
10959 &inf as *const _ as *mut _,
10960 &oo0 as *const _ as *mut _,
10961 &oo1 as *const _ as *mut _,
10962 &oo2 as *const _ as *mut _,
10963 &r0 as *const _ as *mut _,
10964 &r1 as *const _ as *mut _,
10965 &r2 as *const _ as *mut _,
10966 ];
10967 unsafe {
10968 self.launch_pdl(
10969 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
10970 (grid, 1, 1),
10971 (32, rpb, 1),
10972 &mut ps,
10973 )?;
10974 }
10975 }
10976 return Ok(Some((y0, y1, y2)));
10977 }
10978 let __s_b = self.gpu.stream();
10979 let mut b = __s_b.launch_builder(&f);
10980 b.arg(b0)
10981 .arg(b1)
10982 .arg(b2)
10983 .arg(aq)
10984 .arg(ad)
10985 .arg(&mut y0)
10986 .arg(&mut y1)
10987 .arg(&mut y2)
10988 .arg(&inf)
10989 .arg(&oo0)
10990 .arg(&oo1)
10991 .arg(&oo2)
10992 .arg(&r0)
10993 .arg(&r1)
10994 .arg(&r2);
10995 unsafe {
10996 b.launch(cfg)?;
10997 }
10998 Ok(Some((y0, y1, y2)))
10999 }
11000
11001 #[allow(clippy::too_many_arguments)]
11004 pub fn matmul_q4_fused3_into(
11005 &self,
11006 w0: &crate::model::GpuTensor,
11007 w1: &crate::model::GpuTensor,
11008 w2: &crate::model::GpuTensor,
11009 aq: &CudaSlice<i8>,
11010 ad: &CudaSlice<f32>,
11011 y0: &mut CudaSlice<f32>,
11012 y1: &mut CudaSlice<f32>,
11013 y2: &mut CudaSlice<f32>,
11014 ) -> Result<bool, Box<dyn std::error::Error>> {
11015 use crate::model::GpuTensor;
11016 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11017 match w {
11018 GpuTensor::Quant {
11019 qtype, row_bytes, ..
11020 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11021 _ => None,
11022 }
11023 };
11024 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
11025 return Ok(false);
11026 };
11027 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
11028 return Ok(false);
11029 }
11030 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11031 match w {
11032 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11033 Some(m) => (m, true),
11034 None => (bytes, *rp),
11035 },
11036 _ => unreachable!(),
11037 }
11038 }
11039 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
11040 if rp0 != rp1 || rp1 != rp2 {
11041 return Ok(false);
11042 }
11043 let rp = rp0;
11044 let rpb: u32 = 4;
11045 let mr1 = rp && Self::q40_mr1_on();
11046 let nb = |o: usize| {
11047 if mr1 {
11048 (o as u32).div_ceil(rpb)
11049 } else {
11050 (o as u32).div_ceil(2).div_ceil(rpb)
11051 }
11052 };
11053 let grid = nb(o0) + nb(o1) + nb(o2);
11054 debug_assert!(y0.len() >= o0 && y1.len() >= o1 && y2.len() >= o2);
11055 let f = self.func(if mr1 {
11056 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
11057 } else if rp {
11058 "qmatvec_q4_0_mmvq_fused3_rp"
11059 } else {
11060 "qmatvec_q4_0_mmvq_fused3"
11061 });
11062 let cfg = LaunchConfig {
11063 grid_dim: (grid, 1, 1),
11064 block_dim: (32, rpb, 1),
11065 shared_mem_bytes: 0,
11066 };
11067 let inf = w0.in_features() as i32;
11068 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
11069 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
11070 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11072 use cudarc::driver::{DevicePtr, DevicePtrMut};
11073 let s = &self.gpu.stream();
11074 let (p0, _g0) = b0.device_ptr(s);
11075 let (p1, _g1) = b1.device_ptr(s);
11076 let (p2, _g2) = b2.device_ptr(s);
11077 let (paq, _g3) = aq.device_ptr(s);
11078 let (pad, _g4) = ad.device_ptr(s);
11079 let (py0, _g5) = y0.device_ptr_mut(s);
11080 let (py1, _g6) = y1.device_ptr_mut(s);
11081 let (py2, _g7) = y2.device_ptr_mut(s);
11082 let mut ps = [
11083 &p0 as *const _ as *mut std::ffi::c_void,
11084 &p1 as *const _ as *mut _,
11085 &p2 as *const _ as *mut _,
11086 &paq as *const _ as *mut _,
11087 &pad as *const _ as *mut _,
11088 &py0 as *const _ as *mut _,
11089 &py1 as *const _ as *mut _,
11090 &py2 as *const _ as *mut _,
11091 &inf as *const _ as *mut _,
11092 &oo0 as *const _ as *mut _,
11093 &oo1 as *const _ as *mut _,
11094 &oo2 as *const _ as *mut _,
11095 &r0 as *const _ as *mut _,
11096 &r1 as *const _ as *mut _,
11097 &r2 as *const _ as *mut _,
11098 ];
11099 unsafe {
11100 self.launch_pdl(
11101 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
11102 (grid, 1, 1),
11103 (32, rpb, 1),
11104 &mut ps,
11105 )?;
11106 }
11107 return Ok(true);
11108 }
11109 let __s_b = self.gpu.stream();
11110 let mut b = __s_b.launch_builder(&f);
11111 b.arg(b0)
11112 .arg(b1)
11113 .arg(b2)
11114 .arg(aq)
11115 .arg(ad)
11116 .arg(&mut *y0)
11117 .arg(&mut *y1)
11118 .arg(&mut *y2)
11119 .arg(&inf)
11120 .arg(&oo0)
11121 .arg(&oo1)
11122 .arg(&oo2)
11123 .arg(&r0)
11124 .arg(&r1)
11125 .arg(&r2);
11126 unsafe {
11127 b.launch(cfg)?;
11128 }
11129 Ok(true)
11130 }
11131
11132 pub fn matmul_q4_fused2(
11134 &self,
11135 w0: &crate::model::GpuTensor,
11136 w1: &crate::model::GpuTensor,
11137 aq: &CudaSlice<i8>,
11138 ad: &CudaSlice<f32>,
11139 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11140 use crate::model::GpuTensor;
11141 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11142 match w {
11143 GpuTensor::Quant {
11144 qtype, row_bytes, ..
11145 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11146 _ => None,
11147 }
11148 };
11149 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
11150 return Ok(None);
11151 };
11152 if w0.in_features() != w1.in_features() {
11153 return Ok(None);
11154 }
11155 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11157 match w {
11158 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11159 Some(m) => (m, true),
11160 None => (bytes, *rp),
11161 },
11162 _ => unreachable!(),
11163 }
11164 }
11165 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11166 if rp0 != rp1 {
11167 return Ok(None);
11168 }
11169 let rp = rp0;
11170 let rpb: u32 = 4;
11171 let mr1 = rp && Self::q40_mr1_on();
11173 let nb = |o: usize| {
11174 if mr1 {
11175 (o as u32).div_ceil(rpb)
11176 } else {
11177 (o as u32).div_ceil(2).div_ceil(rpb)
11178 }
11179 };
11180 let grid = nb(o0) + nb(o1);
11181 let mut y0 = self.alloc_uninit::<f32>(o0)?;
11182 let mut y1 = self.alloc_uninit::<f32>(o1)?;
11183 let f = self.func(if mr1 {
11184 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
11185 } else if rp {
11186 "qmatvec_q4_0_mmvq_fused2_rp"
11187 } else {
11188 "qmatvec_q4_0_mmvq_fused2"
11189 });
11190 let cfg = LaunchConfig {
11191 grid_dim: (grid, 1, 1),
11192 block_dim: (32, rpb, 1),
11193 shared_mem_bytes: 0,
11194 };
11195 let inf = w0.in_features() as i32;
11196 let (oo0, oo1) = (o0 as i32, o1 as i32);
11197 let (r0, r1) = (rb0 as i64, rb1 as i64);
11198 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11200 {
11201 use cudarc::driver::{DevicePtr, DevicePtrMut};
11202 let s = &self.gpu.stream();
11203 let (p0, _g0) = b0.device_ptr(s);
11204 let (p1, _g1) = b1.device_ptr(s);
11205 let (paq, _g2) = aq.device_ptr(s);
11206 let (pad, _g3) = ad.device_ptr(s);
11207 let (py0, _g4) = y0.device_ptr_mut(s);
11208 let (py1, _g5) = y1.device_ptr_mut(s);
11209 let mut ps = [
11210 &p0 as *const _ as *mut std::ffi::c_void,
11211 &p1 as *const _ as *mut _,
11212 &paq as *const _ as *mut _,
11213 &pad as *const _ as *mut _,
11214 &py0 as *const _ as *mut _,
11215 &py1 as *const _ as *mut _,
11216 &inf as *const _ as *mut _,
11217 &oo0 as *const _ as *mut _,
11218 &oo1 as *const _ as *mut _,
11219 &r0 as *const _ as *mut _,
11220 &r1 as *const _ as *mut _,
11221 ];
11222 unsafe {
11223 self.launch_pdl(
11224 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
11225 (grid, 1, 1),
11226 (32, rpb, 1),
11227 &mut ps,
11228 )?;
11229 }
11230 }
11231 return Ok(Some((y0, y1)));
11232 }
11233 let __s_b = self.gpu.stream();
11234 let mut b = __s_b.launch_builder(&f);
11235 b.arg(b0)
11236 .arg(b1)
11237 .arg(aq)
11238 .arg(ad)
11239 .arg(&mut y0)
11240 .arg(&mut y1)
11241 .arg(&inf)
11242 .arg(&oo0)
11243 .arg(&oo1)
11244 .arg(&r0)
11245 .arg(&r1);
11246 unsafe {
11247 b.launch(cfg)?;
11248 }
11249 Ok(Some((y0, y1)))
11250 }
11251
11252 pub fn matmul_q4_fused2_into(
11254 &self,
11255 w0: &crate::model::GpuTensor,
11256 w1: &crate::model::GpuTensor,
11257 aq: &CudaSlice<i8>,
11258 ad: &CudaSlice<f32>,
11259 y0: &mut CudaSlice<f32>,
11260 y1: &mut CudaSlice<f32>,
11261 ) -> Result<bool, Box<dyn std::error::Error>> {
11262 use crate::model::GpuTensor;
11263 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11264 match w {
11265 GpuTensor::Quant {
11266 qtype, row_bytes, ..
11267 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11268 _ => None,
11269 }
11270 };
11271 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
11272 return Ok(false);
11273 };
11274 if w0.in_features() != w1.in_features() {
11275 return Ok(false);
11276 }
11277 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11278 match w {
11279 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11280 Some(m) => (m, true),
11281 None => (bytes, *rp),
11282 },
11283 _ => unreachable!(),
11284 }
11285 }
11286 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11287 if rp0 != rp1 {
11288 return Ok(false);
11289 }
11290 let rp = rp0;
11291 let rpb: u32 = 4;
11292 let mr1 = rp && Self::q40_mr1_on();
11293 let nb = |o: usize| {
11294 if mr1 {
11295 (o as u32).div_ceil(rpb)
11296 } else {
11297 (o as u32).div_ceil(2).div_ceil(rpb)
11298 }
11299 };
11300 let grid = nb(o0) + nb(o1);
11301 debug_assert!(y0.len() >= o0 && y1.len() >= o1);
11302 let f = self.func(if mr1 {
11303 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
11304 } else if rp {
11305 "qmatvec_q4_0_mmvq_fused2_rp"
11306 } else {
11307 "qmatvec_q4_0_mmvq_fused2"
11308 });
11309 let cfg = LaunchConfig {
11310 grid_dim: (grid, 1, 1),
11311 block_dim: (32, rpb, 1),
11312 shared_mem_bytes: 0,
11313 };
11314 let inf = w0.in_features() as i32;
11315 let (oo0, oo1) = (o0 as i32, o1 as i32);
11316 let (r0, r1) = (rb0 as i64, rb1 as i64);
11317 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11319 use cudarc::driver::{DevicePtr, DevicePtrMut};
11320 let s = &self.gpu.stream();
11321 let (p0, _g0) = b0.device_ptr(s);
11322 let (p1, _g1) = b1.device_ptr(s);
11323 let (paq, _g2) = aq.device_ptr(s);
11324 let (pad, _g3) = ad.device_ptr(s);
11325 let (py0, _g4) = y0.device_ptr_mut(s);
11326 let (py1, _g5) = y1.device_ptr_mut(s);
11327 let mut ps = [
11328 &p0 as *const _ as *mut std::ffi::c_void,
11329 &p1 as *const _ as *mut _,
11330 &paq as *const _ as *mut _,
11331 &pad as *const _ as *mut _,
11332 &py0 as *const _ as *mut _,
11333 &py1 as *const _ as *mut _,
11334 &inf as *const _ as *mut _,
11335 &oo0 as *const _ as *mut _,
11336 &oo1 as *const _ as *mut _,
11337 &r0 as *const _ as *mut _,
11338 &r1 as *const _ as *mut _,
11339 ];
11340 unsafe {
11341 self.launch_pdl(
11342 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
11343 (grid, 1, 1),
11344 (32, rpb, 1),
11345 &mut ps,
11346 )?;
11347 }
11348 return Ok(true);
11349 }
11350 let __s_b = self.gpu.stream();
11351 let mut b = __s_b.launch_builder(&f);
11352 b.arg(b0)
11353 .arg(b1)
11354 .arg(aq)
11355 .arg(ad)
11356 .arg(&mut *y0)
11357 .arg(&mut *y1)
11358 .arg(&inf)
11359 .arg(&oo0)
11360 .arg(&oo1)
11361 .arg(&r0)
11362 .arg(&r1);
11363 unsafe {
11364 b.launch(cfg)?;
11365 }
11366 Ok(true)
11367 }
11368
11369 pub fn matmul_q4_fused2_batched(
11374 &self,
11375 w0: &crate::model::GpuTensor,
11376 w1: &crate::model::GpuTensor,
11377 aq: &CudaSlice<i8>,
11378 ad: &CudaSlice<f32>,
11379 m: usize,
11380 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11381 use crate::model::GpuTensor;
11382 if m < 2 || m > 8 {
11383 return Ok(None);
11384 }
11385 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11386 match w {
11387 GpuTensor::Quant {
11388 qtype, row_bytes, ..
11389 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11390 _ => None,
11391 }
11392 };
11393 let (Some((rb0, o0)), Some((_rb1, o1))) = (q4(w0), q4(w1)) else {
11394 return Ok(None);
11395 };
11396 if w0.in_features() != w1.in_features() {
11397 return Ok(None);
11398 }
11399 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11400 match w {
11401 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11402 Some(mr) => (mr, true),
11403 None => (bytes, *rp),
11404 },
11405 _ => unreachable!(),
11406 }
11407 }
11408 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11409 if !rp0 || !rp1 {
11410 return Ok(None);
11411 }
11412 let mcols = Self::batched_mcols(m);
11413 let rpb: u32 = 4;
11414 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
11415 let grid = nb(o0) + nb(o1);
11416 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11417 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11418 let f = self.func(match mcols {
11419 2 => "qmatvec_q4_0_mmvq_b2_f2_rp",
11420 4 => "qmatvec_q4_0_mmvq_b4_f2_rp",
11421 _ => "qmatvec_q4_0_mmvq_b8_f2_rp",
11422 });
11423 let cfg = LaunchConfig {
11424 grid_dim: (grid, 1, 1),
11425 block_dim: (32, rpb, 1),
11426 shared_mem_bytes: 0,
11427 };
11428 let inf = w0.in_features() as i32;
11429 let (oo0, oo1, mi) = (o0 as i32, o1 as i32, m as i32);
11430 let rb = rb0 as i64;
11431 let __s_b = self.gpu.stream();
11432 let mut b = __s_b.launch_builder(&f);
11433 b.arg(b0)
11434 .arg(b1)
11435 .arg(aq)
11436 .arg(ad)
11437 .arg(&mut y0)
11438 .arg(&mut y1)
11439 .arg(&inf)
11440 .arg(&oo0)
11441 .arg(&oo1)
11442 .arg(&mi)
11443 .arg(&rb);
11444 unsafe {
11445 b.launch(cfg)?;
11446 }
11447 Ok(Some((y0, y1)))
11448 }
11449
11450 #[allow(clippy::too_many_arguments)]
11453 pub fn matmul_q4_fused3_batched(
11454 &self,
11455 w0: &crate::model::GpuTensor,
11456 w1: &crate::model::GpuTensor,
11457 w2: &crate::model::GpuTensor,
11458 aq: &CudaSlice<i8>,
11459 ad: &CudaSlice<f32>,
11460 m: usize,
11461 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11462 {
11463 use crate::model::GpuTensor;
11464 if m < 2 || m > 8 {
11465 return Ok(None);
11466 }
11467 let q4 = |w: &GpuTensor| -> Option<usize> {
11468 match w {
11469 GpuTensor::Quant { qtype, .. } if *qtype == QT_Q4_0 => Some(w.out_features()),
11470 _ => None,
11471 }
11472 };
11473 let (Some(o0), Some(o1), Some(o2)) = (q4(w0), q4(w1), q4(w2)) else {
11474 return Ok(None);
11475 };
11476 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
11477 return Ok(None);
11478 }
11479 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11480 match w {
11481 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11482 Some(mr) => (mr, true),
11483 None => (bytes, *rp),
11484 },
11485 _ => unreachable!(),
11486 }
11487 }
11488 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
11489 if !rp0 || !rp1 || !rp2 {
11490 return Ok(None);
11491 }
11492 let mcols = Self::batched_mcols(m);
11493 let rpb: u32 = 4;
11494 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
11495 let grid = nb(o0) + nb(o1) + nb(o2);
11496 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11497 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11498 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
11499 let f = self.func(match mcols {
11500 2 => "qmatvec_q4_0_mmvq_b2_f3_rp",
11501 4 => "qmatvec_q4_0_mmvq_b4_f3_rp",
11502 _ => "qmatvec_q4_0_mmvq_b8_f3_rp",
11503 });
11504 let cfg = LaunchConfig {
11505 grid_dim: (grid, 1, 1),
11506 block_dim: (32, rpb, 1),
11507 shared_mem_bytes: 0,
11508 };
11509 let inf = w0.in_features() as i32;
11510 let (oo0, oo1, oo2, mi) = (o0 as i32, o1 as i32, o2 as i32, m as i32);
11511 let rb = 0i64;
11512 let __s_b = self.gpu.stream();
11513 let mut b = __s_b.launch_builder(&f);
11514 b.arg(b0)
11515 .arg(b1)
11516 .arg(b2)
11517 .arg(aq)
11518 .arg(ad)
11519 .arg(&mut y0)
11520 .arg(&mut y1)
11521 .arg(&mut y2)
11522 .arg(&inf)
11523 .arg(&oo0)
11524 .arg(&oo1)
11525 .arg(&oo2)
11526 .arg(&mi)
11527 .arg(&rb);
11528 unsafe {
11529 b.launch(cfg)?;
11530 }
11531 Ok(Some((y0, y1, y2)))
11532 }
11533
11534 pub fn matmul_q8_fused3(
11535 &self,
11536 w0: &crate::model::GpuTensor,
11537 w1: &crate::model::GpuTensor,
11538 w2: &crate::model::GpuTensor,
11539 aq: &CudaSlice<i8>,
11540 ad: &CudaSlice<f32>,
11541 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11542 {
11543 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
11546 return Ok(Some(self.e4m3_fused3_core(
11547 p0.0,
11548 p1.0,
11549 p2.0,
11550 aq,
11551 ad,
11552 w0.in_features(),
11553 p0.1,
11554 p1.1,
11555 p2.1,
11556 p0.2,
11557 p0.3,
11558 p1.3,
11559 p2.3,
11560 )?));
11561 }
11562 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
11563 return Ok(None);
11564 };
11565 Ok(Some(self.q8_fused3_core(
11566 p0.0,
11567 p1.0,
11568 p2.0,
11569 aq,
11570 ad,
11571 w0.in_features(),
11572 p0.1,
11573 p1.1,
11574 p2.1,
11575 p0.2,
11576 )?))
11577 }
11578
11579 #[allow(clippy::too_many_arguments)]
11580 fn q8_fused3_core(
11581 &self,
11582 b0: &CudaSlice<u8>,
11583 b1: &CudaSlice<u8>,
11584 b2: &CudaSlice<u8>,
11585 aq: &CudaSlice<i8>,
11586 ad: &CudaSlice<f32>,
11587 in_f: usize,
11588 out0: usize,
11589 out1: usize,
11590 out2: usize,
11591 row_bytes: usize,
11592 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11593 const ROWS_PER_BLOCK: u32 = 4;
11594 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11595 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11596 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
11597 let f = self.func("qmatvec_q8_0_mmvq_fused3");
11598 let mut y0 = self.alloc_uninit::<f32>(out0)?;
11599 let mut y1 = self.alloc_uninit::<f32>(out1)?;
11600 let mut y2 = self.alloc_uninit::<f32>(out2)?;
11601 let cfg = LaunchConfig {
11602 grid_dim: (nb0 + nb1 + nb2, 1, 1),
11603 block_dim: (32, ROWS_PER_BLOCK, 1),
11604 shared_mem_bytes: 0,
11605 };
11606 let (inf, o0, o1, o2, rbl) = (
11607 in_f as i32,
11608 out0 as i32,
11609 out1 as i32,
11610 out2 as i32,
11611 row_bytes as i64,
11612 );
11613 let __s_b = self.gpu.stream();
11614 let mut b = __s_b.launch_builder(&f);
11615 b.arg(b0)
11616 .arg(b1)
11617 .arg(b2)
11618 .arg(aq)
11619 .arg(ad)
11620 .arg(&mut y0)
11621 .arg(&mut y1)
11622 .arg(&mut y2)
11623 .arg(&inf)
11624 .arg(&o0)
11625 .arg(&o1)
11626 .arg(&o2)
11627 .arg(&rbl);
11628 unsafe {
11629 b.launch(cfg)?;
11630 }
11631 Ok((y0, y1, y2))
11632 }
11633
11634 #[allow(clippy::too_many_arguments)]
11636 pub fn qmatvec_q8_fused3_raw(
11637 &self,
11638 b0: &CudaSlice<u8>,
11639 b1: &CudaSlice<u8>,
11640 b2: &CudaSlice<u8>,
11641 x: &CudaSlice<f32>,
11642 in_f: usize,
11643 out0: usize,
11644 out1: usize,
11645 out2: usize,
11646 row_bytes: usize,
11647 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11648 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
11649 self.q8_fused3_core(b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes)
11650 }
11651
11652 pub fn matmul_q8_fused2_t(
11663 &self,
11664 w0: &crate::model::GpuTensor,
11665 w1: &crate::model::GpuTensor,
11666 aq: &CudaSlice<i8>,
11667 ad: &CudaSlice<f32>,
11668 m: usize,
11669 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11670 if !(2..=8).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
11674 return Ok(None);
11675 }
11676 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
11679 if m > 4 && !Self::b8_enabled() {
11680 return Ok(None);
11681 }
11682 return Ok(Some(self.e4m3_fused2_t_core(
11683 p0.0,
11684 p1.0,
11685 aq,
11686 ad,
11687 m,
11688 w0.in_features(),
11689 p0.1,
11690 p1.1,
11691 p0.2,
11692 p0.3,
11693 p1.3,
11694 )?));
11695 }
11696 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
11697 return Ok(None);
11698 };
11699 Ok(Some(self.q8_fused2_t_core(
11700 p0.0,
11701 p1.0,
11702 aq,
11703 ad,
11704 m,
11705 w0.in_features(),
11706 p0.1,
11707 p1.1,
11708 p0.2,
11709 )?))
11710 }
11711
11712 #[allow(clippy::too_many_arguments)]
11713 fn q8_fused2_t_core(
11714 &self,
11715 b0: &CudaSlice<u8>,
11716 b1: &CudaSlice<u8>,
11717 aq: &CudaSlice<i8>,
11718 ad: &CudaSlice<f32>,
11719 m: usize,
11720 in_f: usize,
11721 out0: usize,
11722 out1: usize,
11723 row_bytes: usize,
11724 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11725 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11727 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11728 let f = self.func(match Self::batched_mcols(m) {
11729 2 => "qmatvec_q8_0_mmvq_fused2_b2",
11730 4 => "qmatvec_q8_0_mmvq_fused2_b4",
11731 _ => "qmatvec_q8_0_mmvq_fused2_b8",
11733 });
11734 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
11735 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
11736 let cfg = LaunchConfig {
11737 grid_dim: (nb0 + nb1, 1, 1),
11738 block_dim: (32, ROWS_PER_BLOCK, 1),
11739 shared_mem_bytes: 0,
11740 };
11741 let (inf, o0, o1, mi, rbl) = (
11742 in_f as i32,
11743 out0 as i32,
11744 out1 as i32,
11745 m as i32,
11746 row_bytes as i64,
11747 );
11748 let __s_b = self.gpu.stream();
11749 let mut b = __s_b.launch_builder(&f);
11750 b.arg(b0)
11751 .arg(b1)
11752 .arg(aq)
11753 .arg(ad)
11754 .arg(&mut y0)
11755 .arg(&mut y1)
11756 .arg(&inf)
11757 .arg(&o0)
11758 .arg(&o1)
11759 .arg(&mi)
11760 .arg(&rbl);
11761 unsafe {
11762 b.launch(cfg)?;
11763 }
11764 Ok((y0, y1))
11765 }
11766
11767 #[allow(clippy::too_many_arguments)]
11770 pub fn qmatvec_q8_fused2_t_raw(
11771 &self,
11772 b0: &CudaSlice<u8>,
11773 b1: &CudaSlice<u8>,
11774 x: &CudaSlice<f32>,
11775 m: usize,
11776 in_f: usize,
11777 out0: usize,
11778 out1: usize,
11779 row_bytes: usize,
11780 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11781 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
11782 self.q8_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes)
11783 }
11784
11785 #[allow(clippy::too_many_arguments)]
11788 pub fn matmul_q8_fused3_t(
11789 &self,
11790 w0: &crate::model::GpuTensor,
11791 w1: &crate::model::GpuTensor,
11792 w2: &crate::model::GpuTensor,
11793 aq: &CudaSlice<i8>,
11794 ad: &CudaSlice<f32>,
11795 m: usize,
11796 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11797 {
11798 if !(2..=4).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
11799 return Ok(None);
11800 }
11801 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
11802 return Ok(Some(self.e4m3_fused3_t_core(
11803 p0.0,
11804 p1.0,
11805 p2.0,
11806 aq,
11807 ad,
11808 m,
11809 w0.in_features(),
11810 p0.1,
11811 p1.1,
11812 p2.1,
11813 p0.2,
11814 p0.3,
11815 p1.3,
11816 p2.3,
11817 )?));
11818 }
11819 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
11820 return Ok(None);
11821 };
11822 Ok(Some(self.q8_fused3_t_core(
11823 p0.0,
11824 p1.0,
11825 p2.0,
11826 aq,
11827 ad,
11828 m,
11829 w0.in_features(),
11830 p0.1,
11831 p1.1,
11832 p2.1,
11833 p0.2,
11834 )?))
11835 }
11836
11837 #[allow(clippy::too_many_arguments)]
11838 fn q8_fused3_t_core(
11839 &self,
11840 b0: &CudaSlice<u8>,
11841 b1: &CudaSlice<u8>,
11842 b2: &CudaSlice<u8>,
11843 aq: &CudaSlice<i8>,
11844 ad: &CudaSlice<f32>,
11845 m: usize,
11846 in_f: usize,
11847 out0: usize,
11848 out1: usize,
11849 out2: usize,
11850 row_bytes: usize,
11851 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11852 const ROWS_PER_BLOCK: u32 = 4;
11853 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11854 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11855 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
11856 let f = self.func(if Self::batched_mcols(m) == 2 {
11857 "qmatvec_q8_0_mmvq_fused3_b2"
11858 } else {
11859 "qmatvec_q8_0_mmvq_fused3_b4"
11860 });
11861 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
11862 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
11863 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
11864 let cfg = LaunchConfig {
11865 grid_dim: (nb0 + nb1 + nb2, 1, 1),
11866 block_dim: (32, ROWS_PER_BLOCK, 1),
11867 shared_mem_bytes: 0,
11868 };
11869 let (inf, o0, o1, o2, mi, rbl) = (
11870 in_f as i32,
11871 out0 as i32,
11872 out1 as i32,
11873 out2 as i32,
11874 m as i32,
11875 row_bytes as i64,
11876 );
11877 let __s_b = self.gpu.stream();
11878 let mut b = __s_b.launch_builder(&f);
11879 b.arg(b0)
11880 .arg(b1)
11881 .arg(b2)
11882 .arg(aq)
11883 .arg(ad)
11884 .arg(&mut y0)
11885 .arg(&mut y1)
11886 .arg(&mut y2)
11887 .arg(&inf)
11888 .arg(&o0)
11889 .arg(&o1)
11890 .arg(&o2)
11891 .arg(&mi)
11892 .arg(&rbl);
11893 unsafe {
11894 b.launch(cfg)?;
11895 }
11896 Ok((y0, y1, y2))
11897 }
11898
11899 #[allow(clippy::too_many_arguments)]
11901 pub fn qmatvec_q8_fused3_t_raw(
11902 &self,
11903 b0: &CudaSlice<u8>,
11904 b1: &CudaSlice<u8>,
11905 b2: &CudaSlice<u8>,
11906 x: &CudaSlice<f32>,
11907 m: usize,
11908 in_f: usize,
11909 out0: usize,
11910 out1: usize,
11911 out2: usize,
11912 row_bytes: usize,
11913 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11914 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
11915 self.q8_fused3_t_core(b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes)
11916 }
11917
11918 pub fn q8_ffn_fuse2_on(&self) -> bool {
11922 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11923 *ON.get_or_init(|| std::env::var("MEMRA_Q8_FFN_FUSE2").as_deref() != Ok("0"))
11924 }
11925
11926 #[allow(clippy::type_complexity)]
11932 fn q8_fused_params<'w, const N: usize>(
11933 &self,
11934 ws: &[&'w crate::model::GpuTensor; N],
11935 ) -> Option<[(&'w CudaSlice<u8>, usize, usize); N]> {
11936 use crate::model::GpuTensor;
11937 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
11938 return None;
11939 }
11940 if std::env::var("MEMRA_Q8_DUAL").is_ok_and(|v| v == "0") {
11941 return None;
11942 }
11943 let in_f = ws[0].in_features();
11944 let mut out: [Option<(&CudaSlice<u8>, usize, usize)>; N] = [None; N];
11945 for (i, w) in ws.iter().enumerate() {
11946 match w {
11947 GpuTensor::Quant {
11948 bytes,
11949 qtype,
11950 row_bytes,
11951 scale,
11952 ..
11953 } if *qtype == QT_Q8_0 && *scale == 1.0 && w.in_features() == in_f => {
11954 out[i] = Some((bytes, w.out_features(), *row_bytes))
11955 }
11956 _ => return None,
11957 }
11958 }
11959 Some(out.map(|o| o.unwrap()))
11960 }
11961
11962 pub fn e4m3_dual_on(&self) -> bool {
11965 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11966 *ON.get_or_init(|| std::env::var("MEMRA_E4M3_DUAL").as_deref() != Ok("0"))
11967 }
11968
11969 #[allow(clippy::type_complexity)]
11981 fn e4m3_fused_params<'w, const N: usize>(
11982 &self,
11983 ws: &[&'w crate::model::GpuTensor; N],
11984 ) -> Option<[(&'w CudaSlice<u8>, usize, usize, f32); N]> {
11985 use crate::model::GpuTensor;
11986 if !self.e4m3_dual_on() {
11987 return None;
11988 }
11989 let in_f = ws[0].in_features();
11990 let mut out: [Option<(&CudaSlice<u8>, usize, usize, f32)>; N] = [None; N];
11991 for (i, w) in ws.iter().enumerate() {
11992 match w {
11993 GpuTensor::Quant {
11994 bytes,
11995 qtype,
11996 row_bytes,
11997 scale,
11998 rp,
11999 rp4,
12000 ..
12001 } if *qtype == QT_F8_E4M3
12002 && w.in_features() == in_f
12003 && *row_bytes == in_f
12004 && !*rp
12005 && rp4.is_none() =>
12006 {
12007 out[i] = Some((bytes, w.out_features(), *row_bytes, *scale))
12008 }
12009 _ => return None,
12010 }
12011 }
12012 Some(out.map(|o| o.unwrap()))
12013 }
12014
12015 #[allow(clippy::too_many_arguments)]
12019 fn e4m3_fused2_core(
12020 &self,
12021 b0: &CudaSlice<u8>,
12022 b1: &CudaSlice<u8>,
12023 aq: &CudaSlice<i8>,
12024 ad: &CudaSlice<f32>,
12025 in_f: usize,
12026 out0: usize,
12027 out1: usize,
12028 row_bytes: usize,
12029 ws0: f32,
12030 ws1: f32,
12031 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12032 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12034 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12035 let f = self.func("qmatvec_e4m3_mmvq_fused2");
12036 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12037 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12038 let cfg = LaunchConfig {
12039 grid_dim: (nb0 + nb1, 1, 1),
12040 block_dim: (32, ROWS_PER_BLOCK, 1),
12041 shared_mem_bytes: 0,
12042 };
12043 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
12044 let __s_b = self.gpu.stream();
12045 let mut b = __s_b.launch_builder(&f);
12046 b.arg(b0)
12047 .arg(b1)
12048 .arg(aq)
12049 .arg(ad)
12050 .arg(&mut y0)
12051 .arg(&mut y1)
12052 .arg(&inf)
12053 .arg(&o0)
12054 .arg(&o1)
12055 .arg(&rbl)
12056 .arg(&ws0)
12057 .arg(&ws1);
12058 unsafe {
12059 b.launch(cfg)?;
12060 }
12061 Ok((y0, y1))
12062 }
12063
12064 #[allow(clippy::too_many_arguments)]
12066 fn e4m3_fused3_core(
12067 &self,
12068 b0: &CudaSlice<u8>,
12069 b1: &CudaSlice<u8>,
12070 b2: &CudaSlice<u8>,
12071 aq: &CudaSlice<i8>,
12072 ad: &CudaSlice<f32>,
12073 in_f: usize,
12074 out0: usize,
12075 out1: usize,
12076 out2: usize,
12077 row_bytes: usize,
12078 ws0: f32,
12079 ws1: f32,
12080 ws2: f32,
12081 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12082 const ROWS_PER_BLOCK: u32 = 4;
12083 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12084 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12085 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12086 let f = self.func("qmatvec_e4m3_mmvq_fused3");
12087 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12088 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12089 let mut y2 = self.alloc_uninit::<f32>(out2)?;
12090 let cfg = LaunchConfig {
12091 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12092 block_dim: (32, ROWS_PER_BLOCK, 1),
12093 shared_mem_bytes: 0,
12094 };
12095 let (inf, o0, o1, o2, rbl) = (
12096 in_f as i32,
12097 out0 as i32,
12098 out1 as i32,
12099 out2 as i32,
12100 row_bytes as i64,
12101 );
12102 let __s_b = self.gpu.stream();
12103 let mut b = __s_b.launch_builder(&f);
12104 b.arg(b0)
12105 .arg(b1)
12106 .arg(b2)
12107 .arg(aq)
12108 .arg(ad)
12109 .arg(&mut y0)
12110 .arg(&mut y1)
12111 .arg(&mut y2)
12112 .arg(&inf)
12113 .arg(&o0)
12114 .arg(&o1)
12115 .arg(&o2)
12116 .arg(&rbl)
12117 .arg(&ws0)
12118 .arg(&ws1)
12119 .arg(&ws2);
12120 unsafe {
12121 b.launch(cfg)?;
12122 }
12123 Ok((y0, y1, y2))
12124 }
12125
12126 #[allow(clippy::too_many_arguments)]
12130 fn e4m3_fused2_t_core(
12131 &self,
12132 b0: &CudaSlice<u8>,
12133 b1: &CudaSlice<u8>,
12134 aq: &CudaSlice<i8>,
12135 ad: &CudaSlice<f32>,
12136 m: usize,
12137 in_f: usize,
12138 out0: usize,
12139 out1: usize,
12140 row_bytes: usize,
12141 ws0: f32,
12142 ws1: f32,
12143 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12144 const ROWS_PER_BLOCK: u32 = 4;
12145 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12146 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12147 let f = self.func(match Self::batched_mcols(m) {
12148 2 => "qmatvec_e4m3_mmvq_fused2_b2",
12149 4 => "qmatvec_e4m3_mmvq_fused2_b4",
12150 _ => "qmatvec_e4m3_mmvq_fused2_b8",
12151 });
12152 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12153 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12154 let cfg = LaunchConfig {
12155 grid_dim: (nb0 + nb1, 1, 1),
12156 block_dim: (32, ROWS_PER_BLOCK, 1),
12157 shared_mem_bytes: 0,
12158 };
12159 let (inf, o0, o1, mi, rbl) = (
12160 in_f as i32,
12161 out0 as i32,
12162 out1 as i32,
12163 m as i32,
12164 row_bytes as i64,
12165 );
12166 let __s_b = self.gpu.stream();
12167 let mut b = __s_b.launch_builder(&f);
12168 b.arg(b0)
12169 .arg(b1)
12170 .arg(aq)
12171 .arg(ad)
12172 .arg(&mut y0)
12173 .arg(&mut y1)
12174 .arg(&inf)
12175 .arg(&o0)
12176 .arg(&o1)
12177 .arg(&mi)
12178 .arg(&rbl);
12179 unsafe {
12180 b.launch(cfg)?;
12181 }
12182 if ws0 != 1.0 {
12183 self.scale_inplace(&mut y0, ws0, m * out0)?;
12184 }
12185 if ws1 != 1.0 {
12186 self.scale_inplace(&mut y1, ws1, m * out1)?;
12187 }
12188 Ok((y0, y1))
12189 }
12190
12191 #[allow(clippy::too_many_arguments)]
12193 fn e4m3_fused3_t_core(
12194 &self,
12195 b0: &CudaSlice<u8>,
12196 b1: &CudaSlice<u8>,
12197 b2: &CudaSlice<u8>,
12198 aq: &CudaSlice<i8>,
12199 ad: &CudaSlice<f32>,
12200 m: usize,
12201 in_f: usize,
12202 out0: usize,
12203 out1: usize,
12204 out2: usize,
12205 row_bytes: usize,
12206 ws0: f32,
12207 ws1: f32,
12208 ws2: f32,
12209 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12210 const ROWS_PER_BLOCK: u32 = 4;
12211 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12212 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12213 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12214 let f = self.func(if Self::batched_mcols(m) == 2 {
12215 "qmatvec_e4m3_mmvq_fused3_b2"
12216 } else {
12217 "qmatvec_e4m3_mmvq_fused3_b4"
12218 });
12219 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12220 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12221 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
12222 let cfg = LaunchConfig {
12223 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12224 block_dim: (32, ROWS_PER_BLOCK, 1),
12225 shared_mem_bytes: 0,
12226 };
12227 let (inf, o0, o1, o2, mi, rbl) = (
12228 in_f as i32,
12229 out0 as i32,
12230 out1 as i32,
12231 out2 as i32,
12232 m as i32,
12233 row_bytes as i64,
12234 );
12235 let __s_b = self.gpu.stream();
12236 let mut b = __s_b.launch_builder(&f);
12237 b.arg(b0)
12238 .arg(b1)
12239 .arg(b2)
12240 .arg(aq)
12241 .arg(ad)
12242 .arg(&mut y0)
12243 .arg(&mut y1)
12244 .arg(&mut y2)
12245 .arg(&inf)
12246 .arg(&o0)
12247 .arg(&o1)
12248 .arg(&o2)
12249 .arg(&mi)
12250 .arg(&rbl);
12251 unsafe {
12252 b.launch(cfg)?;
12253 }
12254 if ws0 != 1.0 {
12255 self.scale_inplace(&mut y0, ws0, m * out0)?;
12256 }
12257 if ws1 != 1.0 {
12258 self.scale_inplace(&mut y1, ws1, m * out1)?;
12259 }
12260 if ws2 != 1.0 {
12261 self.scale_inplace(&mut y2, ws2, m * out2)?;
12262 }
12263 Ok((y0, y1, y2))
12264 }
12265
12266 pub fn qmatvec_e4m3_blk_mmvq(
12276 &self,
12277 bytes: &CudaSlice<u8>,
12278 aq: &CudaSlice<i8>,
12279 ad: &CudaSlice<f32>,
12280 scales: &CudaSlice<f32>,
12281 m: usize,
12282 in_f: usize,
12283 out_f: usize,
12284 row_bytes: usize,
12285 scale_cols: usize,
12286 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12287 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_e4m3_blk_mmvq_into(
12289 bytes, aq, ad, scales, m, in_f, out_f, row_bytes, scale_cols, &mut y,
12290 )?;
12291 Ok(y)
12292 }
12293
12294 #[allow(clippy::too_many_arguments)]
12296 pub fn qmatvec_e4m3_blk_mmvq_into(
12297 &self,
12298 bytes: &CudaSlice<u8>,
12299 aq: &CudaSlice<i8>,
12300 ad: &CudaSlice<f32>,
12301 scales: &CudaSlice<f32>,
12302 m: usize,
12303 in_f: usize,
12304 out_f: usize,
12305 row_bytes: usize,
12306 scale_cols: usize,
12307 y: &mut CudaSlice<f32>,
12308 ) -> Result<(), Box<dyn std::error::Error>> {
12309 const ROWS_PER_BLOCK: u32 = 4; let f = self.func("qmatvec_e4m3_blk_mmvq");
12311 let cfg = LaunchConfig {
12312 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), m as u32, 1),
12313 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
12316 let (inf, outf, mi, rb, sc) = (
12317 in_f as i32,
12318 out_f as i32,
12319 m as i32,
12320 row_bytes as i64,
12321 scale_cols as i32,
12322 );
12323 let __s_b = self.gpu.stream();
12324 let mut b = __s_b.launch_builder(&f);
12325 b.arg(bytes)
12326 .arg(aq)
12327 .arg(ad)
12328 .arg(scales)
12329 .arg(&mut *y)
12330 .arg(&inf)
12331 .arg(&outf)
12332 .arg(&mi)
12333 .arg(&rb)
12334 .arg(&sc);
12335 unsafe {
12336 b.launch(cfg)?;
12337 }
12338 Ok(())
12339 }
12340
12341 #[allow(clippy::too_many_arguments)]
12347 pub fn qmatvec_e4m3_blk_mmvq_batched(
12348 &self,
12349 bytes: &CudaSlice<u8>,
12350 aq: &CudaSlice<i8>,
12351 ad: &CudaSlice<f32>,
12352 scales: &CudaSlice<f32>,
12353 m: usize,
12354 in_f: usize,
12355 out_f: usize,
12356 row_bytes: usize,
12357 scale_cols: usize,
12358 mcols: usize,
12359 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12360 const ROWS_PER_BLOCK: u32 = 4; debug_assert!(mcols >= m, "blk batched: mcols {mcols} < m {m}");
12362 let name = match mcols {
12363 2 => "qmatvec_e4m3_blk_mmvq_b2",
12364 4 => "qmatvec_e4m3_blk_mmvq_b4",
12365 8 => "qmatvec_e4m3_blk_mmvq_b8",
12366 16 => "qmatvec_e4m3_blk_mmvq_b16",
12367 _ => {
12368 return Err(
12369 format!("qmatvec_e4m3_blk_mmvq_batched: no kernel for mcols {mcols}").into(),
12370 );
12371 }
12372 };
12373 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
12374 let f = self.func(name);
12375 let cfg = LaunchConfig {
12376 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), 1, 1),
12377 block_dim: (32, ROWS_PER_BLOCK, 1),
12378 shared_mem_bytes: 0,
12379 };
12380 let (inf, outf, mi, rb, sc) = (
12381 in_f as i32,
12382 out_f as i32,
12383 m as i32,
12384 row_bytes as i64,
12385 scale_cols as i32,
12386 );
12387 let __s_b = self.gpu.stream();
12388 let mut b = __s_b.launch_builder(&f);
12389 b.arg(bytes)
12390 .arg(aq)
12391 .arg(ad)
12392 .arg(scales)
12393 .arg(&mut y)
12394 .arg(&inf)
12395 .arg(&outf)
12396 .arg(&mi)
12397 .arg(&rb)
12398 .arg(&sc);
12399 unsafe {
12400 b.launch(cfg)?;
12401 }
12402 Ok(y)
12403 }
12404
12405 #[allow(clippy::too_many_arguments)]
12408 pub fn qmatvec_e4m3_blk_batched_raw(
12409 &self,
12410 bytes: &CudaSlice<u8>,
12411 x: &CudaSlice<f32>,
12412 scales: &CudaSlice<f32>,
12413 m: usize,
12414 in_f: usize,
12415 out_f: usize,
12416 row_bytes: usize,
12417 scale_cols: usize,
12418 mcols: usize,
12419 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12420 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12421 self.qmatvec_e4m3_blk_mmvq_batched(
12422 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols, mcols,
12423 )
12424 }
12425
12426 #[allow(clippy::too_many_arguments)]
12429 pub fn qmatvec_e4m3_blk_mmvq_raw(
12430 &self,
12431 bytes: &CudaSlice<u8>,
12432 x: &CudaSlice<f32>,
12433 scales: &CudaSlice<f32>,
12434 m: usize,
12435 in_f: usize,
12436 out_f: usize,
12437 row_bytes: usize,
12438 scale_cols: usize,
12439 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12440 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12441 self.qmatvec_e4m3_blk_mmvq(
12442 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols,
12443 )
12444 }
12445
12446 #[allow(clippy::too_many_arguments)]
12449 pub fn qmatvec_e4m3_fused2_raw(
12450 &self,
12451 b0: &CudaSlice<u8>,
12452 b1: &CudaSlice<u8>,
12453 x: &CudaSlice<f32>,
12454 in_f: usize,
12455 out0: usize,
12456 out1: usize,
12457 row_bytes: usize,
12458 ws0: f32,
12459 ws1: f32,
12460 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12461 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
12462 self.e4m3_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes, ws0, ws1)
12463 }
12464
12465 #[allow(clippy::too_many_arguments)]
12466 pub fn qmatvec_e4m3_fused3_raw(
12467 &self,
12468 b0: &CudaSlice<u8>,
12469 b1: &CudaSlice<u8>,
12470 b2: &CudaSlice<u8>,
12471 x: &CudaSlice<f32>,
12472 in_f: usize,
12473 out0: usize,
12474 out1: usize,
12475 out2: usize,
12476 row_bytes: usize,
12477 ws0: f32,
12478 ws1: f32,
12479 ws2: f32,
12480 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12481 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
12482 self.e4m3_fused3_core(
12483 b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
12484 )
12485 }
12486
12487 #[allow(clippy::too_many_arguments)]
12488 pub fn qmatvec_e4m3_fused2_t_raw(
12489 &self,
12490 b0: &CudaSlice<u8>,
12491 b1: &CudaSlice<u8>,
12492 x: &CudaSlice<f32>,
12493 m: usize,
12494 in_f: usize,
12495 out0: usize,
12496 out1: usize,
12497 row_bytes: usize,
12498 ws0: f32,
12499 ws1: f32,
12500 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12501 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12502 self.e4m3_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes, ws0, ws1)
12503 }
12504
12505 #[allow(clippy::too_many_arguments)]
12506 pub fn qmatvec_e4m3_fused3_t_raw(
12507 &self,
12508 b0: &CudaSlice<u8>,
12509 b1: &CudaSlice<u8>,
12510 b2: &CudaSlice<u8>,
12511 x: &CudaSlice<f32>,
12512 m: usize,
12513 in_f: usize,
12514 out0: usize,
12515 out1: usize,
12516 out2: usize,
12517 row_bytes: usize,
12518 ws0: f32,
12519 ws1: f32,
12520 ws2: f32,
12521 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12522 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12523 self.e4m3_fused3_t_core(
12524 b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
12525 )
12526 }
12527
12528 fn try_e4m3_blk_pre(
12539 &self,
12540 w: &crate::model::GpuTensor,
12541 aq: &CudaSlice<i8>,
12542 ad: &CudaSlice<f32>,
12543 m: usize,
12544 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
12545 use crate::model::GpuTensor;
12546 if let GpuTensor::Quant {
12547 bytes,
12548 qtype,
12549 row_bytes,
12550 blk: Some(g),
12551 ..
12552 } = w
12553 {
12554 if *qtype == QT_F8_E4M3_BLK {
12555 if (2..=16).contains(&m)
12561 && std::env::var("MEMRA_NO_BATCHED").is_err()
12562 && (m <= 4 || Self::b8_enabled())
12563 {
12564 let mcols = Self::batched_mcols(m);
12565 return Ok(Some(self.qmatvec_e4m3_blk_mmvq_batched(
12566 bytes,
12567 aq,
12568 ad,
12569 &g.scales,
12570 m,
12571 w.in_features(),
12572 w.out_features(),
12573 *row_bytes,
12574 g.cols,
12575 mcols,
12576 )?));
12577 }
12578 return Ok(Some(self.qmatvec_e4m3_blk_mmvq(
12579 bytes,
12580 aq,
12581 ad,
12582 &g.scales,
12583 m,
12584 w.in_features(),
12585 w.out_features(),
12586 *row_bytes,
12587 g.cols,
12588 )?));
12589 }
12590 }
12591 Ok(None)
12592 }
12593
12594 fn try_e4m3_blk_prefill(
12641 &self,
12642 w: &crate::model::GpuTensor,
12643 x: &CudaSlice<f32>,
12644 m: usize,
12645 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
12646 use crate::model::GpuTensor;
12647 let GpuTensor::Quant {
12648 bytes,
12649 qtype,
12650 blk: Some(g),
12651 ..
12652 } = w
12653 else {
12654 return Ok(None);
12655 };
12656 if *qtype != QT_F8_E4M3_BLK {
12657 return Ok(None);
12658 }
12659 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
12664 return Ok(Some(y));
12665 }
12666 let (in_f, out_f) = (w.in_features(), w.out_features());
12667 let slab = self.fp8_blk_dequant_q8_0_dev(bytes, &g.scales, out_f, in_f)?;
12668 let tmp = GpuTensor::Quant {
12669 bytes: slab,
12670 qtype: QT_Q8_0,
12671 row_bytes: in_f / 32 * 34,
12672 ne: vec![in_f as u64, out_f as u64],
12673 scale: 1.0,
12674 rp: false,
12675 #[cfg(memra_cutlass)]
12676 cutlass: None,
12677 fp8: None,
12678 blk: None,
12679 f16: None,
12680 rp4: None,
12681 };
12682 Ok(Some(self.matmul(&tmp, x, m)?))
12684 }
12685
12686 pub fn matmul_pre_noscale(
12687 &self,
12688 w: &crate::model::GpuTensor,
12689 aq: &CudaSlice<i8>,
12690 ad: &CudaSlice<f32>,
12691 m: usize,
12692 ) -> Result<Option<(CudaSlice<f32>, f32)>, Box<dyn std::error::Error>> {
12693 use crate::model::GpuTensor;
12694 if m == 1 {
12698 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
12699 return Ok(Some((y, 1.0)));
12700 }
12701 }
12702 if m != 1 || !self.uses_q8_1_fast(w) {
12704 return Ok(None);
12705 }
12706 let in_f = w.in_features();
12707 let out_f = w.out_features();
12708 let (bytes, qtype, row_bytes, scale, rp) = match w {
12709 GpuTensor::Quant {
12710 bytes,
12711 qtype,
12712 row_bytes,
12713 scale,
12714 rp,
12715 ..
12716 } => (bytes, *qtype, *row_bytes, *scale, *rp),
12717 _ => return Ok(None),
12718 };
12719 if self.mmvq_supports(qtype) {
12721 let (mbytes, mrp) = match w {
12723 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
12724 _ => (bytes, rp),
12725 };
12726 let y = self.qmatvec_mmvq(
12727 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, 1.0, mrp,
12728 )?;
12729 return Ok(Some((y, scale)));
12730 }
12731 let name = match qtype {
12733 QT_Q8_0 => "qmatvec_q8_0_dp4a",
12734 QT_Q4_K => "qmatvec_q4_K_dp4a",
12735 QT_Q6_K => "qmatvec_q6_K_dp4a",
12736 QT_Q5_K => "qmatvec_q5_K_dp4a",
12737 QT_Q3_K => "qmatvec_q3_K_dp4a",
12738 QT_NVFP4 => {
12739 if rp {
12740 "qmatvec_nvfp4_dp4a_rp"
12741 } else {
12742 "qmatvec_nvfp4_dp4a"
12743 }
12744 }
12745 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
12746 _ => return Ok(None),
12747 };
12748 let f = self.func(name);
12749 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
12750 let cfg = LaunchConfig {
12751 grid_dim: (out_f as u32, m as u32, 1),
12752 block_dim: (128, 1, 1),
12753 shared_mem_bytes: 0,
12754 };
12755 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
12756 let __s_b = self.gpu.stream();
12757 let mut b = __s_b.launch_builder(&f);
12758 b.arg(bytes)
12759 .arg(aq)
12760 .arg(ad)
12761 .arg(&mut y)
12762 .arg(&inf)
12763 .arg(&outf)
12764 .arg(&mi)
12765 .arg(&rb);
12766 unsafe {
12767 b.launch(cfg)?;
12768 }
12769 Ok(Some((y, scale)))
12770 }
12771
12772 pub fn mmvq_supports(&self, qtype: i32) -> bool {
12775 if qtype == QT_F8_E4M3 {
12780 return true;
12781 }
12782 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
12783 return false;
12784 }
12785 matches!(
12786 qtype,
12787 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_Q4_0
12788 )
12789 }
12790
12791 pub fn qmatvec_mmvq(
12796 &self,
12797 bytes: &CudaSlice<u8>,
12798 aq: &CudaSlice<i8>,
12799 ad: &CudaSlice<f32>,
12800 m: usize,
12801 in_f: usize,
12802 out_f: usize,
12803 qtype: i32,
12804 row_bytes: usize,
12805 scale: f32,
12806 rp: bool,
12807 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12808 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_mmvq_into(
12810 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp, &mut y,
12811 )?;
12812 Ok(y)
12813 }
12814
12815 #[allow(clippy::too_many_arguments)]
12817 pub fn qmatvec_mmvq_into(
12818 &self,
12819 bytes: &CudaSlice<u8>,
12820 aq: &CudaSlice<i8>,
12821 ad: &CudaSlice<f32>,
12822 m: usize,
12823 in_f: usize,
12824 out_f: usize,
12825 qtype: i32,
12826 row_bytes: usize,
12827 scale: f32,
12828 rp: bool,
12829 y: &mut CudaSlice<f32>,
12830 ) -> Result<(), Box<dyn std::error::Error>> {
12831 debug_assert!(y.len() >= m * out_f);
12832 const ROWS_PER_BLOCK: u32 = 4; if qtype == QT_Q8_0
12838 && rp
12839 && m == 1
12840 && out_f >= 64
12841 && (out_f as u32).div_ceil(ROWS_PER_BLOCK) < 4 * self.sm_count() as u32
12842 && {
12843 static G2: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12844 *G2.get_or_init(|| std::env::var("MEMRA_Q80_G2").as_deref() != Ok("0"))
12845 }
12846 {
12847 let f = self.func("qmatvec_q8_0_mmvq_rp_g2");
12848 let cfg = LaunchConfig {
12849 grid_dim: ((out_f as u32).div_ceil(2), 1, 1),
12850 block_dim: (32, 2, 1),
12851 shared_mem_bytes: 0,
12852 };
12853 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, row_bytes as i64);
12854 let __s_b = self.gpu.stream();
12855 let mut b = __s_b.launch_builder(&f);
12856 b.arg(bytes)
12857 .arg(aq)
12858 .arg(ad)
12859 .arg(&mut *y)
12860 .arg(&inf)
12861 .arg(&outf)
12862 .arg(&mi)
12863 .arg(&rb);
12864 unsafe {
12865 b.launch(cfg)?;
12866 }
12867 if scale != 1.0 {
12868 self.scale_inplace(y, scale, out_f)?;
12869 }
12870 return Ok(());
12871 }
12872 let mut mr: u32 = if m == 1 && (qtype == QT_NVFP4 || qtype == QT_Q5_K) {
12881 2
12882 } else {
12883 1
12884 };
12885 if m == 1 && qtype == QT_Q4_0 {
12890 static Q40MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
12891 mr = *Q40MR.get_or_init(|| {
12894 std::env::var("MEMRA_Q40_MR")
12895 .ok()
12896 .and_then(|v| v.parse().ok())
12897 .unwrap_or(1)
12898 });
12899 }
12900 let q5_mode = std::env::var("MEMRA_Q5K_ISSUE").ok();
12911 let q5_force = q5_mode.as_deref() == Some("2");
12912 let q5_il = qtype == QT_Q5_K
12915 && m == 1
12916 && (q5_force || q5_mode.as_deref().map(|v| v != "0").unwrap_or(true));
12917 if q5_il && !q5_force && out_f > 65536 {
12918 mr = 1;
12919 }
12920 if qtype == QT_Q4_0 && rp && mr != 1 {
12923 mr = 2;
12924 }
12925 if qtype == QT_Q8_0 && rp {
12929 static Q80MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
12930 mr = *Q80MR.get_or_init(|| {
12931 std::env::var("MEMRA_Q80_MR")
12932 .ok()
12933 .and_then(|v| v.parse().ok())
12934 .unwrap_or(1)
12935 });
12936 }
12937 let name = match (qtype, mr, rp) {
12938 (QT_NVFP4, 2, false) => "qmatvec_nvfp4_mmvq_mr2",
12939 (QT_NVFP4, 2, true) => "qmatvec_nvfp4_mmvq_mr2_rp",
12940 (QT_NVFP4, _, true) => "qmatvec_nvfp4_mmvq_rp",
12941 (QT_Q4_0, 1, true) => "qmatvec_q4_0_mmvq_rp",
12942 (QT_Q4_0, _, true) => "qmatvec_q4_0_mmvq_mr2_rp",
12943 (QT_Q5_K, 2, _) => {
12944 if q5_il {
12945 "qmatvec_q5_K_mmvq_mr2_il"
12946 } else {
12947 "qmatvec_q5_K_mmvq_mr2"
12948 }
12949 }
12950 (QT_Q8_0, 2, true) => "qmatvec_q8_0_mmvq_mr2_rp",
12951 (QT_Q8_0, _, true)
12956 if in_f % 1024 == 0 && {
12957 static CA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12958 *CA.get_or_init(|| std::env::var("MEMRA_Q80_CA").as_deref() == Ok("1"))
12959 } =>
12960 {
12961 "qmatvec_q8_0_mmvq_rpca"
12962 }
12963 (QT_Q8_0, _, true) => "qmatvec_q8_0_mmvq_rp",
12964 (QT_Q8_0, _, _) => "qmatvec_q8_0_mmvq",
12965 (QT_Q4_K, _, true) => "qmatvec_q4_K_mmvq_rp",
12969 (QT_Q6_K, _, true) => "qmatvec_q6_K_mmvq_rp",
12970 (QT_Q4_K, _, _) => "qmatvec_q4_K_mmvq",
12971 (QT_Q4_0, 2, false) => "qmatvec_q4_0_mmvq_mr2",
12972 (QT_Q4_0, _, false) => "qmatvec_q4_0_mmvq",
12973 (QT_Q5_K, _, _) => {
12974 if q5_il {
12975 "qmatvec_q5_K_mmvq_il"
12976 } else {
12977 "qmatvec_q5_K_mmvq"
12978 }
12979 }
12980 (QT_Q6_K, _, _) => "qmatvec_q6_K_mmvq",
12981 (QT_NVFP4, _, false) => "qmatvec_nvfp4_mmvq",
12982 (QT_F8_E4M3, _, _) => "qmatvec_e4m3_mmvq",
12983 _ => panic!("qmatvec_mmvq: qtype {qtype} has no MMVQ kernel"),
12984 };
12985 let f = self.func(name);
12986 let rows_per_block = ROWS_PER_BLOCK * mr;
12988 let cfg = LaunchConfig {
12989 grid_dim: (
12990 (out_f as u32 + rows_per_block - 1) / rows_per_block,
12991 m as u32,
12992 1,
12993 ),
12994 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
12997 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
12998 let __s_b = self.gpu.stream();
12999 let mut b = __s_b.launch_builder(&f);
13000 if qtype == QT_NVFP4 || qtype == QT_F8_E4M3 {
13005 b.arg(bytes)
13006 .arg(aq)
13007 .arg(ad)
13008 .arg(&mut *y)
13009 .arg(&inf)
13010 .arg(&outf)
13011 .arg(&mi)
13012 .arg(&rb)
13013 .arg(&scale);
13014 unsafe {
13015 b.launch(cfg)?;
13016 }
13017 } else if Self::pdl_on()
13018 && Self::pdl_mmvq_on()
13019 && matches!(
13020 name,
13021 "qmatvec_q4_0_mmvq_rp" | "qmatvec_q6_K_mmvq" | "qmatvec_q6_K_mmvq_rp"
13022 )
13023 {
13024 {
13028 use cudarc::driver::{DevicePtr, DevicePtrMut};
13029 let s = &self.gpu.stream();
13030 let (pw, _g0) = bytes.device_ptr(s);
13031 let (paq, _g1) = aq.device_ptr(s);
13032 let (pad, _g2) = ad.device_ptr(s);
13033 let (py, _g3) = y.device_ptr_mut(s);
13034 let mut ps = [
13035 &pw as *const _ as *mut std::ffi::c_void,
13036 &paq as *const _ as *mut _,
13037 &pad as *const _ as *mut _,
13038 &py as *const _ as *mut _,
13039 &inf as *const _ as *mut _,
13040 &outf as *const _ as *mut _,
13041 &mi as *const _ as *mut _,
13042 &rb as *const _ as *mut _,
13043 ];
13044 unsafe {
13045 self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
13046 }
13047 }
13048 if scale != 1.0 {
13049 self.scale_inplace(y, scale, m * out_f)?;
13050 }
13051 } else {
13052 b.arg(bytes)
13053 .arg(aq)
13054 .arg(ad)
13055 .arg(&mut *y)
13056 .arg(&inf)
13057 .arg(&outf)
13058 .arg(&mi)
13059 .arg(&rb);
13060 unsafe {
13061 b.launch(cfg)?;
13062 }
13063 if scale != 1.0 {
13064 self.scale_inplace(y, scale, m * out_f)?;
13065 }
13066 }
13067 Ok(())
13068 }
13069
13070 pub fn qmatvec_mmvq_raw(
13074 &self,
13075 bytes: &CudaSlice<u8>,
13076 x: &CudaSlice<f32>,
13077 m: usize,
13078 in_f: usize,
13079 out_f: usize,
13080 qtype: i32,
13081 row_bytes: usize,
13082 rp: bool,
13083 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13084 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13085 self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, 1.0, rp)
13086 }
13087
13088 pub fn batched_supports(&self, qtype: i32) -> bool {
13092 matches!(
13093 qtype,
13094 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_F8_E4M3 | QT_Q4_0
13095 )
13096 }
13097
13098 pub fn iq_fast_enabled() -> bool {
13106 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13107 *ON.get_or_init(|| {
13108 std::env::var("MEMRA_IQ_FAST")
13109 .map(|v| v != "0")
13110 .unwrap_or(true)
13111 })
13112 }
13113
13114 pub fn b8_enabled() -> bool {
13117 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13118 *ON.get_or_init(|| std::env::var("MEMRA_B8").map(|v| v != "0").unwrap_or(true))
13119 }
13120
13121 pub fn batched_mcols(m: usize) -> usize {
13123 if m == 2 {
13124 2
13125 } else if m <= 4 {
13126 4
13127 } else if m <= 8 {
13128 8
13129 } else {
13130 16
13131 }
13132 }
13133
13134 fn batched_kernel_name(qtype: i32, mcols: usize) -> Option<&'static str> {
13139 Some(match (qtype, mcols) {
13140 (QT_Q8_0, 2) => "qmatvec_q8_0_mmvq_b2",
13141 (QT_Q8_0, 4) => "qmatvec_q8_0_mmvq_b4",
13142 (QT_Q8_0, 8) => "qmatvec_q8_0_mmvq_b8",
13143 (QT_Q8_0, 16) => "qmatvec_q8_0_mmvq_b16",
13149 (QT_Q4_K, 2) => "qmatvec_q4_K_mmvq_b2",
13150 (QT_Q4_K, 4) => "qmatvec_q4_K_mmvq_b4",
13151 (QT_Q4_K, 8) => "qmatvec_q4_K_mmvq_b8",
13152 (QT_Q4_K, 16) => "qmatvec_q4_K_mmvq_b16",
13155 (QT_Q5_K, 2) => "qmatvec_q5_K_mmvq_b2",
13156 (QT_Q5_K, 4) => "qmatvec_q5_K_mmvq_b4",
13157 (QT_Q5_K, 8) => "qmatvec_q5_K_mmvq_b8",
13158 (QT_Q5_K, 16) => "qmatvec_q5_K_mmvq_b16",
13161 (QT_Q6_K, 2) => "qmatvec_q6_K_mmvq_b2",
13162 (QT_Q6_K, 4) => "qmatvec_q6_K_mmvq_b4",
13163 (QT_Q6_K, 8) => "qmatvec_q6_K_mmvq_b8",
13164 (QT_Q6_K, 16) => "qmatvec_q6_K_mmvq_b16",
13165 (QT_NVFP4, 2) => "qmatvec_nvfp4_mmvq_b2",
13166 (QT_NVFP4, 4) => "qmatvec_nvfp4_mmvq_b4",
13167 (QT_NVFP4, 8) => "qmatvec_nvfp4_mmvq_b8",
13168 (QT_NVFP4, 16) => "qmatvec_nvfp4_mmvq_b16",
13172 (QT_F8_E4M3, 2) => "qmatvec_e4m3_mmvq_b2",
13173 (QT_F8_E4M3, 4) => "qmatvec_e4m3_mmvq_b4",
13174 (QT_F8_E4M3, 8) => "qmatvec_e4m3_mmvq_b8",
13175 (QT_F8_E4M3, 16) => "qmatvec_e4m3_mmvq_b16",
13179 (QT_Q4_0, 2) => "qmatvec_q4_0_mmvq_b2",
13180 (QT_Q4_0, 4) => "qmatvec_q4_0_mmvq_b4",
13181 (QT_Q4_0, 8) => "qmatvec_q4_0_mmvq_b8",
13182 (QT_Q4_0, 16) => "qmatvec_q4_0_mmvq_b16",
13183 _ => return None,
13184 })
13185 }
13186
13187 pub fn sm_count(&self) -> i32 {
13222 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
13223 *SMS.get_or_init(|| {
13224 use cudarc::driver::sys::CUdevice_attribute_enum as A;
13225 self.gpu
13226 .ctx
13227 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
13228 .unwrap_or(82)
13229 })
13230 }
13231
13232 pub fn batched_variant(
13233 &self,
13234 _m: usize,
13235 in_f: usize,
13236 out_f: usize,
13237 qtype: i32,
13238 row_bytes: usize,
13239 mcols: usize,
13240 rp: bool,
13241 ) -> &'static str {
13242 if qtype == QT_Q8_0 {
13247 return if rp { "rp" } else { "base" };
13248 }
13249 static BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13250 let bv = *BV.get_or_init(|| match std::env::var("MEMRA_MMVQ_BV").as_deref() {
13251 Ok("base") => "base",
13252 Ok("pf") => "pf",
13253 Ok("r2") => "r2",
13254 Ok("r2w8") => "r2w8",
13255 Ok("pfr2") => "pfr2",
13256 Ok("ca") => "ca",
13257 Ok("car2") => "car2",
13258 Ok("rp") => "rp",
13261 Ok("rpr2") => "rpr2",
13262 Ok("rpr2w8") => "rpr2w8",
13263 Ok("rpca") => "rpca",
13266 Ok("rpcar2") => "rpcar2",
13267 Ok("rpsc") => "rpsc",
13274 Ok("rpms") => "rpms",
13275 Ok("rpmsc") => "rpmsc",
13276 Ok("rpks") => "rpks",
13277 Ok("rpksc") => "rpksc",
13278 _ => "auto",
13279 });
13280 let ca_ok = qtype == QT_NVFP4 && (row_bytes % 16 == 0) && (in_f % 1024 == 0);
13284 static KS_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13289 let ks_on = *KS_ON.get_or_init(|| std::env::var("MEMRA_KS").as_deref() != Ok("0"));
13290 let sc_ok = ks_on && qtype == QT_NVFP4 && (in_f % 256 == 0) && (in_f / 64 <= 272);
13291 let ks_ok = ks_on && qtype == QT_NVFP4 && (in_f % 512 == 0) && (in_f / 64 <= 272);
13292 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
13293 let sms = *SMS.get_or_init(|| {
13294 use cudarc::driver::sys::CUdevice_attribute_enum as A;
13295 self.gpu
13296 .ctx
13297 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
13298 .unwrap_or(82)
13299 });
13300 let kq_r2 = matches!(qtype, QT_Q4_K | QT_Q5_K | QT_Q6_K);
13320 static KQBV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13323 let kq_bv = *KQBV.get_or_init(|| match std::env::var("MEMRA_KQ_BV").as_deref() {
13324 Ok("base") => "base",
13325 Ok("r2") => "r2",
13326 Ok("r2w8") => "r2w8",
13327 _ => "auto",
13328 });
13329 let variant: &'static str = if qtype == QT_Q4_0 {
13330 static Q40BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13334 let q40 = *Q40BV.get_or_init(|| match std::env::var("MEMRA_Q40_BV").as_deref() {
13335 Ok("base") => "base",
13341 Ok("r2") => "r2",
13342 Ok("ms") => "ms",
13343 Ok("sm") => "sm",
13344 Ok("la") => "la",
13345 _ => "auto",
13346 });
13347 let v = if q40 != "auto" {
13348 q40
13349 } else if (out_f as u32).div_ceil(8) >= 4 * sms as u32 {
13350 "r2"
13351 } else {
13352 "base"
13353 };
13354 if rp {
13359 match v {
13360 "ms" => "r2ms_rp",
13361 "sm" => "r2sm_rp",
13362 "la" => "r2la_rp",
13363 "r2" => "r2_rp",
13364 _ => "rp",
13365 }
13366 } else if matches!(v, "ms" | "sm" | "la") {
13367 "r2"
13368 } else {
13369 v
13370 }
13371 } else if qtype != QT_NVFP4 && !kq_r2 {
13372 "base"
13373 } else if kq_r2 && rp {
13374 "rp"
13378 } else if kq_r2 {
13379 if kq_bv != "auto" {
13382 if kq_bv == "r2w8" && mcols != 4 {
13383 "r2"
13384 } else {
13385 kq_bv
13386 }
13387 } else if bv != "auto" {
13388 match bv {
13389 "r2" | "pfr2" | "rpr2" | "car2" => "r2",
13390 "r2w8" | "rpr2w8" => {
13391 if mcols != 4 {
13392 "r2"
13393 } else {
13394 "r2w8"
13395 }
13396 }
13397 _ => "base", }
13399 } else {
13400 let blocks = (out_f + 7) / 8;
13401 let waves = blocks as f64 / (7 * sms as usize) as f64;
13402 let filled = blocks >= 4 * sms as usize;
13403 let use_r2 = if qtype == QT_Q4_K {
13404 filled
13405 } else {
13406 waves >= 2.0
13407 };
13408 if use_r2 { "r2" } else { "base" }
13409 }
13410 } else if bv != "auto" {
13411 let v = if bv == "r2w8" && mcols == 2 {
13416 "r2"
13417 } else if bv == "ca" && (!ca_ok || mcols == 8) {
13418 "pf"
13419 } else if bv == "car2" && (!ca_ok || mcols == 8) {
13420 "r2"
13421 } else if bv == "pfr2" && mcols == 8 {
13422 "r2"
13423 } else if (bv == "rpr2w8" || bv == "rpr2") && mcols == 2 {
13424 "rpr2"
13425 }
13426 else if (bv == "rpca" || bv == "rpcar2") && (!ca_ok || mcols == 8) {
13428 if mcols == 8 { "rpr2w8" } else { "rpr2" }
13429 } else if bv == "rpcar2" && mcols == 2 {
13430 "rpca"
13431 }
13432 else if (bv == "rpsc" || bv == "rpmsc") && !sc_ok {
13435 "rpr2"
13436 } else if (bv == "rpks" || bv == "rpksc") && !ks_ok {
13437 "rpr2"
13438 } else {
13439 bv
13440 };
13441 if rp {
13442 match v {
13443 "base" | "pf" | "ca" | "rp" => "rp",
13444 "r2" | "pfr2" | "car2" | "rpr2" => "rpr2",
13445 "r2w8" | "rpr2w8" => {
13446 if mcols == 2 {
13447 "rpr2"
13448 } else {
13449 "rpr2w8"
13450 }
13451 }
13452 other => other, }
13454 } else {
13455 v
13456 }
13457 } else if mcols == 8 {
13458 if rp {
13469 if sc_ok { "rpsc" } else { "rpr2w8" }
13470 } else {
13471 "r2w8"
13472 }
13473 } else if mcols >= 4 {
13474 let blocks = (out_f + 7) / 8;
13478 let r7 = 7 * sms as usize;
13479 let r8 = 8 * sms as usize;
13480 let waves = blocks as f64 / r7 as f64;
13481 let filled = blocks >= 4 * sms as usize;
13482 if filled && blocks.div_ceil(r8) < blocks.div_ceil(r7) {
13486 if rp { "rpr2w8" } else { "r2w8" }
13490 } else if waves >= 2.0 || (waves <= 1.0 && filled) {
13491 if rp { "rpr2" } else { "r2" }
13494 } else {
13495 if rp { "rp" } else { "pf" }
13499 }
13500 } else if in_f >= 6144 {
13501 if rp { "rpr2" } else { "r2" }
13505 } else if rp {
13506 let waves = ((out_f + 7) / 8) as f64 / (7 * sms as usize) as f64;
13511 if sc_ok && waves >= 0.9 && waves <= 1.1 {
13512 "rpsc"
13513 } else {
13514 "rp"
13515 }
13516 } else {
13517 "base"
13518 };
13519 variant
13520 }
13521
13522 pub fn qmatvec_mmvq_batched(
13523 &self,
13524 bytes: &CudaSlice<u8>,
13525 aq: &CudaSlice<i8>,
13526 ad: &CudaSlice<f32>,
13527 m: usize,
13528 in_f: usize,
13529 out_f: usize,
13530 qtype: i32,
13531 row_bytes: usize,
13532 mcols: usize,
13533 scale: f32,
13534 rp: bool,
13535 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13536 const ROWS_PER_BLOCK: u32 = 4;
13537 let forced: Option<&'static str> = {
13542 static V: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
13543 V.get_or_init(|| std::env::var("MEMRA_BVAR").ok())
13544 .as_deref()
13545 .map(|s| Box::leak(s.to_string().into_boxed_str()) as &'static str)
13546 };
13547 let variant = match forced {
13548 Some(v) if !rp || v.contains("rp") => v,
13549 _ => self.batched_variant(m, in_f, out_f, qtype, row_bytes, mcols, rp),
13550 };
13551 let base_name = Self::batched_kernel_name(qtype, mcols).ok_or_else(|| {
13552 format!("qmatvec_mmvq_batched: no kernel for qtype {qtype} mcols {mcols}")
13553 })?;
13554 let variant = if mcols == 16 {
13558 if rp { "rp" } else { "base" }
13559 } else {
13560 variant
13561 };
13562 static B567: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13569 let b567 = *B567.get_or_init(|| std::env::var("MEMRA_B567").as_deref() != Ok("0"));
13570 if b567
13571 && qtype == QT_NVFP4
13572 && rp
13573 && mcols == 8
13574 && (5..=7).contains(&m)
13575 && matches!(variant, "rpsc" | "rpr2w8")
13576 {
13577 let f = self.func(&format!("qmatvec_nvfp4_mmvq_b{m}_{variant}"));
13578 let rows_per_block = ROWS_PER_BLOCK * 2; let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13580 let cfg = LaunchConfig {
13581 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
13582 block_dim: (32, ROWS_PER_BLOCK, 1),
13583 shared_mem_bytes: 0,
13584 };
13585 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13586 let __s_b = self.gpu.stream();
13587 let mut b = __s_b.launch_builder(&f);
13588 b.arg(bytes)
13589 .arg(aq)
13590 .arg(ad)
13591 .arg(&mut y)
13592 .arg(&inf)
13593 .arg(&outf)
13594 .arg(&mi)
13595 .arg(&rb);
13596 unsafe {
13597 b.launch(cfg)?;
13598 }
13599 if scale != 1.0 {
13600 self.scale_inplace(&mut y, scale, m * out_f)?;
13601 }
13602 return Ok(y);
13603 }
13604 let (name, rows_per_block): (std::borrow::Cow<'static, str>, u32) = match variant {
13605 "base" => (base_name.into(), ROWS_PER_BLOCK),
13606 "pf" => (format!("{base_name}_pf").into(), ROWS_PER_BLOCK),
13607 "ca" => (format!("{base_name}_ca").into(), ROWS_PER_BLOCK),
13608 "rp" => (format!("{base_name}_rp").into(), ROWS_PER_BLOCK),
13609 "rpca" => (format!("{base_name}_rpca").into(), ROWS_PER_BLOCK), "rpks" => (format!("{base_name}_rpks").into(), ROWS_PER_BLOCK),
13613 "rpksc" => (format!("{base_name}_rpksc").into(), ROWS_PER_BLOCK),
13614 "rpms" => (format!("{base_name}_rpms").into(), ROWS_PER_BLOCK),
13615 "rpmsc" => (format!("{base_name}_rpmsc").into(), ROWS_PER_BLOCK),
13616 "r2ms_rp" => (format!("{base_name}_r2ms_rp").into(), ROWS_PER_BLOCK),
13617 "r2sm_rp" => (format!("{base_name}_r2sm_rp").into(), ROWS_PER_BLOCK * 2),
13618 "r2la_rp" => (format!("{base_name}_r2la_rp").into(), ROWS_PER_BLOCK * 2),
13619 v => (format!("{base_name}_{v}").into(), ROWS_PER_BLOCK * 2), };
13621 debug_assert!(
13622 !rp || name.contains("_rp"),
13623 "rp weight dispatched to a GGUF-layout kernel"
13624 );
13625 let f = self.func(&name);
13626 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13627 let smem = if name.contains("_r2sm_rp") {
13629 (mcols * 32 * 9 * 4 + mcols * 32 * 4) as u32
13630 } else {
13631 0
13632 };
13633 let cfg = LaunchConfig {
13634 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
13635 block_dim: (32, ROWS_PER_BLOCK, 1),
13636 shared_mem_bytes: smem,
13637 };
13638 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13639 let __s_b = self.gpu.stream();
13640 let mut b = __s_b.launch_builder(&f);
13641 b.arg(bytes)
13642 .arg(aq)
13643 .arg(ad)
13644 .arg(&mut y)
13645 .arg(&inf)
13646 .arg(&outf)
13647 .arg(&mi)
13648 .arg(&rb);
13649 unsafe {
13650 b.launch(cfg)?;
13651 }
13652 if scale != 1.0 {
13653 self.scale_inplace(&mut y, scale, m * out_f)?;
13654 }
13655 Ok(y)
13656 }
13657
13658 pub fn qmatvec_batched_raw(
13662 &self,
13663 bytes: &CudaSlice<u8>,
13664 x: &CudaSlice<f32>,
13665 m: usize,
13666 in_f: usize,
13667 out_f: usize,
13668 qtype: i32,
13669 row_bytes: usize,
13670 mcols: usize,
13671 rp: bool,
13672 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13673 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13674 self.qmatvec_mmvq_batched(
13675 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, 1.0, rp,
13676 )
13677 }
13678
13679 pub fn qmatvec_nvfp4_batched_raw(
13681 &self,
13682 bytes: &CudaSlice<u8>,
13683 x: &CudaSlice<f32>,
13684 m: usize,
13685 in_f: usize,
13686 out_f: usize,
13687 row_bytes: usize,
13688 mcols: usize,
13689 rp: bool,
13690 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13691 self.qmatvec_batched_raw(bytes, x, m, in_f, out_f, QT_NVFP4, row_bytes, mcols, rp)
13692 }
13693
13694 fn try_fp4_gemm(
13698 &self,
13699 w: &crate::model::GpuTensor,
13700 x: &CudaSlice<f32>,
13701 m: usize,
13702 in_f: usize,
13703 out_f: usize,
13704 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13705 use crate::model::GpuTensor;
13706 if cfg!(memra_portable_cuda) {
13707 return Ok(None);
13708 }
13709 if std::env::var("MEMRA_FP4").is_err() {
13710 return Ok(None);
13711 }
13712 #[cfg(memra_cutlass)]
13721 if m >= 128 && std::env::var("MEMRA_FP4_CUTLASS").is_ok() {
13722 if let GpuTensor::Quant {
13723 bytes,
13724 qtype,
13725 scale,
13726 row_bytes,
13727 cutlass,
13728 ..
13729 } = w
13730 {
13731 if *qtype == QT_NVFP4 && in_f % 64 == 0 {
13732 if let Some(cw) = cutlass {
13733 let y = self.cutlass_fp4_gemm(
13735 &cw.b_packed,
13736 &cw.sfb_swizzled,
13737 x,
13738 *scale,
13739 m,
13740 out_f,
13741 in_f,
13742 )?;
13743 return Ok(Some(y));
13744 } else if std::env::var("MEMRA_FP4_CUTLASS_OTF").is_ok() {
13745 let (b_packed, sfb_sw) =
13750 self.build_cutlass_weight(bytes, out_f, in_f, *row_bytes)?;
13751 let y =
13752 self.cutlass_fp4_gemm(&b_packed, &sfb_sw, x, *scale, m, out_f, in_f)?;
13753 return Ok(Some(y));
13754 }
13755 }
13756 }
13757 }
13758 if let GpuTensor::Quant {
13759 bytes,
13760 qtype,
13761 row_bytes,
13762 scale,
13763 rp,
13764 ..
13765 } = w
13766 {
13767 if *qtype == QT_NVFP4 && in_f % 64 == 0 && !*rp {
13770 let y =
13771 self.qmatvec_gemm_nvfp4_fp4(bytes, x, m, in_f, out_f, *row_bytes, *scale)?;
13772 return Ok(Some(y));
13773 }
13774 }
13775 Ok(None)
13776 }
13777
13778 pub fn rms_norm_f16out(
13782 &self,
13783 x: &CudaSlice<f32>,
13784 w: &CudaSlice<f32>,
13785 dst: &mut CudaSlice<f32>,
13786 dst16: &mut CudaSlice<u8>,
13787 ncols: usize,
13788 nrows: usize,
13789 eps: f32,
13790 ) -> Result<(), Box<dyn std::error::Error>> {
13791 let f = self.func("rms_norm_f16out_f32");
13792 let cfg = LaunchConfig {
13793 grid_dim: (nrows as u32, 1, 1),
13794 block_dim: (rms_block(), 1, 1),
13795 shared_mem_bytes: 0,
13796 };
13797 let (nc, e) = (ncols as i32, eps);
13798 let __s_b = self.gpu.stream();
13799 let mut b = __s_b.launch_builder(&f);
13800 b.arg(x).arg(w).arg(dst).arg(dst16).arg(&nc).arg(&e);
13801 unsafe {
13802 b.launch(cfg)?;
13803 }
13804 Ok(())
13805 }
13806
13807 #[allow(clippy::too_many_arguments)]
13810 pub fn add_rms_norm_f16out(
13811 &self,
13812 a: &CudaSlice<f32>,
13813 b: &CudaSlice<f32>,
13814 w: &CudaSlice<f32>,
13815 res: &mut CudaSlice<f32>,
13816 dst: &mut CudaSlice<f32>,
13817 dst16: &mut CudaSlice<u8>,
13818 ncols: usize,
13819 nrows: usize,
13820 eps: f32,
13821 ) -> Result<(), Box<dyn std::error::Error>> {
13822 let f = self.func("add_rms_norm_f16out_f32");
13823 let cfg = LaunchConfig {
13824 grid_dim: (nrows as u32, 1, 1),
13825 block_dim: (rms_block(), 1, 1),
13826 shared_mem_bytes: 0,
13827 };
13828 let (nc, e) = (ncols as i32, eps);
13829 let __s_lb = self.gpu.stream();
13830 let mut lb = __s_lb.launch_builder(&f);
13831 lb.arg(a)
13832 .arg(b)
13833 .arg(w)
13834 .arg(res)
13835 .arg(dst)
13836 .arg(dst16)
13837 .arg(&nc)
13838 .arg(&e);
13839 unsafe {
13840 lb.launch(cfg)?;
13841 }
13842 Ok(())
13843 }
13844
13845 pub fn matmul_group_xh(
13848 &self,
13849 ws: &[&crate::model::GpuTensor],
13850 x: &CudaSlice<f32>,
13851 xh: &CudaSlice<u8>,
13852 m: usize,
13853 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13854 let mut out = Vec::with_capacity(ws.len());
13855 let in_f = ws[0].in_features();
13856 for w in ws {
13857 if w.in_features() == in_f && m >= 16 && !self.verify_exact_on() {
13858 if let Some(y) = self.try_f16_gemm_pre(w, xh, m)? {
13859 out.push(y);
13860 continue;
13861 }
13862 }
13863 out.push(self.matmul(w, x, m)?);
13864 }
13865 Ok(out)
13866 }
13867
13868 pub fn gdn_pad_mask(
13871 &self,
13872 beta: &mut CudaSlice<f32>,
13873 g_log: &mut CudaSlice<f32>,
13874 len_d: &CudaSlice<i32>,
13875 h: usize,
13876 t: usize,
13877 ) -> Result<(), Box<dyn std::error::Error>> {
13878 let f = self.func("gdn_pad_mask_f32");
13879 let cfg = LaunchConfig::for_num_elems((t * h) as u32);
13880 let (hi, ti) = (h as i32, t as i32);
13881 let __s_b = self.gpu.stream();
13882 let mut b = __s_b.launch_builder(&f);
13883 b.arg(beta).arg(g_log).arg(len_d).arg(&hi).arg(&ti);
13884 unsafe {
13885 b.launch(cfg)?;
13886 }
13887 Ok(())
13888 }
13889
13890 pub fn row_gather_dev(
13893 &self,
13894 src: &CudaSlice<f32>,
13895 dst: &mut CudaSlice<f32>,
13896 len_d: &CudaSlice<i32>,
13897 ncols: usize,
13898 ) -> Result<(), Box<dyn std::error::Error>> {
13899 let f = self.func("row_gather_dev_f32");
13900 let cfg = LaunchConfig::for_num_elems(ncols as u32);
13901 let nc = ncols as i32;
13902 let __s_b = self.gpu.stream();
13903 let mut b = __s_b.launch_builder(&f);
13904 b.arg(src).arg(dst).arg(len_d).arg(&nc);
13905 unsafe {
13906 b.launch(cfg)?;
13907 }
13908 Ok(())
13909 }
13910
13911 pub fn matmul_group(
13918 &self,
13919 ws: &[&crate::model::GpuTensor],
13920 x: &CudaSlice<f32>,
13921 m: usize,
13922 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13923 use crate::model::GpuTensor;
13924 let mut out = Vec::with_capacity(ws.len());
13925 let any_mirror = ws
13926 .iter()
13927 .any(|w| matches!(w, GpuTensor::Quant { f16: Some(_), .. }));
13928 if m >= 16 && any_mirror && !self.verify_exact_on() {
13929 let in_f = ws[0].in_features();
13930 let xh = self.f16_act(x, m * in_f, in_f)?;
13931 for w in ws {
13932 if w.in_features() == in_f {
13933 if let Some(y) = self.try_f16_gemm_pre(w, &xh, m)? {
13934 out.push(y);
13935 continue;
13936 }
13937 }
13938 out.push(self.matmul(w, x, m)?);
13939 }
13940 return Ok(out);
13941 }
13942 for w in ws {
13943 out.push(self.matmul(w, x, m)?);
13944 }
13945 Ok(out)
13946 }
13947
13948 pub fn matmul_group_multi(
13955 &self,
13956 ws: &[&crate::model::GpuTensor],
13957 xs: &[&CudaSlice<f32>],
13958 ms: &[usize],
13959 ) -> Result<Vec<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
13960 assert_eq!(xs.len(), ms.len());
13961 let in_f = ws[0].in_features();
13962 let total: usize = ms.iter().sum();
13963 let mut xcat = self.uninit(total * in_f)?;
13964 let mut off = 0usize;
13965 for (x, &m) in xs.iter().zip(ms) {
13966 self.copy_into(&mut xcat, off * in_f, x, m * in_f)?;
13967 off += m;
13968 }
13969 let ys = self.matmul_group(ws, &xcat, total)?;
13970 let mut out: Vec<Vec<CudaSlice<f32>>> = (0..xs.len()).map(|_| Vec::new()).collect();
13971 for (w, y) in ws.iter().zip(ys) {
13972 let out_f = w.out_features();
13973 let mut off = 0usize;
13974 for (s, &m) in ms.iter().enumerate() {
13975 let mut ys_s = self.uninit(m * out_f)?;
13976 let src = y.slice(off * out_f..(off + m) * out_f);
13977 self.gpu.stream().memcpy_dtod(&src, &mut ys_s)?;
13978 out[s].push(ys_s);
13979 off += m;
13980 }
13981 }
13982 Ok(out)
13983 }
13984
13985 pub fn gemm_supports(&self, w: &crate::model::GpuTensor) -> bool {
13995 use crate::model::GpuTensor;
13996 if !legacy_quant_gemm_allowed(
13997 cfg!(memra_portable_cuda),
13998 cfg!(memra_hopper_mma),
13999 std::env::var_os("MEMRA_NO_GEMM").is_some(),
14000 ) {
14001 return false;
14002 }
14003 match w {
14004 GpuTensor::Quant { qtype, .. } => {
14005 matches!(*qtype, QT_Q8_0 | QT_Q4_K | QT_Q6_K | QT_Q5_K | QT_Q4_0)
14006 || (*qtype == QT_NVFP4 && w.in_features() % 64 == 0)
14007 }
14008 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
14009 }
14010 }
14011
14012 pub fn qmatvec_gemm(
14019 &self,
14020 w: &crate::model::GpuTensor,
14021 aq: &CudaSlice<i8>,
14022 ad: &CudaSlice<f32>,
14023 m: usize,
14024 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14025 use crate::model::GpuTensor;
14026 let in_f = w.in_features();
14027 let out_f = w.out_features();
14028 let (bytes, qtype, row_bytes, scale, rp) = match w {
14029 GpuTensor::Quant {
14030 bytes,
14031 qtype,
14032 row_bytes,
14033 scale,
14034 rp,
14035 ..
14036 } => (bytes, *qtype, *row_bytes, *scale, *rp),
14037 _ => unreachable!("gemm_supports guaranteed Quant"),
14038 };
14039 if cfg!(memra_hopper_mma) && qtype == QT_Q8_0 && out_f % 64 == 0 && wgmma_gemm_enabled() {
14045 if let GpuTensor::Quant { rp4: Some(m4), .. } = w {
14046 let mut y = self.qmatvec_gemm_q8_0_wgmma_raw(m4, aq, ad, m, in_f, out_f)?;
14047 if scale != 1.0 {
14048 self.scale_inplace(&mut y, scale, m * out_f)?;
14049 }
14050 return Ok(y);
14051 }
14052 }
14053 let name = match qtype {
14054 QT_Q8_0 => "qmatvec_gemm_q8_0",
14055 QT_Q4_K => "qmatvec_gemm_q4_K",
14056 QT_Q4_0 => {
14057 if rp {
14058 "qmatvec_gemm_q4_0_rp"
14059 } else {
14060 "qmatvec_gemm_q4_0"
14061 }
14062 }
14063 QT_Q5_K => "qmatvec_gemm_q5_K",
14064 QT_Q6_K => "qmatvec_gemm_q6_K",
14065 QT_NVFP4 => {
14066 if rp {
14067 "qmatvec_gemm_nvfp4_rp"
14068 } else {
14069 "qmatvec_gemm_nvfp4"
14070 }
14071 }
14072 _ => unreachable!(),
14073 };
14074 let f = self.func(name);
14075 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);
14080 let k1_tile = if is_k1 {
14082 k1_launch_override().unwrap_or((128, 128, 8))
14083 } else {
14084 (128, 128, 8)
14085 };
14086 let (bm, bn): (u32, u32) = if is_k1 {
14087 (k1_tile.0, k1_tile.1)
14088 } else {
14089 (64, 256)
14090 };
14091 let warps: u32 = if is_k1 {
14092 k1_tile.2
14093 } else {
14094 match qtype {
14095 QT_NVFP4 => 8,
14096 _ => 4,
14097 }
14098 };
14099 let cfg = LaunchConfig {
14100 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
14101 block_dim: (32, warps, 1),
14102 shared_mem_bytes: 0,
14103 };
14104 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14105 let __s_b = self.gpu.stream();
14106 let mut b = __s_b.launch_builder(&f);
14107 b.arg(bytes)
14108 .arg(aq)
14109 .arg(ad)
14110 .arg(&mut y)
14111 .arg(&inf)
14112 .arg(&outf)
14113 .arg(&mi)
14114 .arg(&rb);
14115 unsafe {
14116 b.launch(cfg)?;
14117 }
14118 if scale != 1.0 {
14119 self.scale_inplace(&mut y, scale, m * out_f)?;
14120 }
14121 Ok(y)
14122 }
14123
14124 pub fn qmatvec_gemm_raw(
14129 &self,
14130 bytes: &CudaSlice<u8>,
14131 x: &CudaSlice<f32>,
14132 m: usize,
14133 in_f: usize,
14134 out_f: usize,
14135 qtype: i32,
14136 row_bytes: usize,
14137 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14138 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
14139 let name = match qtype {
14140 QT_Q8_0 => "qmatvec_gemm_q8_0",
14141 QT_Q4_K => "qmatvec_gemm_q4_K",
14142 QT_Q4_0 => "qmatvec_gemm_q4_0",
14143 QT_Q5_K => "qmatvec_gemm_q5_K",
14144 QT_Q6_K => "qmatvec_gemm_q6_K",
14145 QT_NVFP4 => "qmatvec_gemm_nvfp4",
14146 QT_NVFP4_RP => "qmatvec_gemm_nvfp4_rp",
14147 _ => panic!("qmatvec_gemm_raw: qtype {qtype} has no GEMM kernel"),
14148 };
14149 let f = self.func(name);
14150 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);
14154 let k1_tile = if is_k1 {
14156 k1_launch_override().unwrap_or((128, 128, 8))
14157 } else {
14158 (128, 128, 8)
14159 };
14160 let (bm, bn): (u32, u32) = if is_k1 {
14161 (k1_tile.0, k1_tile.1)
14162 } else {
14163 (64, 256)
14164 };
14165 let warps: u32 = if is_k1 {
14166 k1_tile.2
14167 } else {
14168 match qtype {
14169 QT_NVFP4 | QT_NVFP4_RP => 8,
14170 _ => 4,
14171 }
14172 };
14173 let cfg = LaunchConfig {
14174 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
14175 block_dim: (32, warps, 1),
14176 shared_mem_bytes: 0,
14177 };
14178 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14179 let __s_b = self.gpu.stream();
14180 let mut b = __s_b.launch_builder(&f);
14181 b.arg(bytes)
14182 .arg(&aq)
14183 .arg(&ad)
14184 .arg(&mut y)
14185 .arg(&inf)
14186 .arg(&outf)
14187 .arg(&mi)
14188 .arg(&rb);
14189 unsafe {
14190 b.launch(cfg)?;
14191 }
14192 Ok(y)
14193 }
14194
14195 pub fn qmatvec_gemm_q8_0_wgmma_raw(
14202 &self,
14203 rp4: &CudaSlice<u8>,
14204 aq: &CudaSlice<i8>,
14205 ad: &CudaSlice<f32>,
14206 m: usize,
14207 in_f: usize,
14208 out_f: usize,
14209 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14210 assert!(
14211 out_f % 64 == 0 && in_f % 32 == 0,
14212 "wgmma GEMM needs out_f%64==0, in_f%32==0"
14213 );
14214 let f = self.func("qmatvec_gemm_q8_0_wgmma");
14215 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
14217 grid_dim: ((out_f / 64) as u32, (m as u32).div_ceil(64), 1),
14218 block_dim: (128, 1, 1),
14219 shared_mem_bytes: 0,
14220 };
14221 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
14222 let __s_b = self.gpu.stream();
14223 let mut b = __s_b.launch_builder(&f);
14224 b.arg(rp4)
14225 .arg(aq)
14226 .arg(ad)
14227 .arg(&mut y)
14228 .arg(&inf)
14229 .arg(&outf)
14230 .arg(&mi);
14231 unsafe {
14232 b.launch(cfg)?;
14233 }
14234 Ok(y)
14235 }
14236
14237 pub fn scale_inplace(
14239 &self,
14240 y: &mut CudaSlice<f32>,
14241 s: f32,
14242 n: usize,
14243 ) -> Result<(), Box<dyn std::error::Error>> {
14244 let f = self.func("scale_f32");
14245 let cfg = LaunchConfig::for_num_elems(n as u32);
14246 let (sf, ni) = (s, n as i32);
14247 let __s_b = self.gpu.stream();
14248 let mut b = __s_b.launch_builder(&f);
14249 b.arg(y).arg(&sf).arg(&ni);
14250 unsafe {
14251 b.launch(cfg)?;
14252 }
14253 Ok(())
14254 }
14255
14256 pub fn bf16_to_f32(
14261 &self,
14262 data: &cudarc::driver::CudaView<'_, u8>,
14263 n: usize,
14264 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14265 let mut out = self.alloc_uninit::<f32>(n)?;
14266 let f = self.func("bf16_to_f32");
14267 let cfg = LaunchConfig::for_num_elems(n as u32);
14268 let ni = n as i32;
14269 let __s_b = self.gpu.stream();
14270 let mut b = __s_b.launch_builder(&f);
14271 b.arg(data).arg(&mut out).arg(&ni);
14272 unsafe {
14273 b.launch(cfg)?;
14274 }
14275 Ok(out)
14276 }
14277
14278 fn linear_bf16_chunked(
14285 &self,
14286 x: &CudaSlice<f32>,
14287 data: &CudaSlice<u8>,
14288 m: usize,
14289 in_f: usize,
14290 out_f: usize,
14291 exact: bool,
14292 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14293 const CHUNK_BYTES: usize = 256 << 20;
14294 let chunk_rows = (CHUNK_BYTES / (in_f * 4)).max(1).min(out_f);
14295 if chunk_rows >= out_f {
14296 let wf32 = self.bf16_to_f32(&data.slice(0..in_f * out_f * 2), in_f * out_f)?;
14297 return if exact {
14298 self.linear_decode_exact(x, &wf32, m, in_f, out_f)
14299 } else {
14300 self.linear(x, &wf32, m, in_f, out_f)
14301 };
14302 }
14303 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
14304 let mut r0 = 0usize;
14305 while r0 < out_f {
14306 let rows = chunk_rows.min(out_f - r0);
14307 let wslice = data.slice(r0 * in_f * 2..(r0 + rows) * in_f * 2);
14308 let wf32 = self.bf16_to_f32(&wslice, in_f * rows)?;
14309 let yc = if exact {
14310 self.linear_decode_exact(x, &wf32, m, in_f, rows)?
14311 } else {
14312 self.linear(x, &wf32, m, in_f, rows)?
14313 };
14314 for mi in 0..m {
14316 let src = yc.slice(mi * rows..(mi + 1) * rows);
14317 let mut dst = y.slice_mut(mi * out_f + r0..mi * out_f + r0 + rows);
14318 self.gpu.stream().memcpy_dtod(&src, &mut dst)?;
14319 }
14320 r0 += rows;
14321 }
14322 Ok(y)
14323 }
14324
14325 pub fn linear_decode_exact(
14332 &self,
14333 x: &CudaSlice<f32>,
14334 w: &CudaSlice<f32>,
14335 m_tokens: usize,
14336 in_f: usize,
14337 out_f: usize,
14338 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14339 if m_tokens == 1 {
14340 return self.linear(x, w, 1, in_f, out_f);
14341 }
14342 let xv = self.view(x, m_tokens * in_f);
14343 let mut y = self.alloc_uninit::<f32>(m_tokens * out_f)?;
14344 for t in 0..m_tokens {
14345 let row = xv.slice(t * in_f..(t + 1) * in_f);
14346 let mut xr = self.alloc_uninit::<f32>(in_f)?;
14347 self.copy_view_into(&mut xr, 0, &row, in_f)?;
14348 let yr = self.linear(&xr, w, 1, in_f, out_f)?;
14349 self.copy_into(&mut y, t * out_f, &yr, out_f)?;
14350 }
14351 Ok(y)
14352 }
14353
14354 pub fn linear(
14355 &self,
14356 x: &CudaSlice<f32>,
14357 w: &CudaSlice<f32>,
14358 m_tokens: usize,
14359 in_f: usize,
14360 out_f: usize,
14361 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14362 use cudarc::cublaslt::{Matmul, MatmulConfig};
14363 let mut c = self.alloc_uninit::<f32>(m_tokens * out_f)?; let cfg = MatmulConfig {
14365 transa: true,
14366 transb: false,
14367 transc: false,
14368 m: out_f as u64,
14369 n: m_tokens as u64,
14370 k: in_f as u64,
14371 alpha: 1.0,
14372 lda: in_f as i64,
14373 ldb: in_f as i64,
14374 beta: 0.0,
14375 ldc: out_f as i64,
14376 stride_a: None,
14377 stride_b: None,
14378 stride_c: None,
14379 stride_bias: None,
14380 batch_size: None,
14381 };
14382 unsafe {
14383 self.gpu.blas.matmul(cfg, w, x, &mut c, None, None)?;
14384 }
14385 Ok(c)
14386 }
14387
14388 pub fn sdpa_naive(
14390 &self,
14391 q: &CudaSlice<f32>,
14392 k: &CudaSlice<f32>,
14393 v: &CudaSlice<f32>,
14394 o: &mut CudaSlice<f32>,
14395 head_dim: usize,
14396 n_head: usize,
14397 n_head_kv: usize,
14398 t: usize,
14399 t_kv: usize,
14400 scale: f32,
14401 causal: bool,
14402 ) -> Result<(), Box<dyn std::error::Error>> {
14403 let f = self.func("sdpa_naive_f32");
14404 let cfg = LaunchConfig {
14405 grid_dim: (n_head as u32, t as u32, 1),
14406 block_dim: (128, 1, 1),
14407 shared_mem_bytes: (t_kv * 4) as u32,
14408 };
14409 let (hd, nh, nhkv, ti, tkvi, cz) = (
14410 head_dim as i32,
14411 n_head as i32,
14412 n_head_kv as i32,
14413 t as i32,
14414 t_kv as i32,
14415 causal as i32,
14416 );
14417 let __s_b = self.gpu.stream();
14418 let mut b = __s_b.launch_builder(&f);
14419 b.arg(q)
14420 .arg(k)
14421 .arg(v)
14422 .arg(o)
14423 .arg(&hd)
14424 .arg(&nh)
14425 .arg(&nhkv)
14426 .arg(&ti)
14427 .arg(&tkvi)
14428 .arg(&scale)
14429 .arg(&cz);
14430 unsafe {
14431 b.launch(cfg)?;
14432 }
14433 Ok(())
14434 }
14435
14436 #[allow(clippy::too_many_arguments)]
14438 pub fn sdpa_naive_w(
14439 &self,
14440 q: &CudaSlice<f32>,
14441 k: &CudaSlice<f32>,
14442 v: &CudaSlice<f32>,
14443 o: &mut CudaSlice<f32>,
14444 head_dim: usize,
14445 n_head: usize,
14446 n_head_kv: usize,
14447 t: usize,
14448 t_kv: usize,
14449 scale: f32,
14450 causal: bool,
14451 window: usize,
14452 ) -> Result<(), Box<dyn std::error::Error>> {
14453 let f = self.func("sdpa_naive_w_f32");
14454 let cfg = LaunchConfig {
14455 grid_dim: (n_head as u32, t as u32, 1),
14456 block_dim: (128, 1, 1),
14457 shared_mem_bytes: (t_kv * 4) as u32,
14458 };
14459 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
14460 head_dim as i32,
14461 n_head as i32,
14462 n_head_kv as i32,
14463 t as i32,
14464 t_kv as i32,
14465 causal as i32,
14466 window as i32,
14467 );
14468 let __s_b = self.gpu.stream();
14469 let mut b = __s_b.launch_builder(&f);
14470 b.arg(q)
14471 .arg(k)
14472 .arg(v)
14473 .arg(o)
14474 .arg(&hd)
14475 .arg(&nh)
14476 .arg(&nhkv)
14477 .arg(&ti)
14478 .arg(&tkvi)
14479 .arg(&scale)
14480 .arg(&cz)
14481 .arg(&wi);
14482 unsafe {
14483 b.launch(cfg)?;
14484 }
14485 Ok(())
14486 }
14487
14488 pub fn sdpa_naive_view(
14490 &self,
14491 q: &CudaSlice<f32>,
14492 k: &cudarc::driver::CudaView<f32>,
14493 v: &cudarc::driver::CudaView<f32>,
14494 o: &mut CudaSlice<f32>,
14495 head_dim: usize,
14496 n_head: usize,
14497 n_head_kv: usize,
14498 t: usize,
14499 t_kv: usize,
14500 scale: f32,
14501 causal: bool,
14502 ) -> Result<(), Box<dyn std::error::Error>> {
14503 let f = self.func("sdpa_naive_f32");
14504 let cfg = LaunchConfig {
14505 grid_dim: (n_head as u32, t as u32, 1),
14506 block_dim: (128, 1, 1),
14507 shared_mem_bytes: (t_kv * 4) as u32,
14508 };
14509 let (hd, nh, nhkv, ti, tkvi, cz) = (
14510 head_dim as i32,
14511 n_head as i32,
14512 n_head_kv as i32,
14513 t as i32,
14514 t_kv as i32,
14515 causal as i32,
14516 );
14517 let __s_b = self.gpu.stream();
14518 let mut b = __s_b.launch_builder(&f);
14519 b.arg(q)
14520 .arg(k)
14521 .arg(v)
14522 .arg(o)
14523 .arg(&hd)
14524 .arg(&nh)
14525 .arg(&nhkv)
14526 .arg(&ti)
14527 .arg(&tkvi)
14528 .arg(&scale)
14529 .arg(&cz);
14530 unsafe {
14531 b.launch(cfg)?;
14532 }
14533 Ok(())
14534 }
14535
14536 #[allow(clippy::too_many_arguments)]
14544 pub fn fa_dequant_kv_view_f32(
14545 &self,
14546 k: &cudarc::driver::CudaView<u8>,
14547 v: &cudarc::driver::CudaView<u8>,
14548 kf: &mut CudaSlice<f32>,
14549 vf: &mut CudaSlice<f32>,
14550 kv_dim_k: usize,
14551 kv_dim_v: usize,
14552 t_kv: usize,
14553 k_tok_bytes: usize,
14554 v_tok_bytes: usize,
14555 g: bool,
14556 ) -> Result<(), Box<dyn std::error::Error>> {
14557 let f = if g {
14558 self.func_g("fa_dequant_kv_ws_f32")
14559 } else {
14560 self.func("fa_dequant_kv_ws_f32")
14561 };
14562 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
14563 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
14564 let cfg = LaunchConfig {
14565 grid_dim: (nblk.max(1), 1, 1),
14566 block_dim: (256, 1, 1),
14567 shared_mem_bytes: 0,
14568 };
14569 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
14570 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
14571 let __s_b = self.gpu.stream();
14572 let mut b = __s_b.launch_builder(&f);
14573 b.arg(k)
14574 .arg(v)
14575 .arg(&mut *kf)
14576 .arg(&mut *vf)
14577 .arg(&kdk)
14578 .arg(&kdv)
14579 .arg(&tkvi)
14580 .arg(&ktb)
14581 .arg(&vtb);
14582 unsafe {
14583 b.launch(cfg)?;
14584 }
14585 Ok(())
14586 }
14587
14588 #[allow(clippy::too_many_arguments)]
14589 pub fn sdpa_naive_quantized_view(
14590 &self,
14591 q: &CudaSlice<f32>,
14592 k: &cudarc::driver::CudaView<u8>,
14593 v: &cudarc::driver::CudaView<u8>,
14594 o: &mut CudaSlice<f32>,
14595 head_dim: usize,
14596 n_head: usize,
14597 n_head_kv: usize,
14598 t: usize,
14599 t_kv: usize,
14600 scale: f32,
14601 causal: bool,
14602 k_tok_bytes: usize,
14603 v_tok_bytes: usize,
14604 ) -> Result<(), Box<dyn std::error::Error>> {
14605 let kv_dim = n_head_kv * head_dim;
14606 let mut kf = self.uninit(t_kv * kv_dim)?;
14607 let mut vf = self.uninit(t_kv * kv_dim)?;
14608 let f = self.func("fa_dequant_kv_ws_f32");
14609 let total = (2 * t_kv * kv_dim) as u64;
14610 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
14611 let cfg = LaunchConfig {
14612 grid_dim: (nblk.max(1), 1, 1),
14613 block_dim: (256, 1, 1),
14614 shared_mem_bytes: 0,
14615 };
14616 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
14617 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
14618 let __s_b = self.gpu.stream();
14619 let mut b = __s_b.launch_builder(&f);
14620 b.arg(k)
14621 .arg(v)
14622 .arg(&mut kf)
14623 .arg(&mut vf)
14624 .arg(&kv_dim_i)
14625 .arg(&kv_dim_i)
14626 .arg(&t_kv_i)
14627 .arg(&k_tok_bytes_i)
14628 .arg(&v_tok_bytes_i);
14629 unsafe { b.launch(cfg)? };
14630 self.sdpa_naive(
14631 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
14632 )
14633 }
14634
14635 #[allow(clippy::too_many_arguments)]
14647 pub fn sdpa_naive_w_quantized_view(
14648 &self,
14649 q: &CudaSlice<f32>,
14650 k: &cudarc::driver::CudaView<u8>,
14651 v: &cudarc::driver::CudaView<u8>,
14652 o: &mut CudaSlice<f32>,
14653 head_dim: usize,
14654 n_head: usize,
14655 n_head_kv: usize,
14656 t: usize,
14657 t_kv: usize,
14658 scale: f32,
14659 causal: bool,
14660 window: usize,
14661 k_tok_bytes: usize,
14662 v_tok_bytes: usize,
14663 ) -> Result<(), Box<dyn std::error::Error>> {
14664 let kv_dim = n_head_kv * head_dim;
14665 let mut kf = self.uninit(t_kv * kv_dim)?;
14666 let mut vf = self.uninit(t_kv * kv_dim)?;
14667 let f = self.func("fa_dequant_kv_ws_f32");
14668 let total = (2 * t_kv * kv_dim) as u64;
14669 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
14670 let cfg = LaunchConfig {
14671 grid_dim: (nblk.max(1), 1, 1),
14672 block_dim: (256, 1, 1),
14673 shared_mem_bytes: 0,
14674 };
14675 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
14676 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
14677 let __s_b = self.gpu.stream();
14678 let mut b = __s_b.launch_builder(&f);
14679 b.arg(k)
14680 .arg(v)
14681 .arg(&mut kf)
14682 .arg(&mut vf)
14683 .arg(&kv_dim_i)
14684 .arg(&kv_dim_i)
14685 .arg(&t_kv_i)
14686 .arg(&k_tok_bytes_i)
14687 .arg(&v_tok_bytes_i);
14688 unsafe { b.launch(cfg)? };
14689 self.sdpa_naive_w(
14690 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
14691 )
14692 }
14693
14694 pub fn fa_prefill(
14698 &self,
14699 q: &CudaSlice<f32>,
14700 k: &CudaSlice<f32>,
14701 v: &CudaSlice<f32>,
14702 o: &mut CudaSlice<f32>,
14703 head_dim: usize,
14704 n_head: usize,
14705 n_head_kv: usize,
14706 t: usize,
14707 t_kv: usize,
14708 scale: f32,
14709 causal: bool,
14710 ) -> Result<(), Box<dyn std::error::Error>> {
14711 if portable_mma_gated() {
14712 return self.sdpa_naive(
14713 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
14714 );
14715 }
14716 let fa3_on = head_dim == 256
14724 && causal
14725 && t == t_kv
14726 && match std::env::var("MEMRA_FA3").as_deref() {
14727 Ok("0") => false,
14728 Ok("1") => true,
14729 _ => cfg!(memra_hopper_mma),
14730 };
14731 if fa3_on {
14732 let n = t * n_head * head_dim;
14733 let nkv = t * n_head_kv * head_dim;
14734 let mut q16 = self.alloc_u8_uninit(n * 2)?;
14735 let mut k16 = self.alloc_u8_uninit(nkv * 2)?;
14736 let mut v16 = self.alloc_u8_uninit(nkv * 2)?;
14737 self.f32_to_bf16_into(q, &mut q16, n)?;
14738 self.f32_to_bf16_into(k, &mut k16, nkv)?;
14739 self.f32_to_bf16_into(v, &mut v16, nkv)?;
14740 let rc = {
14741 use cudarc::driver::{DevicePtr, DevicePtrMut};
14742 let stream = self.gpu.stream();
14743 let (qp, _g1) = q16.device_ptr(&stream);
14744 let (kp, _g2) = k16.device_ptr(&stream);
14745 let (vp, _g3) = v16.device_ptr(&stream);
14746 let (op, _g4) = o.device_ptr_mut(&stream);
14747 unsafe {
14748 memra_fa3_prefill(
14749 qp as *const core::ffi::c_void,
14750 kp as *const core::ffi::c_void,
14751 vp as *const core::ffi::c_void,
14752 op as *mut f32,
14753 t as i32,
14754 n_head as i32,
14755 n_head_kv as i32,
14756 head_dim as i32,
14757 scale,
14758 stream.cu_stream() as *mut core::ffi::c_void,
14759 )
14760 }
14761 };
14762 if rc != 0 {
14763 return Err(format!("memra_fa3_prefill rc={rc}").into());
14764 }
14765 return Ok(());
14766 }
14767 static FA_P1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14772 let fa_p1 = *FA_P1.get_or_init(|| std::env::var("MEMRA_FA_P1").as_deref() == Ok("1"));
14773 if fa_p1 && head_dim == 256 && !std::env::var("MEMRA_FA_FLOOR").is_ok() {
14774 const BLOCK_Q: usize = 64;
14775 const BKX: usize = 32;
14776 let f = self.func("fa_prefill_bf16_p1");
14777 let shmem = (2 * (2 * BKX * head_dim + BLOCK_Q * BKX)
14778 + 4 * (BLOCK_Q * BKX + 2 * BLOCK_Q)) as u32;
14779 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14780 f.set_attribute(
14781 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14782 shmem as i32,
14783 )?;
14784 let cfg = LaunchConfig {
14785 grid_dim: (
14786 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
14787 n_head as u32,
14788 1,
14789 ),
14790 block_dim: (32, 4, 1),
14791 shared_mem_bytes: shmem,
14792 };
14793 let (hd, nh, nhkv, ti, tkvi, cz) = (
14794 head_dim as i32,
14795 n_head as i32,
14796 n_head_kv as i32,
14797 t as i32,
14798 t_kv as i32,
14799 causal as i32,
14800 );
14801 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
14802 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
14803 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
14804 let __s_b = self.gpu.stream();
14805 let mut b = __s_b.launch_builder(&f);
14806 b.arg(&qb)
14807 .arg(&kb)
14808 .arg(&vb)
14809 .arg(o)
14810 .arg(&hd)
14811 .arg(&nh)
14812 .arg(&nhkv)
14813 .arg(&ti)
14814 .arg(&tkvi)
14815 .arg(&scale)
14816 .arg(&cz);
14817 unsafe {
14818 b.launch(cfg)?;
14819 }
14820 return Ok(());
14821 }
14822 const BK: usize = 32;
14828 let w2 = std::env::var("MEMRA_FA_PP_W2").as_deref() == Ok("1");
14831 let (block_q, warps, w2_sfx): (usize, u32, &str) =
14832 if w2 { (32, 2, "_w2") } else { (64, 4, "") };
14833 let hd_sfx = fa_hd_suffix(head_dim)?;
14837 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
14838 let bf16kv = !floor && !w2 && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0");
14843 let (kb16, vb16) = if bf16kv {
14844 let n = t_kv * n_head_kv * head_dim;
14845 let mut kb = self.alloc_u8_uninit(n * 2)?;
14846 let mut vb = self.alloc_u8_uninit(n * 2)?;
14847 let fcv = self.func("f32_to_bf16_bulk");
14848 let ni = n as i64;
14849 let cfgc = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
14850 let __s_b = self.gpu.stream();
14851 let mut b = __s_b.launch_builder(&fcv);
14852 b.arg(k).arg(&mut kb).arg(&ni);
14853 unsafe {
14854 b.launch(cfgc)?;
14855 }
14856 let __s_b = self.gpu.stream();
14857 let mut b = __s_b.launch_builder(&fcv);
14858 b.arg(v).arg(&mut vb).arg(&ni);
14859 unsafe {
14860 b.launch(cfgc)?;
14861 }
14862 (Some(kb), Some(vb))
14863 } else {
14864 (None, None)
14865 };
14866 let f = self.func(&if bf16kv {
14867 format!("fa_prefill_bf16kv_pp{hd_sfx}")
14868 } else {
14869 format!(
14870 "fa_prefill_f32{}{}{hd_sfx}",
14871 if floor { "" } else { "_pp" },
14872 if floor { "" } else { w2_sfx }
14873 )
14874 });
14875 let kv_stages = if bf16kv { 2 } else { 1 };
14878 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
14879 + 4 * (block_q * BK + 2 * block_q)) as u32;
14880 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14881 f.set_attribute(
14882 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14883 shmem as i32,
14884 )?;
14885 let cfg = LaunchConfig {
14886 grid_dim: (
14887 (t as u32 + block_q as u32 - 1) / block_q as u32,
14888 n_head as u32,
14889 1,
14890 ),
14891 block_dim: (32, warps, 1),
14892 shared_mem_bytes: shmem,
14893 };
14894 let (hd, nh, nhkv, ti, tkvi, cz) = (
14895 head_dim as i32,
14896 n_head as i32,
14897 n_head_kv as i32,
14898 t as i32,
14899 t_kv as i32,
14900 causal as i32,
14901 );
14902 let __s_b = self.gpu.stream();
14903 let mut b = __s_b.launch_builder(&f);
14904 b.arg(q);
14905 match (&kb16, &vb16) {
14906 (Some(kb), Some(vb)) => {
14907 b.arg(kb).arg(vb);
14908 }
14909 _ => {
14910 b.arg(k).arg(v);
14911 }
14912 }
14913 b.arg(o)
14914 .arg(&hd)
14915 .arg(&nh)
14916 .arg(&nhkv)
14917 .arg(&ti)
14918 .arg(&tkvi)
14919 .arg(&scale)
14920 .arg(&cz);
14921 unsafe {
14922 b.launch(cfg)?;
14923 }
14924 Ok(())
14925 }
14926
14927 #[allow(clippy::too_many_arguments)]
14931 pub fn fa_prefill_w(
14932 &self,
14933 q: &CudaSlice<f32>,
14934 k: &CudaSlice<f32>,
14935 v: &CudaSlice<f32>,
14936 o: &mut CudaSlice<f32>,
14937 head_dim: usize,
14938 n_head: usize,
14939 n_head_kv: usize,
14940 t: usize,
14941 t_kv: usize,
14942 scale: f32,
14943 causal: bool,
14944 window: usize,
14945 ) -> Result<(), Box<dyn std::error::Error>> {
14946 if portable_mma_gated() {
14949 return self.sdpa_naive_w(
14950 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
14951 );
14952 }
14953 static FAW_F32: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14957 let faw_f32 =
14958 *FAW_F32.get_or_init(|| std::env::var("MEMRA_FAW_STAGE").as_deref() == Ok("f32"));
14959 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
14960 self.fa_prefill_w_arm(
14961 q,
14962 k,
14963 v,
14964 o,
14965 head_dim,
14966 n_head,
14967 n_head_kv,
14968 t,
14969 t_kv,
14970 scale,
14971 causal,
14972 window,
14973 floor || faw_f32,
14974 floor,
14975 )
14976 }
14977
14978 #[allow(clippy::too_many_arguments)]
14981 pub fn fa_prefill_w_pre(
14982 &self,
14983 qb: &CudaSlice<u8>,
14984 kb: &CudaSlice<u8>,
14985 vb: &CudaSlice<u8>,
14986 o: &mut CudaSlice<f32>,
14987 head_dim: usize,
14988 n_head: usize,
14989 n_head_kv: usize,
14990 t: usize,
14991 t_kv: usize,
14992 scale: f32,
14993 causal: bool,
14994 window: usize,
14995 v_f16: bool,
14996 ) -> Result<(), Box<dyn std::error::Error>> {
14997 const BLOCK_Q: usize = 64;
14998 const BK: usize = 32;
14999 debug_assert_eq!(head_dim, 256);
15000 let hp = fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15001 debug_assert!(!v_f16 || hp, "f16 V emitted but the SWA hp arm is off");
15002 if hp {
15003 const BLOCK_QH: usize = 32;
15004 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
15007 let vh: &CudaSlice<u8> = if v_f16 {
15008 vb
15009 } else {
15010 let n = t_kv * n_head_kv * head_dim;
15011 if vguard.as_ref().map(|b| b.len() < n * 2).unwrap_or(true) {
15012 *vguard = Some(self.alloc_uninit::<u8>(n * 2)?);
15013 }
15014 self.bf16_to_f16_into(vb, n, vguard.as_mut().unwrap())?;
15015 vguard.as_ref().unwrap()
15016 };
15017 let f = self.func("fa_prefill_w_bf16_p1h2");
15018 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
15019 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15020 f.set_attribute(
15021 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15022 shmem as i32,
15023 )?;
15024 let cfg = LaunchConfig {
15025 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
15026 block_dim: (32, 4, 1),
15027 shared_mem_bytes: shmem,
15028 };
15029 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15030 head_dim as i32,
15031 n_head as i32,
15032 n_head_kv as i32,
15033 t as i32,
15034 t_kv as i32,
15035 causal as i32,
15036 window as i32,
15037 );
15038 let __s_b = self.gpu.stream();
15039 let mut b = __s_b.launch_builder(&f);
15040 b.arg(qb)
15041 .arg(kb)
15042 .arg(vh)
15043 .arg(o)
15044 .arg(&hd)
15045 .arg(&nh)
15046 .arg(&nhkv)
15047 .arg(&ti)
15048 .arg(&tkvi)
15049 .arg(&scale)
15050 .arg(&cz)
15051 .arg(&wi);
15052 unsafe {
15053 b.launch(cfg)?;
15054 }
15055 return Ok(());
15056 }
15057 let f = self.func("fa_prefill_w_bf16_p1");
15058 let shmem =
15059 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15060 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15061 f.set_attribute(
15062 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15063 shmem as i32,
15064 )?;
15065 let cfg = LaunchConfig {
15066 grid_dim: (
15067 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15068 n_head as u32,
15069 1,
15070 ),
15071 block_dim: (32, 4, 1),
15072 shared_mem_bytes: shmem,
15073 };
15074 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15075 head_dim as i32,
15076 n_head as i32,
15077 n_head_kv as i32,
15078 t as i32,
15079 t_kv as i32,
15080 causal as i32,
15081 window as i32,
15082 );
15083 let __s_b = self.gpu.stream();
15084 let mut b = __s_b.launch_builder(&f);
15085 b.arg(qb)
15086 .arg(kb)
15087 .arg(vb)
15088 .arg(o)
15089 .arg(&hd)
15090 .arg(&nh)
15091 .arg(&nhkv)
15092 .arg(&ti)
15093 .arg(&tkvi)
15094 .arg(&scale)
15095 .arg(&cz)
15096 .arg(&wi);
15097 unsafe {
15098 b.launch(cfg)?;
15099 }
15100 Ok(())
15101 }
15102
15103 #[allow(clippy::too_many_arguments)]
15105 pub fn fa_prefill_w_arm(
15106 &self,
15107 q: &CudaSlice<f32>,
15108 k: &CudaSlice<f32>,
15109 v: &CudaSlice<f32>,
15110 o: &mut CudaSlice<f32>,
15111 head_dim: usize,
15112 n_head: usize,
15113 n_head_kv: usize,
15114 t: usize,
15115 t_kv: usize,
15116 scale: f32,
15117 causal: bool,
15118 window: usize,
15119 f32_stage: bool,
15120 floor: bool,
15121 ) -> Result<(), Box<dyn std::error::Error>> {
15122 const BLOCK_Q: usize = 64;
15123 const BK: usize = 32;
15124 debug_assert_eq!(head_dim, 256, "fa_prefill_w is stamped hd256 only");
15125 static P1_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15129 let p1 = !floor
15130 && !f32_stage
15131 && *P1_ON.get_or_init(|| {
15132 std::env::var("MEMRA_FAW_P1")
15133 .map(|v| v != "0")
15134 .unwrap_or(true)
15135 });
15136 let hp =
15137 p1 && fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15138 if hp {
15139 const BLOCK_QH: usize = 32;
15140 let f = self.func("fa_prefill_w_bf16_p1h2");
15141 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
15142 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15143 f.set_attribute(
15144 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15145 shmem as i32,
15146 )?;
15147 let cfg = LaunchConfig {
15148 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
15149 block_dim: (32, 4, 1),
15150 shared_mem_bytes: shmem,
15151 };
15152 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15153 head_dim as i32,
15154 n_head as i32,
15155 n_head_kv as i32,
15156 t as i32,
15157 t_kv as i32,
15158 causal as i32,
15159 window as i32,
15160 );
15161 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15162 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15163 let vh = self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?;
15164 let __s_b = self.gpu.stream();
15165 let mut b = __s_b.launch_builder(&f);
15166 b.arg(&qb)
15167 .arg(&kb)
15168 .arg(&vh)
15169 .arg(o)
15170 .arg(&hd)
15171 .arg(&nh)
15172 .arg(&nhkv)
15173 .arg(&ti)
15174 .arg(&tkvi)
15175 .arg(&scale)
15176 .arg(&cz)
15177 .arg(&wi);
15178 unsafe {
15179 b.launch(cfg)?;
15180 }
15181 return Ok(());
15182 }
15183 if p1 {
15184 let f = self.func("fa_prefill_w_bf16_p1");
15185 let shmem =
15186 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15187 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15188 f.set_attribute(
15189 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15190 shmem as i32,
15191 )?;
15192 let cfg = LaunchConfig {
15193 grid_dim: (
15194 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15195 n_head as u32,
15196 1,
15197 ),
15198 block_dim: (32, 4, 1),
15199 shared_mem_bytes: shmem,
15200 };
15201 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15202 head_dim as i32,
15203 n_head as i32,
15204 n_head_kv as i32,
15205 t as i32,
15206 t_kv as i32,
15207 causal as i32,
15208 window as i32,
15209 );
15210 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15211 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15212 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15213 let __s_b = self.gpu.stream();
15214 let mut b = __s_b.launch_builder(&f);
15215 b.arg(&qb)
15216 .arg(&kb)
15217 .arg(&vb)
15218 .arg(o)
15219 .arg(&hd)
15220 .arg(&nh)
15221 .arg(&nhkv)
15222 .arg(&ti)
15223 .arg(&tkvi)
15224 .arg(&scale)
15225 .arg(&cz)
15226 .arg(&wi);
15227 unsafe {
15228 b.launch(cfg)?;
15229 }
15230 return Ok(());
15231 }
15232 static G4_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15235 let g4 = !floor
15236 && !f32_stage
15237 && n_head_kv == 1
15238 && n_head % 4 == 0
15239 && *G4_ON.get_or_init(|| {
15240 std::env::var("MEMRA_FAW_G4")
15241 .map(|v| v != "0")
15242 .unwrap_or(true)
15243 });
15244 if g4 {
15245 const SP_M: usize = 16;
15246 static O2_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15249 let o2 = *O2_ON.get_or_init(|| {
15250 std::env::var("MEMRA_FAW_O2")
15251 .map(|v| v != "0")
15252 .unwrap_or(true)
15253 });
15254 let f = self.func(if o2 {
15255 "fa_prefill_w_bf16_g4o2"
15256 } else {
15257 "fa_prefill_w_bf16_g4"
15258 });
15259 let shmem = if o2 {
15260 (2 * (4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M)) as u32
15261 } else {
15262 (2 * (2 * BK * head_dim + 4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M))
15263 as u32
15264 };
15265 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15266 f.set_attribute(
15267 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15268 shmem as i32,
15269 )?;
15270 let cfg = LaunchConfig {
15271 grid_dim: ((t as u32).div_ceil(SP_M as u32), (n_head / 4) as u32, 1),
15272 block_dim: (32, 4, 1),
15273 shared_mem_bytes: shmem,
15274 };
15275 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15276 head_dim as i32,
15277 n_head as i32,
15278 n_head_kv as i32,
15279 t as i32,
15280 t_kv as i32,
15281 causal as i32,
15282 window as i32,
15283 );
15284 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15285 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15286 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15287 let __s_b = self.gpu.stream();
15288 let mut b = __s_b.launch_builder(&f);
15289 b.arg(&qb)
15290 .arg(&kb)
15291 .arg(&vb)
15292 .arg(o)
15293 .arg(&hd)
15294 .arg(&nh)
15295 .arg(&nhkv)
15296 .arg(&ti)
15297 .arg(&tkvi)
15298 .arg(&scale)
15299 .arg(&cz)
15300 .arg(&wi);
15301 unsafe {
15302 b.launch(cfg)?;
15303 }
15304 return Ok(());
15305 }
15306 let f = self.func(if floor {
15307 "fa_prefill_w_f32"
15308 } else if f32_stage {
15309 "fa_prefill_w_f32_pp"
15310 } else {
15311 "fa_prefill_w_bf16_pp"
15312 });
15313 let shmem =
15314 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15315 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15316 f.set_attribute(
15317 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15318 shmem as i32,
15319 )?;
15320 let cfg = LaunchConfig {
15321 grid_dim: (
15322 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15323 n_head as u32,
15324 1,
15325 ),
15326 block_dim: (32, 4, 1),
15327 shared_mem_bytes: shmem,
15328 };
15329 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15330 head_dim as i32,
15331 n_head as i32,
15332 n_head_kv as i32,
15333 t as i32,
15334 t_kv as i32,
15335 causal as i32,
15336 window as i32,
15337 );
15338 if f32_stage {
15339 let __s_b = self.gpu.stream();
15340 let mut b = __s_b.launch_builder(&f);
15341 b.arg(q)
15342 .arg(k)
15343 .arg(v)
15344 .arg(o)
15345 .arg(&hd)
15346 .arg(&nh)
15347 .arg(&nhkv)
15348 .arg(&ti)
15349 .arg(&tkvi)
15350 .arg(&scale)
15351 .arg(&cz)
15352 .arg(&wi);
15353 unsafe {
15354 b.launch(cfg)?;
15355 }
15356 } else {
15357 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15358 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15359 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15360 let __s_b = self.gpu.stream();
15361 let mut b = __s_b.launch_builder(&f);
15362 b.arg(&qb)
15363 .arg(&kb)
15364 .arg(&vb)
15365 .arg(o)
15366 .arg(&hd)
15367 .arg(&nh)
15368 .arg(&nhkv)
15369 .arg(&ti)
15370 .arg(&tkvi)
15371 .arg(&scale)
15372 .arg(&cz)
15373 .arg(&wi);
15374 unsafe {
15375 b.launch(cfg)?;
15376 }
15377 }
15378 Ok(())
15379 }
15380
15381 #[allow(clippy::too_many_arguments)]
15385 pub fn fa_prefill_hd512(
15386 &self,
15387 q: &CudaSlice<f32>,
15388 k: &CudaSlice<f32>,
15389 v: &CudaSlice<f32>,
15390 o: &mut CudaSlice<f32>,
15391 head_dim: usize,
15392 n_head: usize,
15393 n_head_kv: usize,
15394 t: usize,
15395 t_kv: usize,
15396 scale: f32,
15397 causal: bool,
15398 ) -> Result<(), Box<dyn std::error::Error>> {
15399 if portable_mma_gated() {
15401 return self.sdpa_naive(
15402 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
15403 );
15404 }
15405 static F32_STAGE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15411 let f32_stage =
15412 *F32_STAGE.get_or_init(|| std::env::var("MEMRA_FA512_STAGE").as_deref() == Ok("f32"));
15413 static SP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15417 let sp = !f32_stage
15418 && *SP_ON.get_or_init(|| {
15419 std::env::var("MEMRA_FA512_SP")
15420 .map(|v| v != "0")
15421 .unwrap_or(true)
15422 });
15423 self.fa_prefill_hd512_arm(
15424 q,
15425 k,
15426 v,
15427 o,
15428 head_dim,
15429 n_head,
15430 n_head_kv,
15431 t,
15432 t_kv,
15433 scale,
15434 causal,
15435 f32_stage,
15436 sp,
15437 sp && fa_f16pv_on(),
15438 )
15439 }
15440
15441 #[allow(clippy::too_many_arguments)]
15443 pub fn fa_prefill_hd512_pre(
15444 &self,
15445 qb: &CudaSlice<u8>,
15446 kb: &CudaSlice<u8>,
15447 vb: &CudaSlice<u8>,
15448 o: &mut CudaSlice<f32>,
15449 head_dim: usize,
15450 n_head: usize,
15451 n_head_kv: usize,
15452 t: usize,
15453 t_kv: usize,
15454 scale: f32,
15455 causal: bool,
15456 v_f16: bool,
15457 ) -> Result<(), Box<dyn std::error::Error>> {
15458 debug_assert_eq!(head_dim, 512);
15459 const SP_M: usize = 16;
15460 const BKS: usize = 32;
15461 let f16pv = fa_f16pv_on();
15465 let nw = if f16pv { fa512_wide_warps() } else { 2 };
15466 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15467 debug_assert!(!v_f16 || f16pv, "f16 V emitted without the door on");
15468 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
15469 let vref: &CudaSlice<u8> = if f16pv && !v_f16 {
15470 let n = t_kv * n_head_kv * head_dim;
15472 let need = n * 2;
15473 if vguard.as_ref().map(|b| b.len() < need).unwrap_or(true) {
15474 *vguard = Some(self.alloc_uninit::<u8>(need)?);
15475 }
15476 let dst = vguard.as_mut().unwrap();
15477 self.bf16_to_f16_into(vb, n, dst)?;
15478 vguard.as_ref().unwrap()
15479 } else {
15480 vb
15481 };
15482 let f = self.func(if hp {
15483 "fa_prefill_bf16_hd512_sp16h2"
15484 } else {
15485 match (f16pv, nw) {
15486 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
15487 (true, _) => "fa_prefill_bf16_hd512_sp16",
15488 _ => "fa_prefill_bf16_hd512_sp",
15489 }
15490 });
15491 let (nwarp, npart) = if hp {
15492 (4usize, 4usize)
15493 } else if nw > 2 {
15494 (nw, nw)
15495 } else {
15496 (2, 1)
15497 };
15498 let shmem = if hp {
15500 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS) + 4 * (2 * npart * SP_M * BKS + 2 * SP_M))
15501 as u32
15502 } else {
15503 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
15504 + 4 * (npart * SP_M * BKS + SP_M)) as u32
15505 };
15506 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15507 f.set_attribute(
15508 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15509 shmem as i32,
15510 )?;
15511 let grid_y = if hp {
15512 (n_head / 2) as u32
15513 } else {
15514 n_head as u32
15515 };
15516 let cfg = LaunchConfig {
15517 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
15518 block_dim: (32, nwarp as u32, 1),
15519 shared_mem_bytes: shmem,
15520 };
15521 let (hd, nh, nhkv, ti, tkvi, cz) = (
15522 head_dim as i32,
15523 n_head as i32,
15524 n_head_kv as i32,
15525 t as i32,
15526 t_kv as i32,
15527 causal as i32,
15528 );
15529 let __s_b = self.gpu.stream();
15530 let mut b = __s_b.launch_builder(&f);
15531 b.arg(qb)
15532 .arg(kb)
15533 .arg(vref)
15534 .arg(o)
15535 .arg(&hd)
15536 .arg(&nh)
15537 .arg(&nhkv)
15538 .arg(&ti)
15539 .arg(&tkvi)
15540 .arg(&scale)
15541 .arg(&cz);
15542 unsafe {
15543 b.launch(cfg)?;
15544 }
15545 Ok(())
15546 }
15547
15548 #[allow(clippy::too_many_arguments)]
15551 pub fn fa_prefill_hd512_arm(
15552 &self,
15553 q: &CudaSlice<f32>,
15554 k: &CudaSlice<f32>,
15555 v: &CudaSlice<f32>,
15556 o: &mut CudaSlice<f32>,
15557 head_dim: usize,
15558 n_head: usize,
15559 n_head_kv: usize,
15560 t: usize,
15561 t_kv: usize,
15562 scale: f32,
15563 causal: bool,
15564 f32_stage: bool,
15565 sp: bool,
15566 f16pv: bool,
15567 ) -> Result<(), Box<dyn std::error::Error>> {
15568 debug_assert_eq!(head_dim, 512, "fa_prefill_hd512 is hd512 only");
15569 if sp && !f32_stage {
15570 const SP_M: usize = 16;
15574 const BKS: usize = 32;
15575 let nw = if f16pv { fa512_wide_warps() } else { 2 };
15576 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15577 let f = self.func(if hp {
15578 "fa_prefill_bf16_hd512_sp16h2"
15579 } else {
15580 match (f16pv, nw) {
15581 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
15582 (true, _) => "fa_prefill_bf16_hd512_sp16",
15583 _ => "fa_prefill_bf16_hd512_sp",
15584 }
15585 });
15586 let (nwarp, npart) = if hp {
15587 (4usize, 4usize)
15588 } else if nw > 2 {
15589 (nw, nw)
15590 } else {
15591 (2, 1)
15592 };
15593 let shmem = if hp {
15594 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS)
15595 + 4 * (2 * npart * SP_M * BKS + 2 * SP_M)) as u32
15596 } else {
15597 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
15598 + 4 * (npart * SP_M * BKS + SP_M)) as u32
15599 };
15600 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15601 f.set_attribute(
15602 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15603 shmem as i32,
15604 )?;
15605 let grid_y = if hp {
15606 (n_head / 2) as u32
15607 } else {
15608 n_head as u32
15609 };
15610 let cfg = LaunchConfig {
15611 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
15612 block_dim: (32, nwarp as u32, 1),
15613 shared_mem_bytes: shmem,
15614 };
15615 let (hd, nh, nhkv, ti, tkvi, cz) = (
15616 head_dim as i32,
15617 n_head as i32,
15618 n_head_kv as i32,
15619 t as i32,
15620 t_kv as i32,
15621 causal as i32,
15622 );
15623 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15624 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15625 let vb = if f16pv {
15626 self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?
15627 } else {
15628 self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?
15629 };
15630 let __s_b = self.gpu.stream();
15631 let mut b = __s_b.launch_builder(&f);
15632 b.arg(&qb)
15633 .arg(&kb)
15634 .arg(&vb)
15635 .arg(o)
15636 .arg(&hd)
15637 .arg(&nh)
15638 .arg(&nhkv)
15639 .arg(&ti)
15640 .arg(&tkvi)
15641 .arg(&scale)
15642 .arg(&cz);
15643 unsafe {
15644 b.launch(cfg)?;
15645 }
15646 return Ok(());
15647 }
15648 const BLOCK_Q: usize = 32;
15649 const BK: usize = 32;
15650 const HALF: usize = 256;
15651 let f = self.func(if f32_stage {
15652 "fa_prefill_f32_hd512"
15653 } else {
15654 "fa_prefill_bf16_hd512"
15655 });
15656 let shmem = (2 * (BLOCK_Q * head_dim + BK * head_dim + BK * HALF + BLOCK_Q * BK)
15658 + 4 * BLOCK_Q) as u32;
15659 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15660 f.set_attribute(
15661 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15662 shmem as i32,
15663 )?;
15664 let cfg = LaunchConfig {
15665 grid_dim: (
15666 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15667 n_head as u32,
15668 2,
15669 ),
15670 block_dim: (32, 2, 1),
15671 shared_mem_bytes: shmem,
15672 };
15673 let (hd, nh, nhkv, ti, tkvi, cz) = (
15674 head_dim as i32,
15675 n_head as i32,
15676 n_head_kv as i32,
15677 t as i32,
15678 t_kv as i32,
15679 causal as i32,
15680 );
15681 if f32_stage {
15682 let __s_b = self.gpu.stream();
15683 let mut b = __s_b.launch_builder(&f);
15684 b.arg(q)
15685 .arg(k)
15686 .arg(v)
15687 .arg(o)
15688 .arg(&hd)
15689 .arg(&nh)
15690 .arg(&nhkv)
15691 .arg(&ti)
15692 .arg(&tkvi)
15693 .arg(&scale)
15694 .arg(&cz);
15695 unsafe {
15696 b.launch(cfg)?;
15697 }
15698 } else {
15699 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15700 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15701 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15702 let __s_b = self.gpu.stream();
15703 let mut b = __s_b.launch_builder(&f);
15704 b.arg(&qb)
15705 .arg(&kb)
15706 .arg(&vb)
15707 .arg(o)
15708 .arg(&hd)
15709 .arg(&nh)
15710 .arg(&nhkv)
15711 .arg(&ti)
15712 .arg(&tkvi)
15713 .arg(&scale)
15714 .arg(&cz);
15715 unsafe {
15716 b.launch(cfg)?;
15717 }
15718 }
15719 Ok(())
15720 }
15721
15722 #[allow(clippy::too_many_arguments)]
15726 pub fn rope_neox2_bf16e(
15727 &self,
15728 q: &mut CudaSlice<f32>,
15729 k: &mut CudaSlice<f32>,
15730 qb: &mut CudaSlice<u8>,
15731 kb: &mut CudaSlice<u8>,
15732 pos: &CudaSlice<i32>,
15733 head_dim: usize,
15734 n_dims: usize,
15735 nh_q: usize,
15736 nh_k: usize,
15737 n_tokens: usize,
15738 base: f32,
15739 freq_scale: f32,
15740 ff: Option<&CudaSlice<f32>>,
15741 ) -> Result<(), Box<dyn std::error::Error>> {
15742 let f = self.func("rope_neox2_bf16e_f32");
15743 let rows = ((nh_q + nh_k) * n_tokens) as u32;
15744 let cfg = LaunchConfig {
15745 grid_dim: (rows, 1, 1),
15746 block_dim: ((head_dim / 2) as u32, 1, 1),
15747 shared_mem_bytes: 0,
15748 };
15749 let theta_scale = base.powf(-2.0 / n_dims as f32);
15750 let (hd, nd, nhq, nhk, nt) = (
15751 head_dim as i32,
15752 n_dims as i32,
15753 nh_q as i32,
15754 nh_k as i32,
15755 n_tokens as i32,
15756 );
15757 let __s_b = self.gpu.stream();
15758 let mut b = __s_b.launch_builder(&f);
15759 match ff {
15760 Some(t) => {
15761 b.arg(&mut *q)
15762 .arg(&mut *k)
15763 .arg(&mut *qb)
15764 .arg(&mut *kb)
15765 .arg(pos)
15766 .arg(&hd)
15767 .arg(&nd)
15768 .arg(&nhq)
15769 .arg(&nhk)
15770 .arg(&nt)
15771 .arg(&theta_scale)
15772 .arg(&freq_scale)
15773 .arg(t);
15774 unsafe {
15775 b.launch(cfg)?;
15776 }
15777 }
15778 None => {
15779 let null: u64 = 0;
15780 b.arg(&mut *q)
15781 .arg(&mut *k)
15782 .arg(&mut *qb)
15783 .arg(&mut *kb)
15784 .arg(pos)
15785 .arg(&hd)
15786 .arg(&nd)
15787 .arg(&nhq)
15788 .arg(&nhk)
15789 .arg(&nt)
15790 .arg(&theta_scale)
15791 .arg(&freq_scale)
15792 .arg(&null);
15793 unsafe {
15794 b.launch(cfg)?;
15795 }
15796 }
15797 }
15798 Ok(())
15799 }
15800
15801 pub fn f32_to_bf16(
15804 &self,
15805 x: &CudaSlice<f32>,
15806 n: usize,
15807 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
15808 assert!(n % 4 == 0, "f32_to_bf16 requires n % 4 == 0, got {n}");
15809 let mut y = self.alloc_uninit::<u8>(n * 2)?;
15810 let f = self.func("f32_to_bf16_flat");
15811 let n_i = n as i64;
15812 let cfg = LaunchConfig {
15813 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
15814 block_dim: (256, 1, 1),
15815 shared_mem_bytes: 0,
15816 };
15817 let __s_b = self.gpu.stream();
15818 let mut b = __s_b.launch_builder(&f);
15819 b.arg(x).arg(&mut y).arg(&n_i);
15820 unsafe {
15821 b.launch(cfg)?;
15822 }
15823 Ok(y)
15824 }
15825
15826 pub fn f32_to_f16(
15827 &self,
15828 x: &CudaSlice<f32>,
15829 n: usize,
15830 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
15831 assert!(n % 4 == 0, "f32_to_f16 requires n % 4 == 0, got {n}");
15832 let mut y = self.alloc_uninit::<u8>(n * 2)?;
15833 let f = self.func("f32_to_f16_flat");
15834 let n_i = n as i64;
15835 let cfg = LaunchConfig {
15836 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
15837 block_dim: (256, 1, 1),
15838 shared_mem_bytes: 0,
15839 };
15840 let __s_b = self.gpu.stream();
15841 let mut b = __s_b.launch_builder(&f);
15842 b.arg(x).arg(&mut y).arg(&n_i);
15843 unsafe {
15844 b.launch(cfg)?;
15845 }
15846 Ok(y)
15847 }
15848
15849 pub fn bf16_to_f16(
15851 &self,
15852 xb: &CudaSlice<u8>,
15853 n: usize,
15854 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
15855 let mut y = self.alloc_uninit::<u8>(n * 2)?;
15856 self.bf16_to_f16_into(xb, n, &mut y)?;
15857 Ok(y)
15858 }
15859
15860 pub fn bf16_to_f16_into(
15862 &self,
15863 xb: &CudaSlice<u8>,
15864 n: usize,
15865 y: &mut CudaSlice<u8>,
15866 ) -> Result<(), Box<dyn std::error::Error>> {
15867 assert!(n % 2 == 0, "bf16_to_f16 requires n % 2 == 0, got {n}");
15868 assert!(y.len() >= n * 2);
15869 let f = self.func("bf16_to_f16_flat");
15870 let n2 = (n / 2) as i64;
15871 let cfg = LaunchConfig {
15872 grid_dim: (((n / 2) as u32).div_ceil(256), 1, 1),
15873 block_dim: (256, 1, 1),
15874 shared_mem_bytes: 0,
15875 };
15876 let __s_b = self.gpu.stream();
15877 let mut b = __s_b.launch_builder(&f);
15878 b.arg(xb).arg(y).arg(&n2);
15879 unsafe {
15880 b.launch(cfg)?;
15881 }
15882 Ok(())
15883 }
15884
15885 #[allow(clippy::too_many_arguments)]
15890 pub fn fa_prefill_vl8(
15891 &self,
15892 seqs: &[FaSeqVl],
15893 head_dim: usize,
15894 n_head: usize,
15895 n_head_kv: usize,
15896 scale: f32,
15897 ) -> Result<(), Box<dyn std::error::Error>> {
15898 const BK: usize = 32;
15899 let b = seqs.len();
15900 assert!(b >= 1 && b <= 8);
15901 let mut packed = [FaSeqVl::default(); 8];
15902 packed[..b].copy_from_slice(seqs);
15903 let v = FaVl8(packed);
15904 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
15905 let ept = (n_head_kv * head_dim) as i32;
15906 {
15907 let f = self.func("fa_mirror_vl");
15908 let max_n = (max_t as i64) * ept as i64;
15909 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
15910 for which in 0..2i32 {
15911 let cfg = LaunchConfig {
15912 grid_dim: (blocks, 1, b as u32),
15913 block_dim: (256, 1, 1),
15914 shared_mem_bytes: 0,
15915 };
15916 let __s_lb = self.gpu.stream();
15917 let mut lb = __s_lb.launch_builder(&f);
15918 lb.arg(&v).arg(&ept).arg(&which);
15919 unsafe {
15920 lb.launch(cfg)?;
15921 }
15922 }
15923 }
15924 let hd_sfx = fa_hd_suffix(head_dim)?;
15925 let f = self.func(&format!("fa_prefill_bf16kv_vl{hd_sfx}"));
15926 let block_q = 64usize;
15927 let kv_stages = 2usize;
15928 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
15929 + 4 * (block_q * BK + 2 * block_q)) as u32;
15930 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15931 f.set_attribute(
15932 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15933 shmem as i32,
15934 )?;
15935 let cfg = LaunchConfig {
15936 grid_dim: (max_t.div_ceil(block_q as u32), n_head as u32, b as u32),
15937 block_dim: (32, 4, 1),
15938 shared_mem_bytes: shmem,
15939 };
15940 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
15941 let __s_lb = self.gpu.stream();
15942 let mut lb = __s_lb.launch_builder(&f);
15943 lb.arg(&v).arg(&hd).arg(&nh).arg(&nhkv).arg(&scale);
15944 unsafe {
15945 lb.launch(cfg)?;
15946 }
15947 Ok(())
15948 }
15949
15950 #[allow(clippy::too_many_arguments)]
15954 pub fn attn_pre_vl8(
15955 &self,
15956 seqs: &[AttnPreVl],
15957 wq: &CudaSlice<f32>,
15958 wk: &CudaSlice<f32>,
15959 head_dim: usize,
15960 rope_dims: usize,
15961 n_head: usize,
15962 n_head_kv: usize,
15963 eps: f32,
15964 freq_base: f32,
15965 freq_scale: f32,
15966 kv_dim_k: usize,
15967 kv_dim_v: usize,
15968 k_tok_bytes: usize,
15969 v_tok_bytes: usize,
15970 ) -> Result<(), Box<dyn std::error::Error>> {
15971 let b = seqs.len();
15972 assert!(b >= 1 && b <= 8);
15973 let mut packed = [AttnPreVl::default(); 8];
15974 packed[..b].copy_from_slice(seqs);
15975 let v = AttnPreVl8(packed);
15976 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
15977 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
15978 {
15979 let f = self.func("q_gate_split_vl");
15980 let n = max_t * (n_head * head_dim) as u32;
15981 let cfg = LaunchConfig {
15982 grid_dim: (n.div_ceil(256), 1, b as u32),
15983 block_dim: (256, 1, 1),
15984 shared_mem_bytes: 0,
15985 };
15986 let __s_lb = self.gpu.stream();
15987 let mut lb = __s_lb.launch_builder(&f);
15988 lb.arg(&v).arg(&hd).arg(&nh);
15989 unsafe {
15990 lb.launch(cfg)?;
15991 }
15992 }
15993 {
15994 let f = self.func("attn_rms_vl");
15995 let cfg = LaunchConfig {
15996 grid_dim: (max_t * n_head as u32, 2, b as u32),
15997 block_dim: (rms_block(), 1, 1),
15998 shared_mem_bytes: 0,
15999 };
16000 let __s_lb = self.gpu.stream();
16001 let mut lb = __s_lb.launch_builder(&f);
16002 lb.arg(&v)
16003 .arg(wq)
16004 .arg(wk)
16005 .arg(&hd)
16006 .arg(&nh)
16007 .arg(&nhkv)
16008 .arg(&eps);
16009 unsafe {
16010 lb.launch(cfg)?;
16011 }
16012 }
16013 {
16014 let f = self.func("attn_rope_vl");
16015 let theta_scale = freq_base.powf(-2.0 / rope_dims as f32);
16016 let nd = rope_dims as i32;
16017 let cfg = LaunchConfig {
16018 grid_dim: (max_t * n_head as u32, 2, b as u32),
16019 block_dim: ((head_dim / 2) as u32, 1, 1),
16020 shared_mem_bytes: 0,
16021 };
16022 let __s_lb = self.gpu.stream();
16023 let mut lb = __s_lb.launch_builder(&f);
16024 lb.arg(&v)
16025 .arg(&hd)
16026 .arg(&nd)
16027 .arg(&nh)
16028 .arg(&nhkv)
16029 .arg(&theta_scale)
16030 .arg(&freq_scale);
16031 unsafe {
16032 lb.launch(cfg)?;
16033 }
16034 }
16035 {
16036 let f = self.func("append_kv_vl");
16037 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
16038 let cfg = LaunchConfig {
16039 grid_dim: (nblk, max_t, b as u32),
16040 block_dim: (32, 1, 1),
16041 shared_mem_bytes: 0,
16042 };
16043 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
16044 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16045 let __s_lb = self.gpu.stream();
16046 let mut lb = __s_lb.launch_builder(&f);
16047 lb.arg(&v).arg(&kdk).arg(&kdv).arg(&ktb).arg(&vtb);
16048 unsafe {
16049 lb.launch(cfg)?;
16050 }
16051 }
16052 Ok(())
16053 }
16054
16055 pub fn fa_prefill_view(
16060 &self,
16061 q: &CudaSlice<f32>,
16062 k: &cudarc::driver::CudaView<u8>,
16063 v: &cudarc::driver::CudaView<u8>,
16064 o: &mut CudaSlice<f32>,
16065 head_dim: usize,
16066 n_head: usize,
16067 n_head_kv: usize,
16068 t: usize,
16069 t_kv: usize,
16070 scale: f32,
16071 causal: bool,
16072 k_tok_bytes: usize,
16073 v_tok_bytes: usize,
16074 g: bool,
16075 ) -> Result<(), Box<dyn std::error::Error>> {
16076 if portable_mma_gated() {
16077 return self.sdpa_naive_quantized_view(
16078 q,
16079 k,
16080 v,
16081 o,
16082 head_dim,
16083 n_head,
16084 n_head_kv,
16085 t,
16086 t_kv,
16087 scale,
16088 causal,
16089 k_tok_bytes,
16090 v_tok_bytes,
16091 );
16092 }
16093 const BLOCK_Q: usize = 64;
16094 const BK: usize = 32;
16095 let name = format!("fa_prefill_q{}", fa_hd_suffix(head_dim)?);
16098 let f = if g {
16099 self.func_g(&name)
16100 } else {
16101 self.func(&name)
16102 };
16103 let shmem =
16104 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
16105 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16106 f.set_attribute(
16107 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16108 shmem as i32,
16109 )?;
16110 let cfg = LaunchConfig {
16111 grid_dim: (
16112 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16113 n_head as u32,
16114 1,
16115 ),
16116 block_dim: (32, 4, 1),
16117 shared_mem_bytes: shmem,
16118 };
16119 let (hd, nh, nhkv, ti, tkvi, cz) = (
16120 head_dim as i32,
16121 n_head as i32,
16122 n_head_kv as i32,
16123 t as i32,
16124 t_kv as i32,
16125 causal as i32,
16126 );
16127 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16128 let __s_b = self.gpu.stream();
16129 let mut b = __s_b.launch_builder(&f);
16130 b.arg(q)
16131 .arg(k)
16132 .arg(v)
16133 .arg(o)
16134 .arg(&hd)
16135 .arg(&nh)
16136 .arg(&nhkv)
16137 .arg(&ti)
16138 .arg(&tkvi)
16139 .arg(&scale)
16140 .arg(&cz)
16141 .arg(&ktb)
16142 .arg(&vtb);
16143 unsafe {
16144 b.launch(cfg)?;
16145 }
16146 Ok(())
16147 }
16148
16149 #[allow(clippy::too_many_arguments)]
16159 pub fn fa_prefill_view_ws(
16160 &self,
16161 q: &CudaSlice<f32>,
16162 k: &cudarc::driver::CudaView<u8>,
16163 v: &cudarc::driver::CudaView<u8>,
16164 o: &mut CudaSlice<f32>,
16165 head_dim: usize,
16166 n_head: usize,
16167 n_head_kv: usize,
16168 t: usize,
16169 t_kv: usize,
16170 scale: f32,
16171 causal: bool,
16172 k_tok_bytes: usize,
16173 v_tok_bytes: usize,
16174 g: bool,
16175 ) -> Result<(), Box<dyn std::error::Error>> {
16176 if portable_mma_gated() {
16177 return self.sdpa_naive_quantized_view(
16178 q,
16179 k,
16180 v,
16181 o,
16182 head_dim,
16183 n_head,
16184 n_head_kv,
16185 t,
16186 t_kv,
16187 scale,
16188 causal,
16189 k_tok_bytes,
16190 v_tok_bytes,
16191 );
16192 }
16193 const BLOCK_Q: usize = 64;
16194 const BK: usize = 32;
16195 let kv_dim_k = n_head_kv * head_dim;
16196 let kv_dim_v = n_head_kv * head_dim;
16197 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
16199 let mut guard = self.prime_deqw_ws.lock().unwrap();
16201 let need_grow = match guard.as_ref() {
16202 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
16203 None => true,
16204 };
16205 if need_grow {
16206 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
16207 let (ck, cv) = guard
16208 .as_ref()
16209 .map(|(a, b)| (a.len(), b.len()))
16210 .unwrap_or((0, 0));
16211 *guard = Some((
16212 self.alloc_u8(grow(ck, k_ws_bytes))?,
16213 self.alloc_u8(grow(cv, v_ws_bytes))?,
16214 ));
16215 }
16216 let (kw, vw) = guard.as_mut().unwrap();
16217 {
16219 let f = if g {
16221 self.func_g("fa_dequant_kv_ws_bf16")
16222 } else {
16223 self.func("fa_dequant_kv_ws_bf16")
16224 };
16225 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
16226 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
16227 let cfg = LaunchConfig {
16228 grid_dim: (nblk.max(1), 1, 1),
16229 block_dim: (256, 1, 1),
16230 shared_mem_bytes: 0,
16231 };
16232 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
16233 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16234 let __s_b = self.gpu.stream();
16235 let mut b = __s_b.launch_builder(&f);
16236 b.arg(k)
16237 .arg(v)
16238 .arg(&mut *kw)
16239 .arg(&mut *vw)
16240 .arg(&kdk)
16241 .arg(&kdv)
16242 .arg(&tkvi)
16243 .arg(&ktb)
16244 .arg(&vtb);
16245 unsafe {
16246 b.launch(cfg)?;
16247 }
16248 }
16249 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
16257 .map(|v| v != "0")
16258 .unwrap_or(true);
16259 {
16260 let hd_sfx = fa_hd_suffix(head_dim)?;
16261 let f = self.func(&format!(
16262 "fa_prefill_qw{}{hd_sfx}",
16263 if db { "_db" } else { "" }
16264 ));
16265 let shmem = if db {
16266 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
16268 } else {
16269 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
16270 };
16271 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16272 f.set_attribute(
16273 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16274 shmem as i32,
16275 )?;
16276 let cfg = LaunchConfig {
16277 grid_dim: (
16278 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16279 n_head as u32,
16280 1,
16281 ),
16282 block_dim: (32, 4, 1),
16283 shared_mem_bytes: shmem,
16284 };
16285 let (hd, nh, nhkv, ti, tkvi, cz) = (
16286 head_dim as i32,
16287 n_head as i32,
16288 n_head_kv as i32,
16289 t as i32,
16290 t_kv as i32,
16291 causal as i32,
16292 );
16293 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
16294 let __s_b = self.gpu.stream();
16295 let mut b = __s_b.launch_builder(&f);
16296 b.arg(q)
16297 .arg(&*kw)
16298 .arg(&*vw)
16299 .arg(o)
16300 .arg(&hd)
16301 .arg(&nh)
16302 .arg(&nhkv)
16303 .arg(&ti)
16304 .arg(&tkvi)
16305 .arg(&scale)
16306 .arg(&cz)
16307 .arg(&kdk)
16308 .arg(&kdv);
16309 unsafe {
16310 b.launch(cfg)?;
16311 }
16312 }
16313 Ok(())
16314 }
16315
16316 #[allow(clippy::too_many_arguments)]
16332 pub fn fa_prefill_view_ws_w_hd128(
16333 &self,
16334 q: &CudaSlice<f32>,
16335 k: &cudarc::driver::CudaView<u8>,
16336 v: &cudarc::driver::CudaView<u8>,
16337 o: &mut CudaSlice<f32>,
16338 head_dim: usize,
16339 n_head: usize,
16340 n_head_kv: usize,
16341 t: usize,
16342 t_kv: usize,
16343 scale: f32,
16344 causal: bool,
16345 window: usize,
16346 k_tok_bytes: usize,
16347 v_tok_bytes: usize,
16348 ) -> Result<(), Box<dyn std::error::Error>> {
16349 assert_eq!(
16350 head_dim, 128,
16351 "fa_prefill_view_ws_w_hd128: only the hd128 twin is stamped"
16352 );
16353 if portable_mma_gated() {
16354 return self.sdpa_naive_w_quantized_view(
16355 q,
16356 k,
16357 v,
16358 o,
16359 head_dim,
16360 n_head,
16361 n_head_kv,
16362 t,
16363 t_kv,
16364 scale,
16365 causal,
16366 window,
16367 k_tok_bytes,
16368 v_tok_bytes,
16369 );
16370 }
16371 const BLOCK_Q: usize = 64;
16372 const BK: usize = 32;
16373 let kv_dim_k = n_head_kv * head_dim;
16374 let kv_dim_v = n_head_kv * head_dim;
16375 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
16377 let mut guard = self.prime_deqw_ws.lock().unwrap();
16378 let need_grow = match guard.as_ref() {
16379 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
16380 None => true,
16381 };
16382 if need_grow {
16383 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
16384 let (ck, cv) = guard
16385 .as_ref()
16386 .map(|(a, b)| (a.len(), b.len()))
16387 .unwrap_or((0, 0));
16388 *guard = Some((
16389 self.alloc_u8(grow(ck, k_ws_bytes))?,
16390 self.alloc_u8(grow(cv, v_ws_bytes))?,
16391 ));
16392 }
16393 let (kw, vw) = guard.as_mut().unwrap();
16394 {
16397 let f = self.func("fa_dequant_kv_ws_bf16");
16398 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
16399 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
16400 let cfg = LaunchConfig {
16401 grid_dim: (nblk.max(1), 1, 1),
16402 block_dim: (256, 1, 1),
16403 shared_mem_bytes: 0,
16404 };
16405 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
16406 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16407 let __s_b = self.gpu.stream();
16408 let mut b = __s_b.launch_builder(&f);
16409 b.arg(k)
16410 .arg(v)
16411 .arg(&mut *kw)
16412 .arg(&mut *vw)
16413 .arg(&kdk)
16414 .arg(&kdv)
16415 .arg(&tkvi)
16416 .arg(&ktb)
16417 .arg(&vtb);
16418 unsafe {
16419 b.launch(cfg)?;
16420 }
16421 }
16422 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
16424 .map(|v| v != "0")
16425 .unwrap_or(true);
16426 {
16427 let f = self.func(if db {
16428 "fa_prefill_qw_db_w_hd128"
16429 } else {
16430 "fa_prefill_qw_w_hd128"
16431 });
16432 let shmem = if db {
16433 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
16434 } else {
16435 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
16436 };
16437 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16438 f.set_attribute(
16439 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16440 shmem as i32,
16441 )?;
16442 let cfg = LaunchConfig {
16443 grid_dim: (
16444 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16445 n_head as u32,
16446 1,
16447 ),
16448 block_dim: (32, 4, 1),
16449 shared_mem_bytes: shmem,
16450 };
16451 let (hd, nh, nhkv, ti, tkvi, cz) = (
16452 head_dim as i32,
16453 n_head as i32,
16454 n_head_kv as i32,
16455 t as i32,
16456 t_kv as i32,
16457 causal as i32,
16458 );
16459 let (kdk, kdv, wnd) = (kv_dim_k as i32, kv_dim_v as i32, window as i32);
16460 let __s_b = self.gpu.stream();
16461 let mut b = __s_b.launch_builder(&f);
16462 b.arg(q)
16463 .arg(&*kw)
16464 .arg(&*vw)
16465 .arg(o)
16466 .arg(&hd)
16467 .arg(&nh)
16468 .arg(&nhkv)
16469 .arg(&ti)
16470 .arg(&tkvi)
16471 .arg(&scale)
16472 .arg(&cz)
16473 .arg(&kdk)
16474 .arg(&kdv)
16475 .arg(&wnd);
16476 unsafe {
16477 b.launch(cfg)?;
16478 }
16479 }
16480 Ok(())
16481 }
16482
16483 pub fn fa_decode(
16487 &self,
16488 q: &CudaSlice<f32>,
16489 k: &cudarc::driver::CudaView<u8>,
16490 v: &cudarc::driver::CudaView<u8>,
16491 o: &mut CudaSlice<f32>,
16492 head_dim: usize,
16493 n_head: usize,
16494 n_head_kv: usize,
16495 t_kv: usize,
16496 scale: f32,
16497 k_tok_bytes: usize,
16498 v_tok_bytes: usize,
16499 ) -> Result<(), Box<dyn std::error::Error>> {
16500 self.fa_decode_kvmod(
16501 q,
16502 k,
16503 v,
16504 o,
16505 head_dim,
16506 n_head,
16507 n_head_kv,
16508 t_kv,
16509 scale,
16510 k_tok_bytes,
16511 v_tok_bytes,
16512 false,
16513 )
16514 }
16515
16516 #[allow(clippy::too_many_arguments)]
16520 #[allow(clippy::too_many_arguments)]
16524 #[allow(clippy::too_many_arguments)]
16525 fn fa_decode_scalar_unified(
16526 &self,
16527 q: &cudarc::driver::CudaView<f32>,
16528 k: &cudarc::driver::CudaView<u8>,
16529 v: &cudarc::driver::CudaView<u8>,
16530 o: &mut cudarc::driver::CudaViewMut<f32>,
16531 head_dim: usize,
16532 n_head: usize,
16533 n_head_kv: usize,
16534 t_kv_host: usize,
16535 t_kv_dev: Option<&CudaSlice<i32>>,
16536 scale: f32,
16537 n_splits: usize,
16538 split_keys: usize,
16539 k_tok_bytes: usize,
16540 v_tok_bytes: usize,
16541 g: bool,
16542 part_o: &mut CudaSlice<f32>,
16543 part_m: &mut CudaSlice<f32>,
16544 part_l: &mut CudaSlice<f32>,
16545 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
16546 ) -> Result<(), Box<dyn std::error::Error>> {
16547 let f = if g {
16548 self.func_g("fa_decode_f32")
16549 } else {
16550 self.fa_func("fa_decode_f32", head_dim)
16551 };
16552 let cfg = LaunchConfig {
16553 grid_dim: (n_head as u32, n_splits as u32, 1),
16554 block_dim: (head_dim as u32, 1, 1),
16555 shared_mem_bytes: (4 * (head_dim + 32)) as u32,
16556 };
16557 let (hd, nh, nhkv, nsp) = (
16558 head_dim as i32,
16559 n_head as i32,
16560 n_head_kv as i32,
16561 n_splits as i32,
16562 );
16563 let (ktb, vtb, tkvi, ski) = (
16564 k_tok_bytes as i64,
16565 v_tok_bytes as i64,
16566 t_kv_host as i32,
16567 split_keys as i32,
16568 );
16569 let __s_b = self.gpu.stream();
16570 let mut b = __s_b.launch_builder(&f);
16571 match t_kv_dev {
16572 Some(d) => {
16573 b.arg(q)
16574 .arg(k)
16575 .arg(v)
16576 .arg(&mut *part_o)
16577 .arg(&mut *part_m)
16578 .arg(&mut *part_l)
16579 .arg(&hd)
16580 .arg(&nh)
16581 .arg(&nhkv)
16582 .arg(&tkvi)
16583 .arg(d)
16584 .arg(&scale)
16585 .arg(&nsp)
16586 .arg(&ski)
16587 .arg(&ktb)
16588 .arg(&vtb);
16589 unsafe {
16590 b.launch(cfg)?;
16591 }
16592 }
16593 None => {
16594 let null: u64 = 0;
16595 b.arg(q)
16596 .arg(k)
16597 .arg(v)
16598 .arg(&mut *part_o)
16599 .arg(&mut *part_m)
16600 .arg(&mut *part_l)
16601 .arg(&hd)
16602 .arg(&nh)
16603 .arg(&nhkv)
16604 .arg(&tkvi)
16605 .arg(&null)
16606 .arg(&scale)
16607 .arg(&nsp)
16608 .arg(&ski)
16609 .arg(&ktb)
16610 .arg(&vtb);
16611 unsafe {
16612 b.launch(cfg)?;
16613 }
16614 }
16615 }
16616 let cfg2 = LaunchConfig {
16617 grid_dim: (n_head as u32, 1, 1),
16618 block_dim: (head_dim as u32, 1, 1),
16619 shared_mem_bytes: 0,
16620 };
16621 if let Some((oq, od)) = q8_out {
16622 let fc = if g {
16624 self.func_g("fa_decode_combine_q8_1")
16625 } else {
16626 self.fa_func("fa_decode_combine_q8_1", head_dim)
16627 };
16628 let __s_b2 = self.gpu.stream();
16629 let mut b2 = __s_b2.launch_builder(&fc);
16630 b2.arg(&*part_o)
16631 .arg(&*part_m)
16632 .arg(&*part_l)
16633 .arg(oq)
16634 .arg(od)
16635 .arg(&hd)
16636 .arg(&nh)
16637 .arg(&nsp);
16638 unsafe {
16639 b2.launch(cfg2)?;
16640 }
16641 return Ok(());
16642 }
16643 let fc = if g {
16644 self.func_g("fa_decode_combine_f32")
16645 } else {
16646 self.fa_func("fa_decode_combine_f32", head_dim)
16647 };
16648 let __s_b2 = self.gpu.stream();
16649 let mut b2 = __s_b2.launch_builder(&fc);
16650 b2.arg(&*part_o)
16651 .arg(&*part_m)
16652 .arg(&*part_l)
16653 .arg(o)
16654 .arg(&hd)
16655 .arg(&nh)
16656 .arg(&nsp);
16657 unsafe {
16658 b2.launch(cfg2)?;
16659 }
16660 Ok(())
16661 }
16662
16663 pub fn fa_decode_kvmod(
16664 &self,
16665 q: &CudaSlice<f32>,
16666 k: &cudarc::driver::CudaView<u8>,
16667 v: &cudarc::driver::CudaView<u8>,
16668 o: &mut CudaSlice<f32>,
16669 head_dim: usize,
16670 n_head: usize,
16671 n_head_kv: usize,
16672 t_kv: usize,
16673 scale: f32,
16674 k_tok_bytes: usize,
16675 v_tok_bytes: usize,
16676 g: bool,
16677 ) -> Result<(), Box<dyn std::error::Error>> {
16678 let q_view = q.as_view();
16679 let mut o_view = o.as_view_mut();
16680 self.fa_decode_kvmod_view(
16681 &q_view,
16682 k,
16683 v,
16684 &mut o_view,
16685 head_dim,
16686 n_head,
16687 n_head_kv,
16688 t_kv,
16689 scale,
16690 k_tok_bytes,
16691 v_tok_bytes,
16692 g,
16693 )
16694 }
16695
16696 #[allow(clippy::too_many_arguments)]
16701 pub fn fa_decode_kvmod_view(
16702 &self,
16703 q: &cudarc::driver::CudaView<f32>,
16704 k: &cudarc::driver::CudaView<u8>,
16705 v: &cudarc::driver::CudaView<u8>,
16706 o: &mut cudarc::driver::CudaViewMut<f32>,
16707 head_dim: usize,
16708 n_head: usize,
16709 n_head_kv: usize,
16710 t_kv: usize,
16711 scale: f32,
16712 k_tok_bytes: usize,
16713 v_tok_bytes: usize,
16714 g: bool,
16715 ) -> Result<(), Box<dyn std::error::Error>> {
16716 let mut fa_vec = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
16737 if g && head_dim == 256 && !fa_v4_at(t_kv) {
16741 fa_vec = false;
16742 }
16743 let sp = fa_split_keys(t_kv, n_head_kv);
16744 let n_splits = if fa_vec {
16745 ((t_kv + sp - 1) / sp).max(1)
16746 } else {
16747 ((t_kv + 255) / 256).max(1)
16748 };
16749 let o_len = n_head * n_splits * head_dim;
16750 let ml_len = n_head * n_splits;
16751 let mut part_guard = self.fa_part_pool.lock().unwrap();
16752 if part_guard
16753 .as_ref()
16754 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
16755 .unwrap_or(true)
16756 {
16757 let old = part_guard.take();
16768 let (co, cm) = old
16769 .as_ref()
16770 .map(|pp| (pp.0.len(), pp.1.len()))
16771 .unwrap_or((0, 0));
16772 if let Some(old) = old {
16773 self.fa_part_retired.lock().unwrap().push(old);
16774 }
16775 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
16776 eprintln!(
16777 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
16778 co, o_len, cm, ml_len
16779 );
16780 }
16781 *part_guard = Some((
16782 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
16783 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
16784 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
16785 ));
16786 }
16787 let pg = part_guard.as_mut().unwrap();
16788 self.gpu
16789 .stream()
16790 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
16791 self.gpu
16792 .stream()
16793 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
16794 self.gpu
16795 .stream()
16796 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
16797 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
16798 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
16799 let (hd, nh, nhkv, tkvi, nsp) = (
16800 head_dim as i32,
16801 n_head as i32,
16802 n_head_kv as i32,
16803 t_kv as i32,
16804 n_splits as i32,
16805 );
16806 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16807 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
16811 let fa512_min = fa512_min_tkv();
16816 let deep = fa_vec
16819 && head_dim == 256
16820 && fa_v4_at(t_kv)
16821 && !g
16822 && fa_deep_at(t_kv)
16823 && !matches!(fa_v4_mode(), "noB3" | "stage");
16824 let (f, cfg) = if fa_vec && head_dim == 512 && t_kv >= fa512_min {
16825 let gqa = (n_head / n_head_kv).max(1) as u32;
16828 let fv = self.fa_func("fa_decode_vec_q_dpl16", head_dim);
16829 (
16830 fv,
16831 LaunchConfig {
16832 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16833 block_dim: (32, gqa, 1),
16834 shared_mem_bytes: 0,
16835 },
16836 )
16837 } else if fa_vec && head_dim <= 256 {
16838 let gqa = (n_head / n_head_kv).max(1) as u32;
16839 static SMEM_TKV: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
16850 let smem_tkv = *SMEM_TKV.get_or_init(|| {
16851 std::env::var("MEMRA_FA_SMEM_TKV")
16852 .ok()
16853 .and_then(|v| v.parse().ok())
16854 .unwrap_or_else(|| {
16855 FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
16856 })
16857 });
16858 if fa_v4_at(t_kv) && head_dim == 256 {
16859 let v4name = match fa_v4_mode() {
16863 "noB3" => "fa_decode_vec_q_v4_noB3", "stage" => "fa_decode_vec_q_v4_stage", _ if deep => "fa_decode_vec_q_v4_deep",
16866 _ => "fa_decode_vec_q_v4",
16867 };
16868 let fv = if g {
16869 self.func_g(v4name)
16870 } else {
16871 self.func(v4name)
16872 };
16873 let shmem = (if deep { 12160 } else { 11520 }
16876 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
16877 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16878 fv.set_attribute(
16879 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16880 shmem as i32,
16881 )?;
16882 (
16883 fv,
16884 LaunchConfig {
16885 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16886 block_dim: (32, gqa, 1),
16887 shared_mem_bytes: shmem,
16888 },
16889 )
16890 } else if fa_v3_active(head_dim) {
16891 let fv = if g {
16894 self.func_g("fa_decode_vec_q_v3")
16895 } else {
16896 self.func("fa_decode_vec_q_v3")
16897 };
16898 let shmem = (32 * head_dim * 2) as u32; (
16900 fv,
16901 LaunchConfig {
16902 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16903 block_dim: (32, gqa, 1),
16904 shared_mem_bytes: shmem,
16905 },
16906 )
16907 } else if fa_v2_on() {
16908 let fv = if g {
16912 self.func_g("fa_decode_vec_q_v2")
16913 } else {
16914 self.func("fa_decode_vec_q_v2")
16915 };
16916 let shmem = (2 * 32 * head_dim * 2) as u32; (
16918 fv,
16919 LaunchConfig {
16920 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16921 block_dim: (32, gqa, 1),
16922 shared_mem_bytes: shmem,
16923 },
16924 )
16925 } else if smem_tkv > 0 && t_kv >= smem_tkv && !g && !(head_dim == 512 && Self::gkv_on())
16926 {
16927 let fv = if g {
16931 self.func_g("fa_decode_vec_q_smem")
16932 } else {
16933 self.func("fa_decode_vec_q_smem")
16934 };
16935 let shmem = (2 * 32 * head_dim * 2) as u32; use cudarc::driver::sys::CUfunction_attribute_enum as A;
16937 fv.set_attribute(
16938 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16939 shmem as i32,
16940 )?;
16941 (
16942 fv,
16943 LaunchConfig {
16944 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16945 block_dim: (32, gqa, 1),
16946 shared_mem_bytes: shmem,
16947 },
16948 )
16949 } else {
16950 let fv = if g {
16953 self.func_g("fa_decode_vec_q")
16954 } else {
16955 self.func("fa_decode_vec_q")
16956 };
16957 (
16958 fv,
16959 LaunchConfig {
16960 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16961 block_dim: (32, gqa, 1),
16962 shared_mem_bytes: 0,
16963 },
16964 )
16965 }
16966 } else {
16967 return self.fa_decode_scalar_unified(
16970 q,
16971 k,
16972 v,
16973 o,
16974 head_dim,
16975 n_head,
16976 n_head_kv,
16977 t_kv,
16978 None,
16979 scale,
16980 n_splits,
16981 if fa_vec { sp } else { 256 },
16982 k_tok_bytes,
16983 v_tok_bytes,
16984 g,
16985 part_o,
16986 part_m,
16987 part_l,
16988 None,
16989 );
16990 };
16991 let __s_b = self.gpu.stream();
16992 let mut b = __s_b.launch_builder(&f);
16993 b.arg(q)
16994 .arg(k)
16995 .arg(v)
16996 .arg(&mut *part_o)
16997 .arg(&mut *part_m)
16998 .arg(&mut *part_l)
16999 .arg(&hd)
17000 .arg(&nh)
17001 .arg(&nhkv)
17002 .arg(&tkvi)
17003 .arg(&scale)
17004 .arg(&nsp)
17005 .arg(&ktb)
17006 .arg(&vtb);
17007 unsafe {
17008 b.launch(cfg)?;
17009 }
17010 let (fc, cfg2) = (
17013 if g {
17014 self.func_g("fa_decode_combine_f32")
17015 } else {
17016 self.fa_func("fa_decode_combine_f32", head_dim)
17017 },
17018 LaunchConfig {
17019 grid_dim: (n_head as u32, 1, 1),
17020 block_dim: (head_dim as u32, 1, 1),
17021 shared_mem_bytes: 0,
17022 },
17023 );
17024 let __s_b2 = self.gpu.stream();
17025 let mut b2 = __s_b2.launch_builder(&fc);
17026 b2.arg(&*part_o)
17027 .arg(&*part_m)
17028 .arg(&*part_l)
17029 .arg(o)
17030 .arg(&hd)
17031 .arg(&nh)
17032 .arg(&nsp);
17033 unsafe {
17034 b2.launch(cfg2)?;
17035 }
17036 Ok(())
17037 }
17038
17039 #[allow(clippy::too_many_arguments)]
17050 pub fn fa_decode_batch_seqs_v4(
17051 &self,
17052 q: &CudaSlice<f32>,
17053 kv_ptrs: &cudarc::driver::CudaView<u64>,
17054 pos_seq: &CudaSlice<i32>,
17055 o: &mut CudaSlice<f32>,
17056 head_dim: usize,
17057 n_head: usize,
17058 n_head_kv: usize,
17059 b_n: usize,
17060 t_kv_max: usize,
17061 scale: f32,
17062 split_keys: usize,
17063 k_tok_bytes: usize,
17064 v_tok_bytes: usize,
17065 ) -> Result<(), Box<dyn std::error::Error>> {
17066 debug_assert!(head_dim == 256, "seqs twin is v4-stamped (hd256 only)");
17067 let n_splits_max = (t_kv_max + split_keys - 1) / split_keys;
17068 let o_len = b_n * n_head * n_splits_max * head_dim;
17069 let ml_len = b_n * n_head * n_splits_max;
17070 let mut part_guard = self.fa_part_pool.lock().unwrap();
17071 if part_guard
17072 .as_ref()
17073 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17074 .unwrap_or(true)
17075 {
17076 let old = part_guard.take();
17087 let (co, cm) = old
17088 .as_ref()
17089 .map(|pp| (pp.0.len(), pp.1.len()))
17090 .unwrap_or((0, 0));
17091 if let Some(old) = old {
17092 self.fa_part_retired.lock().unwrap().push(old);
17093 }
17094 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17095 eprintln!(
17096 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17097 co, o_len, cm, ml_len
17098 );
17099 }
17100 *part_guard = Some((
17101 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17102 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17103 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17104 ));
17105 }
17106 let pg = part_guard.as_mut().unwrap();
17107 self.gpu
17108 .stream()
17109 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17110 self.gpu
17111 .stream()
17112 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17113 self.gpu
17114 .stream()
17115 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17116 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17117 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17118 let (nspm, spk) = (n_splits_max as i32, split_keys as i32);
17119 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17120 let gqa = (n_head / n_head_kv).max(1) as u32;
17121 let f = self.func("fa_decode_vec_q_seqs_v4");
17122 let shmem = (11520 + 32 * head_dim * 2) as u32;
17124 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17125 f.set_attribute(
17126 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17127 shmem as i32,
17128 )?;
17129 let cfg = LaunchConfig {
17130 grid_dim: (n_head_kv as u32, n_splits_max as u32, b_n as u32),
17131 block_dim: (32, gqa, 1),
17132 shared_mem_bytes: shmem,
17133 };
17134 {
17135 let __s_b = self.gpu.stream();
17136 let mut b = __s_b.launch_builder(&f);
17137 b.arg(q)
17138 .arg(kv_ptrs)
17139 .arg(pos_seq)
17140 .arg(&mut *part_o)
17141 .arg(&mut *part_m)
17142 .arg(&mut *part_l)
17143 .arg(&hd)
17144 .arg(&nh)
17145 .arg(&nhkv)
17146 .arg(&scale)
17147 .arg(&nspm)
17148 .arg(&spk)
17149 .arg(&ktb)
17150 .arg(&vtb);
17151 unsafe {
17152 b.launch(cfg)?;
17153 }
17154 }
17155 let fc = self.func("fa_decode_combine_seqs");
17156 let cfg2 = LaunchConfig {
17157 grid_dim: (n_head as u32, b_n as u32, 1),
17158 block_dim: (head_dim as u32, 1, 1),
17159 shared_mem_bytes: 0,
17160 };
17161 let __s_b2 = self.gpu.stream();
17162 let mut b2 = __s_b2.launch_builder(&fc);
17163 b2.arg(&*part_o)
17164 .arg(&*part_m)
17165 .arg(&*part_l)
17166 .arg(o)
17167 .arg(&hd)
17168 .arg(&nh)
17169 .arg(pos_seq)
17170 .arg(&nspm)
17171 .arg(&spk);
17172 unsafe {
17173 b2.launch(cfg2)?;
17174 }
17175 Ok(())
17176 }
17177
17178 #[allow(clippy::too_many_arguments)]
17185 pub fn append_kv_quantized_seqs(
17186 &self,
17187 k_rows: &CudaSlice<f32>,
17188 v_rows: &CudaSlice<f32>,
17189 kv_ptrs: &cudarc::driver::CudaView<u64>,
17190 pos_seq: &CudaSlice<i32>,
17191 b_n: usize,
17192 kv_dim_k: usize,
17193 kv_dim_v: usize,
17194 k_tok_bytes: usize,
17195 v_tok_bytes: usize,
17196 ) -> Result<(), Box<dyn std::error::Error>> {
17197 let f = self.func("append_quantize_kv_q8_0_q5_1_seqs");
17198 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
17199 let cfg = LaunchConfig {
17200 grid_dim: (nblk, b_n as u32, 1),
17201 block_dim: (32, 1, 1),
17202 shared_mem_bytes: 0,
17203 };
17204 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
17205 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17206 let __s_b = self.gpu.stream();
17207 let mut b = __s_b.launch_builder(&f);
17208 b.arg(k_rows)
17209 .arg(v_rows)
17210 .arg(kv_ptrs)
17211 .arg(pos_seq)
17212 .arg(&kdk)
17213 .arg(&kdv)
17214 .arg(&ktb)
17215 .arg(&vtb);
17216 unsafe {
17217 b.launch(cfg)?;
17218 }
17219 Ok(())
17220 }
17221
17222 pub fn fa_rows_eligible(&self, base_len: usize, head_dim: usize) -> bool {
17228 std::env::var("MEMRA_NO_FA_VEC").is_err()
17229 && std::env::var("MEMRA_FA_ROWS_OFF").is_err()
17230 && base_len + 1 >= fa_vec_min_tkv()
17231 && head_dim <= 256
17232 && head_dim % 32 == 0
17233 }
17234
17235 #[allow(clippy::too_many_arguments)]
17244 pub fn fa_decode_rows(
17245 &self,
17246 q: &CudaSlice<f32>,
17247 k: &cudarc::driver::CudaView<u8>,
17248 v: &cudarc::driver::CudaView<u8>,
17249 o: &mut CudaSlice<f32>,
17250 head_dim: usize,
17251 n_head: usize,
17252 n_head_kv: usize,
17253 base_len: usize,
17254 t: usize,
17255 scale: f32,
17256 k_tok_bytes: usize,
17257 v_tok_bytes: usize,
17258 base_dev: Option<(&CudaSlice<i32>, i32)>,
17262 kv_shared: bool,
17265 g: bool,
17269 mut q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
17272 ) -> Result<(), Box<dyn std::error::Error>> {
17273 debug_assert!(base_len + 1 >= fa_vec_min_tkv() && head_dim <= 512 && head_dim % 32 == 0);
17274 let t_kv_max = base_len + t; let mut sp = fa_split_keys(t_kv_max, n_head_kv); if head_dim == 512 {
17281 static SP512: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17282 let v = *SP512.get_or_init(|| {
17285 std::env::var("MEMRA_FA_SP512")
17286 .ok()
17287 .and_then(|x| x.parse().ok())
17288 .unwrap_or(0)
17289 });
17290 sp = if v >= 8 {
17291 v
17292 } else {
17293 FA_SP512_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
17294 };
17295 }
17296 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17297 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17298 let gqa = (n_head / n_head_kv).max(1) as u32;
17299 let mut groups: Vec<(usize, usize, usize)> = Vec::new(); if head_dim == 512 || fa_split_keys(base_len + 1, n_head_kv) == sp {
17310 groups.push((0, t, sp));
17311 } else {
17312 let mut r0 = 0usize;
17313 while r0 < t {
17314 let sp_g = fa_split_keys(base_len + r0 + 1, n_head_kv);
17315 let mut r1 = r0 + 1;
17316 while r1 < t && fa_split_keys(base_len + r1 + 1, n_head_kv) == sp_g {
17317 r1 += 1;
17318 }
17319 groups.push((r0, r1 - r0, sp_g));
17320 r0 = r1;
17321 }
17322 }
17323 static SMEM_TKV_R: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17327 let smem_tkv = *SMEM_TKV_R.get_or_init(|| {
17328 std::env::var("MEMRA_FA_SMEM_TKV")
17329 .ok()
17330 .and_then(|v| v.parse().ok())
17331 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
17332 });
17333 let v4 = fa_v4_at(base_len + t) && head_dim == 256;
17334 let v3 = fa_v3_active(head_dim);
17335 let smem_rows =
17336 head_dim <= 256 && !v3 && !fa_v2_on() && smem_tkv > 0 && t_kv_max >= smem_tkv;
17337 let _ = kv_shared;
17342 let i2 = head_dim == 512 && std::env::var("MEMRA_FA_I2").as_deref() != Ok("0");
17345 static TB512: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
17359 let tb512 = head_dim == 512
17361 && sp <= 32
17362 && n_head / n_head_kv.max(1) <= 16
17363 && *TB512.get_or_init(|| std::env::var("MEMRA_FA_TB512").as_deref() != Ok("0"));
17364 let fname = if tb512 {
17365 "fa_decode_vec_q_rows_v4_512_tb"
17366 } else if i2 {
17367 "fa_decode_vec_q_rows_dpl16_i2"
17368 } else if head_dim == 512 {
17369 "fa_decode_vec_q_rows_dpl16"
17370 }
17371 else if v4 {
17373 "fa_decode_vec_q_rows_v4"
17374 } else if v3 {
17375 "fa_decode_vec_q_rows_v3"
17376 } else if fa_v2_on() {
17377 "fa_decode_vec_q_rows_v2"
17378 } else if smem_rows {
17379 "fa_decode_vec_q_rows_smem"
17380 } else {
17381 "fa_decode_vec_q_rows"
17382 };
17383 let f = if head_dim == 512 {
17384 self.fa_func(fname, head_dim)
17385 } else if g {
17386 self.func_g(if smem_rows {
17394 "fa_decode_vec_q_rows"
17395 } else {
17396 fname
17397 })
17398 } else {
17399 self.func(fname)
17400 };
17401 let shmem = if tb512 {
17402 let gk = Self::gkv_on();
17404 let sh =
17405 (8192 + 1024 + 32 * 512 + 32 * 64 + 32 * head_dim * if gk { 1 } else { 2 }) as u32;
17406 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17407 f.set_attribute(
17408 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17409 sh as i32,
17410 )?;
17411 sh
17412 } else if v4 || v3 || smem_rows || fa_v2_on() {
17413 let sh = (if v4 {
17415 11520 + 32 * head_dim * if g { 1 } else { 2 }
17416 } else if v3 {
17417 32 * head_dim * 2
17418 } else {
17419 2 * 32 * head_dim * 2
17420 }) as u32;
17421 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17422 f.set_attribute(
17423 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17424 sh as i32,
17425 )?;
17426 sh
17427 } else {
17428 0
17429 };
17430 for &(r0, t_g, sp_g) in &groups {
17434 let n_splits_g = (base_len + r0 + t_g).div_ceil(sp_g);
17435 let (nspm, spk) = (n_splits_g as i32, sp_g as i32);
17436 let base_i = (base_len + r0) as i32;
17437 let o_len = t_g * n_head * n_splits_g * head_dim;
17438 let ml_len = t_g * n_head * n_splits_g;
17439 let mut part_guard = self.fa_part_pool.lock().unwrap();
17440 if part_guard
17441 .as_ref()
17442 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17443 .unwrap_or(true)
17444 {
17445 let old = part_guard.take();
17456 let (co, cm) = old
17457 .as_ref()
17458 .map(|pp| (pp.0.len(), pp.1.len()))
17459 .unwrap_or((0, 0));
17460 if let Some(old) = old {
17461 self.fa_part_retired.lock().unwrap().push(old);
17462 }
17463 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17464 eprintln!(
17465 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17466 co, o_len, cm, ml_len
17467 );
17468 }
17469 *part_guard = Some((
17470 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17471 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17472 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17473 ));
17474 }
17475 let pg = part_guard.as_mut().unwrap();
17476 self.gpu
17477 .stream()
17478 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17479 self.gpu
17480 .stream()
17481 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17482 self.gpu
17483 .stream()
17484 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17485 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17486 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
17487 let qv = self.view(q, t * n_head * head_dim);
17488 let q_g = qv.slice(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
17489 let cfg = LaunchConfig {
17490 grid_dim: (n_head_kv as u32, n_splits_g as u32, t_g as u32),
17491 block_dim: (32, gqa, 1),
17492 shared_mem_bytes: shmem,
17493 };
17494 {
17495 let __s_b = self.gpu.stream();
17496 let mut b = __s_b.launch_builder(&f);
17497 if tb512 {
17498 let (bd, plus) =
17500 base_dev.expect("hd512 rows twin requires a device base counter");
17501 let plus_g = plus + r0 as i32;
17502 let nr = t_g as i32;
17503 if Self::pdl_on() && Self::pdl_wb_on() {
17504 use cudarc::driver::{DevicePtr, DevicePtrMut};
17506 let s = &self.gpu.stream();
17507 let (pq, _b0) = q_g.device_ptr(s);
17508 let (pk, _b1) = k.device_ptr(s);
17509 let (pv, _b2) = v.device_ptr(s);
17510 let (po, _b3) = part_o.device_ptr_mut(s);
17511 let (pm, _b4) = part_m.device_ptr_mut(s);
17512 let (pl, _b5) = part_l.device_ptr_mut(s);
17513 let (pb, _b6) = bd.device_ptr(s);
17514 let mut ps = [
17515 &pq as *const _ as *mut std::ffi::c_void,
17516 &pk as *const _ as *mut _,
17517 &pv as *const _ as *mut _,
17518 &po as *const _ as *mut _,
17519 &pm as *const _ as *mut _,
17520 &pl as *const _ as *mut _,
17521 &hd as *const _ as *mut _,
17522 &nh as *const _ as *mut _,
17523 &nhkv as *const _ as *mut _,
17524 &pb as *const _ as *mut _,
17525 &plus_g as *const _ as *mut _,
17526 &scale as *const _ as *mut _,
17527 &nspm as *const _ as *mut _,
17528 &spk as *const _ as *mut _,
17529 &ktb as *const _ as *mut _,
17530 &vtb as *const _ as *mut _,
17531 &nr as *const _ as *mut _,
17532 ];
17533 unsafe {
17534 self.launch_pdl_flash(
17535 Self::gkv_on(),
17536 "fa_decode_vec_q_rows_v4_512_tb",
17537 (n_head_kv as u32, n_splits_g as u32, 1),
17538 (32, gqa, 1),
17539 shmem,
17540 &mut ps,
17541 )?;
17542 }
17543 } else {
17544 let cfg_tb = LaunchConfig {
17545 grid_dim: (n_head_kv as u32, n_splits_g as u32, 1),
17546 block_dim: (32, gqa, 1),
17547 shared_mem_bytes: shmem,
17548 };
17549 b.arg(&q_g)
17550 .arg(k)
17551 .arg(v)
17552 .arg(&mut *part_o)
17553 .arg(&mut *part_m)
17554 .arg(&mut *part_l)
17555 .arg(&hd)
17556 .arg(&nh)
17557 .arg(&nhkv)
17558 .arg(bd)
17559 .arg(&plus_g)
17560 .arg(&scale)
17561 .arg(&nspm)
17562 .arg(&spk)
17563 .arg(&ktb)
17564 .arg(&vtb)
17565 .arg(&nr);
17566 unsafe {
17567 b.launch(cfg_tb)?;
17568 }
17569 }
17570 } else if head_dim == 512 {
17571 let (bd, plus) =
17572 base_dev.expect("hd512 rows twin requires a device base counter");
17573 let plus_g = plus + r0 as i32;
17574 b.arg(&q_g)
17575 .arg(k)
17576 .arg(v)
17577 .arg(&mut *part_o)
17578 .arg(&mut *part_m)
17579 .arg(&mut *part_l)
17580 .arg(&hd)
17581 .arg(&nh)
17582 .arg(&nhkv)
17583 .arg(bd)
17584 .arg(&plus_g)
17585 .arg(&scale)
17586 .arg(&nspm)
17587 .arg(&spk)
17588 .arg(&ktb)
17589 .arg(&vtb);
17590 unsafe {
17591 b.launch(cfg)?;
17592 }
17593 } else {
17594 b.arg(&q_g)
17595 .arg(k)
17596 .arg(v)
17597 .arg(&mut *part_o)
17598 .arg(&mut *part_m)
17599 .arg(&mut *part_l)
17600 .arg(&hd)
17601 .arg(&nh)
17602 .arg(&nhkv)
17603 .arg(&base_i)
17604 .arg(&scale)
17605 .arg(&nspm)
17606 .arg(&spk)
17607 .arg(&ktb)
17608 .arg(&vtb);
17609 unsafe {
17610 b.launch(cfg)?;
17611 }
17612 }
17613 }
17614 let cfg2 = LaunchConfig {
17615 grid_dim: (n_head as u32, t_g as u32, 1),
17616 block_dim: (head_dim as u32, 1, 1),
17617 shared_mem_bytes: 0,
17618 };
17619 let mut o_g = o.slice_mut(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
17620 if head_dim == 512 {
17621 let (bd, plus) = base_dev.unwrap();
17624 let plus_g = plus + r0 as i32;
17625 if let Some((oq, od)) = q8_out.as_mut() {
17626 debug_assert!(t == 1, "rows q8 emit is a t=1 decode arm");
17628 if Self::pdl_on() && Self::pdl_wb_on() {
17629 use cudarc::driver::{DevicePtr, DevicePtrMut};
17631 let s = &self.gpu.stream();
17632 let (po, _g0) = part_o.device_ptr(s);
17633 let (pm, _g1) = part_m.device_ptr(s);
17634 let (pl, _g2) = part_l.device_ptr(s);
17635 let (pq, _g3) = oq.device_ptr_mut(s);
17636 let (pd, _g4) = od.device_ptr_mut(s);
17637 let (pb, _g5) = bd.device_ptr(s);
17638 let mut ps = [
17639 &po as *const _ as *mut std::ffi::c_void,
17640 &pm as *const _ as *mut _,
17641 &pl as *const _ as *mut _,
17642 &pq as *const _ as *mut _,
17643 &pd as *const _ as *mut _,
17644 &hd as *const _ as *mut _,
17645 &nh as *const _ as *mut _,
17646 &pb as *const _ as *mut _,
17647 &plus_g as *const _ as *mut _,
17648 &nspm as *const _ as *mut _,
17649 &spk as *const _ as *mut _,
17650 ];
17651 unsafe {
17652 self.launch_pdl_flash(
17653 Self::gkv_on(),
17654 "fa_decode_combine_rows_dc_q8_1",
17655 cfg2.grid_dim,
17656 cfg2.block_dim,
17657 0,
17658 &mut ps,
17659 )?;
17660 }
17661 continue;
17662 }
17663 let fc = self.fa_func("fa_decode_combine_rows_dc_q8_1", head_dim);
17664 let __s_b2 = self.gpu.stream();
17665 let mut b2 = __s_b2.launch_builder(&fc);
17666 b2.arg(&*part_o)
17667 .arg(&*part_m)
17668 .arg(&*part_l)
17669 .arg(&mut **oq)
17670 .arg(&mut **od)
17671 .arg(&hd)
17672 .arg(&nh)
17673 .arg(bd)
17674 .arg(&plus_g)
17675 .arg(&nspm)
17676 .arg(&spk);
17677 unsafe {
17678 b2.launch(cfg2)?;
17679 }
17680 continue;
17681 }
17682 let fc = self.fa_func("fa_decode_combine_rows_dc", head_dim);
17683 let __s_b2 = self.gpu.stream();
17684 let mut b2 = __s_b2.launch_builder(&fc);
17685 b2.arg(&*part_o)
17686 .arg(&*part_m)
17687 .arg(&*part_l)
17688 .arg(&mut o_g)
17689 .arg(&hd)
17690 .arg(&nh)
17691 .arg(bd)
17692 .arg(&plus_g)
17693 .arg(&nspm)
17694 .arg(&spk);
17695 unsafe {
17696 b2.launch(cfg2)?;
17697 }
17698 } else {
17699 assert!(
17702 q8_out.is_none(),
17703 "rows q8 emit requires the hd512 dc combine"
17704 );
17705 let fc = self.func("fa_decode_combine_rows");
17706 let __s_b2 = self.gpu.stream();
17707 let mut b2 = __s_b2.launch_builder(&fc);
17708 b2.arg(&*part_o)
17709 .arg(&*part_m)
17710 .arg(&*part_l)
17711 .arg(&mut o_g)
17712 .arg(&hd)
17713 .arg(&nh)
17714 .arg(&base_i)
17715 .arg(&nspm)
17716 .arg(&spk);
17717 unsafe {
17718 b2.launch(cfg2)?;
17719 }
17720 }
17721 }
17722 Ok(())
17723 }
17724
17725 #[allow(clippy::too_many_arguments)]
17729 pub fn fa_decode_rows_w(
17730 &self,
17731 q: &CudaSlice<f32>,
17732 k: &cudarc::driver::CudaView<u8>,
17733 v: &cudarc::driver::CudaView<u8>,
17734 o: &mut CudaSlice<f32>,
17735 head_dim: usize,
17736 n_head: usize,
17737 n_head_kv: usize,
17738 base_dev: &CudaSlice<i32>,
17739 base_plus: i32,
17740 t: usize,
17741 scale: f32,
17742 window: usize,
17743 k_tok_bytes: usize,
17744 v_tok_bytes: usize,
17745 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
17746 ) -> Result<(), Box<dyn std::error::Error>> {
17747 debug_assert!(head_dim == 256);
17752 let sp = {
17760 static SPW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17761 let v = *SPW.get_or_init(|| {
17762 std::env::var("MEMRA_FA_SPW")
17763 .ok()
17764 .and_then(|x| x.parse().ok())
17765 .unwrap_or(0)
17766 });
17767 if v >= 8 {
17768 v
17769 } else {
17770 FA_SPW_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
17771 }
17772 };
17773 let n_splits_max = (window + sp - 1) / sp;
17774 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17775 let (nspm, spk, wini) = (n_splits_max as i32, sp as i32, window as i32);
17776 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17777 let gqa = (n_head / n_head_kv).max(1) as u32;
17778 let o_len = t * n_head * n_splits_max * head_dim;
17779 let ml_len = t * n_head * n_splits_max;
17780 let mut part_guard = self.fa_part_pool.lock().unwrap();
17781 if part_guard
17782 .as_ref()
17783 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17784 .unwrap_or(true)
17785 {
17786 let old = part_guard.take();
17797 let (co, cm) = old
17798 .as_ref()
17799 .map(|pp| (pp.0.len(), pp.1.len()))
17800 .unwrap_or((0, 0));
17801 if let Some(old) = old {
17802 self.fa_part_retired.lock().unwrap().push(old);
17803 }
17804 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17805 eprintln!(
17806 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17807 co, o_len, cm, ml_len
17808 );
17809 }
17810 *part_guard = Some((
17811 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17812 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17813 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17814 ));
17815 }
17816 let pg = part_guard.as_mut().unwrap();
17817 self.gpu
17818 .stream()
17819 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17820 self.gpu
17821 .stream()
17822 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17823 self.gpu
17824 .stream()
17825 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17826 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17827 static SMEM_TKV_W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17833 let smem_tkv = *SMEM_TKV_W.get_or_init(|| {
17834 std::env::var("MEMRA_FA_SMEM_TKV")
17835 .ok()
17836 .and_then(|v| v.parse().ok())
17837 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
17838 });
17839 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17845 let wg = Self::wkv_on();
17850 let sp2 =
17853 gqa <= 4 && fa_v4_at(window) && std::env::var("MEMRA_FA_SPW2").as_deref() != Ok("0");
17854 if sp2 {
17855 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
17856 if Self::pdl_on() && Self::pdl_wb_on() {
17857 use cudarc::driver::{DevicePtr, DevicePtrMut};
17859 let s = &self.gpu.stream();
17860 let (pq, _b0) = q.device_ptr(s);
17861 let (pk, _b1) = k.device_ptr(s);
17862 let (pv, _b2) = v.device_ptr(s);
17863 let (po, _b3) = part_o.device_ptr_mut(s);
17864 let (pm, _b4) = part_m.device_ptr_mut(s);
17865 let (pl, _b5) = part_l.device_ptr_mut(s);
17866 let (pb, _b6) = base_dev.device_ptr(s);
17867 let mut ps = [
17868 &pq as *const _ as *mut std::ffi::c_void,
17869 &pk as *const _ as *mut _,
17870 &pv as *const _ as *mut _,
17871 &po as *const _ as *mut _,
17872 &pm as *const _ as *mut _,
17873 &pl as *const _ as *mut _,
17874 &hd as *const _ as *mut _,
17875 &nh as *const _ as *mut _,
17876 &nhkv as *const _ as *mut _,
17877 &pb as *const _ as *mut _,
17878 &base_plus as *const _ as *mut _,
17879 &scale as *const _ as *mut _,
17880 &nspm as *const _ as *mut _,
17881 &spk as *const _ as *mut _,
17882 &ktb as *const _ as *mut _,
17883 &vtb as *const _ as *mut _,
17884 &wini as *const _ as *mut _,
17885 ];
17886 unsafe {
17887 self.launch_pdl_flash(
17888 wg,
17889 "fa_decode_vec_q_rows_v4_w_sp",
17890 (n_head_kv as u32, n_splits_max as u32, t as u32),
17891 (32, gqa + 1, 1),
17892 sh,
17893 &mut ps,
17894 )?;
17895 }
17896 } else {
17897 let f = if wg {
17898 self.func_g("fa_decode_vec_q_rows_v4_w_sp")
17899 } else {
17900 self.func("fa_decode_vec_q_rows_v4_w_sp")
17901 };
17902 f.set_attribute(
17903 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17904 sh as i32,
17905 )?;
17906 let cfg = LaunchConfig {
17907 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
17908 block_dim: (32, gqa + 1, 1),
17909 shared_mem_bytes: sh,
17910 };
17911 let __s_b = self.gpu.stream();
17912 let mut b = __s_b.launch_builder(&f);
17913 b.arg(q)
17914 .arg(k)
17915 .arg(v)
17916 .arg(&mut *part_o)
17917 .arg(&mut *part_m)
17918 .arg(&mut *part_l)
17919 .arg(&hd)
17920 .arg(&nh)
17921 .arg(&nhkv)
17922 .arg(base_dev)
17923 .arg(&base_plus)
17924 .arg(&scale)
17925 .arg(&nspm)
17926 .arg(&spk)
17927 .arg(&ktb)
17928 .arg(&vtb)
17929 .arg(&wini);
17930 unsafe {
17931 b.launch(cfg)?;
17932 }
17933 }
17934 } else {
17935 if fa_v4_at(window) && Self::pdl_on() && Self::pdl_wb_on() {
17936 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
17938 use cudarc::driver::{DevicePtr, DevicePtrMut};
17939 let s = &self.gpu.stream();
17940 let (pq, _b0) = q.device_ptr(s);
17941 let (pk, _b1) = k.device_ptr(s);
17942 let (pv, _b2) = v.device_ptr(s);
17943 let (po, _b3) = part_o.device_ptr_mut(s);
17944 let (pm, _b4) = part_m.device_ptr_mut(s);
17945 let (pl, _b5) = part_l.device_ptr_mut(s);
17946 let (pb, _b6) = base_dev.device_ptr(s);
17947 let mut ps = [
17948 &pq as *const _ as *mut std::ffi::c_void,
17949 &pk as *const _ as *mut _,
17950 &pv as *const _ as *mut _,
17951 &po as *const _ as *mut _,
17952 &pm as *const _ as *mut _,
17953 &pl as *const _ as *mut _,
17954 &hd as *const _ as *mut _,
17955 &nh as *const _ as *mut _,
17956 &nhkv as *const _ as *mut _,
17957 &pb as *const _ as *mut _,
17958 &base_plus as *const _ as *mut _,
17959 &scale as *const _ as *mut _,
17960 &nspm as *const _ as *mut _,
17961 &spk as *const _ as *mut _,
17962 &ktb as *const _ as *mut _,
17963 &vtb as *const _ as *mut _,
17964 &wini as *const _ as *mut _,
17965 ];
17966 unsafe {
17967 self.launch_pdl_flash(
17968 wg,
17969 "fa_decode_vec_q_rows_v4_w",
17970 (n_head_kv as u32, n_splits_max as u32, t as u32),
17971 (32, gqa, 1),
17972 sh,
17973 &mut ps,
17974 )?;
17975 }
17976 } else {
17977 let pick = |name: &str| {
17978 if wg {
17979 self.func_g(name)
17980 } else {
17981 self.func(name)
17982 }
17983 };
17984 let (f, sh) = if fa_v4_at(window) {
17985 let f = pick("fa_decode_vec_q_rows_v4_w");
17986 (f, (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32)
17987 } else if smem_tkv > 0 && window >= smem_tkv {
17988 (
17991 pick("fa_decode_vec_q_rows_smem_w"),
17992 (2 * 32 * head_dim * 2) as u32,
17993 )
17994 } else {
17995 (pick("fa_decode_vec_q_rows_reg_w"), 0u32)
17996 };
17997 f.set_attribute(
17998 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17999 sh as i32,
18000 )?;
18001 let cfg = LaunchConfig {
18002 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18003 block_dim: (32, gqa, 1),
18004 shared_mem_bytes: sh,
18005 };
18006 let __s_b = self.gpu.stream();
18007 let mut b = __s_b.launch_builder(&f);
18008 b.arg(q)
18009 .arg(k)
18010 .arg(v)
18011 .arg(&mut *part_o)
18012 .arg(&mut *part_m)
18013 .arg(&mut *part_l)
18014 .arg(&hd)
18015 .arg(&nh)
18016 .arg(&nhkv)
18017 .arg(base_dev)
18018 .arg(&base_plus)
18019 .arg(&scale)
18020 .arg(&nspm)
18021 .arg(&spk)
18022 .arg(&ktb)
18023 .arg(&vtb)
18024 .arg(&wini);
18025 unsafe {
18026 b.launch(cfg)?;
18027 }
18028 }
18029 }
18030 let cfg2 = LaunchConfig {
18031 grid_dim: (n_head as u32, t as u32, 1),
18032 block_dim: (head_dim as u32, 1, 1),
18033 shared_mem_bytes: 0,
18034 };
18035 if let Some((oq, od)) = q8_out {
18036 if Self::pdl_on() && Self::pdl_wb_on() {
18039 use cudarc::driver::{DevicePtr, DevicePtrMut};
18041 let s = &self.gpu.stream();
18042 let (po, _g0) = part_o.device_ptr(s);
18043 let (pm, _g1) = part_m.device_ptr(s);
18044 let (pl, _g2) = part_l.device_ptr(s);
18045 let (pq, _g3) = oq.device_ptr_mut(s);
18046 let (pd, _g4) = od.device_ptr_mut(s);
18047 let mut ps = [
18048 &po as *const _ as *mut std::ffi::c_void,
18049 &pm as *const _ as *mut _,
18050 &pl as *const _ as *mut _,
18051 &pq as *const _ as *mut _,
18052 &pd as *const _ as *mut _,
18053 &hd as *const _ as *mut _,
18054 &nh as *const _ as *mut _,
18055 &nspm as *const _ as *mut _,
18056 &spk as *const _ as *mut _,
18057 &wini as *const _ as *mut _,
18058 ];
18059 unsafe {
18060 self.launch_pdl_flash(
18061 wg,
18062 "fa_decode_combine_rows_w_q8_1",
18063 cfg2.grid_dim,
18064 cfg2.block_dim,
18065 0,
18066 &mut ps,
18067 )?;
18068 }
18069 return Ok(());
18070 }
18071 let fc = if wg {
18072 self.func_g("fa_decode_combine_rows_w_q8_1")
18073 } else {
18074 self.func("fa_decode_combine_rows_w_q8_1")
18075 };
18076 let __s_b2 = self.gpu.stream();
18077 let mut b2 = __s_b2.launch_builder(&fc);
18078 b2.arg(&*part_o)
18079 .arg(&*part_m)
18080 .arg(&*part_l)
18081 .arg(oq)
18082 .arg(od)
18083 .arg(&hd)
18084 .arg(&nh)
18085 .arg(&nspm)
18086 .arg(&spk)
18087 .arg(&wini);
18088 unsafe {
18089 b2.launch(cfg2)?;
18090 }
18091 return Ok(());
18092 }
18093 let fc = if wg {
18094 self.func_g("fa_decode_combine_rows_w")
18095 } else {
18096 self.func("fa_decode_combine_rows_w")
18097 };
18098 let __s_b2 = self.gpu.stream();
18099 let mut b2 = __s_b2.launch_builder(&fc);
18100 b2.arg(&*part_o)
18101 .arg(&*part_m)
18102 .arg(&*part_l)
18103 .arg(o)
18104 .arg(&hd)
18105 .arg(&nh)
18106 .arg(&nspm)
18107 .arg(&spk)
18108 .arg(&wini);
18109 unsafe {
18110 b2.launch(cfg2)?;
18111 }
18112 Ok(())
18113 }
18114
18115 #[allow(clippy::too_many_arguments)]
18121 pub fn fa_decode_rows_dc(
18122 &self,
18123 q: &CudaSlice<f32>,
18124 k: &cudarc::driver::CudaView<u8>,
18125 v: &cudarc::driver::CudaView<u8>,
18126 o: &mut CudaSlice<f32>,
18127 head_dim: usize,
18128 n_head: usize,
18129 n_head_kv: usize,
18130 base_dev: &CudaSlice<i32>,
18131 t_kv_upper: usize,
18132 t: usize,
18133 scale: f32,
18134 k_tok_bytes: usize,
18135 v_tok_bytes: usize,
18136 base_plus: i32,
18137 g: bool,
18138 ) -> Result<(), Box<dyn std::error::Error>> {
18139 let v4 = head_dim == 256 && fa_v4_at(t_kv_upper);
18140 assert!(
18141 v4 || fa_v3_active(head_dim),
18142 "stream fa rows requires the v3 or v4 lane"
18143 );
18144 assert!(v4 || base_plus == 0, "v3_dc kernel takes no plus arg");
18145 if v4 {
18146 let sp = fa_split_keys(t_kv_upper, n_head_kv);
18147 let n_splits_max = (t_kv_upper + sp - 1) / sp;
18148 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18149 let (nspm, spk) = (n_splits_max as i32, sp as i32);
18150 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18151 let gqa = (n_head / n_head_kv).max(1) as u32;
18152 let o_len = t * n_head * n_splits_max * head_dim;
18153 let ml_len = t * n_head * n_splits_max;
18154 let mut part_guard = self.fa_part_pool.lock().unwrap();
18155 if part_guard
18156 .as_ref()
18157 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18158 .unwrap_or(true)
18159 {
18160 let old = part_guard.take();
18171 let (co, cm) = old
18172 .as_ref()
18173 .map(|pp| (pp.0.len(), pp.1.len()))
18174 .unwrap_or((0, 0));
18175 if let Some(old) = old {
18176 self.fa_part_retired.lock().unwrap().push(old);
18177 }
18178 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18179 eprintln!(
18180 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18181 co, o_len, cm, ml_len
18182 );
18183 }
18184 *part_guard = Some((
18185 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18186 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18187 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18188 ));
18189 }
18190 let pg = part_guard.as_mut().unwrap();
18191 self.gpu
18192 .stream()
18193 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18194 self.gpu
18195 .stream()
18196 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18197 self.gpu
18198 .stream()
18199 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18200 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18201 let f = if g {
18202 self.func_g("fa_decode_vec_q_rows_v4_dc")
18203 } else {
18204 self.func("fa_decode_vec_q_rows_v4_dc")
18205 };
18206 let sh = (11520 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
18207 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18208 f.set_attribute(
18209 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18210 sh as i32,
18211 )?;
18212 let cfg = LaunchConfig {
18213 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18214 block_dim: (32, gqa, 1),
18215 shared_mem_bytes: sh,
18216 };
18217 let __s_b = self.gpu.stream();
18218 let mut b = __s_b.launch_builder(&f);
18219 b.arg(q)
18220 .arg(k)
18221 .arg(v)
18222 .arg(&mut *part_o)
18223 .arg(&mut *part_m)
18224 .arg(&mut *part_l)
18225 .arg(&hd)
18226 .arg(&nh)
18227 .arg(&nhkv)
18228 .arg(base_dev)
18229 .arg(&base_plus)
18230 .arg(&scale)
18231 .arg(&nspm)
18232 .arg(&spk)
18233 .arg(&ktb)
18234 .arg(&vtb);
18235 unsafe {
18236 b.launch(cfg)?;
18237 }
18238 let fc = self.func("fa_decode_combine_rows_dc");
18239 let cfg2 = LaunchConfig {
18240 grid_dim: (n_head as u32, t as u32, 1),
18241 block_dim: (head_dim as u32, 1, 1),
18242 shared_mem_bytes: 0,
18243 };
18244 let __s_b2 = self.gpu.stream();
18245 let mut b2 = __s_b2.launch_builder(&fc);
18246 b2.arg(&*part_o)
18247 .arg(&*part_m)
18248 .arg(&*part_l)
18249 .arg(o)
18250 .arg(&hd)
18251 .arg(&nh)
18252 .arg(base_dev)
18253 .arg(&base_plus)
18254 .arg(&nspm)
18255 .arg(&spk);
18256 unsafe {
18257 b2.launch(cfg2)?;
18258 }
18259 return Ok(());
18260 }
18261 let sp = fa_split_keys(t_kv_upper, n_head_kv);
18262 let n_splits_max = (t_kv_upper + sp - 1) / sp;
18263 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18264 let (nspm, spk) = (n_splits_max as i32, sp as i32);
18265 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18266 let gqa = (n_head / n_head_kv).max(1) as u32;
18267 let o_len = t * n_head * n_splits_max * head_dim;
18268 let ml_len = t * n_head * n_splits_max;
18269 let mut part_guard = self.fa_part_pool.lock().unwrap();
18270 if part_guard
18271 .as_ref()
18272 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18273 .unwrap_or(true)
18274 {
18275 let old = part_guard.take();
18286 let (co, cm) = old
18287 .as_ref()
18288 .map(|pp| (pp.0.len(), pp.1.len()))
18289 .unwrap_or((0, 0));
18290 if let Some(old) = old {
18291 self.fa_part_retired.lock().unwrap().push(old);
18292 }
18293 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18294 eprintln!(
18295 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18296 co, o_len, cm, ml_len
18297 );
18298 }
18299 *part_guard = Some((
18300 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18301 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18302 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18303 ));
18304 }
18305 let pg = part_guard.as_mut().unwrap();
18306 self.gpu
18307 .stream()
18308 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18309 self.gpu
18310 .stream()
18311 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18312 self.gpu
18313 .stream()
18314 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18315 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18316 let f = self.func("fa_decode_vec_q_rows_v3_dc");
18317 let sh = (32 * head_dim * 2) as u32;
18318 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18319 f.set_attribute(
18320 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18321 sh as i32,
18322 )?;
18323 let cfg = LaunchConfig {
18324 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18325 block_dim: (32, gqa, 1),
18326 shared_mem_bytes: sh,
18327 };
18328 let __s_b = self.gpu.stream();
18329 let mut b = __s_b.launch_builder(&f);
18330 b.arg(q)
18331 .arg(k)
18332 .arg(v)
18333 .arg(&mut *part_o)
18334 .arg(&mut *part_m)
18335 .arg(&mut *part_l)
18336 .arg(&hd)
18337 .arg(&nh)
18338 .arg(&nhkv)
18339 .arg(base_dev)
18340 .arg(&scale)
18341 .arg(&nspm)
18342 .arg(&spk)
18343 .arg(&ktb)
18344 .arg(&vtb);
18345 unsafe {
18346 b.launch(cfg)?;
18347 }
18348 let fc = self.func("fa_decode_combine_rows_dc");
18349 let cfg2 = LaunchConfig {
18350 grid_dim: (n_head as u32, t as u32, 1),
18351 block_dim: (head_dim as u32, 1, 1),
18352 shared_mem_bytes: 0,
18353 };
18354 let plus0 = 0i32;
18355 let __s_b2 = self.gpu.stream();
18356 let mut b2 = __s_b2.launch_builder(&fc);
18357 b2.arg(&*part_o)
18358 .arg(&*part_m)
18359 .arg(&*part_l)
18360 .arg(o)
18361 .arg(&hd)
18362 .arg(&nh)
18363 .arg(base_dev)
18364 .arg(&plus0)
18365 .arg(&nspm)
18366 .arg(&spk);
18367 unsafe {
18368 b2.launch(cfg2)?;
18369 }
18370 Ok(())
18371 }
18372
18373 pub fn fa_decode_dc(
18384 &self,
18385 q: &CudaSlice<f32>,
18386 k: &cudarc::driver::CudaView<u8>,
18387 v: &cudarc::driver::CudaView<u8>,
18388 o: &mut CudaSlice<f32>,
18389 head_dim: usize,
18390 n_head: usize,
18391 n_head_kv: usize,
18392 t_kv_dev: &CudaSlice<i32>,
18393 bucket_max: usize,
18394 scale: f32,
18395 k_tok_bytes: usize,
18396 v_tok_bytes: usize,
18397 g: bool,
18398 ) -> Result<(), Box<dyn std::error::Error>> {
18399 self.fa_decode_dc_q8(
18400 q,
18401 k,
18402 v,
18403 o,
18404 head_dim,
18405 n_head,
18406 n_head_kv,
18407 t_kv_dev,
18408 bucket_max,
18409 scale,
18410 k_tok_bytes,
18411 v_tok_bytes,
18412 g,
18413 None,
18414 )
18415 }
18416
18417 #[allow(clippy::too_many_arguments)]
18420 pub fn fa_decode_dc_q8(
18421 &self,
18422 q: &CudaSlice<f32>,
18423 k: &cudarc::driver::CudaView<u8>,
18424 v: &cudarc::driver::CudaView<u8>,
18425 o: &mut CudaSlice<f32>,
18426 head_dim: usize,
18427 n_head: usize,
18428 n_head_kv: usize,
18429 t_kv_dev: &CudaSlice<i32>,
18430 bucket_max: usize,
18431 scale: f32,
18432 k_tok_bytes: usize,
18433 v_tok_bytes: usize,
18434 g: bool,
18435 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
18436 ) -> Result<(), Box<dyn std::error::Error>> {
18437 let mut fa_vec =
18445 std::env::var("MEMRA_NO_FA_VEC").is_err() && bucket_max >= fa_vec_min_tkv();
18446 if g && head_dim == 256 && !fa_v4_at(bucket_max) {
18447 fa_vec = false;
18448 } let sp = fa_split_keys(bucket_max, n_head_kv);
18450 let n_splits = if fa_vec {
18451 ((bucket_max + sp - 1) / sp).max(1)
18452 } else {
18453 ((bucket_max + 255) / 256).max(1)
18454 };
18455 let o_len = n_head * n_splits * head_dim;
18456 let ml_len = n_head * n_splits;
18457 let mut part_guard = self.fa_part_pool.lock().unwrap();
18458 if part_guard
18459 .as_ref()
18460 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18461 .unwrap_or(true)
18462 {
18463 let old = part_guard.take();
18474 let (co, cm) = old
18475 .as_ref()
18476 .map(|pp| (pp.0.len(), pp.1.len()))
18477 .unwrap_or((0, 0));
18478 if let Some(old) = old {
18479 self.fa_part_retired.lock().unwrap().push(old);
18480 }
18481 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18482 eprintln!(
18483 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18484 co, o_len, cm, ml_len
18485 );
18486 }
18487 *part_guard = Some((
18488 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18489 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18490 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18491 ));
18492 }
18493 let pg = part_guard.as_mut().unwrap();
18494 self.gpu
18495 .stream()
18496 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18497 self.gpu
18498 .stream()
18499 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18500 self.gpu
18501 .stream()
18502 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18503 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18504 let (hd, nh, nhkv, nsp) = (
18505 head_dim as i32,
18506 n_head as i32,
18507 n_head_kv as i32,
18508 n_splits as i32,
18509 );
18510 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18511 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
18512 let deep = fa_vec
18515 && head_dim == 256
18516 && fa_v4_at(bucket_max)
18517 && !g
18518 && fa_deep_at(bucket_max)
18519 && !matches!(fa_v4_mode(), "noB3" | "stage");
18520 let (f, cfg) = if fa_vec
18521 && head_dim == 512
18522 && bucket_max >= {
18523 static FA512_MIN_DC: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18524 *FA512_MIN_DC.get_or_init(|| {
18525 std::env::var("MEMRA_FA512_MIN")
18526 .ok()
18527 .and_then(|v| v.parse().ok())
18528 .unwrap_or(512)
18529 })
18530 } {
18531 let gqa = (n_head / n_head_kv).max(1) as u32;
18533 (
18534 self.fa_func("fa_decode_vec_q_dpl16_dc", head_dim),
18535 LaunchConfig {
18536 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18537 block_dim: (32, gqa, 1),
18538 shared_mem_bytes: 0,
18539 },
18540 )
18541 } else if fa_vec && head_dim == 512 {
18542 let q_view = q.as_view();
18545 let mut o_view = o.as_view_mut();
18546 return self.fa_decode_scalar_unified(
18547 &q_view,
18548 k,
18549 v,
18550 &mut o_view,
18551 head_dim,
18552 n_head,
18553 n_head_kv,
18554 0,
18555 Some(t_kv_dev),
18556 scale,
18557 n_splits,
18558 sp,
18559 k_tok_bytes,
18560 v_tok_bytes,
18561 g,
18562 &mut *part_o,
18563 &mut *part_m,
18564 &mut *part_l,
18565 q8_out,
18566 );
18567 } else if fa_vec && head_dim == 256 && fa_v4_at(bucket_max) {
18568 let gqa = (n_head / n_head_kv).max(1) as u32;
18571 let fv = if g {
18572 self.func_g("fa_decode_vec_q_v4_dc")
18573 } else if deep {
18574 self.func("fa_decode_vec_q_v4_deep_dc")
18575 } else {
18576 self.func("fa_decode_vec_q_v4_dc")
18577 };
18578 let shmem =
18579 (if deep { 12160 } else { 11520 } + 32 * head_dim * if g { 1 } else { 2 }) as u32;
18580 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18581 fv.set_attribute(
18582 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18583 shmem as i32,
18584 )?;
18585 (
18586 fv,
18587 LaunchConfig {
18588 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18589 block_dim: (32, gqa, 1),
18590 shared_mem_bytes: shmem,
18591 },
18592 )
18593 } else if fa_vec && fa_v3_active(head_dim) {
18594 let gqa = (n_head / n_head_kv).max(1) as u32;
18597 let fv = if g {
18598 self.func_g("fa_decode_vec_q_v3_dc")
18599 } else {
18600 self.func("fa_decode_vec_q_v3_dc")
18601 };
18602 let shmem = (32 * head_dim * 2) as u32; (
18604 fv,
18605 LaunchConfig {
18606 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18607 block_dim: (32, gqa, 1),
18608 shared_mem_bytes: shmem,
18609 },
18610 )
18611 } else if fa_vec && fa_v2_on() {
18612 let gqa = (n_head / n_head_kv).max(1) as u32;
18616 let fv = if g {
18617 self.func_g("fa_decode_vec_q_v2_dc")
18618 } else {
18619 self.func("fa_decode_vec_q_v2_dc")
18620 };
18621 let shmem = (2 * 32 * head_dim * 2) as u32; (
18623 fv,
18624 LaunchConfig {
18625 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18626 block_dim: (32, gqa, 1),
18627 shared_mem_bytes: shmem,
18628 },
18629 )
18630 } else if fa_vec {
18631 let gqa = (n_head / n_head_kv).max(1) as u32;
18632 let fv = if g {
18634 self.func_g("fa_decode_vec_q_dc")
18635 } else {
18636 self.func("fa_decode_vec_q_dc")
18637 };
18638 (
18639 fv,
18640 LaunchConfig {
18641 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18642 block_dim: (32, gqa, 1),
18643 shared_mem_bytes: 0,
18644 },
18645 )
18646 } else {
18647 let q_view = q.as_view();
18648 let mut o_view = o.as_view_mut();
18649 return self.fa_decode_scalar_unified(
18650 &q_view,
18651 k,
18652 v,
18653 &mut o_view,
18654 head_dim,
18655 n_head,
18656 n_head_kv,
18657 0,
18658 Some(t_kv_dev),
18659 scale,
18660 n_splits,
18661 if fa_vec { sp } else { 256 },
18662 k_tok_bytes,
18663 v_tok_bytes,
18664 g,
18665 &mut *part_o,
18666 &mut *part_m,
18667 &mut *part_l,
18668 q8_out,
18669 );
18670 };
18671 let ski = sp as i32; let __s_b = self.gpu.stream();
18673 let mut b = __s_b.launch_builder(&f);
18674 b.arg(q)
18675 .arg(k)
18676 .arg(v)
18677 .arg(&mut *part_o)
18678 .arg(&mut *part_m)
18679 .arg(&mut *part_l)
18680 .arg(&hd)
18681 .arg(&nh)
18682 .arg(&nhkv)
18683 .arg(t_kv_dev)
18684 .arg(&scale)
18685 .arg(&nsp)
18686 .arg(&ski)
18687 .arg(&ktb)
18688 .arg(&vtb);
18689 unsafe {
18690 b.launch(cfg)?;
18691 }
18692 let cfg2 = LaunchConfig {
18693 grid_dim: (n_head as u32, 1, 1),
18694 block_dim: (head_dim as u32, 1, 1),
18695 shared_mem_bytes: 0,
18696 };
18697 if let Some((oq, od)) = q8_out {
18698 let fc = if g {
18699 self.func_g("fa_decode_combine_q8_1")
18700 } else {
18701 self.fa_func("fa_decode_combine_q8_1", head_dim)
18702 };
18703 let __s_b2 = self.gpu.stream();
18704 let mut b2 = __s_b2.launch_builder(&fc);
18705 b2.arg(&*part_o)
18706 .arg(&*part_m)
18707 .arg(&*part_l)
18708 .arg(oq)
18709 .arg(od)
18710 .arg(&hd)
18711 .arg(&nh)
18712 .arg(&nsp);
18713 unsafe {
18714 b2.launch(cfg2)?;
18715 }
18716 return Ok(());
18717 }
18718 let fc = if g {
18719 self.func_g("fa_decode_combine_f32")
18720 } else {
18721 self.fa_func("fa_decode_combine_f32", head_dim)
18722 };
18723 let __s_b2 = self.gpu.stream();
18724 let mut b2 = __s_b2.launch_builder(&fc);
18725 b2.arg(&*part_o)
18726 .arg(&*part_m)
18727 .arg(&*part_l)
18728 .arg(o)
18729 .arg(&hd)
18730 .arg(&nh)
18731 .arg(&nsp);
18732 unsafe {
18733 b2.launch(cfg2)?;
18734 }
18735 Ok(())
18736 }
18737
18738 pub fn fa_geom_eager(
18744 &self,
18745 t_kv: usize,
18746 head_dim: usize,
18747 n_head_kv: usize,
18748 g: bool,
18749 ) -> (bool, usize) {
18750 let fa_ok = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
18754 let vec512 = fa_ok && head_dim == 512 && t_kv >= fa512_min_tkv();
18760 let mut fa_vec = vec512 || (fa_ok && head_dim <= 256 && head_dim % 32 == 0);
18761 if g && head_dim == 256 && !fa_v4_at(t_kv) {
18767 fa_vec = false;
18768 }
18769 let sp = fa_split_keys(t_kv, n_head_kv);
18770 let n_splits = if fa_vec {
18771 ((t_kv + sp - 1) / sp).max(1)
18772 } else {
18773 ((t_kv + 255) / 256).max(1)
18774 };
18775 (fa_vec, n_splits)
18776 }
18777
18778 pub fn fa_bucket_key(
18784 &self,
18785 t_kv: usize,
18786 head_dim: usize,
18787 n_head_kv: usize,
18788 g: bool,
18789 ) -> (bool, usize) {
18790 self.fa_geom_eager(t_kv, head_dim, n_head_kv, g)
18791 }
18792
18793 pub fn capture_graph_retained<F>(
18805 &self,
18806 step: F,
18807 ) -> Result<
18808 (
18809 cudarc::driver::CudaGraph,
18810 Vec<Box<dyn std::any::Any + Send>>,
18811 ),
18812 Box<dyn std::error::Error>,
18813 >
18814 where
18815 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
18816 {
18817 use cudarc::driver::sys::CUgraphInstantiate_flags;
18818 self.capture_graph_retained_flags(
18819 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
18820 step,
18821 )
18822 }
18823
18824 pub fn capture_graph_retained_flags<F>(
18829 &self,
18830 flags: cudarc::driver::sys::CUgraphInstantiate_flags,
18831 mut step: F,
18832 ) -> Result<
18833 (
18834 cudarc::driver::CudaGraph,
18835 Vec<Box<dyn std::any::Any + Send>>,
18836 ),
18837 Box<dyn std::error::Error>,
18838 >
18839 where
18840 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
18841 {
18842 use cudarc::driver::sys::CUstreamCaptureMode;
18843 self.capture_keep.lock().unwrap().clear();
18851 let was_tracking = self.gpu.ctx.is_event_tracking();
18852 if was_tracking {
18853 unsafe {
18854 self.gpu.ctx.disable_event_tracking();
18855 }
18856 }
18857 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
18858 self.capture_keep_on
18859 .store(true, std::sync::atomic::Ordering::Relaxed);
18860 let w = (|| {
18861 step(self)?;
18862 step(self)
18863 })();
18864 self.capture_keep_on
18865 .store(false, std::sync::atomic::Ordering::Relaxed);
18866 w?;
18867 self.gpu.stream().synchronize()?;
18868 self.gpu
18869 .stream()
18870 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
18871 let r = step(self);
18872 let g = self.gpu.stream().end_capture(flags);
18873 r?;
18874 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
18875 graph.upload()?;
18876 Ok(graph)
18877 };
18878 let result = run();
18879 self.capture_keep_on
18880 .store(false, std::sync::atomic::Ordering::Relaxed);
18881 if was_tracking {
18882 unsafe {
18883 self.gpu.ctx.enable_event_tracking();
18884 }
18885 }
18886 let keeper = std::mem::take(&mut *self.capture_keep.lock().unwrap());
18887 Ok((result?, keeper))
18888 }
18889
18890 pub fn capture_graph<F>(
18891 &self,
18892 mut step: F,
18893 ) -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>>
18894 where
18895 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
18896 {
18897 use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
18898 let was_tracking = self.gpu.ctx.is_event_tracking();
18906 if was_tracking {
18907 unsafe {
18908 self.gpu.ctx.disable_event_tracking();
18909 }
18910 }
18911 let iflag = {
18918 static F: std::sync::OnceLock<CUgraphInstantiate_flags> = std::sync::OnceLock::new();
18919 *F.get_or_init(|| match std::env::var("MEMRA_GRAPH_IFLAG").as_deref() {
18920 Ok("upload") => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_UPLOAD,
18923 Ok("priority") => {
18924 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY
18925 }
18926 _ => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
18927 })
18928 };
18929 let ct = {
18936 static T: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
18937 *T.get_or_init(|| std::env::var("MEMRA_GRAPH_CAPTIME").as_deref() == Ok("1"))
18938 };
18939 let warmups = {
18962 static W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18963 *W.get_or_init(|| {
18964 std::env::var("MEMRA_GRAPH_WARMUPS")
18965 .ok()
18966 .and_then(|v| v.parse().ok())
18967 .filter(|n| *n >= 1)
18968 .unwrap_or(1)
18969 })
18970 };
18971 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
18972 let t_w = std::time::Instant::now();
18973 for _ in 0..warmups {
18975 step(self)?;
18976 }
18977 self.gpu.stream().synchronize()?;
18978 let ms_warm = t_w.elapsed().as_secs_f64() * 1e3;
18979 let t_c = std::time::Instant::now();
18981 self.gpu
18982 .stream()
18983 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
18984 let r = step(self);
18987 let ms_body = t_c.elapsed().as_secs_f64() * 1e3;
18988 let t_i = std::time::Instant::now();
18989 let g = self.gpu.stream().end_capture(iflag);
18990 let ms_inst = t_i.elapsed().as_secs_f64() * 1e3;
18991 r?;
18992 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
18993 let t_u = std::time::Instant::now();
18994 graph.upload()?;
18995 if ct {
18996 println!(
18997 "[graph-captime] warmup2x {ms_warm:.2} ms capture-body {ms_body:.2} ms \
18998 instantiate {ms_inst:.2} ms upload {:.2} ms",
18999 t_u.elapsed().as_secs_f64() * 1e3
19000 );
19001 }
19002 Ok(graph)
19003 };
19004 let result = run();
19005 if was_tracking {
19006 unsafe {
19007 self.gpu.ctx.enable_event_tracking();
19008 }
19009 }
19010 result
19011 }
19012
19013 pub fn gdn_scan_s128_view(
19015 &self,
19016 q: &CudaSlice<f32>,
19017 k: &CudaSlice<f32>,
19018 v: &CudaSlice<f32>,
19019 g: &CudaSlice<f32>,
19020 beta: &CudaSlice<f32>,
19021 state_in: &cudarc::driver::CudaView<f32>,
19022 state_out: &mut cudarc::driver::CudaViewMut<f32>,
19023 o: &mut CudaSlice<f32>,
19024 n_head: usize,
19025 t: usize,
19026 scale: f32,
19027 ) -> Result<(), Box<dyn std::error::Error>> {
19028 let f = self.func("gdn_scan_s128");
19029 const S_V: u32 = 128;
19030 const WARP: u32 = 32;
19031 const COLS: u32 = 4;
19032 let cfg = LaunchConfig {
19033 grid_dim: (n_head as u32, 1, S_V / COLS),
19034 block_dim: (WARP, COLS, 1),
19035 shared_mem_bytes: 0,
19036 };
19037 let (h, ti) = (n_head as i32, t as i32);
19038 let __s_b = self.gpu.stream();
19039 let mut b = __s_b.launch_builder(&f);
19040 b.arg(q)
19041 .arg(k)
19042 .arg(v)
19043 .arg(g)
19044 .arg(beta)
19045 .arg(state_in)
19046 .arg(state_out)
19047 .arg(o)
19048 .arg(&h)
19049 .arg(&ti)
19050 .arg(&scale);
19051 unsafe {
19052 b.launch(cfg)?;
19053 }
19054 Ok(())
19055 }
19056
19057 pub fn ssm_conv1d_view(
19059 &self,
19060 x: &cudarc::driver::CudaView<f32>,
19061 w: &CudaSlice<f32>,
19062 y: &mut CudaSlice<f32>,
19063 conv_dim: usize,
19064 t: usize,
19065 d_conv: usize,
19066 silu: bool,
19067 ) -> Result<(), Box<dyn std::error::Error>> {
19068 let f = self.func("ssm_conv1d_silu_f32");
19069 let cfg = LaunchConfig {
19071 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
19072 block_dim: (256, 1, 1),
19073 shared_mem_bytes: 0,
19074 };
19075 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
19076 let __s_b = self.gpu.stream();
19077 let mut b = __s_b.launch_builder(&f);
19078 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
19079 unsafe {
19080 b.launch(cfg)?;
19081 }
19082 Ok(())
19083 }
19084
19085 pub fn ssm_conv1d_tm(
19092 &self,
19093 qkv_tm: &CudaSlice<f32>,
19094 w: &CudaSlice<f32>,
19095 y: &mut CudaSlice<f32>,
19096 conv_dim: usize,
19097 t: usize,
19098 d_conv: usize,
19099 ) -> Result<(), Box<dyn std::error::Error>> {
19100 let f = self.func("ssm_conv1d_tm_f32");
19101 let cfg = LaunchConfig {
19102 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19103 block_dim: (256, 1, 1),
19104 shared_mem_bytes: 0,
19105 };
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(w).arg(y).arg(&cd).arg(&ti).arg(&dc);
19110 unsafe {
19111 b.launch(cfg)?;
19112 }
19113 Ok(())
19114 }
19115
19116 pub fn ssm_conv1d_tm_state(
19124 &self,
19125 qkv_tm: &CudaSlice<f32>,
19126 conv_state: &mut CudaSlice<f32>,
19127 w: &CudaSlice<f32>,
19128 y: &mut CudaSlice<f32>,
19129 conv_dim: usize,
19130 t: usize,
19131 d_conv: usize,
19132 ) -> Result<(), Box<dyn std::error::Error>> {
19133 self.ssm_conv1d_tm_state_pad(qkv_tm, conv_state, w, y, conv_dim, t, d_conv, None)
19134 }
19135
19136 #[allow(clippy::too_many_arguments)]
19139 pub fn ssm_conv1d_tm_state_pad(
19140 &self,
19141 qkv_tm: &CudaSlice<f32>,
19142 conv_state: &mut CudaSlice<f32>,
19143 w: &CudaSlice<f32>,
19144 y: &mut CudaSlice<f32>,
19145 conv_dim: usize,
19146 t: usize,
19147 d_conv: usize,
19148 pad_len: Option<&CudaSlice<i32>>,
19149 ) -> Result<(), Box<dyn std::error::Error>> {
19150 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
19151 let ring_old = if t < d_conv - 1 {
19155 Some(self.clone_dtod(conv_state)?)
19156 } else {
19157 None
19158 };
19159 {
19160 let f = self.func("ssm_conv1d_tm_state_f32");
19161 let cfg = LaunchConfig {
19162 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19163 block_dim: (256, 1, 1),
19164 shared_mem_bytes: 0,
19165 };
19166 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19167 let __s_b = self.gpu.stream();
19168 let mut b = __s_b.launch_builder(&f);
19169 b.arg(qkv_tm)
19170 .arg(&*conv_state)
19171 .arg(w)
19172 .arg(y)
19173 .arg(&cd)
19174 .arg(&ti)
19175 .arg(&dc);
19176 unsafe {
19177 b.launch(cfg)?;
19178 }
19179 }
19180 match (ring_old, pad_len) {
19181 (None, Some(len_d)) => {
19182 let f = self.func("ssm_conv_ring_update_dev_f32");
19183 let n = conv_dim * (d_conv - 1);
19184 let cfg = LaunchConfig::for_num_elems(n as u32);
19185 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19186 let __s_b = self.gpu.stream();
19187 let mut b = __s_b.launch_builder(&f);
19188 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
19189 unsafe {
19190 b.launch(cfg)?;
19191 }
19192 }
19193 (None, None) => {
19194 let f = self.func("ssm_conv_ring_update_f32");
19195 let n = conv_dim * (d_conv - 1);
19196 let cfg = LaunchConfig::for_num_elems(n as u32);
19197 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19198 let __s_b = self.gpu.stream();
19199 let mut b = __s_b.launch_builder(&f);
19200 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
19201 unsafe {
19202 b.launch(cfg)?;
19203 }
19204 }
19205 (Some(old), _) => {
19206 self.ssm_conv_ring_rebuild(qkv_tm, &old, conv_state, conv_dim, t, d_conv)?
19207 }
19208 }
19209 Ok(())
19210 }
19211
19212 pub fn ssm_conv1d_tm_state_pad_v(
19214 &self,
19215 qkv_tm: &cudarc::driver::CudaView<f32>,
19216 conv_state: &mut CudaSlice<f32>,
19217 w: &CudaSlice<f32>,
19218 y: &mut CudaSlice<f32>,
19219 conv_dim: usize,
19220 t: usize,
19221 d_conv: usize,
19222 pad_len: Option<&CudaSlice<i32>>,
19223 ) -> Result<(), Box<dyn std::error::Error>> {
19224 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
19225 let ring_old = if t < d_conv - 1 {
19229 Some(self.clone_dtod(conv_state)?)
19230 } else {
19231 None
19232 };
19233 {
19234 let f = self.func("ssm_conv1d_tm_state_f32");
19235 let cfg = LaunchConfig {
19236 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19237 block_dim: (256, 1, 1),
19238 shared_mem_bytes: 0,
19239 };
19240 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19241 let __s_b = self.gpu.stream();
19242 let mut b = __s_b.launch_builder(&f);
19243 b.arg(qkv_tm)
19244 .arg(&*conv_state)
19245 .arg(w)
19246 .arg(y)
19247 .arg(&cd)
19248 .arg(&ti)
19249 .arg(&dc);
19250 unsafe {
19251 b.launch(cfg)?;
19252 }
19253 }
19254 match (ring_old, pad_len) {
19255 (None, Some(len_d)) => {
19256 let f = self.func("ssm_conv_ring_update_dev_f32");
19257 let n = conv_dim * (d_conv - 1);
19258 let cfg = LaunchConfig::for_num_elems(n as u32);
19259 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19260 let __s_b = self.gpu.stream();
19261 let mut b = __s_b.launch_builder(&f);
19262 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
19263 unsafe {
19264 b.launch(cfg)?;
19265 }
19266 }
19267 (None, None) => {
19268 let f = self.func("ssm_conv_ring_update_f32");
19269 let n = conv_dim * (d_conv - 1);
19270 let cfg = LaunchConfig::for_num_elems(n as u32);
19271 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19272 let __s_b = self.gpu.stream();
19273 let mut b = __s_b.launch_builder(&f);
19274 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
19275 unsafe {
19276 b.launch(cfg)?;
19277 }
19278 }
19279 (Some(_), _) => unreachable!(
19280 "ssm_conv1d_tm_state_pad_v: T < d_conv-1 has no view path (PRIME_MIN_T gates it)"
19281 ),
19282 }
19283 Ok(())
19284 }
19285
19286 pub fn ssm_conv_ring_rebuild(
19291 &self,
19292 qkv_tm: &CudaSlice<f32>,
19293 ring_old: &CudaSlice<f32>,
19294 conv_state: &mut CudaSlice<f32>,
19295 conv_dim: usize,
19296 tc: usize,
19297 d_conv: usize,
19298 ) -> Result<(), Box<dyn std::error::Error>> {
19299 let f = self.func("ssm_conv_ring_rebuild_f32");
19300 let n = conv_dim * (d_conv - 1);
19301 let cfg = LaunchConfig::for_num_elems(n as u32);
19302 let (cd, ti, dc) = (conv_dim as i32, tc as i32, d_conv as i32);
19303 let __s_b = self.gpu.stream();
19304 let mut b = __s_b.launch_builder(&f);
19305 b.arg(qkv_tm)
19306 .arg(ring_old)
19307 .arg(conv_state)
19308 .arg(&cd)
19309 .arg(&ti)
19310 .arg(&dc);
19311 unsafe {
19312 b.launch(cfg)?;
19313 }
19314 Ok(())
19315 }
19316
19317 #[allow(clippy::too_many_arguments)]
19322 pub fn gdn_prep_decode(
19323 &self,
19324 conv_out: &CudaSlice<f32>,
19325 beta_raw: &CudaSlice<f32>,
19326 alpha: &CudaSlice<f32>,
19327 dt_bias: &CudaSlice<f32>,
19328 a: &CudaSlice<f32>,
19329 q_l2: &mut CudaSlice<f32>,
19330 k_l2: &mut CudaSlice<f32>,
19331 v_g: &mut CudaSlice<f32>,
19332 beta: &mut CudaSlice<f32>,
19333 g_log: &mut CudaSlice<f32>,
19334 d_state: usize,
19335 num_v: usize,
19336 num_k: usize,
19337 key_dim: usize,
19338 eps: f32,
19339 ) -> Result<(), Box<dyn std::error::Error>> {
19340 let f = self.func("gdn_prep_decode_f32");
19341 let cfg = LaunchConfig {
19342 grid_dim: (num_v as u32, 1, 1),
19343 block_dim: (32, 4, 1),
19344 shared_mem_bytes: 0,
19345 };
19346 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
19347 let __s_b = self.gpu.stream();
19348 let mut b = __s_b.launch_builder(&f);
19349 b.arg(conv_out)
19350 .arg(beta_raw)
19351 .arg(alpha)
19352 .arg(dt_bias)
19353 .arg(a)
19354 .arg(q_l2)
19355 .arg(k_l2)
19356 .arg(v_g)
19357 .arg(beta)
19358 .arg(g_log)
19359 .arg(&ds)
19360 .arg(&nv)
19361 .arg(&nk)
19362 .arg(&kd)
19363 .arg(&eps);
19364 unsafe {
19365 b.launch(cfg)?;
19366 }
19367 Ok(())
19368 }
19369
19370 #[allow(clippy::too_many_arguments)]
19374 pub fn ssm_conv1d_gdn(
19375 &self,
19376 qkv_tm: &CudaSlice<f32>,
19377 w: &CudaSlice<f32>,
19378 q_g: &mut CudaSlice<f32>,
19379 k_g: &mut CudaSlice<f32>,
19380 v_g: &mut CudaSlice<f32>,
19381 conv_dim: usize,
19382 t: usize,
19383 d_conv: usize,
19384 d_state: usize,
19385 num_v: usize,
19386 num_k: usize,
19387 key_dim: usize,
19388 ) -> Result<(), Box<dyn std::error::Error>> {
19389 let f = self.func("ssm_conv1d_gdn_f32");
19390 let cfg = LaunchConfig {
19391 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19392 block_dim: (256, 1, 1),
19393 shared_mem_bytes: 0,
19394 };
19395 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19396 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
19397 let __s_b = self.gpu.stream();
19398 let mut b = __s_b.launch_builder(&f);
19399 b.arg(qkv_tm)
19400 .arg(w)
19401 .arg(q_g)
19402 .arg(k_g)
19403 .arg(v_g)
19404 .arg(&cd)
19405 .arg(&ti)
19406 .arg(&dc)
19407 .arg(&ds)
19408 .arg(&nv)
19409 .arg(&nk)
19410 .arg(&kd);
19411 unsafe {
19412 b.launch(cfg)?;
19413 }
19414 Ok(())
19415 }
19416
19417 pub fn ssm_conv1d(
19418 &self,
19419 x: &CudaSlice<f32>,
19420 w: &CudaSlice<f32>,
19421 y: &mut CudaSlice<f32>,
19422 conv_dim: usize,
19423 t: usize,
19424 d_conv: usize,
19425 silu: bool,
19426 ) -> Result<(), Box<dyn std::error::Error>> {
19427 let f = self.func("ssm_conv1d_silu_f32");
19428 let cfg = LaunchConfig {
19429 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
19430 block_dim: (256, 1, 1),
19431 shared_mem_bytes: 0,
19432 };
19433 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
19434 let __s_b = self.gpu.stream();
19435 let mut b = __s_b.launch_builder(&f);
19436 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
19437 unsafe {
19438 b.launch(cfg)?;
19439 }
19440 Ok(())
19441 }
19442
19443 pub fn gdn_scan_s128(
19446 &self,
19447 q: &CudaSlice<f32>,
19448 k: &CudaSlice<f32>,
19449 v: &CudaSlice<f32>,
19450 g: &CudaSlice<f32>,
19451 beta: &CudaSlice<f32>,
19452 state_in: &CudaSlice<f32>,
19453 state_out: &mut CudaSlice<f32>,
19454 o: &mut CudaSlice<f32>,
19455 n_head: usize,
19456 t: usize,
19457 scale: f32,
19458 ) -> Result<(), Box<dyn std::error::Error>> {
19459 let f = self.func("gdn_scan_s128");
19460 const S_V: u32 = 128;
19461 const WARP: u32 = 32;
19462 const COLS_PER_BLOCK: u32 = 4;
19463 let cfg = LaunchConfig {
19464 grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
19465 block_dim: (WARP, COLS_PER_BLOCK, 1),
19466 shared_mem_bytes: 0,
19467 };
19468 let (h, ti) = (n_head as i32, t as i32);
19469 let __s_b = self.gpu.stream();
19470 let mut b = __s_b.launch_builder(&f);
19471 b.arg(q)
19472 .arg(k)
19473 .arg(v)
19474 .arg(g)
19475 .arg(beta)
19476 .arg(state_in)
19477 .arg(state_out)
19478 .arg(o)
19479 .arg(&h)
19480 .arg(&ti)
19481 .arg(&scale);
19482 unsafe {
19483 b.launch(cfg)?;
19484 }
19485 Ok(())
19486 }
19487
19488 #[allow(clippy::too_many_arguments)]
19493 pub fn ssm_conv1d_fused_decode_b(
19494 &self,
19495 qkv_cols: &CudaSlice<f32>,
19496 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
19497 w: &CudaSlice<f32>,
19498 conv_outs: &mut CudaSlice<f32>,
19499 conv_dim: usize,
19500 d_conv: usize,
19501 b_n: usize,
19502 ) -> Result<(), Box<dyn std::error::Error>> {
19503 let f = self.func("ssm_conv1d_fused_decode_b_f32");
19504 let cfg = LaunchConfig {
19505 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
19506 block_dim: (256, 1, 1),
19507 shared_mem_bytes: 0,
19508 };
19509 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19510 let __s_b = self.gpu.stream();
19511 let mut b = __s_b.launch_builder(&f);
19512 b.arg(qkv_cols)
19513 .arg(conv_state_ptrs)
19514 .arg(w)
19515 .arg(conv_outs)
19516 .arg(&cd)
19517 .arg(&dc);
19518 unsafe {
19519 b.launch(cfg)?;
19520 }
19521 Ok(())
19522 }
19523
19524 #[allow(clippy::too_many_arguments)]
19525 pub fn gdn_prep_decode_b(
19526 &self,
19527 conv_outs: &CudaSlice<f32>,
19528 beta_raws: &CudaSlice<f32>,
19529 alphas: &CudaSlice<f32>,
19530 dt_bias: &CudaSlice<f32>,
19531 a: &CudaSlice<f32>,
19532 q_l2: &mut CudaSlice<f32>,
19533 k_l2: &mut CudaSlice<f32>,
19534 v_g: &mut CudaSlice<f32>,
19535 beta: &mut CudaSlice<f32>,
19536 g_log: &mut CudaSlice<f32>,
19537 d_state: usize,
19538 num_v: usize,
19539 num_k: usize,
19540 key_dim: usize,
19541 eps: f32,
19542 conv_dim: usize,
19543 b_n: usize,
19544 ) -> Result<(), Box<dyn std::error::Error>> {
19545 let f = self.func("gdn_prep_decode_b_f32");
19546 let cfg = LaunchConfig {
19547 grid_dim: (num_v as u32, 1, b_n as u32),
19548 block_dim: (32, 4, 1),
19549 shared_mem_bytes: 0,
19550 };
19551 let (ds, nv, nk, kd, cd) = (
19552 d_state as i32,
19553 num_v as i32,
19554 num_k as i32,
19555 key_dim as i32,
19556 conv_dim as i32,
19557 );
19558 let __s_b = self.gpu.stream();
19559 let mut b = __s_b.launch_builder(&f);
19560 b.arg(conv_outs)
19561 .arg(beta_raws)
19562 .arg(alphas)
19563 .arg(dt_bias)
19564 .arg(a)
19565 .arg(q_l2)
19566 .arg(k_l2)
19567 .arg(v_g)
19568 .arg(beta)
19569 .arg(g_log)
19570 .arg(&ds)
19571 .arg(&nv)
19572 .arg(&nk)
19573 .arg(&kd)
19574 .arg(&eps)
19575 .arg(&cd);
19576 unsafe {
19577 b.launch(cfg)?;
19578 }
19579 Ok(())
19580 }
19581
19582 #[allow(clippy::too_many_arguments)]
19583 pub fn gdn_scan_s128_batched(
19584 &self,
19585 q: &CudaSlice<f32>,
19586 k: &CudaSlice<f32>,
19587 v: &CudaSlice<f32>,
19588 g: &CudaSlice<f32>,
19589 beta: &CudaSlice<f32>,
19590 state_in_ptrs: &cudarc::driver::CudaView<u64>,
19591 state_out_ptrs: &cudarc::driver::CudaView<u64>,
19592 o: &mut CudaSlice<f32>,
19593 n_head: usize,
19594 b_n: usize,
19595 scale: f32,
19596 ) -> Result<(), Box<dyn std::error::Error>> {
19597 let f = self.func("gdn_scan_s128_b");
19598 const S_V: u32 = 128;
19599 const WARP: u32 = 32;
19600 const COLS_PER_BLOCK: u32 = 4;
19601 let cfg = LaunchConfig {
19602 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
19603 block_dim: (WARP, COLS_PER_BLOCK, 1),
19604 shared_mem_bytes: 0,
19605 };
19606 let h = n_head as i32;
19607 let __s_b = self.gpu.stream();
19608 let mut b = __s_b.launch_builder(&f);
19609 b.arg(q)
19610 .arg(k)
19611 .arg(v)
19612 .arg(g)
19613 .arg(beta)
19614 .arg(state_in_ptrs)
19615 .arg(state_out_ptrs)
19616 .arg(o)
19617 .arg(&h)
19618 .arg(&scale);
19619 unsafe {
19620 b.launch(cfg)?;
19621 }
19622 Ok(())
19623 }
19624
19625 #[allow(clippy::too_many_arguments)]
19631 pub fn ssm_conv1d_fused_decode_b_view(
19632 &self,
19633 qkv_cols: &cudarc::driver::CudaView<f32>,
19634 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
19635 w: &CudaSlice<f32>,
19636 conv_outs: &mut CudaSlice<f32>,
19637 conv_dim: usize,
19638 d_conv: usize,
19639 b_n: usize,
19640 ) -> Result<(), Box<dyn std::error::Error>> {
19641 let f = self.func("ssm_conv1d_fused_decode_b_f32");
19642 let cfg = LaunchConfig {
19643 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
19644 block_dim: (256, 1, 1),
19645 shared_mem_bytes: 0,
19646 };
19647 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19648 let __s_b = self.gpu.stream();
19649 let mut b = __s_b.launch_builder(&f);
19650 b.arg(qkv_cols)
19651 .arg(conv_state_ptrs)
19652 .arg(w)
19653 .arg(conv_outs)
19654 .arg(&cd)
19655 .arg(&dc);
19656 unsafe {
19657 b.launch(cfg)?;
19658 }
19659 Ok(())
19660 }
19661
19662 #[allow(clippy::too_many_arguments)]
19663 pub fn gdn_prep_decode_b_view(
19664 &self,
19665 conv_outs: &CudaSlice<f32>,
19666 beta_raws: &cudarc::driver::CudaView<f32>,
19667 alphas: &cudarc::driver::CudaView<f32>,
19668 dt_bias: &CudaSlice<f32>,
19669 a: &CudaSlice<f32>,
19670 q_l2: &mut CudaSlice<f32>,
19671 k_l2: &mut CudaSlice<f32>,
19672 v_g: &mut CudaSlice<f32>,
19673 beta: &mut CudaSlice<f32>,
19674 g_log: &mut CudaSlice<f32>,
19675 d_state: usize,
19676 num_v: usize,
19677 num_k: usize,
19678 key_dim: usize,
19679 eps: f32,
19680 conv_dim: usize,
19681 b_n: usize,
19682 ) -> Result<(), Box<dyn std::error::Error>> {
19683 let f = self.func("gdn_prep_decode_b_f32");
19684 let cfg = LaunchConfig {
19685 grid_dim: (num_v as u32, 1, b_n as u32),
19686 block_dim: (32, 4, 1),
19687 shared_mem_bytes: 0,
19688 };
19689 let (ds, nv, nk, kd, cd) = (
19690 d_state as i32,
19691 num_v as i32,
19692 num_k as i32,
19693 key_dim as i32,
19694 conv_dim as i32,
19695 );
19696 let __s_b = self.gpu.stream();
19697 let mut b = __s_b.launch_builder(&f);
19698 b.arg(conv_outs)
19699 .arg(beta_raws)
19700 .arg(alphas)
19701 .arg(dt_bias)
19702 .arg(a)
19703 .arg(q_l2)
19704 .arg(k_l2)
19705 .arg(v_g)
19706 .arg(beta)
19707 .arg(g_log)
19708 .arg(&ds)
19709 .arg(&nv)
19710 .arg(&nk)
19711 .arg(&kd)
19712 .arg(&eps)
19713 .arg(&cd);
19714 unsafe {
19715 b.launch(cfg)?;
19716 }
19717 Ok(())
19718 }
19719
19720 #[allow(clippy::too_many_arguments)]
19721 pub fn gdn_scan_s128_batched_view(
19722 &self,
19723 q: &CudaSlice<f32>,
19724 k: &CudaSlice<f32>,
19725 v: &CudaSlice<f32>,
19726 g: &CudaSlice<f32>,
19727 beta: &CudaSlice<f32>,
19728 state_in_ptrs: &cudarc::driver::CudaView<u64>,
19729 state_out_ptrs: &cudarc::driver::CudaView<u64>,
19730 o: &mut cudarc::driver::CudaViewMut<f32>,
19731 n_head: usize,
19732 b_n: usize,
19733 scale: f32,
19734 ) -> Result<(), Box<dyn std::error::Error>> {
19735 let f = self.func("gdn_scan_s128_b");
19736 const S_V: u32 = 128;
19737 const WARP: u32 = 32;
19738 const COLS_PER_BLOCK: u32 = 4;
19739 let cfg = LaunchConfig {
19740 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
19741 block_dim: (WARP, COLS_PER_BLOCK, 1),
19742 shared_mem_bytes: 0,
19743 };
19744 let h = n_head as i32;
19745 let __s_b = self.gpu.stream();
19746 let mut b = __s_b.launch_builder(&f);
19747 b.arg(q)
19748 .arg(k)
19749 .arg(v)
19750 .arg(g)
19751 .arg(beta)
19752 .arg(state_in_ptrs)
19753 .arg(state_out_ptrs)
19754 .arg(o)
19755 .arg(&h)
19756 .arg(&scale);
19757 unsafe {
19758 b.launch(cfg)?;
19759 }
19760 Ok(())
19761 }
19762
19763 pub fn gdn_chunked_enabled() -> bool {
19772 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19773 *E.get_or_init(|| {
19774 std::env::var("MEMRA_GDN_CHUNKED")
19775 .map(|v| v != "0")
19776 .unwrap_or(true)
19777 })
19778 }
19779
19780 pub fn gdn_chunk_size() -> usize {
19785 static C: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
19786 *C.get_or_init(|| {
19787 let c: usize = std::env::var("MEMRA_GDN_CHUNK")
19788 .ok()
19789 .and_then(|v| v.parse().ok())
19790 .unwrap_or(32);
19791 c.clamp(32, 128) / 32 * 32
19792 })
19793 }
19794
19795 #[allow(clippy::too_many_arguments)]
19800 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
19803 #[allow(clippy::too_many_arguments)]
19804 pub fn gdn_chunk_k123(
19805 &self,
19806 q: &CudaSlice<f32>,
19807 k: &CudaSlice<f32>,
19808 v: &CudaSlice<f32>,
19809 g: &CudaSlice<f32>,
19810 beta: &CudaSlice<f32>,
19811 wb16: Option<&mut CudaSlice<u8>>,
19812 n_head: usize,
19813 t: usize,
19814 c: usize,
19815 hk: usize,
19816 k2w: Option<(&CudaSlice<u8>, &CudaSlice<u8>, &mut CudaSlice<u8>)>,
19817 ) -> Result<
19818 (
19819 CudaSlice<f32>,
19820 CudaSlice<f32>,
19821 CudaSlice<f32>,
19822 CudaSlice<f32>,
19823 ),
19824 Box<dyn std::error::Error>,
19825 > {
19826 const D: usize = 128;
19827 let h = n_head;
19828 let nc = (t + c - 1) / c;
19829 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
19830 let mut gcum = self.uninit(t * h)?;
19831 let mut a = self.uninit(nc * h * c * c)?;
19832 let mut p = self.uninit(nc * h * c * c)?;
19833 let mut u = self.uninit(nc * h * c * D)?;
19834 let mut w = self.uninit(nc * h * c * D)?;
19835 {
19836 let f = self.func("gdn_chunk_cumgate_f32");
19838 let cfg = LaunchConfig {
19839 grid_dim: (nc as u32, h as u32, 1),
19840 block_dim: (32, 1, 1),
19841 shared_mem_bytes: 0,
19842 };
19843 let __s_b = self.gpu.stream();
19844 let mut b = __s_b.launch_builder(&f);
19845 b.arg(g).arg(&mut gcum).arg(&hi).arg(&ti).arg(&ci);
19846 unsafe {
19847 b.launch(cfg)?;
19848 }
19849 }
19850 if let Some((qb, kb, pb)) = k2w {
19851 assert!(c == 32, "gdn_k2_wgmma is a C==32 tile");
19854 let f = self.func("gdn_k2_wgmma");
19855 let cfg = LaunchConfig {
19856 grid_dim: (nc as u32, h as u32, 1),
19857 block_dim: (128, 1, 1),
19858 shared_mem_bytes: 0,
19859 };
19860 let hki = hk as i32;
19861 let __s_b = self.gpu.stream();
19862 let mut b = __s_b.launch_builder(&f);
19863 b.arg(qb)
19864 .arg(kb)
19865 .arg(&gcum)
19866 .arg(beta)
19867 .arg(&mut a)
19868 .arg(&mut *pb)
19869 .arg(&hi)
19870 .arg(&ti)
19871 .arg(&ci)
19872 .arg(&hki);
19873 unsafe {
19874 b.launch(cfg)?;
19875 }
19876 } else if c <= 64 && !portable_mma_gated() {
19877 let f = self.func("gdn_chunk_attn_f32");
19879 let jt = ((c + 31) / 32) as u32;
19880 let cfg = LaunchConfig {
19881 grid_dim: (nc as u32, h as u32, jt),
19882 block_dim: (256, 1, 1),
19883 shared_mem_bytes: 0,
19884 };
19885 let hki = hk as i32;
19886 let __s_b = self.gpu.stream();
19887 let mut b = __s_b.launch_builder(&f);
19888 b.arg(q)
19889 .arg(k)
19890 .arg(&gcum)
19891 .arg(beta)
19892 .arg(&mut a)
19893 .arg(&mut p)
19894 .arg(&hi)
19895 .arg(&ti)
19896 .arg(&ci)
19897 .arg(&hki);
19898 unsafe {
19899 b.launch(cfg)?;
19900 }
19901 } else {
19902 assert!(
19904 hk == h,
19905 "generic K2 is broadcast-only (de-broadcast rides C==32)"
19906 );
19907 let f = self.func("gdn_chunk_attn_g_f32");
19908 let cfg = LaunchConfig {
19909 grid_dim: (nc as u32, h as u32, 1),
19910 block_dim: (32, 8, 1),
19911 shared_mem_bytes: 0,
19912 };
19913 let __s_b = self.gpu.stream();
19914 let mut b = __s_b.launch_builder(&f);
19915 b.arg(q)
19916 .arg(k)
19917 .arg(&gcum)
19918 .arg(beta)
19919 .arg(&mut a)
19920 .arg(&mut p)
19921 .arg(&hi)
19922 .arg(&ti)
19923 .arg(&ci);
19924 unsafe {
19925 b.launch(cfg)?;
19926 }
19927 }
19928 {
19929 let cfg = LaunchConfig {
19931 grid_dim: (nc as u32, h as u32, 1),
19932 block_dim: (256, 1, 1),
19933 shared_mem_bytes: 0,
19934 };
19935 match c {
19936 32 | 64 => {
19937 let f = self.func(if c == 32 {
19938 "gdn_chunk_solve32_f32"
19939 } else {
19940 "gdn_chunk_solve64_f32"
19941 });
19942 let wb: u64 = match wb16 {
19944 Some(d) => self.addr_u8(d),
19945 None => 0,
19946 };
19947 let hki = hk as i32;
19948 let __s_b = self.gpu.stream();
19949 let mut b = __s_b.launch_builder(&f);
19950 b.arg(v)
19951 .arg(k)
19952 .arg(&a)
19953 .arg(&gcum)
19954 .arg(&mut u)
19955 .arg(&mut w)
19956 .arg(&wb)
19957 .arg(&hi)
19958 .arg(&ti)
19959 .arg(&hki);
19960 unsafe {
19961 b.launch(cfg)?;
19962 }
19963 }
19964 _ => {
19965 assert!(hk == h, "generic K3 is broadcast-only");
19966 let f = self.func("gdn_chunk_solve_f32");
19967 let __s_b = self.gpu.stream();
19968 let mut b = __s_b.launch_builder(&f);
19969 b.arg(v)
19970 .arg(k)
19971 .arg(&a)
19972 .arg(&gcum)
19973 .arg(&mut u)
19974 .arg(&mut w)
19975 .arg(&hi)
19976 .arg(&ti)
19977 .arg(&ci);
19978 unsafe {
19979 b.launch(cfg)?;
19980 }
19981 }
19982 }
19983 }
19984 Ok((gcum, p, u, w))
19985 }
19986
19987 pub fn gdn_db_on() -> bool {
19991 std::env::var("MEMRA_GDN_DB").as_deref() != Ok("0")
19992 }
19993
19994 pub fn gdn_mma_enabled(&self, c: usize) -> bool {
19997 !portable_mma_gated()
19998 && c == 32
19999 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
20000 Ok("1") => true,
20001 Ok("0") => false,
20002 _ => cfg!(memra_hopper_mma),
20003 }
20004 }
20005
20006 pub fn gdn_wgmma_on(&self, c: usize) -> bool {
20009 self.gdn_mma_enabled(c)
20010 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
20011 Ok("0") => false,
20012 Ok("1") => true,
20013 _ => cfg!(memra_hopper_mma),
20014 }
20015 }
20016
20017 #[allow(clippy::too_many_arguments)]
20022 pub fn ssm_conv1d_gdn_state_pad(
20023 &self,
20024 qkv_tm: &cudarc::driver::CudaView<f32>,
20025 conv_state: &mut CudaSlice<f32>,
20026 w: &CudaSlice<f32>,
20027 q_g: &mut CudaSlice<f32>,
20028 k_g: &mut CudaSlice<f32>,
20029 v_g: &mut CudaSlice<f32>,
20030 conv_dim: usize,
20031 t: usize,
20032 d_conv: usize,
20033 d_state: usize,
20034 num_v: usize,
20035 num_k: usize,
20036 key_dim: usize,
20037 hk: usize,
20038 pad_len: Option<&CudaSlice<i32>>,
20039 ) -> Result<(), Box<dyn std::error::Error>> {
20040 assert!(
20041 t >= d_conv - 1,
20042 "fused state conv requires T >= pad (PRIME_MIN_T gates)"
20043 );
20044 {
20045 let f = self.func("ssm_conv1d_gdn_state_f32");
20046 let cfg = LaunchConfig {
20047 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
20048 block_dim: (256, 1, 1),
20049 shared_mem_bytes: 0,
20050 };
20051 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20052 let (ds, nv, nk, kd, hki) = (
20053 d_state as i32,
20054 num_v as i32,
20055 num_k as i32,
20056 key_dim as i32,
20057 hk as i32,
20058 );
20059 let __s_b = self.gpu.stream();
20060 let mut b = __s_b.launch_builder(&f);
20061 b.arg(qkv_tm)
20062 .arg(&*conv_state)
20063 .arg(w)
20064 .arg(q_g)
20065 .arg(k_g)
20066 .arg(v_g)
20067 .arg(&cd)
20068 .arg(&ti)
20069 .arg(&dc)
20070 .arg(&ds)
20071 .arg(&nv)
20072 .arg(&nk)
20073 .arg(&kd)
20074 .arg(&hki);
20075 unsafe {
20076 b.launch(cfg)?;
20077 }
20078 }
20079 match pad_len {
20080 Some(len_d) => {
20081 let f = self.func("ssm_conv_ring_update_dev_f32");
20082 let n = conv_dim * (d_conv - 1);
20083 let cfg = LaunchConfig::for_num_elems(n as u32);
20084 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20085 let __s_b = self.gpu.stream();
20086 let mut b = __s_b.launch_builder(&f);
20087 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
20088 unsafe {
20089 b.launch(cfg)?;
20090 }
20091 }
20092 None => {
20093 let f = self.func("ssm_conv_ring_update_f32");
20094 let n = conv_dim * (d_conv - 1);
20095 let cfg = LaunchConfig::for_num_elems(n as u32);
20096 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20097 let __s_b = self.gpu.stream();
20098 let mut b = __s_b.launch_builder(&f);
20099 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
20100 unsafe {
20101 b.launch(cfg)?;
20102 }
20103 }
20104 }
20105 Ok(())
20106 }
20107
20108 pub fn gdn_chunk_alloc(
20112 &self,
20113 n_head: usize,
20114 t: usize,
20115 c: usize,
20116 hk: usize,
20117 ) -> Result<GdnChunkBufs, Box<dyn std::error::Error>> {
20118 const D: usize = 128;
20119 assert!(
20120 c == 32,
20121 "gdn_chunk_alloc: varlen chain is the C==32 mma pair"
20122 );
20123 let h = n_head;
20124 let nc = (t + c - 1) / c;
20125 Ok(GdnChunkBufs {
20126 gcum: self.uninit(t * h)?,
20127 a: self.uninit(nc * h * c * c)?,
20128 p: self.uninit(nc * h * c * c)?,
20129 u: self.uninit(nc * h * c * D)?,
20130 w: self.uninit(nc * h * c * D)?,
20131 kb16: self.alloc_u8_uninit(t * hk * D * 2)?,
20132 wb16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
20133 y16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
20134 ssnap16: self.alloc_u8_uninit(nc * h * D * D * 2)?,
20135 qb16: self.alloc_u8_uninit(t * hk * D * 2)?,
20136 pb16: self.alloc_u8_uninit(nc * h * c * c * 2)?,
20137 o: self.uninit(D * h * t)?,
20138 t,
20139 nc,
20140 })
20141 }
20142
20143 pub fn f32_to_bf16_v(
20145 &self,
20146 x: &cudarc::driver::CudaView<f32>,
20147 dst: &mut CudaSlice<u8>,
20148 n: usize,
20149 ) -> Result<(), Box<dyn std::error::Error>> {
20150 let f = self.func("f32_to_bf16_bulk");
20151 let ni = n as i64;
20152 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
20153 let __s_b = self.gpu.stream();
20154 let mut b = __s_b.launch_builder(&f);
20155 b.arg(x).arg(dst).arg(&ni);
20156 unsafe {
20157 b.launch(cfg)?;
20158 }
20159 Ok(())
20160 }
20161
20162 pub fn f32_to_bf16_into(
20164 &self,
20165 x: &CudaSlice<f32>,
20166 dst: &mut CudaSlice<u8>,
20167 n: usize,
20168 ) -> Result<(), Box<dyn std::error::Error>> {
20169 let f = self.func("f32_to_bf16_bulk");
20170 let ni = n as i64;
20171 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
20172 let __s_b = self.gpu.stream();
20173 let mut b = __s_b.launch_builder(&f);
20174 b.arg(x).arg(dst).arg(&ni);
20175 unsafe {
20176 b.launch(cfg)?;
20177 }
20178 Ok(())
20179 }
20180
20181 pub fn gdn_chunk_k123_vl8(
20184 &self,
20185 seqs: &[GdnSeqVl],
20186 n_head: usize,
20187 hk: usize,
20188 wq: Option<&GdnWVl8>,
20189 ) -> Result<(), Box<dyn std::error::Error>> {
20190 let b = seqs.len();
20191 assert!(b >= 1 && b <= 8, "gdn_chunk_k123_vl8: 1..=8 sequences");
20192 let mut packed = [GdnSeqVl::default(); 8];
20193 packed[..b].copy_from_slice(seqs);
20194 let v = GdnVl8(packed);
20195 let (hi, ci) = (n_head as i32, 32i32);
20196 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
20197 {
20198 let f = self.func("gdn_chunk_cumgate_vl");
20199 let cfg = LaunchConfig {
20200 grid_dim: (max_nc, n_head as u32, b as u32),
20201 block_dim: (32, 1, 1),
20202 shared_mem_bytes: 0,
20203 };
20204 let __s_lb = self.gpu.stream();
20205 let mut lb = __s_lb.launch_builder(&f);
20206 lb.arg(&v).arg(&hi).arg(&ci);
20207 unsafe {
20208 lb.launch(cfg)?;
20209 }
20210 }
20211 let hki = hk as i32;
20212 if let Some(w) = wq {
20213 let f = self.func("gdn_k2_wgmma_vl");
20215 let cfg = LaunchConfig {
20216 grid_dim: (max_nc, n_head as u32, b as u32),
20217 block_dim: (128, 1, 1),
20218 shared_mem_bytes: 0,
20219 };
20220 let __s_lb = self.gpu.stream();
20221 let mut lb = __s_lb.launch_builder(&f);
20222 lb.arg(&v).arg(w).arg(&hi).arg(&ci).arg(&hki);
20223 unsafe {
20224 lb.launch(cfg)?;
20225 }
20226 } else {
20227 let f = self.func("gdn_chunk_attn_vl");
20228 let cfg = LaunchConfig {
20229 grid_dim: (max_nc, n_head as u32, b as u32),
20230 block_dim: (256, 1, 1),
20231 shared_mem_bytes: 0,
20232 };
20233 let __s_lb = self.gpu.stream();
20234 let mut lb = __s_lb.launch_builder(&f);
20235 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
20236 unsafe {
20237 lb.launch(cfg)?;
20238 }
20239 }
20240 {
20241 let f = self.func("gdn_chunk_solve32_vl");
20242 let cfg = LaunchConfig {
20243 grid_dim: (max_nc, n_head as u32, b as u32),
20244 block_dim: (256, 1, 1),
20245 shared_mem_bytes: 0,
20246 };
20247 let __s_lb = self.gpu.stream();
20248 let mut lb = __s_lb.launch_builder(&f);
20249 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
20250 unsafe {
20251 lb.launch(cfg)?;
20252 }
20253 }
20254 Ok(())
20255 }
20256
20257 #[allow(clippy::too_many_arguments)]
20261 pub fn gdn_prep_vl8(
20262 &self,
20263 seqs: &[GdnPrepVl],
20264 conv_w: &CudaSlice<f32>,
20265 dt_bias: &CudaSlice<f32>,
20266 a: &CudaSlice<f32>,
20267 conv_dim: usize,
20268 d_conv: usize,
20269 d_state: usize,
20270 num_v: usize,
20271 num_k: usize,
20272 key_dim: usize,
20273 hk: usize,
20274 eps: f32,
20275 ) -> Result<(), Box<dyn std::error::Error>> {
20276 let b = seqs.len();
20277 assert!(b >= 1 && b <= 8);
20278 let mut packed = [GdnPrepVl::default(); 8];
20279 packed[..b].copy_from_slice(seqs);
20280 let v = GdnPrepVl8(packed);
20281 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
20282 let (cdi, dci) = (conv_dim as i32, d_conv as i32);
20283 let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
20284 assert!(
20285 conv_fuse || hk == num_v,
20286 "de-broadcast requires the fused conv"
20287 );
20288 if conv_fuse {
20289 let f = self.func("ssm_conv1d_gdn_state_vl");
20290 let cfg = LaunchConfig {
20291 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
20292 block_dim: (256, 1, 1),
20293 shared_mem_bytes: 0,
20294 };
20295 let (dsi, nvi, nki, kdi, hki) = (
20296 d_state as i32,
20297 num_v as i32,
20298 num_k as i32,
20299 key_dim as i32,
20300 hk as i32,
20301 );
20302 let __s_lb = self.gpu.stream();
20303 let mut lb = __s_lb.launch_builder(&f);
20304 lb.arg(&v)
20305 .arg(conv_w)
20306 .arg(&cdi)
20307 .arg(&dci)
20308 .arg(&dsi)
20309 .arg(&nvi)
20310 .arg(&nki)
20311 .arg(&kdi)
20312 .arg(&hki);
20313 unsafe {
20314 lb.launch(cfg)?;
20315 }
20316 } else {
20317 let f = self.func("ssm_conv1d_tm_state_vl");
20318 let cfg = LaunchConfig {
20319 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
20320 block_dim: (256, 1, 1),
20321 shared_mem_bytes: 0,
20322 };
20323 let __s_lb = self.gpu.stream();
20324 let mut lb = __s_lb.launch_builder(&f);
20325 lb.arg(&v).arg(conv_w).arg(&cdi).arg(&dci);
20326 unsafe {
20327 lb.launch(cfg)?;
20328 }
20329 }
20330 {
20331 let f = self.func("ssm_conv_ring_update_vl");
20332 let n = (conv_dim * (d_conv - 1)) as u32;
20333 let cfg = LaunchConfig {
20334 grid_dim: (n.div_ceil(256), 1, b as u32),
20335 block_dim: (256, 1, 1),
20336 shared_mem_bytes: 0,
20337 };
20338 let __s_lb = self.gpu.stream();
20339 let mut lb = __s_lb.launch_builder(&f);
20340 lb.arg(&v).arg(&cdi).arg(&dci);
20341 unsafe {
20342 lb.launch(cfg)?;
20343 }
20344 }
20345 if !conv_fuse {
20346 let f = self.func("qkv_to_gdn_repack_vl");
20347 let n = max_t * (num_v * d_state) as u32;
20348 let cfg = LaunchConfig {
20349 grid_dim: (n.div_ceil(256), 1, b as u32),
20350 block_dim: (256, 1, 1),
20351 shared_mem_bytes: 0,
20352 };
20353 let (dsi, nvi, nki, kdi) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
20354 let __s_lb = self.gpu.stream();
20355 let mut lb = __s_lb.launch_builder(&f);
20356 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&nki).arg(&kdi);
20357 unsafe {
20358 lb.launch(cfg)?;
20359 }
20360 }
20361 if Self::l2_v2_on(d_state) {
20362 let f = self.func("gdn_l2_v2_vl");
20363 let cfg = LaunchConfig {
20364 grid_dim: ((max_t * hk as u32).div_ceil(8), 2, b as u32),
20365 block_dim: (256, 1, 1),
20366 shared_mem_bytes: 0,
20367 };
20368 let (dsi, nvi) = (d_state as i32, hk as i32);
20369 let __s_lb = self.gpu.stream();
20370 let mut lb = __s_lb.launch_builder(&f);
20371 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
20372 unsafe {
20373 lb.launch(cfg)?;
20374 }
20375 } else {
20376 let f = self.func("gdn_l2_vl");
20377 let cfg = LaunchConfig {
20378 grid_dim: (max_t * hk as u32, 2, b as u32),
20379 block_dim: (256, 1, 1),
20380 shared_mem_bytes: 0,
20381 };
20382 let (dsi, nvi) = (d_state as i32, hk as i32);
20383 let __s_lb = self.gpu.stream();
20384 let mut lb = __s_lb.launch_builder(&f);
20385 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
20386 unsafe {
20387 lb.launch(cfg)?;
20388 }
20389 }
20390 {
20391 let f = self.func("gdn_gate_prep_vl");
20392 let n = max_t * num_v as u32;
20393 let cfg = LaunchConfig {
20394 grid_dim: (n.div_ceil(256), 1, b as u32),
20395 block_dim: (256, 1, 1),
20396 shared_mem_bytes: 0,
20397 };
20398 let nvi = num_v as i32;
20399 let __s_lb = self.gpu.stream();
20400 let mut lb = __s_lb.launch_builder(&f);
20401 lb.arg(&v).arg(dt_bias).arg(a).arg(&nvi);
20402 unsafe {
20403 lb.launch(cfg)?;
20404 }
20405 }
20406 Ok(())
20407 }
20408
20409 pub fn gdn_mirror_vl8(
20411 &self,
20412 seqs: &[GdnSeqVl],
20413 n_head: usize,
20414 which: i32,
20415 hk: usize,
20416 ) -> Result<(), Box<dyn std::error::Error>> {
20417 let b = seqs.len();
20418 assert!(b >= 1 && b <= 8);
20419 let mut packed = [GdnSeqVl::default(); 8];
20420 packed[..b].copy_from_slice(seqs);
20421 let v = GdnVl8(packed);
20422 let ept = (if which == 0 { hk } else { n_head } * 128) as i32;
20423 let max_n = seqs
20424 .iter()
20425 .map(|s| {
20426 if which == 0 {
20427 s.t as i64 * ept as i64
20428 } else {
20429 s.nc as i64 * ept as i64 * 32
20430 }
20431 })
20432 .max()
20433 .unwrap();
20434 let f = self.func("gdn_mirror_vl");
20435 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
20436 let cfg = LaunchConfig {
20437 grid_dim: (blocks, 1, b as u32),
20438 block_dim: (256, 1, 1),
20439 shared_mem_bytes: 0,
20440 };
20441 let __s_lb = self.gpu.stream();
20442 let mut lb = __s_lb.launch_builder(&f);
20443 lb.arg(&v).arg(&ept).arg(&which);
20444 unsafe {
20445 lb.launch(cfg)?;
20446 }
20447 Ok(())
20448 }
20449
20450 pub fn gdn_tail_vl8(
20452 &self,
20453 seqs: &[GdnPrepVl],
20454 norm_w: &CudaSlice<f32>,
20455 d_state: usize,
20456 num_v: usize,
20457 eps: f32,
20458 ) -> Result<(), Box<dyn std::error::Error>> {
20459 let b = seqs.len();
20460 assert!(b >= 1 && b <= 8);
20461 let mut packed = [GdnPrepVl::default(); 8];
20462 packed[..b].copy_from_slice(seqs);
20463 let v = GdnPrepVl8(packed);
20464 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
20465 let f = self.func("gated_rmsnorm_f16out_vl");
20466 let cfg = LaunchConfig {
20468 grid_dim: (max_t * num_v as u32, 1, b as u32),
20469 block_dim: (128, 1, 1),
20470 shared_mem_bytes: 0,
20471 };
20472 let (dsi, nvi) = (d_state as i32, num_v as i32);
20473 let __s_lb = self.gpu.stream();
20474 let mut lb = __s_lb.launch_builder(&f);
20475 lb.arg(&v).arg(norm_w).arg(&dsi).arg(&nvi).arg(&eps);
20476 unsafe {
20477 lb.launch(cfg)?;
20478 }
20479 Ok(())
20480 }
20481
20482 pub fn addr_f32(&self, x: &CudaSlice<f32>) -> u64 {
20485 use cudarc::driver::DevicePtr;
20486 let s = self.gpu.stream();
20487 let (p, _g) = x.device_ptr(&s);
20488 p as u64
20489 }
20490 pub fn addr_f32_mut(&self, x: &mut CudaSlice<f32>) -> u64 {
20491 use cudarc::driver::DevicePtrMut;
20492 let s = self.gpu.stream();
20493 let (p, _g) = x.device_ptr_mut(&s);
20494 p as u64
20495 }
20496 pub fn addr_f32v(&self, x: &cudarc::driver::CudaView<f32>) -> u64 {
20497 use cudarc::driver::DevicePtr;
20498 let s = self.gpu.stream();
20499 let (p, _g) = x.device_ptr(&s);
20500 p as u64
20501 }
20502 pub fn addr_u8(&self, x: &CudaSlice<u8>) -> u64 {
20503 use cudarc::driver::DevicePtr;
20504 let s = self.gpu.stream();
20505 let (p, _g) = x.device_ptr(&s);
20506 p as u64
20507 }
20508
20509 pub fn gdn_chunk_vl8(
20513 &self,
20514 seqs: &[GdnSeqVl],
20515 n_head: usize,
20516 scale: f32,
20517 hk: usize,
20518 wq: Option<&GdnWVl8>,
20519 ) -> Result<(), Box<dyn std::error::Error>> {
20520 const NSPLIT: u32 = 4;
20521 let b = seqs.len();
20522 assert!(b >= 1 && b <= 8, "gdn_chunk_vl8: 1..=8 sequences");
20523 let mut packed = [GdnSeqVl::default(); 8];
20524 packed[..b].copy_from_slice(seqs);
20525 let v = GdnVl8(packed);
20526 let (hi, ci) = (n_head as i32, 32i32);
20527 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
20528 let hki = hk as i32;
20529 if let Some(w) = wq {
20530 let f = self.func("gdn_k45_wgmma_vl");
20532 let cfg = LaunchConfig {
20533 grid_dim: (n_head as u32, NSPLIT, b as u32),
20534 block_dim: (256, 1, 1),
20535 shared_mem_bytes: 0,
20536 };
20537 let __s_lb = self.gpu.stream();
20538 let mut lb = __s_lb.launch_builder(&f);
20539 lb.arg(&v).arg(w).arg(&scale).arg(&hi).arg(&ci).arg(&hki);
20540 unsafe {
20541 lb.launch(cfg)?;
20542 }
20543 let _ = max_nc;
20544 return Ok(());
20545 }
20546 {
20547 let f = self.func("gdn_chunk_state_mma_vl");
20548 let cfg = LaunchConfig {
20549 grid_dim: (n_head as u32, NSPLIT, b as u32),
20550 block_dim: (256, 1, 1),
20551 shared_mem_bytes: 0,
20552 };
20553 let __s_lb = self.gpu.stream();
20554 let mut lb = __s_lb.launch_builder(&f);
20555 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
20556 unsafe {
20557 lb.launch(cfg)?;
20558 }
20559 }
20560 {
20561 let f = self.func("gdn_chunk_output_mma_vl");
20562 let cfg = LaunchConfig {
20563 grid_dim: (max_nc, n_head as u32, b as u32),
20564 block_dim: (256, 1, 1),
20565 shared_mem_bytes: 0,
20566 };
20567 let __s_lb = self.gpu.stream();
20568 let mut lb = __s_lb.launch_builder(&f);
20569 lb.arg(&v).arg(&hi).arg(&ci).arg(&scale).arg(&hki);
20570 unsafe {
20571 lb.launch(cfg)?;
20572 }
20573 }
20574 Ok(())
20575 }
20576 pub fn gdn_scan_chunked(
20577 &self,
20578 q: &CudaSlice<f32>,
20579 k: &CudaSlice<f32>,
20580 v: &CudaSlice<f32>,
20581 g: &CudaSlice<f32>,
20582 beta: &CudaSlice<f32>,
20583 kb16_pre: Option<&CudaSlice<u8>>,
20584 qb16_pre: Option<&CudaSlice<u8>>,
20585 state_in: &CudaSlice<f32>,
20586 state_out: &mut CudaSlice<f32>,
20587 o: &mut CudaSlice<f32>,
20588 n_head: usize,
20589 t: usize,
20590 scale: f32,
20591 c: usize,
20592 hk: usize,
20593 ) -> Result<(), Box<dyn std::error::Error>> {
20594 const D: usize = 128;
20595 const NSPLIT: u32 = 4;
20596 assert!(c >= 1 && c <= 128, "gdn_scan_chunked: C must be in 1..=128");
20597 let h = n_head;
20598 let nc = (t + c - 1) / c;
20599 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
20600 let gdn_mma_pre = !portable_mma_gated()
20604 && c == 32
20605 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
20606 Ok("1") => true,
20607 Ok("0") => false,
20608 _ => cfg!(memra_hopper_mma),
20609 };
20610 let mut wb16_pre: Option<CudaSlice<u8>> = if gdn_mma_pre {
20611 Some(self.alloc_u8_uninit(nc * h * c * D * 2)?)
20612 } else {
20613 None
20614 };
20615 let gdn_wgmma_pre = gdn_mma_pre
20619 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
20620 Ok("0") => false,
20621 Ok("1") => true,
20622 _ => cfg!(memra_hopper_mma),
20623 };
20624 let nk = t * hk * D;
20625 let mut kb16_local: Option<CudaSlice<u8>> = None;
20626 if gdn_mma_pre && kb16_pre.is_none() {
20627 let mut kb = self.alloc_u8_uninit(nk * 2)?;
20628 let f = self.func("f32_to_bf16_bulk");
20629 let n2 = nk as i64;
20630 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
20631 let __s_b = self.gpu.stream();
20632 let mut b = __s_b.launch_builder(&f);
20633 b.arg(k).arg(&mut kb).arg(&n2);
20634 unsafe {
20635 b.launch(cfg2)?;
20636 }
20637 kb16_local = Some(kb);
20638 }
20639 let kb16_ref0: Option<&CudaSlice<u8>> = kb16_local.as_ref().or(kb16_pre);
20640 if let Some(kb) = kb16_pre {
20641 assert!(kb.len() >= nk * 2, "kb16_pre too small");
20642 }
20643 let mut qb16: Option<CudaSlice<u8>> = None;
20644 let mut pb16: Option<CudaSlice<u8>> = None;
20645 if gdn_wgmma_pre {
20646 if qb16_pre.is_none() {
20649 let mut qb = self.alloc_u8_uninit(nk * 2)?;
20650 let f = self.func("f32_to_bf16_bulk");
20651 let n2 = nk as i64;
20652 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
20653 let __s_b = self.gpu.stream();
20654 let mut b = __s_b.launch_builder(&f);
20655 b.arg(q).arg(&mut qb).arg(&n2);
20656 unsafe {
20657 b.launch(cfg2)?;
20658 }
20659 qb16 = Some(qb);
20660 } else if let Some(qb) = qb16_pre {
20661 assert!(qb.len() >= nk * 2, "qb16_pre too small");
20662 }
20663 pb16 = Some(self.alloc_u8_uninit(nc * h * c * c * 2)?);
20664 }
20665 let qb16_ref0: Option<&CudaSlice<u8>> = qb16.as_ref().or(qb16_pre);
20666 let k2w = if gdn_wgmma_pre {
20667 Some((
20668 *qb16_ref0.as_ref().unwrap(),
20669 *kb16_ref0.as_ref().unwrap(),
20670 pb16.as_mut().unwrap(),
20671 ))
20672 } else {
20673 None
20674 };
20675 let (gcum, p, u, w) =
20676 self.gdn_chunk_k123(q, k, v, g, beta, wb16_pre.as_mut(), n_head, t, c, hk, k2w)?;
20677 let _ = &w;
20678 let mut y = self.uninit(nc * h * c * D)?;
20679 let mut ssnap = self.uninit(nc * h * D * D)?; let gdn_mma = !portable_mma_gated()
20691 && c == 32
20692 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
20693 Ok("1") => true,
20694 Ok("0") => false,
20695 _ => cfg!(memra_hopper_mma),
20696 };
20697 if gdn_mma {
20698 let wb16 = wb16_pre
20699 .take()
20700 .expect("mma path pre-allocates wb16 (K3 store fold)");
20701 let kb16_ref: &CudaSlice<u8> = kb16_ref0.expect("mma path pre-builds kb16 above K123");
20702 if gdn_wgmma_pre {
20714 let qb16 = qb16_ref0.unwrap();
20716 let pb16 = pb16.as_ref().unwrap();
20717 {
20718 let f = self.func("gdn_k45_wgmma");
20719 let cfg = LaunchConfig {
20720 grid_dim: (h as u32, 4, 1),
20721 block_dim: (256, 1, 1),
20722 shared_mem_bytes: 0,
20723 };
20724 let hki = hk as i32;
20725 let __s_b = self.gpu.stream();
20726 let mut b = __s_b.launch_builder(&f);
20727 b.arg(kb16_ref)
20728 .arg(&gcum)
20729 .arg(beta)
20730 .arg(&u)
20731 .arg(&wb16)
20732 .arg(qb16)
20733 .arg(pb16)
20734 .arg(o)
20735 .arg(&scale)
20736 .arg(state_in)
20737 .arg(&mut *state_out)
20738 .arg(&hi)
20739 .arg(&ti)
20740 .arg(&ci)
20741 .arg(&hki);
20742 unsafe {
20743 b.launch(cfg)?;
20744 }
20745 }
20746 return Ok(());
20747 }
20748 let mut y16 = self.alloc_u8_uninit(nc * h * c * D * 2)?;
20752 let mut ssnap16 = self.alloc_u8_uninit(nc * h * D * D * 2)?;
20753 {
20754 let f = self.func("gdn_chunk_state_mma");
20755 let cfg = LaunchConfig {
20756 grid_dim: (h as u32, NSPLIT, 1),
20757 block_dim: (256, 1, 1),
20758 shared_mem_bytes: 0,
20759 };
20760 let hki = hk as i32;
20761 let __s_b = self.gpu.stream();
20762 let mut b = __s_b.launch_builder(&f);
20763 b.arg(kb16_ref)
20764 .arg(&gcum)
20765 .arg(beta)
20766 .arg(&u)
20767 .arg(&wb16)
20768 .arg(&mut y16)
20769 .arg(&mut ssnap16)
20770 .arg(state_in)
20771 .arg(&mut *state_out)
20772 .arg(&hi)
20773 .arg(&ti)
20774 .arg(&ci)
20775 .arg(&hki);
20776 unsafe {
20777 b.launch(cfg)?;
20778 }
20779 }
20780 {
20781 let f = self.func("gdn_chunk_output_mma");
20783 let jt = ((c + 31) / 32) as u32;
20784 let cfg = LaunchConfig {
20785 grid_dim: (nc as u32, h as u32, jt),
20786 block_dim: (256, 1, 1),
20787 shared_mem_bytes: 0,
20788 };
20789 let hki = hk as i32;
20790 let __s_b = self.gpu.stream();
20791 let mut b = __s_b.launch_builder(&f);
20792 b.arg(q)
20793 .arg(&gcum)
20794 .arg(&p)
20795 .arg(&y16)
20796 .arg(&ssnap16)
20797 .arg(o)
20798 .arg(&hi)
20799 .arg(&ti)
20800 .arg(&ci)
20801 .arg(&scale)
20802 .arg(&hki);
20803 unsafe {
20804 b.launch(cfg)?;
20805 }
20806 }
20807 return Ok(());
20808 }
20809 {
20810 let f = self.func("gdn_chunk_state_f32");
20812 let cfg = LaunchConfig {
20813 grid_dim: (h as u32, NSPLIT, 1),
20814 block_dim: (256, 1, 1),
20815 shared_mem_bytes: 0,
20816 };
20817 let __s_b = self.gpu.stream();
20818 let mut b = __s_b.launch_builder(&f);
20819 b.arg(k)
20820 .arg(&gcum)
20821 .arg(beta)
20822 .arg(&u)
20823 .arg(&w)
20824 .arg(&mut y)
20825 .arg(&mut ssnap)
20826 .arg(state_in)
20827 .arg(&mut *state_out)
20828 .arg(&hi)
20829 .arg(&ti)
20830 .arg(&ci);
20831 unsafe {
20832 b.launch(cfg)?;
20833 }
20834 }
20835 {
20836 let f = self.func("gdn_chunk_output_f32");
20838 let jt = ((c + 31) / 32) as u32;
20839 let cfg = LaunchConfig {
20840 grid_dim: (nc as u32, h as u32, jt),
20841 block_dim: (256, 1, 1),
20842 shared_mem_bytes: 0,
20843 };
20844 let __s_b = self.gpu.stream();
20845 let mut b = __s_b.launch_builder(&f);
20846 b.arg(q)
20847 .arg(&gcum)
20848 .arg(&p)
20849 .arg(&y)
20850 .arg(&ssnap)
20851 .arg(o)
20852 .arg(&hi)
20853 .arg(&ti)
20854 .arg(&ci)
20855 .arg(&scale);
20856 unsafe {
20857 b.launch(cfg)?;
20858 }
20859 }
20860 Ok(())
20861 }
20862
20863 #[allow(clippy::too_many_arguments)]
20872 #[allow(clippy::too_many_arguments)]
20873 pub fn gdn_scan_prefill(
20874 &self,
20875 q: &CudaSlice<f32>,
20876 k: &CudaSlice<f32>,
20877 v: &CudaSlice<f32>,
20878 g: &CudaSlice<f32>,
20879 beta: &CudaSlice<f32>,
20880 kb16_pre: Option<&CudaSlice<u8>>,
20881 qb16_pre: Option<&CudaSlice<u8>>,
20882 state_in: &CudaSlice<f32>,
20883 state_out: &mut CudaSlice<f32>,
20884 o: &mut CudaSlice<f32>,
20885 n_head: usize,
20886 t: usize,
20887 scale: f32,
20888 hk: usize,
20889 ) -> Result<(), Box<dyn std::error::Error>> {
20890 if std::env::var("MEMRA_GDN_DIFF").is_ok() && t >= 16 {
20891 assert!(hk == n_head, "GDN_DIFF oracle is broadcast-only");
20892 return self.gdn_scan_diff(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale);
20893 }
20894 if Self::gdn_chunked_enabled() && t >= 16 {
20895 self.gdn_scan_chunked(
20896 q,
20897 k,
20898 v,
20899 g,
20900 beta,
20901 kb16_pre,
20902 qb16_pre,
20903 state_in,
20904 state_out,
20905 o,
20906 n_head,
20907 t,
20908 scale,
20909 Self::gdn_chunk_size(),
20910 hk,
20911 )
20912 } else {
20913 assert!(
20914 hk == n_head,
20915 "s128 scan is broadcast-only (prep guarantees by predicate)"
20916 );
20917 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)
20918 }
20919 }
20920
20921 #[allow(clippy::too_many_arguments)]
20923 fn gdn_scan_diff(
20924 &self,
20925 q: &CudaSlice<f32>,
20926 k: &CudaSlice<f32>,
20927 v: &CudaSlice<f32>,
20928 g: &CudaSlice<f32>,
20929 beta: &CudaSlice<f32>,
20930 state_in: &CudaSlice<f32>,
20931 state_out: &mut CudaSlice<f32>,
20932 o: &mut CudaSlice<f32>,
20933 n_head: usize,
20934 t: usize,
20935 scale: f32,
20936 ) -> Result<(), Box<dyn std::error::Error>> {
20937 static CALL: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
20938 let call = CALL.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
20939 let mut o_c = self.uninit(o.len())?;
20940 let mut st_c = self.uninit(state_out.len())?;
20941 self.gdn_scan_chunked(
20942 q,
20943 k,
20944 v,
20945 g,
20946 beta,
20947 None,
20948 None,
20949 state_in,
20950 &mut st_c,
20951 &mut o_c,
20952 n_head,
20953 t,
20954 scale,
20955 Self::gdn_chunk_size(),
20956 n_head,
20957 )?;
20958 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)?;
20959 let (oh_s, oh_c) = (self.dtoh(o)?, self.dtoh(&o_c)?);
20960 let (sh_s, sh_c) = (self.dtoh(state_out)?, self.dtoh(&st_c)?);
20961 let stats = |a: &[f32], b: &[f32]| -> (f32, f32, f64) {
20962 let mut max_abs = 0f32;
20963 let mut max_rel = 0f32;
20964 let mut sum_rel = 0f64;
20965 for (x, y) in a.iter().zip(b) {
20966 let ad = (x - y).abs();
20967 let rel = ad / x.abs().max(y.abs()).max(1e-3);
20968 if ad > max_abs {
20969 max_abs = ad;
20970 }
20971 if rel > max_rel {
20972 max_rel = rel;
20973 }
20974 sum_rel += rel as f64;
20975 }
20976 (max_abs, max_rel, sum_rel / a.len() as f64)
20977 };
20978 let (o_ma, o_mr, o_mean) = stats(&oh_s, &oh_c);
20979 let (s_ma, s_mr, s_mean) = stats(&sh_s, &sh_c);
20980 println!(
20981 "[gdn-diff call {call:3} T={t} C={}] out: max_abs={o_ma:.3e} max_rel={o_mr:.3e} mean_rel={o_mean:.3e} | \
20982 state: max_abs={s_ma:.3e} max_rel={s_mr:.3e} mean_rel={s_mean:.3e}",
20983 Self::gdn_chunk_size()
20984 );
20985 Ok(())
20986 }
20987
20988 pub fn gdn_glog(
20990 &self,
20991 alpha: &CudaSlice<f32>,
20992 dt_bias: &CudaSlice<f32>,
20993 a: &CudaSlice<f32>,
20994 g_log: &mut CudaSlice<f32>,
20995 n_head: usize,
20996 t: usize,
20997 ) -> Result<(), Box<dyn std::error::Error>> {
20998 let f = self.func("gdn_glog_f32");
20999 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
21000 let (h, ti) = (n_head as i32, t as i32);
21001 let __s_b = self.gpu.stream();
21002 let mut b = __s_b.launch_builder(&f);
21003 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
21004 unsafe {
21005 b.launch(cfg)?;
21006 }
21007 Ok(())
21008 }
21009
21010 pub fn sigmoid_v(
21013 &self,
21014 x: &cudarc::driver::CudaView<f32>,
21015 y: &mut CudaSlice<f32>,
21016 n: usize,
21017 ) -> Result<(), Box<dyn std::error::Error>> {
21018 let f = self.func("sigmoid_f32");
21019 let cfg = LaunchConfig::for_num_elems(n as u32);
21020 let ni = n as i32;
21021 let __s_b = self.gpu.stream();
21022 let mut b = __s_b.launch_builder(&f);
21023 b.arg(x).arg(y).arg(&ni);
21024 unsafe {
21025 b.launch(cfg)?;
21026 }
21027 Ok(())
21028 }
21029
21030 pub fn gdn_glog_v(
21031 &self,
21032 alpha: &cudarc::driver::CudaView<f32>,
21033 dt_bias: &CudaSlice<f32>,
21034 a: &CudaSlice<f32>,
21035 g_log: &mut CudaSlice<f32>,
21036 n_head: usize,
21037 t: usize,
21038 ) -> Result<(), Box<dyn std::error::Error>> {
21039 let f = self.func("gdn_glog_f32");
21040 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
21041 let (h, ti) = (n_head as i32, t as i32);
21042 let __s_b = self.gpu.stream();
21043 let mut b = __s_b.launch_builder(&f);
21044 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
21045 unsafe {
21046 b.launch(cfg)?;
21047 }
21048 Ok(())
21049 }
21050
21051 pub fn sigmoid(
21052 &self,
21053 x: &CudaSlice<f32>,
21054 y: &mut CudaSlice<f32>,
21055 n: usize,
21056 ) -> Result<(), Box<dyn std::error::Error>> {
21057 let f = self.func("sigmoid_f32");
21058 let cfg = LaunchConfig::for_num_elems(n as u32);
21059 let ni = n as i32;
21060 let __s_b = self.gpu.stream();
21061 let mut b = __s_b.launch_builder(&f);
21062 b.arg(x).arg(y).arg(&ni);
21063 unsafe {
21064 b.launch(cfg)?;
21065 }
21066 Ok(())
21067 }
21068
21069 pub fn sig_mul_f16out(
21072 &self,
21073 a: &CudaSlice<f32>,
21074 g: &CudaSlice<f32>,
21075 dst: &mut CudaSlice<f32>,
21076 dst16: &mut CudaSlice<u8>,
21077 n: usize,
21078 ) -> Result<(), Box<dyn std::error::Error>> {
21079 let f = self.func("sig_mul_f16out_f32");
21080 let cfg = LaunchConfig::for_num_elems(n as u32);
21081 let ni = n as i32;
21082 let __s_b = self.gpu.stream();
21083 let mut b = __s_b.launch_builder(&f);
21084 b.arg(a).arg(g).arg(dst).arg(dst16).arg(&ni);
21085 unsafe {
21086 b.launch(cfg)?;
21087 }
21088 Ok(())
21089 }
21090
21091 #[allow(clippy::too_many_arguments)]
21100 pub fn attn_head_gate(
21101 &self,
21102 a: &CudaSlice<f32>,
21103 g: &CudaSlice<f32>,
21104 dst: &mut CudaSlice<f32>,
21105 dst16: Option<&mut CudaSlice<u8>>,
21106 head_dim: usize,
21107 n_head: usize,
21108 t: usize,
21109 ) -> Result<(), Box<dyn std::error::Error>> {
21110 let f = self.func("attn_head_gate_f32");
21111 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
21112 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
21113 let d16: u64 = match dst16 {
21115 Some(d) => self.addr_u8(d),
21116 None => 0,
21117 };
21118 let __s_b = self.gpu.stream();
21119 let mut b = __s_b.launch_builder(&f);
21120 b.arg(a)
21121 .arg(g)
21122 .arg(dst)
21123 .arg(&d16)
21124 .arg(&hd)
21125 .arg(&nh)
21126 .arg(&ti);
21127 unsafe {
21128 b.launch(cfg)?;
21129 }
21130 Ok(())
21131 }
21132
21133 #[allow(clippy::too_many_arguments)]
21142 pub fn swiglu_clamped_mul_scaled(
21143 &self,
21144 gate: &CudaSlice<f32>,
21145 up: &CudaSlice<f32>,
21146 gs: f32,
21147 us: f32,
21148 limit: f32,
21149 dst: &mut CudaSlice<f32>,
21150 n: usize,
21151 ) -> Result<(), Box<dyn std::error::Error>> {
21152 debug_assert!(
21153 limit > 1e-6,
21154 "swiglu_clamped needs a live limit; use silu_mul_scaled"
21155 );
21156 let f = self.func("swiglu_clamped_mul_scaled_f32");
21157 let cfg = LaunchConfig::for_num_elems(n as u32);
21158 let ni = n as i32;
21159 let __s_b = self.gpu.stream();
21160 let mut b = __s_b.launch_builder(&f);
21161 b.arg(gate)
21162 .arg(up)
21163 .arg(&gs)
21164 .arg(&us)
21165 .arg(&limit)
21166 .arg(dst)
21167 .arg(&ni);
21168 unsafe {
21169 b.launch(cfg)?;
21170 }
21171 Ok(())
21172 }
21173
21174 pub fn gated_rmsnorm(
21176 &self,
21177 o: &CudaSlice<f32>,
21178 w: &CudaSlice<f32>,
21179 z: &CudaSlice<f32>,
21180 dst: &mut CudaSlice<f32>,
21181 ncols: usize,
21182 nrows: usize,
21183 eps: f32,
21184 ) -> Result<(), Box<dyn std::error::Error>> {
21185 let f = self.func("gated_rmsnorm_f32");
21186 let cfg = LaunchConfig {
21187 grid_dim: (nrows as u32, 1, 1),
21188 block_dim: (128, 1, 1),
21189 shared_mem_bytes: 0,
21190 };
21191 let (nc, e) = (ncols as i32, eps);
21192 let __s_b = self.gpu.stream();
21193 let mut b = __s_b.launch_builder(&f);
21194 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
21195 unsafe {
21196 b.launch(cfg)?;
21197 }
21198 Ok(())
21199 }
21200
21201 pub fn gated_rmsnorm_f16out(
21204 &self,
21205 o: &CudaSlice<f32>,
21206 w: &CudaSlice<f32>,
21207 z: &CudaSlice<f32>,
21208 dst: &mut CudaSlice<f32>,
21209 dst16: &mut CudaSlice<u8>,
21210 ncols: usize,
21211 nrows: usize,
21212 eps: f32,
21213 ) -> Result<(), Box<dyn std::error::Error>> {
21214 let f = self.func("gated_rmsnorm_f16out_f32");
21215 let cfg = LaunchConfig {
21217 grid_dim: (nrows as u32, 1, 1),
21218 block_dim: (128, 1, 1),
21219 shared_mem_bytes: 0,
21220 };
21221 let (nc, e) = (ncols as i32, eps);
21222 let __s_b = self.gpu.stream();
21223 let mut b = __s_b.launch_builder(&f);
21224 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
21225 unsafe {
21226 b.launch(cfg)?;
21227 }
21228 Ok(())
21229 }
21230
21231 #[allow(clippy::too_many_arguments)]
21235 pub fn add_rms_norm_zq8(
21236 &self,
21237 a: &CudaSlice<f32>,
21238 b_in: &CudaSlice<f32>,
21239 w: &CudaSlice<f32>,
21240 res: &mut CudaSlice<f32>,
21241 z: &mut CudaSlice<f32>,
21242 ncols: usize,
21243 nrows: usize,
21244 eps: f32,
21245 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
21246 assert!(ncols % 32 == 0);
21247 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
21248 let mut d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
21249 let f = self.func("add_rms_norm_zq8");
21250 let cfg = LaunchConfig {
21251 grid_dim: (nrows as u32, 1, 1),
21252 block_dim: (1024, 1, 1),
21253 shared_mem_bytes: 0,
21254 };
21255 let (nc, ep) = (ncols as i32, eps);
21256 let __s_b = self.gpu.stream();
21257 let mut b = __s_b.launch_builder(&f);
21258 b.arg(a)
21259 .arg(b_in)
21260 .arg(w)
21261 .arg(res)
21262 .arg(z)
21263 .arg(&mut q)
21264 .arg(&mut d)
21265 .arg(&nc)
21266 .arg(&ep);
21267 unsafe {
21268 b.launch(cfg)?;
21269 }
21270 Ok((q, d))
21271 }
21272
21273 pub fn gated_rmsnorm_zv(
21278 &self,
21279 o: &CudaSlice<f32>,
21280 w: &CudaSlice<f32>,
21281 z: &cudarc::driver::CudaView<f32>,
21282 dst: &mut CudaSlice<f32>,
21283 ncols: usize,
21284 nrows: usize,
21285 eps: f32,
21286 ) -> Result<(), Box<dyn std::error::Error>> {
21287 let f = self.func("gated_rmsnorm_f32");
21288 let cfg = LaunchConfig {
21289 grid_dim: (nrows as u32, 1, 1),
21290 block_dim: (128, 1, 1),
21291 shared_mem_bytes: 0,
21292 };
21293 let (nc, e) = (ncols as i32, eps);
21294 let __s_b = self.gpu.stream();
21295 let mut b = __s_b.launch_builder(&f);
21296 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
21297 unsafe {
21298 b.launch(cfg)?;
21299 }
21300 Ok(())
21301 }
21302
21303 pub fn gated_rmsnorm_f16out_zv(
21304 &self,
21305 o: &CudaSlice<f32>,
21306 w: &CudaSlice<f32>,
21307 z: &cudarc::driver::CudaView<f32>,
21308 dst: &mut CudaSlice<f32>,
21309 dst16: &mut CudaSlice<u8>,
21310 ncols: usize,
21311 nrows: usize,
21312 eps: f32,
21313 ) -> Result<(), Box<dyn std::error::Error>> {
21314 let f = self.func("gated_rmsnorm_f16out_f32");
21315 let cfg = LaunchConfig {
21317 grid_dim: (nrows as u32, 1, 1),
21318 block_dim: (128, 1, 1),
21319 shared_mem_bytes: 0,
21320 };
21321 let (nc, e) = (ncols as i32, eps);
21322 let __s_b = self.gpu.stream();
21323 let mut b = __s_b.launch_builder(&f);
21324 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
21325 unsafe {
21326 b.launch(cfg)?;
21327 }
21328 Ok(())
21329 }
21330
21331 pub fn gated_rmsnorm_q8_1(
21332 &self,
21333 o: &CudaSlice<f32>,
21334 w: &CudaSlice<f32>,
21335 z: &CudaSlice<f32>,
21336 ncols: usize,
21337 nrows: usize,
21338 eps: f32,
21339 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
21340 assert!(ncols % 32 == 0);
21341 let f = self.func("gated_rmsnorm_q8_1");
21342 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
21343 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
21344 let cfg = LaunchConfig {
21345 grid_dim: (nrows as u32, 1, 1),
21346 block_dim: (128, 1, 1),
21347 shared_mem_bytes: 0,
21348 };
21349 let (nc, ep) = (ncols as i32, eps);
21350 let __s_b = self.gpu.stream();
21351 let mut b = __s_b.launch_builder(&f);
21352 b.arg(o)
21353 .arg(w)
21354 .arg(z)
21355 .arg(&mut out_q)
21356 .arg(&mut out_d)
21357 .arg(&nc)
21358 .arg(&ep);
21359 unsafe {
21360 b.launch(cfg)?;
21361 }
21362 Ok((out_q, out_d))
21363 }
21364
21365 pub fn transpose(
21367 &self,
21368 inp: &CudaSlice<f32>,
21369 rows: usize,
21370 cols: usize,
21371 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21372 let f = self.func("transpose_f32");
21373 let mut out = self.zeros(rows * cols)?;
21374 let cfg = LaunchConfig::for_num_elems((rows * cols) as u32);
21375 let (r, c) = (rows as i32, cols as i32);
21376 let __s_b = self.gpu.stream();
21377 let mut b = __s_b.launch_builder(&f);
21378 b.arg(inp).arg(&mut out).arg(&r).arg(&c);
21379 unsafe {
21380 b.launch(cfg)?;
21381 }
21382 Ok(out)
21383 }
21384
21385 pub fn repeat_heads(
21387 &self,
21388 inp: &CudaSlice<f32>,
21389 out: &mut CudaSlice<f32>,
21390 head_dim: usize,
21391 n_in: usize,
21392 n_out: usize,
21393 t: usize,
21394 ) -> Result<(), Box<dyn std::error::Error>> {
21395 let f = self.func("repeat_heads_f32");
21396 let cfg = LaunchConfig::for_num_elems((head_dim * n_out * t) as u32);
21397 let (hd, ni, no, ti) = (head_dim as i32, n_in as i32, n_out as i32, t as i32);
21398 let __s_b = self.gpu.stream();
21399 let mut b = __s_b.launch_builder(&f);
21400 b.arg(inp).arg(out).arg(&hd).arg(&ni).arg(&no).arg(&ti);
21401 unsafe {
21402 b.launch(cfg)?;
21403 }
21404 Ok(())
21405 }
21406
21407 pub fn q_gate_split(
21410 &self,
21411 qf: &CudaSlice<f32>,
21412 q_out: &mut CudaSlice<f32>,
21413 gate_out: &mut CudaSlice<f32>,
21414 head_dim: usize,
21415 n_head: usize,
21416 t: usize,
21417 ) -> Result<(), Box<dyn std::error::Error>> {
21418 let f = self.func("q_gate_split_f32");
21419 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
21420 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
21421 let __s_b = self.gpu.stream();
21422 let mut b = __s_b.launch_builder(&f);
21423 b.arg(qf)
21424 .arg(q_out)
21425 .arg(gate_out)
21426 .arg(&hd)
21427 .arg(&nh)
21428 .arg(&ti);
21429 unsafe {
21430 b.launch(cfg)?;
21431 }
21432 Ok(())
21433 }
21434
21435 pub fn qkv_to_gdn_repack(
21439 &self,
21440 conv_out: &CudaSlice<f32>,
21441 q_g: &mut CudaSlice<f32>,
21442 k_g: &mut CudaSlice<f32>,
21443 v_g: &mut CudaSlice<f32>,
21444 d_state: usize,
21445 num_v: usize,
21446 num_k: usize,
21447 key_dim: usize,
21448 t: usize,
21449 ) -> Result<(), Box<dyn std::error::Error>> {
21450 let f = self.func("qkv_to_gdn_repack_f32");
21451 let cfg = LaunchConfig::for_num_elems((d_state * num_v * t) as u32);
21452 let (ds, nv, nk, kd, ti) = (
21453 d_state as i32,
21454 num_v as i32,
21455 num_k as i32,
21456 key_dim as i32,
21457 t as i32,
21458 );
21459 let __s_b = self.gpu.stream();
21460 let mut b = __s_b.launch_builder(&f);
21461 b.arg(conv_out)
21462 .arg(q_g)
21463 .arg(k_g)
21464 .arg(v_g)
21465 .arg(&ds)
21466 .arg(&nv)
21467 .arg(&nk)
21468 .arg(&kd)
21469 .arg(&ti);
21470 unsafe {
21471 b.launch(cfg)?;
21472 }
21473 Ok(())
21474 }
21475
21476 pub fn conv_left_pad(
21479 &self,
21480 src: &CudaSlice<f32>,
21481 dst: &mut CudaSlice<f32>,
21482 conv_dim: usize,
21483 t: usize,
21484 pad: usize,
21485 ) -> Result<(), Box<dyn std::error::Error>> {
21486 let f = self.func("conv_left_pad_f32");
21487 let cfg = LaunchConfig::for_num_elems((conv_dim * t) as u32);
21488 let (cd, ti, p) = (conv_dim as i32, t as i32, pad as i32);
21489 let __s_b = self.gpu.stream();
21490 let mut b = __s_b.launch_builder(&f);
21491 b.arg(src).arg(dst).arg(&cd).arg(&ti).arg(&p);
21492 unsafe {
21493 b.launch(cfg)?;
21494 }
21495 Ok(())
21496 }
21497
21498 pub fn conv_assemble_and_roll(
21502 &self,
21503 qkv_col: &CudaSlice<f32>,
21504 conv_state: &mut CudaSlice<f32>,
21505 conv_in: &mut CudaSlice<f32>,
21506 conv_dim: usize,
21507 pad: usize,
21508 ) -> Result<(), Box<dyn std::error::Error>> {
21509 let f = self.func("conv_assemble_and_roll_f32");
21510 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
21511 let (cd, p) = (conv_dim as i32, pad as i32);
21512 let __s_b = self.gpu.stream();
21513 let mut b = __s_b.launch_builder(&f);
21514 b.arg(qkv_col).arg(conv_state).arg(conv_in).arg(&cd).arg(&p);
21515 unsafe {
21516 b.launch(cfg)?;
21517 }
21518 Ok(())
21519 }
21520
21521 pub fn ssm_conv1d_fused_decode(
21527 &self,
21528 qkv_col: &CudaSlice<f32>,
21529 conv_state: &mut CudaSlice<f32>,
21530 w: &CudaSlice<f32>,
21531 conv_out: &mut CudaSlice<f32>,
21532 conv_dim: usize,
21533 d_conv: usize,
21534 ) -> Result<(), Box<dyn std::error::Error>> {
21535 let f = self.func("ssm_conv1d_fused_decode_f32");
21536 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
21537 let (cd, dc) = (conv_dim as i32, d_conv as i32);
21538 let __s_b = self.gpu.stream();
21539 let mut b = __s_b.launch_builder(&f);
21540 b.arg(qkv_col)
21541 .arg(conv_state)
21542 .arg(w)
21543 .arg(conv_out)
21544 .arg(&cd)
21545 .arg(&dc);
21546 unsafe {
21547 b.launch(cfg)?;
21548 }
21549 Ok(())
21550 }
21551
21552 pub fn slice_range(
21555 &self,
21556 src: &CudaSlice<f32>,
21557 start: usize,
21558 len: usize,
21559 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21560 let host = self.gpu.stream().clone_dtoh(src)?;
21561 self.gpu.stream().synchronize()?;
21562 Ok(self.htod(&host[start..start + len])?)
21563 }
21564}
21565
21566#[cfg(test)]
21567mod target_dispatch_tests {
21568 use super::legacy_quant_gemm_allowed;
21569
21570 #[test]
21571 fn legacy_quant_gemm_arch_policy_honors_the_escape_hatch() {
21572 assert!(legacy_quant_gemm_allowed(false, false, false));
21574 assert!(!legacy_quant_gemm_allowed(false, false, true));
21575 assert!(!legacy_quant_gemm_allowed(true, false, false));
21577 assert!(!legacy_quant_gemm_allowed(true, false, true));
21578 assert!(legacy_quant_gemm_allowed(true, true, false));
21580 assert!(!legacy_quant_gemm_allowed(true, true, true));
21581 }
21582
21583 #[cfg(all(memra_portable_cuda, not(memra_hopper_mma)))]
21584 #[test]
21585 fn portable_build_disables_legacy_quant_gemm_without_an_env_override() {
21586 assert!(!legacy_quant_gemm_allowed(
21587 cfg!(memra_portable_cuda),
21588 cfg!(memra_hopper_mma),
21589 false
21590 ));
21591 }
21592
21593 #[cfg(memra_hopper_mma)]
21594 #[test]
21595 fn hopper_mma_build_re_admits_legacy_quant_gemm() {
21596 assert!(legacy_quant_gemm_allowed(
21597 cfg!(memra_portable_cuda),
21598 cfg!(memra_hopper_mma),
21599 false
21600 ));
21601 assert!(super::portable_mma_gated() == false);
21602 }
21603}
21604
21605impl memra_kv::KvDev for Engine {
21608 fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21609 Engine::zeros(self, n)
21610 }
21611 fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21612 Engine::uninit(self, n)
21613 }
21614 fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
21615 Engine::alloc_u8(self, n)
21616 }
21617 fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
21618 Engine::htod_i32(self, v)
21619 }
21620 fn clone_dtod(
21621 &self,
21622 src: &CudaSlice<f32>,
21623 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21624 Engine::clone_dtod(self, src)
21625 }
21626 fn copy_into(
21627 &self,
21628 dst: &mut CudaSlice<f32>,
21629 off: usize,
21630 src: &CudaSlice<f32>,
21631 len: usize,
21632 ) -> Result<(), Box<dyn std::error::Error>> {
21633 Engine::copy_into(self, dst, off, src, len)
21634 }
21635 fn set_i32_one(
21636 &self,
21637 d: &mut CudaSlice<i32>,
21638 v: i32,
21639 ) -> Result<(), Box<dyn std::error::Error>> {
21640 Engine::set_i32_one(self, d, v)
21641 }
21642}