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 #[allow(clippy::type_complexity)]
10712 pub fn matmul_nvfp4_fused4(
10713 &self,
10714 w0: &crate::model::GpuTensor,
10715 w1: &crate::model::GpuTensor,
10716 w2: &crate::model::GpuTensor,
10717 w3: &crate::model::GpuTensor,
10718 aq: &CudaSlice<i8>,
10719 ad: &CudaSlice<f32>,
10720 m: usize,
10721 ) -> Result<
10722 Option<(
10723 CudaSlice<f32>,
10724 CudaSlice<f32>,
10725 CudaSlice<f32>,
10726 CudaSlice<f32>,
10727 )>,
10728 Box<dyn std::error::Error>,
10729 > {
10730 use crate::model::GpuTensor;
10731 if m != 1
10733 || std::env::var("MEMRA_NVFP4_FUSED4").as_deref() == Ok("0")
10734 || !self.mmvq_supports(QT_NVFP4)
10735 || !self.uses_q8_1_fast(w0)
10736 || !self.uses_q8_1_fast(w1)
10737 || !self.uses_q8_1_fast(w2)
10738 || !self.uses_q8_1_fast(w3)
10739 {
10740 return Ok(None);
10741 }
10742 let unpack = |w: &crate::model::GpuTensor| match w {
10743 GpuTensor::Quant {
10744 bytes,
10745 qtype,
10746 scale,
10747 rp,
10748 ..
10749 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
10750 _ => None,
10751 };
10752 let (Some(p0), Some(p1), Some(p2), Some(p3)) =
10753 (unpack(w0), unpack(w1), unpack(w2), unpack(w3))
10754 else {
10755 return Ok(None);
10756 };
10757 let in_f = w0.in_features();
10758 if w1.in_features() != in_f || w2.in_features() != in_f || w3.in_features() != in_f {
10759 return Ok(None);
10760 }
10761 let (o0, o1, o2, o3) = (
10762 w0.out_features(),
10763 w1.out_features(),
10764 w2.out_features(),
10765 w3.out_features(),
10766 );
10767 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
10769 let rows_pb = ROWS_PER_BLOCK * RPW;
10770 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
10771 let f = self.func("qmatvec_nvfp4_mmvq_fused4_rp");
10772 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
10773 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
10774 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
10775 let mut y3 = self.alloc_uninit::<f32>(m * o3)?;
10776 let cfg = LaunchConfig {
10777 grid_dim: (nb(o0) + nb(o1) + nb(o2) + nb(o3), m as u32, 1),
10778 block_dim: (32, ROWS_PER_BLOCK, 1),
10779 shared_mem_bytes: 0,
10780 };
10781 let (inf, oi0, oi1, oi2, oi3, mi) = (
10782 in_f as i32,
10783 o0 as i32,
10784 o1 as i32,
10785 o2 as i32,
10786 o3 as i32,
10787 m as i32,
10788 );
10789 let (b0, b1, b2, b3) = unsafe { (&*p0.0, &*p1.0, &*p2.0, &*p3.0) };
10792 let __s_b = self.gpu.stream();
10793 let mut b = __s_b.launch_builder(&f);
10794 b.arg(b0)
10795 .arg(b1)
10796 .arg(b2)
10797 .arg(b3)
10798 .arg(aq)
10799 .arg(ad)
10800 .arg(&mut y0)
10801 .arg(&mut y1)
10802 .arg(&mut y2)
10803 .arg(&mut y3)
10804 .arg(&inf)
10805 .arg(&oi0)
10806 .arg(&oi1)
10807 .arg(&oi2)
10808 .arg(&oi3)
10809 .arg(&mi)
10810 .arg(&p0.1)
10811 .arg(&p1.1)
10812 .arg(&p2.1)
10813 .arg(&p3.1);
10814 unsafe {
10815 b.launch(cfg)?;
10816 }
10817 Ok(Some((y0, y1, y2, y3)))
10818 }
10819
10820 pub fn matmul_q8_fused2(
10828 &self,
10829 w0: &crate::model::GpuTensor,
10830 w1: &crate::model::GpuTensor,
10831 aq: &CudaSlice<i8>,
10832 ad: &CudaSlice<f32>,
10833 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10834 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
10840 return Ok(Some(self.e4m3_fused2_core(
10841 p0.0,
10842 p1.0,
10843 aq,
10844 ad,
10845 w0.in_features(),
10846 p0.1,
10847 p1.1,
10848 p0.2,
10849 p0.3,
10850 p1.3,
10851 )?));
10852 }
10853 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
10854 return Ok(None);
10855 };
10856 Ok(Some(self.q8_fused2_core(
10857 p0.0,
10858 p1.0,
10859 aq,
10860 ad,
10861 w0.in_features(),
10862 p0.1,
10863 p1.1,
10864 p0.2,
10865 )?))
10866 }
10867
10868 #[allow(clippy::too_many_arguments)]
10869 fn q8_fused2_core(
10870 &self,
10871 b0: &CudaSlice<u8>,
10872 b1: &CudaSlice<u8>,
10873 aq: &CudaSlice<i8>,
10874 ad: &CudaSlice<f32>,
10875 in_f: usize,
10876 out0: usize,
10877 out1: usize,
10878 row_bytes: usize,
10879 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10880 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
10882 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
10883 let f = self.func("qmatvec_q8_0_mmvq_fused2");
10884 let mut y0 = self.alloc_uninit::<f32>(out0)?;
10885 let mut y1 = self.alloc_uninit::<f32>(out1)?;
10886 let cfg = LaunchConfig {
10887 grid_dim: (nb0 + nb1, 1, 1),
10888 block_dim: (32, ROWS_PER_BLOCK, 1),
10889 shared_mem_bytes: 0,
10890 };
10891 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
10892 let __s_b = self.gpu.stream();
10893 let mut b = __s_b.launch_builder(&f);
10894 b.arg(b0)
10895 .arg(b1)
10896 .arg(aq)
10897 .arg(ad)
10898 .arg(&mut y0)
10899 .arg(&mut y1)
10900 .arg(&inf)
10901 .arg(&o0)
10902 .arg(&o1)
10903 .arg(&rbl);
10904 unsafe {
10905 b.launch(cfg)?;
10906 }
10907 Ok((y0, y1))
10908 }
10909
10910 pub fn matmul_q8_fused2_x(
10916 &self,
10917 w0: &crate::model::GpuTensor,
10918 w1: &crate::model::GpuTensor,
10919 x: &CudaSlice<f32>,
10920 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10921 if !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
10922 return Ok(None);
10923 }
10924 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
10925 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
10926 return Ok(Some(self.e4m3_fused2_core(
10927 p0.0,
10928 p1.0,
10929 &aq,
10930 &ad,
10931 w0.in_features(),
10932 p0.1,
10933 p1.1,
10934 p0.2,
10935 p0.3,
10936 p1.3,
10937 )?));
10938 }
10939 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
10940 return Ok(None);
10941 };
10942 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
10943 Ok(Some(self.q8_fused2_core(
10944 p0.0,
10945 p1.0,
10946 &aq,
10947 &ad,
10948 w0.in_features(),
10949 p0.1,
10950 p1.1,
10951 p0.2,
10952 )?))
10953 }
10954
10955 #[allow(clippy::too_many_arguments)]
10958 pub fn qmatvec_q8_fused2_raw(
10959 &self,
10960 b0: &CudaSlice<u8>,
10961 b1: &CudaSlice<u8>,
10962 x: &CudaSlice<f32>,
10963 in_f: usize,
10964 out0: usize,
10965 out1: usize,
10966 row_bytes: usize,
10967 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10968 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
10969 self.q8_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes)
10970 }
10971
10972 pub fn matmul_q4_fused3(
10978 &self,
10979 w0: &crate::model::GpuTensor,
10980 w1: &crate::model::GpuTensor,
10981 w2: &crate::model::GpuTensor,
10982 aq: &CudaSlice<i8>,
10983 ad: &CudaSlice<f32>,
10984 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
10985 {
10986 use crate::model::GpuTensor;
10987 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
10988 match w {
10989 GpuTensor::Quant {
10990 qtype, row_bytes, ..
10991 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
10992 _ => None,
10993 }
10994 };
10995 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
10996 return Ok(None);
10997 };
10998 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
10999 return Ok(None);
11000 }
11001 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11005 match w {
11006 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11007 Some(m) => (m, true),
11008 None => (bytes, *rp),
11009 },
11010 _ => unreachable!(),
11011 }
11012 }
11013 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
11014 if rp0 != rp1 || rp1 != rp2 {
11015 return Ok(None);
11016 }
11017 let rp = rp0;
11018 let rpb: u32 = 4;
11019 let mr1 = rp && Self::q40_mr1_on();
11023 let nb = |o: usize| {
11024 if mr1 {
11025 (o as u32).div_ceil(rpb)
11026 } else {
11027 (o as u32).div_ceil(2).div_ceil(rpb)
11028 }
11029 };
11030 let grid = nb(o0) + nb(o1) + nb(o2);
11031 let mut y0 = self.alloc_uninit::<f32>(o0)?;
11032 let mut y1 = self.alloc_uninit::<f32>(o1)?;
11033 let mut y2 = self.alloc_uninit::<f32>(o2)?;
11034 let f = self.func(if mr1 {
11035 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
11036 } else if rp {
11037 "qmatvec_q4_0_mmvq_fused3_rp"
11038 } else {
11039 "qmatvec_q4_0_mmvq_fused3"
11040 });
11041 let cfg = LaunchConfig {
11042 grid_dim: (grid, 1, 1),
11043 block_dim: (32, rpb, 1),
11044 shared_mem_bytes: 0,
11045 };
11046 let inf = w0.in_features() as i32;
11047 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
11048 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
11049 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11052 {
11053 use cudarc::driver::{DevicePtr, DevicePtrMut};
11054 let s = &self.gpu.stream();
11055 let (p0, _g0) = b0.device_ptr(s);
11056 let (p1, _g1) = b1.device_ptr(s);
11057 let (p2, _g2) = b2.device_ptr(s);
11058 let (paq, _g3) = aq.device_ptr(s);
11059 let (pad, _g4) = ad.device_ptr(s);
11060 let (py0, _g5) = y0.device_ptr_mut(s);
11061 let (py1, _g6) = y1.device_ptr_mut(s);
11062 let (py2, _g7) = y2.device_ptr_mut(s);
11063 let mut ps = [
11064 &p0 as *const _ as *mut std::ffi::c_void,
11065 &p1 as *const _ as *mut _,
11066 &p2 as *const _ as *mut _,
11067 &paq as *const _ as *mut _,
11068 &pad as *const _ as *mut _,
11069 &py0 as *const _ as *mut _,
11070 &py1 as *const _ as *mut _,
11071 &py2 as *const _ as *mut _,
11072 &inf as *const _ as *mut _,
11073 &oo0 as *const _ as *mut _,
11074 &oo1 as *const _ as *mut _,
11075 &oo2 as *const _ as *mut _,
11076 &r0 as *const _ as *mut _,
11077 &r1 as *const _ as *mut _,
11078 &r2 as *const _ as *mut _,
11079 ];
11080 unsafe {
11081 self.launch_pdl(
11082 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
11083 (grid, 1, 1),
11084 (32, rpb, 1),
11085 &mut ps,
11086 )?;
11087 }
11088 }
11089 return Ok(Some((y0, y1, y2)));
11090 }
11091 let __s_b = self.gpu.stream();
11092 let mut b = __s_b.launch_builder(&f);
11093 b.arg(b0)
11094 .arg(b1)
11095 .arg(b2)
11096 .arg(aq)
11097 .arg(ad)
11098 .arg(&mut y0)
11099 .arg(&mut y1)
11100 .arg(&mut y2)
11101 .arg(&inf)
11102 .arg(&oo0)
11103 .arg(&oo1)
11104 .arg(&oo2)
11105 .arg(&r0)
11106 .arg(&r1)
11107 .arg(&r2);
11108 unsafe {
11109 b.launch(cfg)?;
11110 }
11111 Ok(Some((y0, y1, y2)))
11112 }
11113
11114 #[allow(clippy::too_many_arguments)]
11117 pub fn matmul_q4_fused3_into(
11118 &self,
11119 w0: &crate::model::GpuTensor,
11120 w1: &crate::model::GpuTensor,
11121 w2: &crate::model::GpuTensor,
11122 aq: &CudaSlice<i8>,
11123 ad: &CudaSlice<f32>,
11124 y0: &mut CudaSlice<f32>,
11125 y1: &mut CudaSlice<f32>,
11126 y2: &mut CudaSlice<f32>,
11127 ) -> Result<bool, Box<dyn std::error::Error>> {
11128 use crate::model::GpuTensor;
11129 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11130 match w {
11131 GpuTensor::Quant {
11132 qtype, row_bytes, ..
11133 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11134 _ => None,
11135 }
11136 };
11137 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
11138 return Ok(false);
11139 };
11140 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
11141 return Ok(false);
11142 }
11143 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11144 match w {
11145 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11146 Some(m) => (m, true),
11147 None => (bytes, *rp),
11148 },
11149 _ => unreachable!(),
11150 }
11151 }
11152 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
11153 if rp0 != rp1 || rp1 != rp2 {
11154 return Ok(false);
11155 }
11156 let rp = rp0;
11157 let rpb: u32 = 4;
11158 let mr1 = rp && Self::q40_mr1_on();
11159 let nb = |o: usize| {
11160 if mr1 {
11161 (o as u32).div_ceil(rpb)
11162 } else {
11163 (o as u32).div_ceil(2).div_ceil(rpb)
11164 }
11165 };
11166 let grid = nb(o0) + nb(o1) + nb(o2);
11167 debug_assert!(y0.len() >= o0 && y1.len() >= o1 && y2.len() >= o2);
11168 let f = self.func(if mr1 {
11169 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
11170 } else if rp {
11171 "qmatvec_q4_0_mmvq_fused3_rp"
11172 } else {
11173 "qmatvec_q4_0_mmvq_fused3"
11174 });
11175 let cfg = LaunchConfig {
11176 grid_dim: (grid, 1, 1),
11177 block_dim: (32, rpb, 1),
11178 shared_mem_bytes: 0,
11179 };
11180 let inf = w0.in_features() as i32;
11181 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
11182 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
11183 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11185 use cudarc::driver::{DevicePtr, DevicePtrMut};
11186 let s = &self.gpu.stream();
11187 let (p0, _g0) = b0.device_ptr(s);
11188 let (p1, _g1) = b1.device_ptr(s);
11189 let (p2, _g2) = b2.device_ptr(s);
11190 let (paq, _g3) = aq.device_ptr(s);
11191 let (pad, _g4) = ad.device_ptr(s);
11192 let (py0, _g5) = y0.device_ptr_mut(s);
11193 let (py1, _g6) = y1.device_ptr_mut(s);
11194 let (py2, _g7) = y2.device_ptr_mut(s);
11195 let mut ps = [
11196 &p0 as *const _ as *mut std::ffi::c_void,
11197 &p1 as *const _ as *mut _,
11198 &p2 as *const _ as *mut _,
11199 &paq as *const _ as *mut _,
11200 &pad as *const _ as *mut _,
11201 &py0 as *const _ as *mut _,
11202 &py1 as *const _ as *mut _,
11203 &py2 as *const _ as *mut _,
11204 &inf as *const _ as *mut _,
11205 &oo0 as *const _ as *mut _,
11206 &oo1 as *const _ as *mut _,
11207 &oo2 as *const _ as *mut _,
11208 &r0 as *const _ as *mut _,
11209 &r1 as *const _ as *mut _,
11210 &r2 as *const _ as *mut _,
11211 ];
11212 unsafe {
11213 self.launch_pdl(
11214 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
11215 (grid, 1, 1),
11216 (32, rpb, 1),
11217 &mut ps,
11218 )?;
11219 }
11220 return Ok(true);
11221 }
11222 let __s_b = self.gpu.stream();
11223 let mut b = __s_b.launch_builder(&f);
11224 b.arg(b0)
11225 .arg(b1)
11226 .arg(b2)
11227 .arg(aq)
11228 .arg(ad)
11229 .arg(&mut *y0)
11230 .arg(&mut *y1)
11231 .arg(&mut *y2)
11232 .arg(&inf)
11233 .arg(&oo0)
11234 .arg(&oo1)
11235 .arg(&oo2)
11236 .arg(&r0)
11237 .arg(&r1)
11238 .arg(&r2);
11239 unsafe {
11240 b.launch(cfg)?;
11241 }
11242 Ok(true)
11243 }
11244
11245 pub fn matmul_q4_fused2(
11247 &self,
11248 w0: &crate::model::GpuTensor,
11249 w1: &crate::model::GpuTensor,
11250 aq: &CudaSlice<i8>,
11251 ad: &CudaSlice<f32>,
11252 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11253 use crate::model::GpuTensor;
11254 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11255 match w {
11256 GpuTensor::Quant {
11257 qtype, row_bytes, ..
11258 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11259 _ => None,
11260 }
11261 };
11262 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
11263 return Ok(None);
11264 };
11265 if w0.in_features() != w1.in_features() {
11266 return Ok(None);
11267 }
11268 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11270 match w {
11271 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11272 Some(m) => (m, true),
11273 None => (bytes, *rp),
11274 },
11275 _ => unreachable!(),
11276 }
11277 }
11278 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11279 if rp0 != rp1 {
11280 return Ok(None);
11281 }
11282 let rp = rp0;
11283 let rpb: u32 = 4;
11284 let mr1 = rp && Self::q40_mr1_on();
11286 let nb = |o: usize| {
11287 if mr1 {
11288 (o as u32).div_ceil(rpb)
11289 } else {
11290 (o as u32).div_ceil(2).div_ceil(rpb)
11291 }
11292 };
11293 let grid = nb(o0) + nb(o1);
11294 let mut y0 = self.alloc_uninit::<f32>(o0)?;
11295 let mut y1 = self.alloc_uninit::<f32>(o1)?;
11296 let f = self.func(if mr1 {
11297 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
11298 } else if rp {
11299 "qmatvec_q4_0_mmvq_fused2_rp"
11300 } else {
11301 "qmatvec_q4_0_mmvq_fused2"
11302 });
11303 let cfg = LaunchConfig {
11304 grid_dim: (grid, 1, 1),
11305 block_dim: (32, rpb, 1),
11306 shared_mem_bytes: 0,
11307 };
11308 let inf = w0.in_features() as i32;
11309 let (oo0, oo1) = (o0 as i32, o1 as i32);
11310 let (r0, r1) = (rb0 as i64, rb1 as i64);
11311 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11313 {
11314 use cudarc::driver::{DevicePtr, DevicePtrMut};
11315 let s = &self.gpu.stream();
11316 let (p0, _g0) = b0.device_ptr(s);
11317 let (p1, _g1) = b1.device_ptr(s);
11318 let (paq, _g2) = aq.device_ptr(s);
11319 let (pad, _g3) = ad.device_ptr(s);
11320 let (py0, _g4) = y0.device_ptr_mut(s);
11321 let (py1, _g5) = y1.device_ptr_mut(s);
11322 let mut ps = [
11323 &p0 as *const _ as *mut std::ffi::c_void,
11324 &p1 as *const _ as *mut _,
11325 &paq as *const _ as *mut _,
11326 &pad as *const _ as *mut _,
11327 &py0 as *const _ as *mut _,
11328 &py1 as *const _ as *mut _,
11329 &inf as *const _ as *mut _,
11330 &oo0 as *const _ as *mut _,
11331 &oo1 as *const _ as *mut _,
11332 &r0 as *const _ as *mut _,
11333 &r1 as *const _ as *mut _,
11334 ];
11335 unsafe {
11336 self.launch_pdl(
11337 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
11338 (grid, 1, 1),
11339 (32, rpb, 1),
11340 &mut ps,
11341 )?;
11342 }
11343 }
11344 return Ok(Some((y0, y1)));
11345 }
11346 let __s_b = self.gpu.stream();
11347 let mut b = __s_b.launch_builder(&f);
11348 b.arg(b0)
11349 .arg(b1)
11350 .arg(aq)
11351 .arg(ad)
11352 .arg(&mut y0)
11353 .arg(&mut y1)
11354 .arg(&inf)
11355 .arg(&oo0)
11356 .arg(&oo1)
11357 .arg(&r0)
11358 .arg(&r1);
11359 unsafe {
11360 b.launch(cfg)?;
11361 }
11362 Ok(Some((y0, y1)))
11363 }
11364
11365 pub fn matmul_q4_fused2_into(
11367 &self,
11368 w0: &crate::model::GpuTensor,
11369 w1: &crate::model::GpuTensor,
11370 aq: &CudaSlice<i8>,
11371 ad: &CudaSlice<f32>,
11372 y0: &mut CudaSlice<f32>,
11373 y1: &mut CudaSlice<f32>,
11374 ) -> Result<bool, Box<dyn std::error::Error>> {
11375 use crate::model::GpuTensor;
11376 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11377 match w {
11378 GpuTensor::Quant {
11379 qtype, row_bytes, ..
11380 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11381 _ => None,
11382 }
11383 };
11384 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
11385 return Ok(false);
11386 };
11387 if w0.in_features() != w1.in_features() {
11388 return Ok(false);
11389 }
11390 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11391 match w {
11392 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11393 Some(m) => (m, true),
11394 None => (bytes, *rp),
11395 },
11396 _ => unreachable!(),
11397 }
11398 }
11399 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11400 if rp0 != rp1 {
11401 return Ok(false);
11402 }
11403 let rp = rp0;
11404 let rpb: u32 = 4;
11405 let mr1 = rp && Self::q40_mr1_on();
11406 let nb = |o: usize| {
11407 if mr1 {
11408 (o as u32).div_ceil(rpb)
11409 } else {
11410 (o as u32).div_ceil(2).div_ceil(rpb)
11411 }
11412 };
11413 let grid = nb(o0) + nb(o1);
11414 debug_assert!(y0.len() >= o0 && y1.len() >= o1);
11415 let f = self.func(if mr1 {
11416 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
11417 } else if rp {
11418 "qmatvec_q4_0_mmvq_fused2_rp"
11419 } else {
11420 "qmatvec_q4_0_mmvq_fused2"
11421 });
11422 let cfg = LaunchConfig {
11423 grid_dim: (grid, 1, 1),
11424 block_dim: (32, rpb, 1),
11425 shared_mem_bytes: 0,
11426 };
11427 let inf = w0.in_features() as i32;
11428 let (oo0, oo1) = (o0 as i32, o1 as i32);
11429 let (r0, r1) = (rb0 as i64, rb1 as i64);
11430 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11432 use cudarc::driver::{DevicePtr, DevicePtrMut};
11433 let s = &self.gpu.stream();
11434 let (p0, _g0) = b0.device_ptr(s);
11435 let (p1, _g1) = b1.device_ptr(s);
11436 let (paq, _g2) = aq.device_ptr(s);
11437 let (pad, _g3) = ad.device_ptr(s);
11438 let (py0, _g4) = y0.device_ptr_mut(s);
11439 let (py1, _g5) = y1.device_ptr_mut(s);
11440 let mut ps = [
11441 &p0 as *const _ as *mut std::ffi::c_void,
11442 &p1 as *const _ as *mut _,
11443 &paq as *const _ as *mut _,
11444 &pad as *const _ as *mut _,
11445 &py0 as *const _ as *mut _,
11446 &py1 as *const _ as *mut _,
11447 &inf as *const _ as *mut _,
11448 &oo0 as *const _ as *mut _,
11449 &oo1 as *const _ as *mut _,
11450 &r0 as *const _ as *mut _,
11451 &r1 as *const _ as *mut _,
11452 ];
11453 unsafe {
11454 self.launch_pdl(
11455 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
11456 (grid, 1, 1),
11457 (32, rpb, 1),
11458 &mut ps,
11459 )?;
11460 }
11461 return Ok(true);
11462 }
11463 let __s_b = self.gpu.stream();
11464 let mut b = __s_b.launch_builder(&f);
11465 b.arg(b0)
11466 .arg(b1)
11467 .arg(aq)
11468 .arg(ad)
11469 .arg(&mut *y0)
11470 .arg(&mut *y1)
11471 .arg(&inf)
11472 .arg(&oo0)
11473 .arg(&oo1)
11474 .arg(&r0)
11475 .arg(&r1);
11476 unsafe {
11477 b.launch(cfg)?;
11478 }
11479 Ok(true)
11480 }
11481
11482 pub fn matmul_q4_fused2_batched(
11487 &self,
11488 w0: &crate::model::GpuTensor,
11489 w1: &crate::model::GpuTensor,
11490 aq: &CudaSlice<i8>,
11491 ad: &CudaSlice<f32>,
11492 m: usize,
11493 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11494 use crate::model::GpuTensor;
11495 if m < 2 || m > 8 {
11496 return Ok(None);
11497 }
11498 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11499 match w {
11500 GpuTensor::Quant {
11501 qtype, row_bytes, ..
11502 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11503 _ => None,
11504 }
11505 };
11506 let (Some((rb0, o0)), Some((_rb1, o1))) = (q4(w0), q4(w1)) else {
11507 return Ok(None);
11508 };
11509 if w0.in_features() != w1.in_features() {
11510 return Ok(None);
11511 }
11512 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11513 match w {
11514 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11515 Some(mr) => (mr, true),
11516 None => (bytes, *rp),
11517 },
11518 _ => unreachable!(),
11519 }
11520 }
11521 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11522 if !rp0 || !rp1 {
11523 return Ok(None);
11524 }
11525 let mcols = Self::batched_mcols(m);
11526 let rpb: u32 = 4;
11527 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
11528 let grid = nb(o0) + nb(o1);
11529 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11530 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11531 let f = self.func(match mcols {
11532 2 => "qmatvec_q4_0_mmvq_b2_f2_rp",
11533 4 => "qmatvec_q4_0_mmvq_b4_f2_rp",
11534 _ => "qmatvec_q4_0_mmvq_b8_f2_rp",
11535 });
11536 let cfg = LaunchConfig {
11537 grid_dim: (grid, 1, 1),
11538 block_dim: (32, rpb, 1),
11539 shared_mem_bytes: 0,
11540 };
11541 let inf = w0.in_features() as i32;
11542 let (oo0, oo1, mi) = (o0 as i32, o1 as i32, m as i32);
11543 let rb = rb0 as i64;
11544 let __s_b = self.gpu.stream();
11545 let mut b = __s_b.launch_builder(&f);
11546 b.arg(b0)
11547 .arg(b1)
11548 .arg(aq)
11549 .arg(ad)
11550 .arg(&mut y0)
11551 .arg(&mut y1)
11552 .arg(&inf)
11553 .arg(&oo0)
11554 .arg(&oo1)
11555 .arg(&mi)
11556 .arg(&rb);
11557 unsafe {
11558 b.launch(cfg)?;
11559 }
11560 Ok(Some((y0, y1)))
11561 }
11562
11563 #[allow(clippy::too_many_arguments)]
11566 pub fn matmul_q4_fused3_batched(
11567 &self,
11568 w0: &crate::model::GpuTensor,
11569 w1: &crate::model::GpuTensor,
11570 w2: &crate::model::GpuTensor,
11571 aq: &CudaSlice<i8>,
11572 ad: &CudaSlice<f32>,
11573 m: usize,
11574 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11575 {
11576 use crate::model::GpuTensor;
11577 if m < 2 || m > 8 {
11578 return Ok(None);
11579 }
11580 let q4 = |w: &GpuTensor| -> Option<usize> {
11581 match w {
11582 GpuTensor::Quant { qtype, .. } if *qtype == QT_Q4_0 => Some(w.out_features()),
11583 _ => None,
11584 }
11585 };
11586 let (Some(o0), Some(o1), Some(o2)) = (q4(w0), q4(w1), q4(w2)) else {
11587 return Ok(None);
11588 };
11589 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
11590 return Ok(None);
11591 }
11592 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11593 match w {
11594 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11595 Some(mr) => (mr, true),
11596 None => (bytes, *rp),
11597 },
11598 _ => unreachable!(),
11599 }
11600 }
11601 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
11602 if !rp0 || !rp1 || !rp2 {
11603 return Ok(None);
11604 }
11605 let mcols = Self::batched_mcols(m);
11606 let rpb: u32 = 4;
11607 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
11608 let grid = nb(o0) + nb(o1) + nb(o2);
11609 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11610 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11611 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
11612 let f = self.func(match mcols {
11613 2 => "qmatvec_q4_0_mmvq_b2_f3_rp",
11614 4 => "qmatvec_q4_0_mmvq_b4_f3_rp",
11615 _ => "qmatvec_q4_0_mmvq_b8_f3_rp",
11616 });
11617 let cfg = LaunchConfig {
11618 grid_dim: (grid, 1, 1),
11619 block_dim: (32, rpb, 1),
11620 shared_mem_bytes: 0,
11621 };
11622 let inf = w0.in_features() as i32;
11623 let (oo0, oo1, oo2, mi) = (o0 as i32, o1 as i32, o2 as i32, m as i32);
11624 let rb = 0i64;
11625 let __s_b = self.gpu.stream();
11626 let mut b = __s_b.launch_builder(&f);
11627 b.arg(b0)
11628 .arg(b1)
11629 .arg(b2)
11630 .arg(aq)
11631 .arg(ad)
11632 .arg(&mut y0)
11633 .arg(&mut y1)
11634 .arg(&mut y2)
11635 .arg(&inf)
11636 .arg(&oo0)
11637 .arg(&oo1)
11638 .arg(&oo2)
11639 .arg(&mi)
11640 .arg(&rb);
11641 unsafe {
11642 b.launch(cfg)?;
11643 }
11644 Ok(Some((y0, y1, y2)))
11645 }
11646
11647 pub fn matmul_q8_fused3(
11648 &self,
11649 w0: &crate::model::GpuTensor,
11650 w1: &crate::model::GpuTensor,
11651 w2: &crate::model::GpuTensor,
11652 aq: &CudaSlice<i8>,
11653 ad: &CudaSlice<f32>,
11654 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11655 {
11656 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
11659 return Ok(Some(self.e4m3_fused3_core(
11660 p0.0,
11661 p1.0,
11662 p2.0,
11663 aq,
11664 ad,
11665 w0.in_features(),
11666 p0.1,
11667 p1.1,
11668 p2.1,
11669 p0.2,
11670 p0.3,
11671 p1.3,
11672 p2.3,
11673 )?));
11674 }
11675 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
11676 return Ok(None);
11677 };
11678 Ok(Some(self.q8_fused3_core(
11679 p0.0,
11680 p1.0,
11681 p2.0,
11682 aq,
11683 ad,
11684 w0.in_features(),
11685 p0.1,
11686 p1.1,
11687 p2.1,
11688 p0.2,
11689 )?))
11690 }
11691
11692 #[allow(clippy::too_many_arguments)]
11693 fn q8_fused3_core(
11694 &self,
11695 b0: &CudaSlice<u8>,
11696 b1: &CudaSlice<u8>,
11697 b2: &CudaSlice<u8>,
11698 aq: &CudaSlice<i8>,
11699 ad: &CudaSlice<f32>,
11700 in_f: usize,
11701 out0: usize,
11702 out1: usize,
11703 out2: usize,
11704 row_bytes: usize,
11705 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11706 const ROWS_PER_BLOCK: u32 = 4;
11707 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11708 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11709 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
11710 let f = self.func("qmatvec_q8_0_mmvq_fused3");
11711 let mut y0 = self.alloc_uninit::<f32>(out0)?;
11712 let mut y1 = self.alloc_uninit::<f32>(out1)?;
11713 let mut y2 = self.alloc_uninit::<f32>(out2)?;
11714 let cfg = LaunchConfig {
11715 grid_dim: (nb0 + nb1 + nb2, 1, 1),
11716 block_dim: (32, ROWS_PER_BLOCK, 1),
11717 shared_mem_bytes: 0,
11718 };
11719 let (inf, o0, o1, o2, rbl) = (
11720 in_f as i32,
11721 out0 as i32,
11722 out1 as i32,
11723 out2 as i32,
11724 row_bytes as i64,
11725 );
11726 let __s_b = self.gpu.stream();
11727 let mut b = __s_b.launch_builder(&f);
11728 b.arg(b0)
11729 .arg(b1)
11730 .arg(b2)
11731 .arg(aq)
11732 .arg(ad)
11733 .arg(&mut y0)
11734 .arg(&mut y1)
11735 .arg(&mut y2)
11736 .arg(&inf)
11737 .arg(&o0)
11738 .arg(&o1)
11739 .arg(&o2)
11740 .arg(&rbl);
11741 unsafe {
11742 b.launch(cfg)?;
11743 }
11744 Ok((y0, y1, y2))
11745 }
11746
11747 #[allow(clippy::too_many_arguments)]
11749 pub fn qmatvec_q8_fused3_raw(
11750 &self,
11751 b0: &CudaSlice<u8>,
11752 b1: &CudaSlice<u8>,
11753 b2: &CudaSlice<u8>,
11754 x: &CudaSlice<f32>,
11755 in_f: usize,
11756 out0: usize,
11757 out1: usize,
11758 out2: usize,
11759 row_bytes: usize,
11760 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11761 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
11762 self.q8_fused3_core(b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes)
11763 }
11764
11765 pub fn matmul_q8_fused2_t(
11776 &self,
11777 w0: &crate::model::GpuTensor,
11778 w1: &crate::model::GpuTensor,
11779 aq: &CudaSlice<i8>,
11780 ad: &CudaSlice<f32>,
11781 m: usize,
11782 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11783 if !(2..=8).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
11787 return Ok(None);
11788 }
11789 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
11792 if m > 4 && !Self::b8_enabled() {
11793 return Ok(None);
11794 }
11795 return Ok(Some(self.e4m3_fused2_t_core(
11796 p0.0,
11797 p1.0,
11798 aq,
11799 ad,
11800 m,
11801 w0.in_features(),
11802 p0.1,
11803 p1.1,
11804 p0.2,
11805 p0.3,
11806 p1.3,
11807 )?));
11808 }
11809 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
11810 return Ok(None);
11811 };
11812 Ok(Some(self.q8_fused2_t_core(
11813 p0.0,
11814 p1.0,
11815 aq,
11816 ad,
11817 m,
11818 w0.in_features(),
11819 p0.1,
11820 p1.1,
11821 p0.2,
11822 )?))
11823 }
11824
11825 #[allow(clippy::too_many_arguments)]
11826 fn q8_fused2_t_core(
11827 &self,
11828 b0: &CudaSlice<u8>,
11829 b1: &CudaSlice<u8>,
11830 aq: &CudaSlice<i8>,
11831 ad: &CudaSlice<f32>,
11832 m: usize,
11833 in_f: usize,
11834 out0: usize,
11835 out1: usize,
11836 row_bytes: usize,
11837 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11838 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11840 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11841 let f = self.func(match Self::batched_mcols(m) {
11842 2 => "qmatvec_q8_0_mmvq_fused2_b2",
11843 4 => "qmatvec_q8_0_mmvq_fused2_b4",
11844 _ => "qmatvec_q8_0_mmvq_fused2_b8",
11846 });
11847 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
11848 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
11849 let cfg = LaunchConfig {
11850 grid_dim: (nb0 + nb1, 1, 1),
11851 block_dim: (32, ROWS_PER_BLOCK, 1),
11852 shared_mem_bytes: 0,
11853 };
11854 let (inf, o0, o1, mi, rbl) = (
11855 in_f as i32,
11856 out0 as i32,
11857 out1 as i32,
11858 m as i32,
11859 row_bytes as i64,
11860 );
11861 let __s_b = self.gpu.stream();
11862 let mut b = __s_b.launch_builder(&f);
11863 b.arg(b0)
11864 .arg(b1)
11865 .arg(aq)
11866 .arg(ad)
11867 .arg(&mut y0)
11868 .arg(&mut y1)
11869 .arg(&inf)
11870 .arg(&o0)
11871 .arg(&o1)
11872 .arg(&mi)
11873 .arg(&rbl);
11874 unsafe {
11875 b.launch(cfg)?;
11876 }
11877 Ok((y0, y1))
11878 }
11879
11880 #[allow(clippy::too_many_arguments)]
11883 pub fn qmatvec_q8_fused2_t_raw(
11884 &self,
11885 b0: &CudaSlice<u8>,
11886 b1: &CudaSlice<u8>,
11887 x: &CudaSlice<f32>,
11888 m: usize,
11889 in_f: usize,
11890 out0: usize,
11891 out1: usize,
11892 row_bytes: usize,
11893 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11894 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
11895 self.q8_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes)
11896 }
11897
11898 #[allow(clippy::too_many_arguments)]
11901 pub fn matmul_q8_fused3_t(
11902 &self,
11903 w0: &crate::model::GpuTensor,
11904 w1: &crate::model::GpuTensor,
11905 w2: &crate::model::GpuTensor,
11906 aq: &CudaSlice<i8>,
11907 ad: &CudaSlice<f32>,
11908 m: usize,
11909 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11910 {
11911 if !(2..=4).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
11912 return Ok(None);
11913 }
11914 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
11915 return Ok(Some(self.e4m3_fused3_t_core(
11916 p0.0,
11917 p1.0,
11918 p2.0,
11919 aq,
11920 ad,
11921 m,
11922 w0.in_features(),
11923 p0.1,
11924 p1.1,
11925 p2.1,
11926 p0.2,
11927 p0.3,
11928 p1.3,
11929 p2.3,
11930 )?));
11931 }
11932 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
11933 return Ok(None);
11934 };
11935 Ok(Some(self.q8_fused3_t_core(
11936 p0.0,
11937 p1.0,
11938 p2.0,
11939 aq,
11940 ad,
11941 m,
11942 w0.in_features(),
11943 p0.1,
11944 p1.1,
11945 p2.1,
11946 p0.2,
11947 )?))
11948 }
11949
11950 #[allow(clippy::too_many_arguments)]
11951 fn q8_fused3_t_core(
11952 &self,
11953 b0: &CudaSlice<u8>,
11954 b1: &CudaSlice<u8>,
11955 b2: &CudaSlice<u8>,
11956 aq: &CudaSlice<i8>,
11957 ad: &CudaSlice<f32>,
11958 m: usize,
11959 in_f: usize,
11960 out0: usize,
11961 out1: usize,
11962 out2: usize,
11963 row_bytes: usize,
11964 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11965 const ROWS_PER_BLOCK: u32 = 4;
11966 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11967 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11968 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
11969 let f = self.func(if Self::batched_mcols(m) == 2 {
11970 "qmatvec_q8_0_mmvq_fused3_b2"
11971 } else {
11972 "qmatvec_q8_0_mmvq_fused3_b4"
11973 });
11974 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
11975 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
11976 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
11977 let cfg = LaunchConfig {
11978 grid_dim: (nb0 + nb1 + nb2, 1, 1),
11979 block_dim: (32, ROWS_PER_BLOCK, 1),
11980 shared_mem_bytes: 0,
11981 };
11982 let (inf, o0, o1, o2, mi, rbl) = (
11983 in_f as i32,
11984 out0 as i32,
11985 out1 as i32,
11986 out2 as i32,
11987 m as i32,
11988 row_bytes as i64,
11989 );
11990 let __s_b = self.gpu.stream();
11991 let mut b = __s_b.launch_builder(&f);
11992 b.arg(b0)
11993 .arg(b1)
11994 .arg(b2)
11995 .arg(aq)
11996 .arg(ad)
11997 .arg(&mut y0)
11998 .arg(&mut y1)
11999 .arg(&mut y2)
12000 .arg(&inf)
12001 .arg(&o0)
12002 .arg(&o1)
12003 .arg(&o2)
12004 .arg(&mi)
12005 .arg(&rbl);
12006 unsafe {
12007 b.launch(cfg)?;
12008 }
12009 Ok((y0, y1, y2))
12010 }
12011
12012 #[allow(clippy::too_many_arguments)]
12014 pub fn qmatvec_q8_fused3_t_raw(
12015 &self,
12016 b0: &CudaSlice<u8>,
12017 b1: &CudaSlice<u8>,
12018 b2: &CudaSlice<u8>,
12019 x: &CudaSlice<f32>,
12020 m: usize,
12021 in_f: usize,
12022 out0: usize,
12023 out1: usize,
12024 out2: usize,
12025 row_bytes: usize,
12026 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12027 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12028 self.q8_fused3_t_core(b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes)
12029 }
12030
12031 pub fn q8_ffn_fuse2_on(&self) -> bool {
12035 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12036 *ON.get_or_init(|| std::env::var("MEMRA_Q8_FFN_FUSE2").as_deref() != Ok("0"))
12037 }
12038
12039 #[allow(clippy::type_complexity)]
12045 fn q8_fused_params<'w, const N: usize>(
12046 &self,
12047 ws: &[&'w crate::model::GpuTensor; N],
12048 ) -> Option<[(&'w CudaSlice<u8>, usize, usize); N]> {
12049 use crate::model::GpuTensor;
12050 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
12051 return None;
12052 }
12053 if std::env::var("MEMRA_Q8_DUAL").is_ok_and(|v| v == "0") {
12054 return None;
12055 }
12056 let in_f = ws[0].in_features();
12057 let mut out: [Option<(&CudaSlice<u8>, usize, usize)>; N] = [None; N];
12058 for (i, w) in ws.iter().enumerate() {
12059 match w {
12060 GpuTensor::Quant {
12061 bytes,
12062 qtype,
12063 row_bytes,
12064 scale,
12065 ..
12066 } if *qtype == QT_Q8_0 && *scale == 1.0 && w.in_features() == in_f => {
12067 out[i] = Some((bytes, w.out_features(), *row_bytes))
12068 }
12069 _ => return None,
12070 }
12071 }
12072 Some(out.map(|o| o.unwrap()))
12073 }
12074
12075 pub fn e4m3_dual_on(&self) -> bool {
12078 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12079 *ON.get_or_init(|| std::env::var("MEMRA_E4M3_DUAL").as_deref() != Ok("0"))
12080 }
12081
12082 #[allow(clippy::type_complexity)]
12094 fn e4m3_fused_params<'w, const N: usize>(
12095 &self,
12096 ws: &[&'w crate::model::GpuTensor; N],
12097 ) -> Option<[(&'w CudaSlice<u8>, usize, usize, f32); N]> {
12098 use crate::model::GpuTensor;
12099 if !self.e4m3_dual_on() {
12100 return None;
12101 }
12102 let in_f = ws[0].in_features();
12103 let mut out: [Option<(&CudaSlice<u8>, usize, usize, f32)>; N] = [None; N];
12104 for (i, w) in ws.iter().enumerate() {
12105 match w {
12106 GpuTensor::Quant {
12107 bytes,
12108 qtype,
12109 row_bytes,
12110 scale,
12111 rp,
12112 rp4,
12113 ..
12114 } if *qtype == QT_F8_E4M3
12115 && w.in_features() == in_f
12116 && *row_bytes == in_f
12117 && !*rp
12118 && rp4.is_none() =>
12119 {
12120 out[i] = Some((bytes, w.out_features(), *row_bytes, *scale))
12121 }
12122 _ => return None,
12123 }
12124 }
12125 Some(out.map(|o| o.unwrap()))
12126 }
12127
12128 #[allow(clippy::too_many_arguments)]
12132 fn e4m3_fused2_core(
12133 &self,
12134 b0: &CudaSlice<u8>,
12135 b1: &CudaSlice<u8>,
12136 aq: &CudaSlice<i8>,
12137 ad: &CudaSlice<f32>,
12138 in_f: usize,
12139 out0: usize,
12140 out1: usize,
12141 row_bytes: usize,
12142 ws0: f32,
12143 ws1: f32,
12144 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12145 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12147 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12148 let f = self.func("qmatvec_e4m3_mmvq_fused2");
12149 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12150 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12151 let cfg = LaunchConfig {
12152 grid_dim: (nb0 + nb1, 1, 1),
12153 block_dim: (32, ROWS_PER_BLOCK, 1),
12154 shared_mem_bytes: 0,
12155 };
12156 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
12157 let __s_b = self.gpu.stream();
12158 let mut b = __s_b.launch_builder(&f);
12159 b.arg(b0)
12160 .arg(b1)
12161 .arg(aq)
12162 .arg(ad)
12163 .arg(&mut y0)
12164 .arg(&mut y1)
12165 .arg(&inf)
12166 .arg(&o0)
12167 .arg(&o1)
12168 .arg(&rbl)
12169 .arg(&ws0)
12170 .arg(&ws1);
12171 unsafe {
12172 b.launch(cfg)?;
12173 }
12174 Ok((y0, y1))
12175 }
12176
12177 #[allow(clippy::too_many_arguments)]
12179 fn e4m3_fused3_core(
12180 &self,
12181 b0: &CudaSlice<u8>,
12182 b1: &CudaSlice<u8>,
12183 b2: &CudaSlice<u8>,
12184 aq: &CudaSlice<i8>,
12185 ad: &CudaSlice<f32>,
12186 in_f: usize,
12187 out0: usize,
12188 out1: usize,
12189 out2: usize,
12190 row_bytes: usize,
12191 ws0: f32,
12192 ws1: f32,
12193 ws2: f32,
12194 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12195 const ROWS_PER_BLOCK: u32 = 4;
12196 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12197 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12198 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12199 let f = self.func("qmatvec_e4m3_mmvq_fused3");
12200 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12201 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12202 let mut y2 = self.alloc_uninit::<f32>(out2)?;
12203 let cfg = LaunchConfig {
12204 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12205 block_dim: (32, ROWS_PER_BLOCK, 1),
12206 shared_mem_bytes: 0,
12207 };
12208 let (inf, o0, o1, o2, rbl) = (
12209 in_f as i32,
12210 out0 as i32,
12211 out1 as i32,
12212 out2 as i32,
12213 row_bytes as i64,
12214 );
12215 let __s_b = self.gpu.stream();
12216 let mut b = __s_b.launch_builder(&f);
12217 b.arg(b0)
12218 .arg(b1)
12219 .arg(b2)
12220 .arg(aq)
12221 .arg(ad)
12222 .arg(&mut y0)
12223 .arg(&mut y1)
12224 .arg(&mut y2)
12225 .arg(&inf)
12226 .arg(&o0)
12227 .arg(&o1)
12228 .arg(&o2)
12229 .arg(&rbl)
12230 .arg(&ws0)
12231 .arg(&ws1)
12232 .arg(&ws2);
12233 unsafe {
12234 b.launch(cfg)?;
12235 }
12236 Ok((y0, y1, y2))
12237 }
12238
12239 #[allow(clippy::too_many_arguments)]
12243 fn e4m3_fused2_t_core(
12244 &self,
12245 b0: &CudaSlice<u8>,
12246 b1: &CudaSlice<u8>,
12247 aq: &CudaSlice<i8>,
12248 ad: &CudaSlice<f32>,
12249 m: usize,
12250 in_f: usize,
12251 out0: usize,
12252 out1: usize,
12253 row_bytes: usize,
12254 ws0: f32,
12255 ws1: f32,
12256 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12257 const ROWS_PER_BLOCK: u32 = 4;
12258 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12259 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12260 let f = self.func(match Self::batched_mcols(m) {
12261 2 => "qmatvec_e4m3_mmvq_fused2_b2",
12262 4 => "qmatvec_e4m3_mmvq_fused2_b4",
12263 _ => "qmatvec_e4m3_mmvq_fused2_b8",
12264 });
12265 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12266 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12267 let cfg = LaunchConfig {
12268 grid_dim: (nb0 + nb1, 1, 1),
12269 block_dim: (32, ROWS_PER_BLOCK, 1),
12270 shared_mem_bytes: 0,
12271 };
12272 let (inf, o0, o1, mi, rbl) = (
12273 in_f as i32,
12274 out0 as i32,
12275 out1 as i32,
12276 m as i32,
12277 row_bytes as i64,
12278 );
12279 let __s_b = self.gpu.stream();
12280 let mut b = __s_b.launch_builder(&f);
12281 b.arg(b0)
12282 .arg(b1)
12283 .arg(aq)
12284 .arg(ad)
12285 .arg(&mut y0)
12286 .arg(&mut y1)
12287 .arg(&inf)
12288 .arg(&o0)
12289 .arg(&o1)
12290 .arg(&mi)
12291 .arg(&rbl);
12292 unsafe {
12293 b.launch(cfg)?;
12294 }
12295 if ws0 != 1.0 {
12296 self.scale_inplace(&mut y0, ws0, m * out0)?;
12297 }
12298 if ws1 != 1.0 {
12299 self.scale_inplace(&mut y1, ws1, m * out1)?;
12300 }
12301 Ok((y0, y1))
12302 }
12303
12304 #[allow(clippy::too_many_arguments)]
12306 fn e4m3_fused3_t_core(
12307 &self,
12308 b0: &CudaSlice<u8>,
12309 b1: &CudaSlice<u8>,
12310 b2: &CudaSlice<u8>,
12311 aq: &CudaSlice<i8>,
12312 ad: &CudaSlice<f32>,
12313 m: usize,
12314 in_f: usize,
12315 out0: usize,
12316 out1: usize,
12317 out2: usize,
12318 row_bytes: usize,
12319 ws0: f32,
12320 ws1: f32,
12321 ws2: f32,
12322 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12323 const ROWS_PER_BLOCK: u32 = 4;
12324 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12325 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12326 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12327 let f = self.func(if Self::batched_mcols(m) == 2 {
12328 "qmatvec_e4m3_mmvq_fused3_b2"
12329 } else {
12330 "qmatvec_e4m3_mmvq_fused3_b4"
12331 });
12332 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12333 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12334 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
12335 let cfg = LaunchConfig {
12336 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12337 block_dim: (32, ROWS_PER_BLOCK, 1),
12338 shared_mem_bytes: 0,
12339 };
12340 let (inf, o0, o1, o2, mi, rbl) = (
12341 in_f as i32,
12342 out0 as i32,
12343 out1 as i32,
12344 out2 as i32,
12345 m as i32,
12346 row_bytes as i64,
12347 );
12348 let __s_b = self.gpu.stream();
12349 let mut b = __s_b.launch_builder(&f);
12350 b.arg(b0)
12351 .arg(b1)
12352 .arg(b2)
12353 .arg(aq)
12354 .arg(ad)
12355 .arg(&mut y0)
12356 .arg(&mut y1)
12357 .arg(&mut y2)
12358 .arg(&inf)
12359 .arg(&o0)
12360 .arg(&o1)
12361 .arg(&o2)
12362 .arg(&mi)
12363 .arg(&rbl);
12364 unsafe {
12365 b.launch(cfg)?;
12366 }
12367 if ws0 != 1.0 {
12368 self.scale_inplace(&mut y0, ws0, m * out0)?;
12369 }
12370 if ws1 != 1.0 {
12371 self.scale_inplace(&mut y1, ws1, m * out1)?;
12372 }
12373 if ws2 != 1.0 {
12374 self.scale_inplace(&mut y2, ws2, m * out2)?;
12375 }
12376 Ok((y0, y1, y2))
12377 }
12378
12379 pub fn qmatvec_e4m3_blk_mmvq(
12389 &self,
12390 bytes: &CudaSlice<u8>,
12391 aq: &CudaSlice<i8>,
12392 ad: &CudaSlice<f32>,
12393 scales: &CudaSlice<f32>,
12394 m: usize,
12395 in_f: usize,
12396 out_f: usize,
12397 row_bytes: usize,
12398 scale_cols: usize,
12399 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12400 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_e4m3_blk_mmvq_into(
12402 bytes, aq, ad, scales, m, in_f, out_f, row_bytes, scale_cols, &mut y,
12403 )?;
12404 Ok(y)
12405 }
12406
12407 #[allow(clippy::too_many_arguments)]
12409 pub fn qmatvec_e4m3_blk_mmvq_into(
12410 &self,
12411 bytes: &CudaSlice<u8>,
12412 aq: &CudaSlice<i8>,
12413 ad: &CudaSlice<f32>,
12414 scales: &CudaSlice<f32>,
12415 m: usize,
12416 in_f: usize,
12417 out_f: usize,
12418 row_bytes: usize,
12419 scale_cols: usize,
12420 y: &mut CudaSlice<f32>,
12421 ) -> Result<(), Box<dyn std::error::Error>> {
12422 const ROWS_PER_BLOCK: u32 = 4; let f = self.func("qmatvec_e4m3_blk_mmvq");
12424 let cfg = LaunchConfig {
12425 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), m as u32, 1),
12426 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
12429 let (inf, outf, mi, rb, sc) = (
12430 in_f as i32,
12431 out_f as i32,
12432 m as i32,
12433 row_bytes as i64,
12434 scale_cols as i32,
12435 );
12436 let __s_b = self.gpu.stream();
12437 let mut b = __s_b.launch_builder(&f);
12438 b.arg(bytes)
12439 .arg(aq)
12440 .arg(ad)
12441 .arg(scales)
12442 .arg(&mut *y)
12443 .arg(&inf)
12444 .arg(&outf)
12445 .arg(&mi)
12446 .arg(&rb)
12447 .arg(&sc);
12448 unsafe {
12449 b.launch(cfg)?;
12450 }
12451 Ok(())
12452 }
12453
12454 #[allow(clippy::too_many_arguments)]
12460 pub fn qmatvec_e4m3_blk_mmvq_batched(
12461 &self,
12462 bytes: &CudaSlice<u8>,
12463 aq: &CudaSlice<i8>,
12464 ad: &CudaSlice<f32>,
12465 scales: &CudaSlice<f32>,
12466 m: usize,
12467 in_f: usize,
12468 out_f: usize,
12469 row_bytes: usize,
12470 scale_cols: usize,
12471 mcols: usize,
12472 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12473 const ROWS_PER_BLOCK: u32 = 4; debug_assert!(mcols >= m, "blk batched: mcols {mcols} < m {m}");
12475 let name = match mcols {
12476 2 => "qmatvec_e4m3_blk_mmvq_b2",
12477 4 => "qmatvec_e4m3_blk_mmvq_b4",
12478 8 => "qmatvec_e4m3_blk_mmvq_b8",
12479 16 => "qmatvec_e4m3_blk_mmvq_b16",
12480 _ => {
12481 return Err(
12482 format!("qmatvec_e4m3_blk_mmvq_batched: no kernel for mcols {mcols}").into(),
12483 );
12484 }
12485 };
12486 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
12487 let f = self.func(name);
12488 let cfg = LaunchConfig {
12489 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), 1, 1),
12490 block_dim: (32, ROWS_PER_BLOCK, 1),
12491 shared_mem_bytes: 0,
12492 };
12493 let (inf, outf, mi, rb, sc) = (
12494 in_f as i32,
12495 out_f as i32,
12496 m as i32,
12497 row_bytes as i64,
12498 scale_cols as i32,
12499 );
12500 let __s_b = self.gpu.stream();
12501 let mut b = __s_b.launch_builder(&f);
12502 b.arg(bytes)
12503 .arg(aq)
12504 .arg(ad)
12505 .arg(scales)
12506 .arg(&mut y)
12507 .arg(&inf)
12508 .arg(&outf)
12509 .arg(&mi)
12510 .arg(&rb)
12511 .arg(&sc);
12512 unsafe {
12513 b.launch(cfg)?;
12514 }
12515 Ok(y)
12516 }
12517
12518 #[allow(clippy::too_many_arguments)]
12521 pub fn qmatvec_e4m3_blk_batched_raw(
12522 &self,
12523 bytes: &CudaSlice<u8>,
12524 x: &CudaSlice<f32>,
12525 scales: &CudaSlice<f32>,
12526 m: usize,
12527 in_f: usize,
12528 out_f: usize,
12529 row_bytes: usize,
12530 scale_cols: usize,
12531 mcols: usize,
12532 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12533 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12534 self.qmatvec_e4m3_blk_mmvq_batched(
12535 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols, mcols,
12536 )
12537 }
12538
12539 #[allow(clippy::too_many_arguments)]
12542 pub fn qmatvec_e4m3_blk_mmvq_raw(
12543 &self,
12544 bytes: &CudaSlice<u8>,
12545 x: &CudaSlice<f32>,
12546 scales: &CudaSlice<f32>,
12547 m: usize,
12548 in_f: usize,
12549 out_f: usize,
12550 row_bytes: usize,
12551 scale_cols: usize,
12552 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12553 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12554 self.qmatvec_e4m3_blk_mmvq(
12555 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols,
12556 )
12557 }
12558
12559 #[allow(clippy::too_many_arguments)]
12562 pub fn qmatvec_e4m3_fused2_raw(
12563 &self,
12564 b0: &CudaSlice<u8>,
12565 b1: &CudaSlice<u8>,
12566 x: &CudaSlice<f32>,
12567 in_f: usize,
12568 out0: usize,
12569 out1: usize,
12570 row_bytes: usize,
12571 ws0: f32,
12572 ws1: f32,
12573 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12574 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
12575 self.e4m3_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes, ws0, ws1)
12576 }
12577
12578 #[allow(clippy::too_many_arguments)]
12579 pub fn qmatvec_e4m3_fused3_raw(
12580 &self,
12581 b0: &CudaSlice<u8>,
12582 b1: &CudaSlice<u8>,
12583 b2: &CudaSlice<u8>,
12584 x: &CudaSlice<f32>,
12585 in_f: usize,
12586 out0: usize,
12587 out1: usize,
12588 out2: usize,
12589 row_bytes: usize,
12590 ws0: f32,
12591 ws1: f32,
12592 ws2: f32,
12593 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12594 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
12595 self.e4m3_fused3_core(
12596 b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
12597 )
12598 }
12599
12600 #[allow(clippy::too_many_arguments)]
12601 pub fn qmatvec_e4m3_fused2_t_raw(
12602 &self,
12603 b0: &CudaSlice<u8>,
12604 b1: &CudaSlice<u8>,
12605 x: &CudaSlice<f32>,
12606 m: usize,
12607 in_f: usize,
12608 out0: usize,
12609 out1: usize,
12610 row_bytes: usize,
12611 ws0: f32,
12612 ws1: f32,
12613 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12614 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12615 self.e4m3_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes, ws0, ws1)
12616 }
12617
12618 #[allow(clippy::too_many_arguments)]
12619 pub fn qmatvec_e4m3_fused3_t_raw(
12620 &self,
12621 b0: &CudaSlice<u8>,
12622 b1: &CudaSlice<u8>,
12623 b2: &CudaSlice<u8>,
12624 x: &CudaSlice<f32>,
12625 m: usize,
12626 in_f: usize,
12627 out0: usize,
12628 out1: usize,
12629 out2: usize,
12630 row_bytes: usize,
12631 ws0: f32,
12632 ws1: f32,
12633 ws2: f32,
12634 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12635 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12636 self.e4m3_fused3_t_core(
12637 b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
12638 )
12639 }
12640
12641 fn try_e4m3_blk_pre(
12652 &self,
12653 w: &crate::model::GpuTensor,
12654 aq: &CudaSlice<i8>,
12655 ad: &CudaSlice<f32>,
12656 m: usize,
12657 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
12658 use crate::model::GpuTensor;
12659 if let GpuTensor::Quant {
12660 bytes,
12661 qtype,
12662 row_bytes,
12663 blk: Some(g),
12664 ..
12665 } = w
12666 {
12667 if *qtype == QT_F8_E4M3_BLK {
12668 if (2..=16).contains(&m)
12674 && std::env::var("MEMRA_NO_BATCHED").is_err()
12675 && (m <= 4 || Self::b8_enabled())
12676 {
12677 let mcols = Self::batched_mcols(m);
12678 return Ok(Some(self.qmatvec_e4m3_blk_mmvq_batched(
12679 bytes,
12680 aq,
12681 ad,
12682 &g.scales,
12683 m,
12684 w.in_features(),
12685 w.out_features(),
12686 *row_bytes,
12687 g.cols,
12688 mcols,
12689 )?));
12690 }
12691 return Ok(Some(self.qmatvec_e4m3_blk_mmvq(
12692 bytes,
12693 aq,
12694 ad,
12695 &g.scales,
12696 m,
12697 w.in_features(),
12698 w.out_features(),
12699 *row_bytes,
12700 g.cols,
12701 )?));
12702 }
12703 }
12704 Ok(None)
12705 }
12706
12707 fn try_e4m3_blk_prefill(
12754 &self,
12755 w: &crate::model::GpuTensor,
12756 x: &CudaSlice<f32>,
12757 m: usize,
12758 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
12759 use crate::model::GpuTensor;
12760 let GpuTensor::Quant {
12761 bytes,
12762 qtype,
12763 blk: Some(g),
12764 ..
12765 } = w
12766 else {
12767 return Ok(None);
12768 };
12769 if *qtype != QT_F8_E4M3_BLK {
12770 return Ok(None);
12771 }
12772 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
12777 return Ok(Some(y));
12778 }
12779 let (in_f, out_f) = (w.in_features(), w.out_features());
12780 let slab = self.fp8_blk_dequant_q8_0_dev(bytes, &g.scales, out_f, in_f)?;
12781 let tmp = GpuTensor::Quant {
12782 bytes: slab,
12783 qtype: QT_Q8_0,
12784 row_bytes: in_f / 32 * 34,
12785 ne: vec![in_f as u64, out_f as u64],
12786 scale: 1.0,
12787 rp: false,
12788 #[cfg(memra_cutlass)]
12789 cutlass: None,
12790 fp8: None,
12791 blk: None,
12792 f16: None,
12793 rp4: None,
12794 };
12795 Ok(Some(self.matmul(&tmp, x, m)?))
12797 }
12798
12799 pub fn matmul_pre_noscale(
12800 &self,
12801 w: &crate::model::GpuTensor,
12802 aq: &CudaSlice<i8>,
12803 ad: &CudaSlice<f32>,
12804 m: usize,
12805 ) -> Result<Option<(CudaSlice<f32>, f32)>, Box<dyn std::error::Error>> {
12806 use crate::model::GpuTensor;
12807 if m == 1 {
12811 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
12812 return Ok(Some((y, 1.0)));
12813 }
12814 }
12815 if m != 1 || !self.uses_q8_1_fast(w) {
12817 return Ok(None);
12818 }
12819 let in_f = w.in_features();
12820 let out_f = w.out_features();
12821 let (bytes, qtype, row_bytes, scale, rp) = match w {
12822 GpuTensor::Quant {
12823 bytes,
12824 qtype,
12825 row_bytes,
12826 scale,
12827 rp,
12828 ..
12829 } => (bytes, *qtype, *row_bytes, *scale, *rp),
12830 _ => return Ok(None),
12831 };
12832 if self.mmvq_supports(qtype) {
12834 let (mbytes, mrp) = match w {
12836 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
12837 _ => (bytes, rp),
12838 };
12839 let y = self.qmatvec_mmvq(
12840 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, 1.0, mrp,
12841 )?;
12842 return Ok(Some((y, scale)));
12843 }
12844 let name = match qtype {
12846 QT_Q8_0 => "qmatvec_q8_0_dp4a",
12847 QT_Q4_K => "qmatvec_q4_K_dp4a",
12848 QT_Q6_K => "qmatvec_q6_K_dp4a",
12849 QT_Q5_K => "qmatvec_q5_K_dp4a",
12850 QT_Q3_K => "qmatvec_q3_K_dp4a",
12851 QT_NVFP4 => {
12852 if rp {
12853 "qmatvec_nvfp4_dp4a_rp"
12854 } else {
12855 "qmatvec_nvfp4_dp4a"
12856 }
12857 }
12858 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
12859 _ => return Ok(None),
12860 };
12861 let f = self.func(name);
12862 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
12863 let cfg = LaunchConfig {
12864 grid_dim: (out_f as u32, m as u32, 1),
12865 block_dim: (128, 1, 1),
12866 shared_mem_bytes: 0,
12867 };
12868 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
12869 let __s_b = self.gpu.stream();
12870 let mut b = __s_b.launch_builder(&f);
12871 b.arg(bytes)
12872 .arg(aq)
12873 .arg(ad)
12874 .arg(&mut y)
12875 .arg(&inf)
12876 .arg(&outf)
12877 .arg(&mi)
12878 .arg(&rb);
12879 unsafe {
12880 b.launch(cfg)?;
12881 }
12882 Ok(Some((y, scale)))
12883 }
12884
12885 pub fn mmvq_supports(&self, qtype: i32) -> bool {
12888 if qtype == QT_F8_E4M3 {
12893 return true;
12894 }
12895 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
12896 return false;
12897 }
12898 matches!(
12899 qtype,
12900 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_Q4_0
12901 )
12902 }
12903
12904 pub fn qmatvec_mmvq(
12909 &self,
12910 bytes: &CudaSlice<u8>,
12911 aq: &CudaSlice<i8>,
12912 ad: &CudaSlice<f32>,
12913 m: usize,
12914 in_f: usize,
12915 out_f: usize,
12916 qtype: i32,
12917 row_bytes: usize,
12918 scale: f32,
12919 rp: bool,
12920 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12921 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_mmvq_into(
12923 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp, &mut y,
12924 )?;
12925 Ok(y)
12926 }
12927
12928 #[allow(clippy::too_many_arguments)]
12930 pub fn qmatvec_mmvq_into(
12931 &self,
12932 bytes: &CudaSlice<u8>,
12933 aq: &CudaSlice<i8>,
12934 ad: &CudaSlice<f32>,
12935 m: usize,
12936 in_f: usize,
12937 out_f: usize,
12938 qtype: i32,
12939 row_bytes: usize,
12940 scale: f32,
12941 rp: bool,
12942 y: &mut CudaSlice<f32>,
12943 ) -> Result<(), Box<dyn std::error::Error>> {
12944 debug_assert!(y.len() >= m * out_f);
12945 const ROWS_PER_BLOCK: u32 = 4; if qtype == QT_Q8_0
12951 && rp
12952 && m == 1
12953 && out_f >= 64
12954 && (out_f as u32).div_ceil(ROWS_PER_BLOCK) < 4 * self.sm_count() as u32
12955 && {
12956 static G2: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12957 *G2.get_or_init(|| std::env::var("MEMRA_Q80_G2").as_deref() != Ok("0"))
12958 }
12959 {
12960 let f = self.func("qmatvec_q8_0_mmvq_rp_g2");
12961 let cfg = LaunchConfig {
12962 grid_dim: ((out_f as u32).div_ceil(2), 1, 1),
12963 block_dim: (32, 2, 1),
12964 shared_mem_bytes: 0,
12965 };
12966 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, row_bytes as i64);
12967 let __s_b = self.gpu.stream();
12968 let mut b = __s_b.launch_builder(&f);
12969 b.arg(bytes)
12970 .arg(aq)
12971 .arg(ad)
12972 .arg(&mut *y)
12973 .arg(&inf)
12974 .arg(&outf)
12975 .arg(&mi)
12976 .arg(&rb);
12977 unsafe {
12978 b.launch(cfg)?;
12979 }
12980 if scale != 1.0 {
12981 self.scale_inplace(y, scale, out_f)?;
12982 }
12983 return Ok(());
12984 }
12985 let mut mr: u32 = if m == 1 && (qtype == QT_NVFP4 || qtype == QT_Q5_K) {
12994 2
12995 } else {
12996 1
12997 };
12998 if m == 1 && qtype == QT_Q4_0 {
13003 static Q40MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
13004 mr = *Q40MR.get_or_init(|| {
13007 std::env::var("MEMRA_Q40_MR")
13008 .ok()
13009 .and_then(|v| v.parse().ok())
13010 .unwrap_or(1)
13011 });
13012 }
13013 let q5_mode = std::env::var("MEMRA_Q5K_ISSUE").ok();
13024 let q5_force = q5_mode.as_deref() == Some("2");
13025 let q5_il = qtype == QT_Q5_K
13028 && m == 1
13029 && (q5_force || q5_mode.as_deref().map(|v| v != "0").unwrap_or(true));
13030 if q5_il && !q5_force && out_f > 65536 {
13031 mr = 1;
13032 }
13033 if qtype == QT_Q4_0 && rp && mr != 1 {
13036 mr = 2;
13037 }
13038 if qtype == QT_Q8_0 && rp {
13042 static Q80MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
13043 mr = *Q80MR.get_or_init(|| {
13044 std::env::var("MEMRA_Q80_MR")
13045 .ok()
13046 .and_then(|v| v.parse().ok())
13047 .unwrap_or(1)
13048 });
13049 }
13050 let name = match (qtype, mr, rp) {
13051 (QT_NVFP4, 2, false) => "qmatvec_nvfp4_mmvq_mr2",
13052 (QT_NVFP4, 2, true) => "qmatvec_nvfp4_mmvq_mr2_rp",
13053 (QT_NVFP4, _, true) => "qmatvec_nvfp4_mmvq_rp",
13054 (QT_Q4_0, 1, true) => "qmatvec_q4_0_mmvq_rp",
13055 (QT_Q4_0, _, true) => "qmatvec_q4_0_mmvq_mr2_rp",
13056 (QT_Q5_K, 2, _) => {
13057 if q5_il {
13058 "qmatvec_q5_K_mmvq_mr2_il"
13059 } else {
13060 "qmatvec_q5_K_mmvq_mr2"
13061 }
13062 }
13063 (QT_Q8_0, 2, true) => "qmatvec_q8_0_mmvq_mr2_rp",
13064 (QT_Q8_0, _, true)
13069 if in_f % 1024 == 0 && {
13070 static CA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13071 *CA.get_or_init(|| std::env::var("MEMRA_Q80_CA").as_deref() == Ok("1"))
13072 } =>
13073 {
13074 "qmatvec_q8_0_mmvq_rpca"
13075 }
13076 (QT_Q8_0, _, true) => "qmatvec_q8_0_mmvq_rp",
13077 (QT_Q8_0, _, _) => "qmatvec_q8_0_mmvq",
13078 (QT_Q4_K, _, true) => "qmatvec_q4_K_mmvq_rp",
13082 (QT_Q6_K, _, true) => "qmatvec_q6_K_mmvq_rp",
13083 (QT_Q4_K, _, _) => "qmatvec_q4_K_mmvq",
13084 (QT_Q4_0, 2, false) => "qmatvec_q4_0_mmvq_mr2",
13085 (QT_Q4_0, _, false) => "qmatvec_q4_0_mmvq",
13086 (QT_Q5_K, _, _) => {
13087 if q5_il {
13088 "qmatvec_q5_K_mmvq_il"
13089 } else {
13090 "qmatvec_q5_K_mmvq"
13091 }
13092 }
13093 (QT_Q6_K, _, _) => "qmatvec_q6_K_mmvq",
13094 (QT_NVFP4, _, false) => "qmatvec_nvfp4_mmvq",
13095 (QT_F8_E4M3, _, _) => "qmatvec_e4m3_mmvq",
13096 _ => panic!("qmatvec_mmvq: qtype {qtype} has no MMVQ kernel"),
13097 };
13098 let f = self.func(name);
13099 let rows_per_block = ROWS_PER_BLOCK * mr;
13101 let cfg = LaunchConfig {
13102 grid_dim: (
13103 (out_f as u32 + rows_per_block - 1) / rows_per_block,
13104 m as u32,
13105 1,
13106 ),
13107 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
13110 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13111 let __s_b = self.gpu.stream();
13112 let mut b = __s_b.launch_builder(&f);
13113 if qtype == QT_NVFP4 || qtype == QT_F8_E4M3 {
13118 b.arg(bytes)
13119 .arg(aq)
13120 .arg(ad)
13121 .arg(&mut *y)
13122 .arg(&inf)
13123 .arg(&outf)
13124 .arg(&mi)
13125 .arg(&rb)
13126 .arg(&scale);
13127 unsafe {
13128 b.launch(cfg)?;
13129 }
13130 } else if Self::pdl_on()
13131 && Self::pdl_mmvq_on()
13132 && matches!(
13133 name,
13134 "qmatvec_q4_0_mmvq_rp" | "qmatvec_q6_K_mmvq" | "qmatvec_q6_K_mmvq_rp"
13135 )
13136 {
13137 {
13141 use cudarc::driver::{DevicePtr, DevicePtrMut};
13142 let s = &self.gpu.stream();
13143 let (pw, _g0) = bytes.device_ptr(s);
13144 let (paq, _g1) = aq.device_ptr(s);
13145 let (pad, _g2) = ad.device_ptr(s);
13146 let (py, _g3) = y.device_ptr_mut(s);
13147 let mut ps = [
13148 &pw as *const _ as *mut std::ffi::c_void,
13149 &paq as *const _ as *mut _,
13150 &pad as *const _ as *mut _,
13151 &py as *const _ as *mut _,
13152 &inf as *const _ as *mut _,
13153 &outf as *const _ as *mut _,
13154 &mi as *const _ as *mut _,
13155 &rb as *const _ as *mut _,
13156 ];
13157 unsafe {
13158 self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
13159 }
13160 }
13161 if scale != 1.0 {
13162 self.scale_inplace(y, scale, m * out_f)?;
13163 }
13164 } else {
13165 b.arg(bytes)
13166 .arg(aq)
13167 .arg(ad)
13168 .arg(&mut *y)
13169 .arg(&inf)
13170 .arg(&outf)
13171 .arg(&mi)
13172 .arg(&rb);
13173 unsafe {
13174 b.launch(cfg)?;
13175 }
13176 if scale != 1.0 {
13177 self.scale_inplace(y, scale, m * out_f)?;
13178 }
13179 }
13180 Ok(())
13181 }
13182
13183 pub fn qmatvec_mmvq_raw(
13187 &self,
13188 bytes: &CudaSlice<u8>,
13189 x: &CudaSlice<f32>,
13190 m: usize,
13191 in_f: usize,
13192 out_f: usize,
13193 qtype: i32,
13194 row_bytes: usize,
13195 rp: bool,
13196 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13197 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13198 self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, 1.0, rp)
13199 }
13200
13201 pub fn batched_supports(&self, qtype: i32) -> bool {
13205 matches!(
13206 qtype,
13207 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_F8_E4M3 | QT_Q4_0
13208 )
13209 }
13210
13211 pub fn iq_fast_enabled() -> bool {
13219 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13220 *ON.get_or_init(|| {
13221 std::env::var("MEMRA_IQ_FAST")
13222 .map(|v| v != "0")
13223 .unwrap_or(true)
13224 })
13225 }
13226
13227 pub fn b8_enabled() -> bool {
13230 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13231 *ON.get_or_init(|| std::env::var("MEMRA_B8").map(|v| v != "0").unwrap_or(true))
13232 }
13233
13234 pub fn batched_mcols(m: usize) -> usize {
13236 if m == 2 {
13237 2
13238 } else if m <= 4 {
13239 4
13240 } else if m <= 8 {
13241 8
13242 } else {
13243 16
13244 }
13245 }
13246
13247 fn batched_kernel_name(qtype: i32, mcols: usize) -> Option<&'static str> {
13252 Some(match (qtype, mcols) {
13253 (QT_Q8_0, 2) => "qmatvec_q8_0_mmvq_b2",
13254 (QT_Q8_0, 4) => "qmatvec_q8_0_mmvq_b4",
13255 (QT_Q8_0, 8) => "qmatvec_q8_0_mmvq_b8",
13256 (QT_Q8_0, 16) => "qmatvec_q8_0_mmvq_b16",
13262 (QT_Q4_K, 2) => "qmatvec_q4_K_mmvq_b2",
13263 (QT_Q4_K, 4) => "qmatvec_q4_K_mmvq_b4",
13264 (QT_Q4_K, 8) => "qmatvec_q4_K_mmvq_b8",
13265 (QT_Q4_K, 16) => "qmatvec_q4_K_mmvq_b16",
13268 (QT_Q5_K, 2) => "qmatvec_q5_K_mmvq_b2",
13269 (QT_Q5_K, 4) => "qmatvec_q5_K_mmvq_b4",
13270 (QT_Q5_K, 8) => "qmatvec_q5_K_mmvq_b8",
13271 (QT_Q5_K, 16) => "qmatvec_q5_K_mmvq_b16",
13274 (QT_Q6_K, 2) => "qmatvec_q6_K_mmvq_b2",
13275 (QT_Q6_K, 4) => "qmatvec_q6_K_mmvq_b4",
13276 (QT_Q6_K, 8) => "qmatvec_q6_K_mmvq_b8",
13277 (QT_Q6_K, 16) => "qmatvec_q6_K_mmvq_b16",
13278 (QT_NVFP4, 2) => "qmatvec_nvfp4_mmvq_b2",
13279 (QT_NVFP4, 4) => "qmatvec_nvfp4_mmvq_b4",
13280 (QT_NVFP4, 8) => "qmatvec_nvfp4_mmvq_b8",
13281 (QT_NVFP4, 16) => "qmatvec_nvfp4_mmvq_b16",
13285 (QT_F8_E4M3, 2) => "qmatvec_e4m3_mmvq_b2",
13286 (QT_F8_E4M3, 4) => "qmatvec_e4m3_mmvq_b4",
13287 (QT_F8_E4M3, 8) => "qmatvec_e4m3_mmvq_b8",
13288 (QT_F8_E4M3, 16) => "qmatvec_e4m3_mmvq_b16",
13292 (QT_Q4_0, 2) => "qmatvec_q4_0_mmvq_b2",
13293 (QT_Q4_0, 4) => "qmatvec_q4_0_mmvq_b4",
13294 (QT_Q4_0, 8) => "qmatvec_q4_0_mmvq_b8",
13295 (QT_Q4_0, 16) => "qmatvec_q4_0_mmvq_b16",
13296 _ => return None,
13297 })
13298 }
13299
13300 pub fn sm_count(&self) -> i32 {
13335 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
13336 *SMS.get_or_init(|| {
13337 use cudarc::driver::sys::CUdevice_attribute_enum as A;
13338 self.gpu
13339 .ctx
13340 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
13341 .unwrap_or(82)
13342 })
13343 }
13344
13345 pub fn batched_variant(
13346 &self,
13347 _m: usize,
13348 in_f: usize,
13349 out_f: usize,
13350 qtype: i32,
13351 row_bytes: usize,
13352 mcols: usize,
13353 rp: bool,
13354 ) -> &'static str {
13355 if qtype == QT_Q8_0 {
13360 return if rp { "rp" } else { "base" };
13361 }
13362 static BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13363 let bv = *BV.get_or_init(|| match std::env::var("MEMRA_MMVQ_BV").as_deref() {
13364 Ok("base") => "base",
13365 Ok("pf") => "pf",
13366 Ok("r2") => "r2",
13367 Ok("r2w8") => "r2w8",
13368 Ok("pfr2") => "pfr2",
13369 Ok("ca") => "ca",
13370 Ok("car2") => "car2",
13371 Ok("rp") => "rp",
13374 Ok("rpr2") => "rpr2",
13375 Ok("rpr2w8") => "rpr2w8",
13376 Ok("rpca") => "rpca",
13379 Ok("rpcar2") => "rpcar2",
13380 Ok("rpsc") => "rpsc",
13387 Ok("rpms") => "rpms",
13388 Ok("rpmsc") => "rpmsc",
13389 Ok("rpks") => "rpks",
13390 Ok("rpksc") => "rpksc",
13391 _ => "auto",
13392 });
13393 let ca_ok = qtype == QT_NVFP4 && (row_bytes % 16 == 0) && (in_f % 1024 == 0);
13397 static KS_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13402 let ks_on = *KS_ON.get_or_init(|| std::env::var("MEMRA_KS").as_deref() != Ok("0"));
13403 let sc_ok = ks_on && qtype == QT_NVFP4 && (in_f % 256 == 0) && (in_f / 64 <= 272);
13404 let ks_ok = ks_on && qtype == QT_NVFP4 && (in_f % 512 == 0) && (in_f / 64 <= 272);
13405 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
13406 let sms = *SMS.get_or_init(|| {
13407 use cudarc::driver::sys::CUdevice_attribute_enum as A;
13408 self.gpu
13409 .ctx
13410 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
13411 .unwrap_or(82)
13412 });
13413 let kq_r2 = matches!(qtype, QT_Q4_K | QT_Q5_K | QT_Q6_K);
13433 static KQBV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13436 let kq_bv = *KQBV.get_or_init(|| match std::env::var("MEMRA_KQ_BV").as_deref() {
13437 Ok("base") => "base",
13438 Ok("r2") => "r2",
13439 Ok("r2w8") => "r2w8",
13440 _ => "auto",
13441 });
13442 let variant: &'static str = if qtype == QT_Q4_0 {
13443 static Q40BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
13447 let q40 = *Q40BV.get_or_init(|| match std::env::var("MEMRA_Q40_BV").as_deref() {
13448 Ok("base") => "base",
13454 Ok("r2") => "r2",
13455 Ok("ms") => "ms",
13456 Ok("sm") => "sm",
13457 Ok("la") => "la",
13458 _ => "auto",
13459 });
13460 let v = if q40 != "auto" {
13461 q40
13462 } else if (out_f as u32).div_ceil(8) >= 4 * sms as u32 {
13463 "r2"
13464 } else {
13465 "base"
13466 };
13467 if rp {
13472 match v {
13473 "ms" => "r2ms_rp",
13474 "sm" => "r2sm_rp",
13475 "la" => "r2la_rp",
13476 "r2" => "r2_rp",
13477 _ => "rp",
13478 }
13479 } else if matches!(v, "ms" | "sm" | "la") {
13480 "r2"
13481 } else {
13482 v
13483 }
13484 } else if qtype != QT_NVFP4 && !kq_r2 {
13485 "base"
13486 } else if kq_r2 && rp {
13487 "rp"
13491 } else if kq_r2 {
13492 if kq_bv != "auto" {
13495 if kq_bv == "r2w8" && mcols != 4 {
13496 "r2"
13497 } else {
13498 kq_bv
13499 }
13500 } else if bv != "auto" {
13501 match bv {
13502 "r2" | "pfr2" | "rpr2" | "car2" => "r2",
13503 "r2w8" | "rpr2w8" => {
13504 if mcols != 4 {
13505 "r2"
13506 } else {
13507 "r2w8"
13508 }
13509 }
13510 _ => "base", }
13512 } else {
13513 let blocks = (out_f + 7) / 8;
13514 let waves = blocks as f64 / (7 * sms as usize) as f64;
13515 let filled = blocks >= 4 * sms as usize;
13516 let use_r2 = if qtype == QT_Q4_K {
13517 filled
13518 } else {
13519 waves >= 2.0
13520 };
13521 if use_r2 { "r2" } else { "base" }
13522 }
13523 } else if bv != "auto" {
13524 let v = if bv == "r2w8" && mcols == 2 {
13529 "r2"
13530 } else if bv == "ca" && (!ca_ok || mcols == 8) {
13531 "pf"
13532 } else if bv == "car2" && (!ca_ok || mcols == 8) {
13533 "r2"
13534 } else if bv == "pfr2" && mcols == 8 {
13535 "r2"
13536 } else if (bv == "rpr2w8" || bv == "rpr2") && mcols == 2 {
13537 "rpr2"
13538 }
13539 else if (bv == "rpca" || bv == "rpcar2") && (!ca_ok || mcols == 8) {
13541 if mcols == 8 { "rpr2w8" } else { "rpr2" }
13542 } else if bv == "rpcar2" && mcols == 2 {
13543 "rpca"
13544 }
13545 else if (bv == "rpsc" || bv == "rpmsc") && !sc_ok {
13548 "rpr2"
13549 } else if (bv == "rpks" || bv == "rpksc") && !ks_ok {
13550 "rpr2"
13551 } else {
13552 bv
13553 };
13554 if rp {
13555 match v {
13556 "base" | "pf" | "ca" | "rp" => "rp",
13557 "r2" | "pfr2" | "car2" | "rpr2" => "rpr2",
13558 "r2w8" | "rpr2w8" => {
13559 if mcols == 2 {
13560 "rpr2"
13561 } else {
13562 "rpr2w8"
13563 }
13564 }
13565 other => other, }
13567 } else {
13568 v
13569 }
13570 } else if mcols == 8 {
13571 if rp {
13582 if sc_ok { "rpsc" } else { "rpr2w8" }
13583 } else {
13584 "r2w8"
13585 }
13586 } else if mcols >= 4 {
13587 let blocks = (out_f + 7) / 8;
13591 let r7 = 7 * sms as usize;
13592 let r8 = 8 * sms as usize;
13593 let waves = blocks as f64 / r7 as f64;
13594 let filled = blocks >= 4 * sms as usize;
13595 if filled && blocks.div_ceil(r8) < blocks.div_ceil(r7) {
13599 if rp { "rpr2w8" } else { "r2w8" }
13603 } else if waves >= 2.0 || (waves <= 1.0 && filled) {
13604 if rp { "rpr2" } else { "r2" }
13607 } else {
13608 if rp { "rp" } else { "pf" }
13612 }
13613 } else if in_f >= 6144 {
13614 if rp { "rpr2" } else { "r2" }
13618 } else if rp {
13619 let waves = ((out_f + 7) / 8) as f64 / (7 * sms as usize) as f64;
13624 if sc_ok && waves >= 0.9 && waves <= 1.1 {
13625 "rpsc"
13626 } else {
13627 "rp"
13628 }
13629 } else {
13630 "base"
13631 };
13632 variant
13633 }
13634
13635 pub fn qmatvec_mmvq_batched(
13636 &self,
13637 bytes: &CudaSlice<u8>,
13638 aq: &CudaSlice<i8>,
13639 ad: &CudaSlice<f32>,
13640 m: usize,
13641 in_f: usize,
13642 out_f: usize,
13643 qtype: i32,
13644 row_bytes: usize,
13645 mcols: usize,
13646 scale: f32,
13647 rp: bool,
13648 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13649 const ROWS_PER_BLOCK: u32 = 4;
13650 let forced: Option<&'static str> = {
13655 static V: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
13656 V.get_or_init(|| std::env::var("MEMRA_BVAR").ok())
13657 .as_deref()
13658 .map(|s| Box::leak(s.to_string().into_boxed_str()) as &'static str)
13659 };
13660 let variant = match forced {
13661 Some(v) if !rp || v.contains("rp") => v,
13662 _ => self.batched_variant(m, in_f, out_f, qtype, row_bytes, mcols, rp),
13663 };
13664 let base_name = Self::batched_kernel_name(qtype, mcols).ok_or_else(|| {
13665 format!("qmatvec_mmvq_batched: no kernel for qtype {qtype} mcols {mcols}")
13666 })?;
13667 let variant = if mcols == 16 {
13671 if rp { "rp" } else { "base" }
13672 } else {
13673 variant
13674 };
13675 static B567: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13682 let b567 = *B567.get_or_init(|| std::env::var("MEMRA_B567").as_deref() != Ok("0"));
13683 if b567
13684 && qtype == QT_NVFP4
13685 && rp
13686 && mcols == 8
13687 && (5..=7).contains(&m)
13688 && matches!(variant, "rpsc" | "rpr2w8")
13689 {
13690 let f = self.func(&format!("qmatvec_nvfp4_mmvq_b{m}_{variant}"));
13691 let rows_per_block = ROWS_PER_BLOCK * 2; let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13693 let cfg = LaunchConfig {
13694 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
13695 block_dim: (32, ROWS_PER_BLOCK, 1),
13696 shared_mem_bytes: 0,
13697 };
13698 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13699 let __s_b = self.gpu.stream();
13700 let mut b = __s_b.launch_builder(&f);
13701 b.arg(bytes)
13702 .arg(aq)
13703 .arg(ad)
13704 .arg(&mut y)
13705 .arg(&inf)
13706 .arg(&outf)
13707 .arg(&mi)
13708 .arg(&rb);
13709 unsafe {
13710 b.launch(cfg)?;
13711 }
13712 if scale != 1.0 {
13713 self.scale_inplace(&mut y, scale, m * out_f)?;
13714 }
13715 return Ok(y);
13716 }
13717 let (name, rows_per_block): (std::borrow::Cow<'static, str>, u32) = match variant {
13718 "base" => (base_name.into(), ROWS_PER_BLOCK),
13719 "pf" => (format!("{base_name}_pf").into(), ROWS_PER_BLOCK),
13720 "ca" => (format!("{base_name}_ca").into(), ROWS_PER_BLOCK),
13721 "rp" => (format!("{base_name}_rp").into(), ROWS_PER_BLOCK),
13722 "rpca" => (format!("{base_name}_rpca").into(), ROWS_PER_BLOCK), "rpks" => (format!("{base_name}_rpks").into(), ROWS_PER_BLOCK),
13726 "rpksc" => (format!("{base_name}_rpksc").into(), ROWS_PER_BLOCK),
13727 "rpms" => (format!("{base_name}_rpms").into(), ROWS_PER_BLOCK),
13728 "rpmsc" => (format!("{base_name}_rpmsc").into(), ROWS_PER_BLOCK),
13729 "r2ms_rp" => (format!("{base_name}_r2ms_rp").into(), ROWS_PER_BLOCK),
13730 "r2sm_rp" => (format!("{base_name}_r2sm_rp").into(), ROWS_PER_BLOCK * 2),
13731 "r2la_rp" => (format!("{base_name}_r2la_rp").into(), ROWS_PER_BLOCK * 2),
13732 v => (format!("{base_name}_{v}").into(), ROWS_PER_BLOCK * 2), };
13734 debug_assert!(
13735 !rp || name.contains("_rp"),
13736 "rp weight dispatched to a GGUF-layout kernel"
13737 );
13738 let f = self.func(&name);
13739 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13740 let smem = if name.contains("_r2sm_rp") {
13742 (mcols * 32 * 9 * 4 + mcols * 32 * 4) as u32
13743 } else {
13744 0
13745 };
13746 let cfg = LaunchConfig {
13747 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
13748 block_dim: (32, ROWS_PER_BLOCK, 1),
13749 shared_mem_bytes: smem,
13750 };
13751 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13752 let __s_b = self.gpu.stream();
13753 let mut b = __s_b.launch_builder(&f);
13754 b.arg(bytes)
13755 .arg(aq)
13756 .arg(ad)
13757 .arg(&mut y)
13758 .arg(&inf)
13759 .arg(&outf)
13760 .arg(&mi)
13761 .arg(&rb);
13762 unsafe {
13763 b.launch(cfg)?;
13764 }
13765 if scale != 1.0 {
13766 self.scale_inplace(&mut y, scale, m * out_f)?;
13767 }
13768 Ok(y)
13769 }
13770
13771 pub fn qmatvec_batched_raw(
13775 &self,
13776 bytes: &CudaSlice<u8>,
13777 x: &CudaSlice<f32>,
13778 m: usize,
13779 in_f: usize,
13780 out_f: usize,
13781 qtype: i32,
13782 row_bytes: usize,
13783 mcols: usize,
13784 rp: bool,
13785 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13786 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13787 self.qmatvec_mmvq_batched(
13788 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, 1.0, rp,
13789 )
13790 }
13791
13792 pub fn qmatvec_nvfp4_batched_raw(
13794 &self,
13795 bytes: &CudaSlice<u8>,
13796 x: &CudaSlice<f32>,
13797 m: usize,
13798 in_f: usize,
13799 out_f: usize,
13800 row_bytes: usize,
13801 mcols: usize,
13802 rp: bool,
13803 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13804 self.qmatvec_batched_raw(bytes, x, m, in_f, out_f, QT_NVFP4, row_bytes, mcols, rp)
13805 }
13806
13807 fn try_fp4_gemm(
13811 &self,
13812 w: &crate::model::GpuTensor,
13813 x: &CudaSlice<f32>,
13814 m: usize,
13815 in_f: usize,
13816 out_f: usize,
13817 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13818 use crate::model::GpuTensor;
13819 if cfg!(memra_portable_cuda) {
13820 return Ok(None);
13821 }
13822 if std::env::var("MEMRA_FP4").is_err() {
13823 return Ok(None);
13824 }
13825 #[cfg(memra_cutlass)]
13834 if m >= 128 && std::env::var("MEMRA_FP4_CUTLASS").is_ok() {
13835 if let GpuTensor::Quant {
13836 bytes,
13837 qtype,
13838 scale,
13839 row_bytes,
13840 cutlass,
13841 ..
13842 } = w
13843 {
13844 if *qtype == QT_NVFP4 && in_f % 64 == 0 {
13845 if let Some(cw) = cutlass {
13846 let y = self.cutlass_fp4_gemm(
13848 &cw.b_packed,
13849 &cw.sfb_swizzled,
13850 x,
13851 *scale,
13852 m,
13853 out_f,
13854 in_f,
13855 )?;
13856 return Ok(Some(y));
13857 } else if std::env::var("MEMRA_FP4_CUTLASS_OTF").is_ok() {
13858 let (b_packed, sfb_sw) =
13863 self.build_cutlass_weight(bytes, out_f, in_f, *row_bytes)?;
13864 let y =
13865 self.cutlass_fp4_gemm(&b_packed, &sfb_sw, x, *scale, m, out_f, in_f)?;
13866 return Ok(Some(y));
13867 }
13868 }
13869 }
13870 }
13871 if let GpuTensor::Quant {
13872 bytes,
13873 qtype,
13874 row_bytes,
13875 scale,
13876 rp,
13877 ..
13878 } = w
13879 {
13880 if *qtype == QT_NVFP4 && in_f % 64 == 0 && !*rp {
13883 let y =
13884 self.qmatvec_gemm_nvfp4_fp4(bytes, x, m, in_f, out_f, *row_bytes, *scale)?;
13885 return Ok(Some(y));
13886 }
13887 }
13888 Ok(None)
13889 }
13890
13891 pub fn rms_norm_f16out(
13895 &self,
13896 x: &CudaSlice<f32>,
13897 w: &CudaSlice<f32>,
13898 dst: &mut CudaSlice<f32>,
13899 dst16: &mut CudaSlice<u8>,
13900 ncols: usize,
13901 nrows: usize,
13902 eps: f32,
13903 ) -> Result<(), Box<dyn std::error::Error>> {
13904 let f = self.func("rms_norm_f16out_f32");
13905 let cfg = LaunchConfig {
13906 grid_dim: (nrows as u32, 1, 1),
13907 block_dim: (rms_block(), 1, 1),
13908 shared_mem_bytes: 0,
13909 };
13910 let (nc, e) = (ncols as i32, eps);
13911 let __s_b = self.gpu.stream();
13912 let mut b = __s_b.launch_builder(&f);
13913 b.arg(x).arg(w).arg(dst).arg(dst16).arg(&nc).arg(&e);
13914 unsafe {
13915 b.launch(cfg)?;
13916 }
13917 Ok(())
13918 }
13919
13920 #[allow(clippy::too_many_arguments)]
13923 pub fn add_rms_norm_f16out(
13924 &self,
13925 a: &CudaSlice<f32>,
13926 b: &CudaSlice<f32>,
13927 w: &CudaSlice<f32>,
13928 res: &mut CudaSlice<f32>,
13929 dst: &mut CudaSlice<f32>,
13930 dst16: &mut CudaSlice<u8>,
13931 ncols: usize,
13932 nrows: usize,
13933 eps: f32,
13934 ) -> Result<(), Box<dyn std::error::Error>> {
13935 let f = self.func("add_rms_norm_f16out_f32");
13936 let cfg = LaunchConfig {
13937 grid_dim: (nrows as u32, 1, 1),
13938 block_dim: (rms_block(), 1, 1),
13939 shared_mem_bytes: 0,
13940 };
13941 let (nc, e) = (ncols as i32, eps);
13942 let __s_lb = self.gpu.stream();
13943 let mut lb = __s_lb.launch_builder(&f);
13944 lb.arg(a)
13945 .arg(b)
13946 .arg(w)
13947 .arg(res)
13948 .arg(dst)
13949 .arg(dst16)
13950 .arg(&nc)
13951 .arg(&e);
13952 unsafe {
13953 lb.launch(cfg)?;
13954 }
13955 Ok(())
13956 }
13957
13958 pub fn matmul_group_xh(
13961 &self,
13962 ws: &[&crate::model::GpuTensor],
13963 x: &CudaSlice<f32>,
13964 xh: &CudaSlice<u8>,
13965 m: usize,
13966 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13967 let mut out = Vec::with_capacity(ws.len());
13968 let in_f = ws[0].in_features();
13969 for w in ws {
13970 if w.in_features() == in_f && m >= 16 && !self.verify_exact_on() {
13971 if let Some(y) = self.try_f16_gemm_pre(w, xh, m)? {
13972 out.push(y);
13973 continue;
13974 }
13975 }
13976 out.push(self.matmul(w, x, m)?);
13977 }
13978 Ok(out)
13979 }
13980
13981 pub fn gdn_pad_mask(
13984 &self,
13985 beta: &mut CudaSlice<f32>,
13986 g_log: &mut CudaSlice<f32>,
13987 len_d: &CudaSlice<i32>,
13988 h: usize,
13989 t: usize,
13990 ) -> Result<(), Box<dyn std::error::Error>> {
13991 let f = self.func("gdn_pad_mask_f32");
13992 let cfg = LaunchConfig::for_num_elems((t * h) as u32);
13993 let (hi, ti) = (h as i32, t as i32);
13994 let __s_b = self.gpu.stream();
13995 let mut b = __s_b.launch_builder(&f);
13996 b.arg(beta).arg(g_log).arg(len_d).arg(&hi).arg(&ti);
13997 unsafe {
13998 b.launch(cfg)?;
13999 }
14000 Ok(())
14001 }
14002
14003 pub fn row_gather_dev(
14006 &self,
14007 src: &CudaSlice<f32>,
14008 dst: &mut CudaSlice<f32>,
14009 len_d: &CudaSlice<i32>,
14010 ncols: usize,
14011 ) -> Result<(), Box<dyn std::error::Error>> {
14012 let f = self.func("row_gather_dev_f32");
14013 let cfg = LaunchConfig::for_num_elems(ncols as u32);
14014 let nc = ncols as i32;
14015 let __s_b = self.gpu.stream();
14016 let mut b = __s_b.launch_builder(&f);
14017 b.arg(src).arg(dst).arg(len_d).arg(&nc);
14018 unsafe {
14019 b.launch(cfg)?;
14020 }
14021 Ok(())
14022 }
14023
14024 pub fn matmul_group(
14031 &self,
14032 ws: &[&crate::model::GpuTensor],
14033 x: &CudaSlice<f32>,
14034 m: usize,
14035 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
14036 use crate::model::GpuTensor;
14037 let mut out = Vec::with_capacity(ws.len());
14038 let any_mirror = ws
14039 .iter()
14040 .any(|w| matches!(w, GpuTensor::Quant { f16: Some(_), .. }));
14041 if m >= 16 && any_mirror && !self.verify_exact_on() {
14042 let in_f = ws[0].in_features();
14043 let xh = self.f16_act(x, m * in_f, in_f)?;
14044 for w in ws {
14045 if w.in_features() == in_f {
14046 if let Some(y) = self.try_f16_gemm_pre(w, &xh, m)? {
14047 out.push(y);
14048 continue;
14049 }
14050 }
14051 out.push(self.matmul(w, x, m)?);
14052 }
14053 return Ok(out);
14054 }
14055 for w in ws {
14056 out.push(self.matmul(w, x, m)?);
14057 }
14058 Ok(out)
14059 }
14060
14061 pub fn matmul_group_multi(
14068 &self,
14069 ws: &[&crate::model::GpuTensor],
14070 xs: &[&CudaSlice<f32>],
14071 ms: &[usize],
14072 ) -> Result<Vec<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
14073 assert_eq!(xs.len(), ms.len());
14074 let in_f = ws[0].in_features();
14075 let total: usize = ms.iter().sum();
14076 let mut xcat = self.uninit(total * in_f)?;
14077 let mut off = 0usize;
14078 for (x, &m) in xs.iter().zip(ms) {
14079 self.copy_into(&mut xcat, off * in_f, x, m * in_f)?;
14080 off += m;
14081 }
14082 let ys = self.matmul_group(ws, &xcat, total)?;
14083 let mut out: Vec<Vec<CudaSlice<f32>>> = (0..xs.len()).map(|_| Vec::new()).collect();
14084 for (w, y) in ws.iter().zip(ys) {
14085 let out_f = w.out_features();
14086 let mut off = 0usize;
14087 for (s, &m) in ms.iter().enumerate() {
14088 let mut ys_s = self.uninit(m * out_f)?;
14089 let src = y.slice(off * out_f..(off + m) * out_f);
14090 self.gpu.stream().memcpy_dtod(&src, &mut ys_s)?;
14091 out[s].push(ys_s);
14092 off += m;
14093 }
14094 }
14095 Ok(out)
14096 }
14097
14098 pub fn gemm_supports(&self, w: &crate::model::GpuTensor) -> bool {
14108 use crate::model::GpuTensor;
14109 if !legacy_quant_gemm_allowed(
14110 cfg!(memra_portable_cuda),
14111 cfg!(memra_hopper_mma),
14112 std::env::var_os("MEMRA_NO_GEMM").is_some(),
14113 ) {
14114 return false;
14115 }
14116 match w {
14117 GpuTensor::Quant { qtype, .. } => {
14118 matches!(*qtype, QT_Q8_0 | QT_Q4_K | QT_Q6_K | QT_Q5_K | QT_Q4_0)
14119 || (*qtype == QT_NVFP4 && w.in_features() % 64 == 0)
14120 }
14121 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
14122 }
14123 }
14124
14125 pub fn qmatvec_gemm(
14132 &self,
14133 w: &crate::model::GpuTensor,
14134 aq: &CudaSlice<i8>,
14135 ad: &CudaSlice<f32>,
14136 m: usize,
14137 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14138 use crate::model::GpuTensor;
14139 let in_f = w.in_features();
14140 let out_f = w.out_features();
14141 let (bytes, qtype, row_bytes, scale, rp) = match w {
14142 GpuTensor::Quant {
14143 bytes,
14144 qtype,
14145 row_bytes,
14146 scale,
14147 rp,
14148 ..
14149 } => (bytes, *qtype, *row_bytes, *scale, *rp),
14150 _ => unreachable!("gemm_supports guaranteed Quant"),
14151 };
14152 if cfg!(memra_hopper_mma) && qtype == QT_Q8_0 && out_f % 64 == 0 && wgmma_gemm_enabled() {
14158 if let GpuTensor::Quant { rp4: Some(m4), .. } = w {
14159 let mut y = self.qmatvec_gemm_q8_0_wgmma_raw(m4, aq, ad, m, in_f, out_f)?;
14160 if scale != 1.0 {
14161 self.scale_inplace(&mut y, scale, m * out_f)?;
14162 }
14163 return Ok(y);
14164 }
14165 }
14166 let name = match qtype {
14167 QT_Q8_0 => "qmatvec_gemm_q8_0",
14168 QT_Q4_K => "qmatvec_gemm_q4_K",
14169 QT_Q4_0 => {
14170 if rp {
14171 "qmatvec_gemm_q4_0_rp"
14172 } else {
14173 "qmatvec_gemm_q4_0"
14174 }
14175 }
14176 QT_Q5_K => "qmatvec_gemm_q5_K",
14177 QT_Q6_K => "qmatvec_gemm_q6_K",
14178 QT_NVFP4 => {
14179 if rp {
14180 "qmatvec_gemm_nvfp4_rp"
14181 } else {
14182 "qmatvec_gemm_nvfp4"
14183 }
14184 }
14185 _ => unreachable!(),
14186 };
14187 let f = self.func(name);
14188 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);
14193 let k1_tile = if is_k1 {
14195 k1_launch_override().unwrap_or((128, 128, 8))
14196 } else {
14197 (128, 128, 8)
14198 };
14199 let (bm, bn): (u32, u32) = if is_k1 {
14200 (k1_tile.0, k1_tile.1)
14201 } else {
14202 (64, 256)
14203 };
14204 let warps: u32 = if is_k1 {
14205 k1_tile.2
14206 } else {
14207 match qtype {
14208 QT_NVFP4 => 8,
14209 _ => 4,
14210 }
14211 };
14212 let cfg = LaunchConfig {
14213 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
14214 block_dim: (32, warps, 1),
14215 shared_mem_bytes: 0,
14216 };
14217 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14218 let __s_b = self.gpu.stream();
14219 let mut b = __s_b.launch_builder(&f);
14220 b.arg(bytes)
14221 .arg(aq)
14222 .arg(ad)
14223 .arg(&mut y)
14224 .arg(&inf)
14225 .arg(&outf)
14226 .arg(&mi)
14227 .arg(&rb);
14228 unsafe {
14229 b.launch(cfg)?;
14230 }
14231 if scale != 1.0 {
14232 self.scale_inplace(&mut y, scale, m * out_f)?;
14233 }
14234 Ok(y)
14235 }
14236
14237 pub fn qmatvec_gemm_raw(
14242 &self,
14243 bytes: &CudaSlice<u8>,
14244 x: &CudaSlice<f32>,
14245 m: usize,
14246 in_f: usize,
14247 out_f: usize,
14248 qtype: i32,
14249 row_bytes: usize,
14250 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14251 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
14252 let name = match qtype {
14253 QT_Q8_0 => "qmatvec_gemm_q8_0",
14254 QT_Q4_K => "qmatvec_gemm_q4_K",
14255 QT_Q4_0 => "qmatvec_gemm_q4_0",
14256 QT_Q5_K => "qmatvec_gemm_q5_K",
14257 QT_Q6_K => "qmatvec_gemm_q6_K",
14258 QT_NVFP4 => "qmatvec_gemm_nvfp4",
14259 QT_NVFP4_RP => "qmatvec_gemm_nvfp4_rp",
14260 _ => panic!("qmatvec_gemm_raw: qtype {qtype} has no GEMM kernel"),
14261 };
14262 let f = self.func(name);
14263 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);
14267 let k1_tile = if is_k1 {
14269 k1_launch_override().unwrap_or((128, 128, 8))
14270 } else {
14271 (128, 128, 8)
14272 };
14273 let (bm, bn): (u32, u32) = if is_k1 {
14274 (k1_tile.0, k1_tile.1)
14275 } else {
14276 (64, 256)
14277 };
14278 let warps: u32 = if is_k1 {
14279 k1_tile.2
14280 } else {
14281 match qtype {
14282 QT_NVFP4 | QT_NVFP4_RP => 8,
14283 _ => 4,
14284 }
14285 };
14286 let cfg = LaunchConfig {
14287 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
14288 block_dim: (32, warps, 1),
14289 shared_mem_bytes: 0,
14290 };
14291 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14292 let __s_b = self.gpu.stream();
14293 let mut b = __s_b.launch_builder(&f);
14294 b.arg(bytes)
14295 .arg(&aq)
14296 .arg(&ad)
14297 .arg(&mut y)
14298 .arg(&inf)
14299 .arg(&outf)
14300 .arg(&mi)
14301 .arg(&rb);
14302 unsafe {
14303 b.launch(cfg)?;
14304 }
14305 Ok(y)
14306 }
14307
14308 pub fn qmatvec_gemm_q8_0_wgmma_raw(
14315 &self,
14316 rp4: &CudaSlice<u8>,
14317 aq: &CudaSlice<i8>,
14318 ad: &CudaSlice<f32>,
14319 m: usize,
14320 in_f: usize,
14321 out_f: usize,
14322 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14323 assert!(
14324 out_f % 64 == 0 && in_f % 32 == 0,
14325 "wgmma GEMM needs out_f%64==0, in_f%32==0"
14326 );
14327 let f = self.func("qmatvec_gemm_q8_0_wgmma");
14328 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
14330 grid_dim: ((out_f / 64) as u32, (m as u32).div_ceil(64), 1),
14331 block_dim: (128, 1, 1),
14332 shared_mem_bytes: 0,
14333 };
14334 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
14335 let __s_b = self.gpu.stream();
14336 let mut b = __s_b.launch_builder(&f);
14337 b.arg(rp4)
14338 .arg(aq)
14339 .arg(ad)
14340 .arg(&mut y)
14341 .arg(&inf)
14342 .arg(&outf)
14343 .arg(&mi);
14344 unsafe {
14345 b.launch(cfg)?;
14346 }
14347 Ok(y)
14348 }
14349
14350 pub fn scale_inplace(
14352 &self,
14353 y: &mut CudaSlice<f32>,
14354 s: f32,
14355 n: usize,
14356 ) -> Result<(), Box<dyn std::error::Error>> {
14357 let f = self.func("scale_f32");
14358 let cfg = LaunchConfig::for_num_elems(n as u32);
14359 let (sf, ni) = (s, n as i32);
14360 let __s_b = self.gpu.stream();
14361 let mut b = __s_b.launch_builder(&f);
14362 b.arg(y).arg(&sf).arg(&ni);
14363 unsafe {
14364 b.launch(cfg)?;
14365 }
14366 Ok(())
14367 }
14368
14369 pub fn bf16_to_f32(
14374 &self,
14375 data: &cudarc::driver::CudaView<'_, u8>,
14376 n: usize,
14377 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14378 let mut out = self.alloc_uninit::<f32>(n)?;
14379 let f = self.func("bf16_to_f32");
14380 let cfg = LaunchConfig::for_num_elems(n as u32);
14381 let ni = n as i32;
14382 let __s_b = self.gpu.stream();
14383 let mut b = __s_b.launch_builder(&f);
14384 b.arg(data).arg(&mut out).arg(&ni);
14385 unsafe {
14386 b.launch(cfg)?;
14387 }
14388 Ok(out)
14389 }
14390
14391 fn linear_bf16_chunked(
14398 &self,
14399 x: &CudaSlice<f32>,
14400 data: &CudaSlice<u8>,
14401 m: usize,
14402 in_f: usize,
14403 out_f: usize,
14404 exact: bool,
14405 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14406 const CHUNK_BYTES: usize = 256 << 20;
14407 let chunk_rows = (CHUNK_BYTES / (in_f * 4)).max(1).min(out_f);
14408 if chunk_rows >= out_f {
14409 let wf32 = self.bf16_to_f32(&data.slice(0..in_f * out_f * 2), in_f * out_f)?;
14410 return if exact {
14411 self.linear_decode_exact(x, &wf32, m, in_f, out_f)
14412 } else {
14413 self.linear(x, &wf32, m, in_f, out_f)
14414 };
14415 }
14416 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
14417 let mut r0 = 0usize;
14418 while r0 < out_f {
14419 let rows = chunk_rows.min(out_f - r0);
14420 let wslice = data.slice(r0 * in_f * 2..(r0 + rows) * in_f * 2);
14421 let wf32 = self.bf16_to_f32(&wslice, in_f * rows)?;
14422 let yc = if exact {
14423 self.linear_decode_exact(x, &wf32, m, in_f, rows)?
14424 } else {
14425 self.linear(x, &wf32, m, in_f, rows)?
14426 };
14427 for mi in 0..m {
14429 let src = yc.slice(mi * rows..(mi + 1) * rows);
14430 let mut dst = y.slice_mut(mi * out_f + r0..mi * out_f + r0 + rows);
14431 self.gpu.stream().memcpy_dtod(&src, &mut dst)?;
14432 }
14433 r0 += rows;
14434 }
14435 Ok(y)
14436 }
14437
14438 pub fn linear_decode_exact(
14445 &self,
14446 x: &CudaSlice<f32>,
14447 w: &CudaSlice<f32>,
14448 m_tokens: usize,
14449 in_f: usize,
14450 out_f: usize,
14451 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14452 if m_tokens == 1 {
14453 return self.linear(x, w, 1, in_f, out_f);
14454 }
14455 let xv = self.view(x, m_tokens * in_f);
14456 let mut y = self.alloc_uninit::<f32>(m_tokens * out_f)?;
14457 for t in 0..m_tokens {
14458 let row = xv.slice(t * in_f..(t + 1) * in_f);
14459 let mut xr = self.alloc_uninit::<f32>(in_f)?;
14460 self.copy_view_into(&mut xr, 0, &row, in_f)?;
14461 let yr = self.linear(&xr, w, 1, in_f, out_f)?;
14462 self.copy_into(&mut y, t * out_f, &yr, out_f)?;
14463 }
14464 Ok(y)
14465 }
14466
14467 pub fn linear(
14468 &self,
14469 x: &CudaSlice<f32>,
14470 w: &CudaSlice<f32>,
14471 m_tokens: usize,
14472 in_f: usize,
14473 out_f: usize,
14474 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14475 use cudarc::cublaslt::{Matmul, MatmulConfig};
14476 let mut c = self.alloc_uninit::<f32>(m_tokens * out_f)?; let cfg = MatmulConfig {
14478 transa: true,
14479 transb: false,
14480 transc: false,
14481 m: out_f as u64,
14482 n: m_tokens as u64,
14483 k: in_f as u64,
14484 alpha: 1.0,
14485 lda: in_f as i64,
14486 ldb: in_f as i64,
14487 beta: 0.0,
14488 ldc: out_f as i64,
14489 stride_a: None,
14490 stride_b: None,
14491 stride_c: None,
14492 stride_bias: None,
14493 batch_size: None,
14494 };
14495 unsafe {
14496 self.gpu.blas.matmul(cfg, w, x, &mut c, None, None)?;
14497 }
14498 Ok(c)
14499 }
14500
14501 pub fn sdpa_naive(
14503 &self,
14504 q: &CudaSlice<f32>,
14505 k: &CudaSlice<f32>,
14506 v: &CudaSlice<f32>,
14507 o: &mut CudaSlice<f32>,
14508 head_dim: usize,
14509 n_head: usize,
14510 n_head_kv: usize,
14511 t: usize,
14512 t_kv: usize,
14513 scale: f32,
14514 causal: bool,
14515 ) -> Result<(), Box<dyn std::error::Error>> {
14516 let f = self.func("sdpa_naive_f32");
14517 let cfg = LaunchConfig {
14518 grid_dim: (n_head as u32, t as u32, 1),
14519 block_dim: (128, 1, 1),
14520 shared_mem_bytes: (t_kv * 4) as u32,
14521 };
14522 let (hd, nh, nhkv, ti, tkvi, cz) = (
14523 head_dim as i32,
14524 n_head as i32,
14525 n_head_kv as i32,
14526 t as i32,
14527 t_kv as i32,
14528 causal as i32,
14529 );
14530 let __s_b = self.gpu.stream();
14531 let mut b = __s_b.launch_builder(&f);
14532 b.arg(q)
14533 .arg(k)
14534 .arg(v)
14535 .arg(o)
14536 .arg(&hd)
14537 .arg(&nh)
14538 .arg(&nhkv)
14539 .arg(&ti)
14540 .arg(&tkvi)
14541 .arg(&scale)
14542 .arg(&cz);
14543 unsafe {
14544 b.launch(cfg)?;
14545 }
14546 Ok(())
14547 }
14548
14549 #[allow(clippy::too_many_arguments)]
14551 pub fn sdpa_naive_w(
14552 &self,
14553 q: &CudaSlice<f32>,
14554 k: &CudaSlice<f32>,
14555 v: &CudaSlice<f32>,
14556 o: &mut CudaSlice<f32>,
14557 head_dim: usize,
14558 n_head: usize,
14559 n_head_kv: usize,
14560 t: usize,
14561 t_kv: usize,
14562 scale: f32,
14563 causal: bool,
14564 window: usize,
14565 ) -> Result<(), Box<dyn std::error::Error>> {
14566 let f = self.func("sdpa_naive_w_f32");
14567 let cfg = LaunchConfig {
14568 grid_dim: (n_head as u32, t as u32, 1),
14569 block_dim: (128, 1, 1),
14570 shared_mem_bytes: (t_kv * 4) as u32,
14571 };
14572 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
14573 head_dim as i32,
14574 n_head as i32,
14575 n_head_kv as i32,
14576 t as i32,
14577 t_kv as i32,
14578 causal as i32,
14579 window as i32,
14580 );
14581 let __s_b = self.gpu.stream();
14582 let mut b = __s_b.launch_builder(&f);
14583 b.arg(q)
14584 .arg(k)
14585 .arg(v)
14586 .arg(o)
14587 .arg(&hd)
14588 .arg(&nh)
14589 .arg(&nhkv)
14590 .arg(&ti)
14591 .arg(&tkvi)
14592 .arg(&scale)
14593 .arg(&cz)
14594 .arg(&wi);
14595 unsafe {
14596 b.launch(cfg)?;
14597 }
14598 Ok(())
14599 }
14600
14601 pub fn sdpa_naive_view(
14603 &self,
14604 q: &CudaSlice<f32>,
14605 k: &cudarc::driver::CudaView<f32>,
14606 v: &cudarc::driver::CudaView<f32>,
14607 o: &mut CudaSlice<f32>,
14608 head_dim: usize,
14609 n_head: usize,
14610 n_head_kv: usize,
14611 t: usize,
14612 t_kv: usize,
14613 scale: f32,
14614 causal: bool,
14615 ) -> Result<(), Box<dyn std::error::Error>> {
14616 let f = self.func("sdpa_naive_f32");
14617 let cfg = LaunchConfig {
14618 grid_dim: (n_head as u32, t as u32, 1),
14619 block_dim: (128, 1, 1),
14620 shared_mem_bytes: (t_kv * 4) as u32,
14621 };
14622 let (hd, nh, nhkv, ti, tkvi, cz) = (
14623 head_dim as i32,
14624 n_head as i32,
14625 n_head_kv as i32,
14626 t as i32,
14627 t_kv as i32,
14628 causal as i32,
14629 );
14630 let __s_b = self.gpu.stream();
14631 let mut b = __s_b.launch_builder(&f);
14632 b.arg(q)
14633 .arg(k)
14634 .arg(v)
14635 .arg(o)
14636 .arg(&hd)
14637 .arg(&nh)
14638 .arg(&nhkv)
14639 .arg(&ti)
14640 .arg(&tkvi)
14641 .arg(&scale)
14642 .arg(&cz);
14643 unsafe {
14644 b.launch(cfg)?;
14645 }
14646 Ok(())
14647 }
14648
14649 #[allow(clippy::too_many_arguments)]
14657 pub fn fa_dequant_kv_view_f32(
14658 &self,
14659 k: &cudarc::driver::CudaView<u8>,
14660 v: &cudarc::driver::CudaView<u8>,
14661 kf: &mut CudaSlice<f32>,
14662 vf: &mut CudaSlice<f32>,
14663 kv_dim_k: usize,
14664 kv_dim_v: usize,
14665 t_kv: usize,
14666 k_tok_bytes: usize,
14667 v_tok_bytes: usize,
14668 g: bool,
14669 ) -> Result<(), Box<dyn std::error::Error>> {
14670 let f = if g {
14671 self.func_g("fa_dequant_kv_ws_f32")
14672 } else {
14673 self.func("fa_dequant_kv_ws_f32")
14674 };
14675 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
14676 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
14677 let cfg = LaunchConfig {
14678 grid_dim: (nblk.max(1), 1, 1),
14679 block_dim: (256, 1, 1),
14680 shared_mem_bytes: 0,
14681 };
14682 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
14683 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
14684 let __s_b = self.gpu.stream();
14685 let mut b = __s_b.launch_builder(&f);
14686 b.arg(k)
14687 .arg(v)
14688 .arg(&mut *kf)
14689 .arg(&mut *vf)
14690 .arg(&kdk)
14691 .arg(&kdv)
14692 .arg(&tkvi)
14693 .arg(&ktb)
14694 .arg(&vtb);
14695 unsafe {
14696 b.launch(cfg)?;
14697 }
14698 Ok(())
14699 }
14700
14701 #[allow(clippy::too_many_arguments)]
14702 pub fn sdpa_naive_quantized_view(
14703 &self,
14704 q: &CudaSlice<f32>,
14705 k: &cudarc::driver::CudaView<u8>,
14706 v: &cudarc::driver::CudaView<u8>,
14707 o: &mut CudaSlice<f32>,
14708 head_dim: usize,
14709 n_head: usize,
14710 n_head_kv: usize,
14711 t: usize,
14712 t_kv: usize,
14713 scale: f32,
14714 causal: bool,
14715 k_tok_bytes: usize,
14716 v_tok_bytes: usize,
14717 ) -> Result<(), Box<dyn std::error::Error>> {
14718 let kv_dim = n_head_kv * head_dim;
14719 let mut kf = self.uninit(t_kv * kv_dim)?;
14720 let mut vf = self.uninit(t_kv * kv_dim)?;
14721 let f = self.func("fa_dequant_kv_ws_f32");
14722 let total = (2 * t_kv * kv_dim) as u64;
14723 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
14724 let cfg = LaunchConfig {
14725 grid_dim: (nblk.max(1), 1, 1),
14726 block_dim: (256, 1, 1),
14727 shared_mem_bytes: 0,
14728 };
14729 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
14730 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
14731 let __s_b = self.gpu.stream();
14732 let mut b = __s_b.launch_builder(&f);
14733 b.arg(k)
14734 .arg(v)
14735 .arg(&mut kf)
14736 .arg(&mut vf)
14737 .arg(&kv_dim_i)
14738 .arg(&kv_dim_i)
14739 .arg(&t_kv_i)
14740 .arg(&k_tok_bytes_i)
14741 .arg(&v_tok_bytes_i);
14742 unsafe { b.launch(cfg)? };
14743 self.sdpa_naive(
14744 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
14745 )
14746 }
14747
14748 #[allow(clippy::too_many_arguments)]
14760 pub fn sdpa_naive_w_quantized_view(
14761 &self,
14762 q: &CudaSlice<f32>,
14763 k: &cudarc::driver::CudaView<u8>,
14764 v: &cudarc::driver::CudaView<u8>,
14765 o: &mut CudaSlice<f32>,
14766 head_dim: usize,
14767 n_head: usize,
14768 n_head_kv: usize,
14769 t: usize,
14770 t_kv: usize,
14771 scale: f32,
14772 causal: bool,
14773 window: usize,
14774 k_tok_bytes: usize,
14775 v_tok_bytes: usize,
14776 ) -> Result<(), Box<dyn std::error::Error>> {
14777 let kv_dim = n_head_kv * head_dim;
14778 let mut kf = self.uninit(t_kv * kv_dim)?;
14779 let mut vf = self.uninit(t_kv * kv_dim)?;
14780 let f = self.func("fa_dequant_kv_ws_f32");
14781 let total = (2 * t_kv * kv_dim) as u64;
14782 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
14783 let cfg = LaunchConfig {
14784 grid_dim: (nblk.max(1), 1, 1),
14785 block_dim: (256, 1, 1),
14786 shared_mem_bytes: 0,
14787 };
14788 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
14789 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
14790 let __s_b = self.gpu.stream();
14791 let mut b = __s_b.launch_builder(&f);
14792 b.arg(k)
14793 .arg(v)
14794 .arg(&mut kf)
14795 .arg(&mut vf)
14796 .arg(&kv_dim_i)
14797 .arg(&kv_dim_i)
14798 .arg(&t_kv_i)
14799 .arg(&k_tok_bytes_i)
14800 .arg(&v_tok_bytes_i);
14801 unsafe { b.launch(cfg)? };
14802 self.sdpa_naive_w(
14803 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
14804 )
14805 }
14806
14807 pub fn fa_prefill(
14811 &self,
14812 q: &CudaSlice<f32>,
14813 k: &CudaSlice<f32>,
14814 v: &CudaSlice<f32>,
14815 o: &mut CudaSlice<f32>,
14816 head_dim: usize,
14817 n_head: usize,
14818 n_head_kv: usize,
14819 t: usize,
14820 t_kv: usize,
14821 scale: f32,
14822 causal: bool,
14823 ) -> Result<(), Box<dyn std::error::Error>> {
14824 if portable_mma_gated() {
14825 return self.sdpa_naive(
14826 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
14827 );
14828 }
14829 let fa3_on = head_dim == 256
14837 && causal
14838 && t == t_kv
14839 && match std::env::var("MEMRA_FA3").as_deref() {
14840 Ok("0") => false,
14841 Ok("1") => true,
14842 _ => cfg!(memra_hopper_mma),
14843 };
14844 if fa3_on {
14845 let n = t * n_head * head_dim;
14846 let nkv = t * n_head_kv * head_dim;
14847 let mut q16 = self.alloc_u8_uninit(n * 2)?;
14848 let mut k16 = self.alloc_u8_uninit(nkv * 2)?;
14849 let mut v16 = self.alloc_u8_uninit(nkv * 2)?;
14850 self.f32_to_bf16_into(q, &mut q16, n)?;
14851 self.f32_to_bf16_into(k, &mut k16, nkv)?;
14852 self.f32_to_bf16_into(v, &mut v16, nkv)?;
14853 let rc = {
14854 use cudarc::driver::{DevicePtr, DevicePtrMut};
14855 let stream = self.gpu.stream();
14856 let (qp, _g1) = q16.device_ptr(&stream);
14857 let (kp, _g2) = k16.device_ptr(&stream);
14858 let (vp, _g3) = v16.device_ptr(&stream);
14859 let (op, _g4) = o.device_ptr_mut(&stream);
14860 unsafe {
14861 memra_fa3_prefill(
14862 qp as *const core::ffi::c_void,
14863 kp as *const core::ffi::c_void,
14864 vp as *const core::ffi::c_void,
14865 op as *mut f32,
14866 t as i32,
14867 n_head as i32,
14868 n_head_kv as i32,
14869 head_dim as i32,
14870 scale,
14871 stream.cu_stream() as *mut core::ffi::c_void,
14872 )
14873 }
14874 };
14875 if rc != 0 {
14876 return Err(format!("memra_fa3_prefill rc={rc}").into());
14877 }
14878 return Ok(());
14879 }
14880 static FA_P1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14885 let fa_p1 = *FA_P1.get_or_init(|| std::env::var("MEMRA_FA_P1").as_deref() == Ok("1"));
14886 if fa_p1 && head_dim == 256 && !std::env::var("MEMRA_FA_FLOOR").is_ok() {
14887 const BLOCK_Q: usize = 64;
14888 const BKX: usize = 32;
14889 let f = self.func("fa_prefill_bf16_p1");
14890 let shmem = (2 * (2 * BKX * head_dim + BLOCK_Q * BKX)
14891 + 4 * (BLOCK_Q * BKX + 2 * BLOCK_Q)) as u32;
14892 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14893 f.set_attribute(
14894 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14895 shmem as i32,
14896 )?;
14897 let cfg = LaunchConfig {
14898 grid_dim: (
14899 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
14900 n_head as u32,
14901 1,
14902 ),
14903 block_dim: (32, 4, 1),
14904 shared_mem_bytes: shmem,
14905 };
14906 let (hd, nh, nhkv, ti, tkvi, cz) = (
14907 head_dim as i32,
14908 n_head as i32,
14909 n_head_kv as i32,
14910 t as i32,
14911 t_kv as i32,
14912 causal as i32,
14913 );
14914 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
14915 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
14916 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
14917 let __s_b = self.gpu.stream();
14918 let mut b = __s_b.launch_builder(&f);
14919 b.arg(&qb)
14920 .arg(&kb)
14921 .arg(&vb)
14922 .arg(o)
14923 .arg(&hd)
14924 .arg(&nh)
14925 .arg(&nhkv)
14926 .arg(&ti)
14927 .arg(&tkvi)
14928 .arg(&scale)
14929 .arg(&cz);
14930 unsafe {
14931 b.launch(cfg)?;
14932 }
14933 return Ok(());
14934 }
14935 const BK: usize = 32;
14941 let w2 = std::env::var("MEMRA_FA_PP_W2").as_deref() == Ok("1");
14944 let (block_q, warps, w2_sfx): (usize, u32, &str) =
14945 if w2 { (32, 2, "_w2") } else { (64, 4, "") };
14946 let hd_sfx = fa_hd_suffix(head_dim)?;
14950 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
14951 let bf16kv = !floor && !w2 && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0");
14956 let (kb16, vb16) = if bf16kv {
14957 let n = t_kv * n_head_kv * head_dim;
14958 let mut kb = self.alloc_u8_uninit(n * 2)?;
14959 let mut vb = self.alloc_u8_uninit(n * 2)?;
14960 let fcv = self.func("f32_to_bf16_bulk");
14961 let ni = n as i64;
14962 let cfgc = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
14963 let __s_b = self.gpu.stream();
14964 let mut b = __s_b.launch_builder(&fcv);
14965 b.arg(k).arg(&mut kb).arg(&ni);
14966 unsafe {
14967 b.launch(cfgc)?;
14968 }
14969 let __s_b = self.gpu.stream();
14970 let mut b = __s_b.launch_builder(&fcv);
14971 b.arg(v).arg(&mut vb).arg(&ni);
14972 unsafe {
14973 b.launch(cfgc)?;
14974 }
14975 (Some(kb), Some(vb))
14976 } else {
14977 (None, None)
14978 };
14979 let f = self.func(&if bf16kv {
14980 format!("fa_prefill_bf16kv_pp{hd_sfx}")
14981 } else {
14982 format!(
14983 "fa_prefill_f32{}{}{hd_sfx}",
14984 if floor { "" } else { "_pp" },
14985 if floor { "" } else { w2_sfx }
14986 )
14987 });
14988 let kv_stages = if bf16kv { 2 } else { 1 };
14991 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
14992 + 4 * (block_q * BK + 2 * block_q)) as u32;
14993 use cudarc::driver::sys::CUfunction_attribute_enum as A;
14994 f.set_attribute(
14995 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
14996 shmem as i32,
14997 )?;
14998 let cfg = LaunchConfig {
14999 grid_dim: (
15000 (t as u32 + block_q as u32 - 1) / block_q as u32,
15001 n_head as u32,
15002 1,
15003 ),
15004 block_dim: (32, warps, 1),
15005 shared_mem_bytes: shmem,
15006 };
15007 let (hd, nh, nhkv, ti, tkvi, cz) = (
15008 head_dim as i32,
15009 n_head as i32,
15010 n_head_kv as i32,
15011 t as i32,
15012 t_kv as i32,
15013 causal as i32,
15014 );
15015 let __s_b = self.gpu.stream();
15016 let mut b = __s_b.launch_builder(&f);
15017 b.arg(q);
15018 match (&kb16, &vb16) {
15019 (Some(kb), Some(vb)) => {
15020 b.arg(kb).arg(vb);
15021 }
15022 _ => {
15023 b.arg(k).arg(v);
15024 }
15025 }
15026 b.arg(o)
15027 .arg(&hd)
15028 .arg(&nh)
15029 .arg(&nhkv)
15030 .arg(&ti)
15031 .arg(&tkvi)
15032 .arg(&scale)
15033 .arg(&cz);
15034 unsafe {
15035 b.launch(cfg)?;
15036 }
15037 Ok(())
15038 }
15039
15040 #[allow(clippy::too_many_arguments)]
15044 pub fn fa_prefill_w(
15045 &self,
15046 q: &CudaSlice<f32>,
15047 k: &CudaSlice<f32>,
15048 v: &CudaSlice<f32>,
15049 o: &mut CudaSlice<f32>,
15050 head_dim: usize,
15051 n_head: usize,
15052 n_head_kv: usize,
15053 t: usize,
15054 t_kv: usize,
15055 scale: f32,
15056 causal: bool,
15057 window: usize,
15058 ) -> Result<(), Box<dyn std::error::Error>> {
15059 if portable_mma_gated() {
15062 return self.sdpa_naive_w(
15063 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
15064 );
15065 }
15066 static FAW_F32: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15070 let faw_f32 =
15071 *FAW_F32.get_or_init(|| std::env::var("MEMRA_FAW_STAGE").as_deref() == Ok("f32"));
15072 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
15073 self.fa_prefill_w_arm(
15074 q,
15075 k,
15076 v,
15077 o,
15078 head_dim,
15079 n_head,
15080 n_head_kv,
15081 t,
15082 t_kv,
15083 scale,
15084 causal,
15085 window,
15086 floor || faw_f32,
15087 floor,
15088 )
15089 }
15090
15091 #[allow(clippy::too_many_arguments)]
15094 pub fn fa_prefill_w_pre(
15095 &self,
15096 qb: &CudaSlice<u8>,
15097 kb: &CudaSlice<u8>,
15098 vb: &CudaSlice<u8>,
15099 o: &mut CudaSlice<f32>,
15100 head_dim: usize,
15101 n_head: usize,
15102 n_head_kv: usize,
15103 t: usize,
15104 t_kv: usize,
15105 scale: f32,
15106 causal: bool,
15107 window: usize,
15108 v_f16: bool,
15109 ) -> Result<(), Box<dyn std::error::Error>> {
15110 const BLOCK_Q: usize = 64;
15111 const BK: usize = 32;
15112 debug_assert_eq!(head_dim, 256);
15113 let hp = fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15114 debug_assert!(!v_f16 || hp, "f16 V emitted but the SWA hp arm is off");
15115 if hp {
15116 const BLOCK_QH: usize = 32;
15117 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
15120 let vh: &CudaSlice<u8> = if v_f16 {
15121 vb
15122 } else {
15123 let n = t_kv * n_head_kv * head_dim;
15124 if vguard.as_ref().map(|b| b.len() < n * 2).unwrap_or(true) {
15125 *vguard = Some(self.alloc_uninit::<u8>(n * 2)?);
15126 }
15127 self.bf16_to_f16_into(vb, n, vguard.as_mut().unwrap())?;
15128 vguard.as_ref().unwrap()
15129 };
15130 let f = self.func("fa_prefill_w_bf16_p1h2");
15131 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
15132 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15133 f.set_attribute(
15134 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15135 shmem as i32,
15136 )?;
15137 let cfg = LaunchConfig {
15138 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
15139 block_dim: (32, 4, 1),
15140 shared_mem_bytes: shmem,
15141 };
15142 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15143 head_dim as i32,
15144 n_head as i32,
15145 n_head_kv as i32,
15146 t as i32,
15147 t_kv as i32,
15148 causal as i32,
15149 window as i32,
15150 );
15151 let __s_b = self.gpu.stream();
15152 let mut b = __s_b.launch_builder(&f);
15153 b.arg(qb)
15154 .arg(kb)
15155 .arg(vh)
15156 .arg(o)
15157 .arg(&hd)
15158 .arg(&nh)
15159 .arg(&nhkv)
15160 .arg(&ti)
15161 .arg(&tkvi)
15162 .arg(&scale)
15163 .arg(&cz)
15164 .arg(&wi);
15165 unsafe {
15166 b.launch(cfg)?;
15167 }
15168 return Ok(());
15169 }
15170 let f = self.func("fa_prefill_w_bf16_p1");
15171 let shmem =
15172 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15173 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15174 f.set_attribute(
15175 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15176 shmem as i32,
15177 )?;
15178 let cfg = LaunchConfig {
15179 grid_dim: (
15180 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15181 n_head as u32,
15182 1,
15183 ),
15184 block_dim: (32, 4, 1),
15185 shared_mem_bytes: shmem,
15186 };
15187 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15188 head_dim as i32,
15189 n_head as i32,
15190 n_head_kv as i32,
15191 t as i32,
15192 t_kv as i32,
15193 causal as i32,
15194 window as i32,
15195 );
15196 let __s_b = self.gpu.stream();
15197 let mut b = __s_b.launch_builder(&f);
15198 b.arg(qb)
15199 .arg(kb)
15200 .arg(vb)
15201 .arg(o)
15202 .arg(&hd)
15203 .arg(&nh)
15204 .arg(&nhkv)
15205 .arg(&ti)
15206 .arg(&tkvi)
15207 .arg(&scale)
15208 .arg(&cz)
15209 .arg(&wi);
15210 unsafe {
15211 b.launch(cfg)?;
15212 }
15213 Ok(())
15214 }
15215
15216 #[allow(clippy::too_many_arguments)]
15218 pub fn fa_prefill_w_arm(
15219 &self,
15220 q: &CudaSlice<f32>,
15221 k: &CudaSlice<f32>,
15222 v: &CudaSlice<f32>,
15223 o: &mut CudaSlice<f32>,
15224 head_dim: usize,
15225 n_head: usize,
15226 n_head_kv: usize,
15227 t: usize,
15228 t_kv: usize,
15229 scale: f32,
15230 causal: bool,
15231 window: usize,
15232 f32_stage: bool,
15233 floor: bool,
15234 ) -> Result<(), Box<dyn std::error::Error>> {
15235 const BLOCK_Q: usize = 64;
15236 const BK: usize = 32;
15237 debug_assert_eq!(head_dim, 256, "fa_prefill_w is stamped hd256 only");
15238 static P1_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15242 let p1 = !floor
15243 && !f32_stage
15244 && *P1_ON.get_or_init(|| {
15245 std::env::var("MEMRA_FAW_P1")
15246 .map(|v| v != "0")
15247 .unwrap_or(true)
15248 });
15249 let hp =
15250 p1 && fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15251 if hp {
15252 const BLOCK_QH: usize = 32;
15253 let f = self.func("fa_prefill_w_bf16_p1h2");
15254 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
15255 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15256 f.set_attribute(
15257 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15258 shmem as i32,
15259 )?;
15260 let cfg = LaunchConfig {
15261 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
15262 block_dim: (32, 4, 1),
15263 shared_mem_bytes: shmem,
15264 };
15265 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15266 head_dim as i32,
15267 n_head as i32,
15268 n_head_kv as i32,
15269 t as i32,
15270 t_kv as i32,
15271 causal as i32,
15272 window as i32,
15273 );
15274 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15275 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15276 let vh = self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?;
15277 let __s_b = self.gpu.stream();
15278 let mut b = __s_b.launch_builder(&f);
15279 b.arg(&qb)
15280 .arg(&kb)
15281 .arg(&vh)
15282 .arg(o)
15283 .arg(&hd)
15284 .arg(&nh)
15285 .arg(&nhkv)
15286 .arg(&ti)
15287 .arg(&tkvi)
15288 .arg(&scale)
15289 .arg(&cz)
15290 .arg(&wi);
15291 unsafe {
15292 b.launch(cfg)?;
15293 }
15294 return Ok(());
15295 }
15296 if p1 {
15297 let f = self.func("fa_prefill_w_bf16_p1");
15298 let shmem =
15299 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15300 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15301 f.set_attribute(
15302 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15303 shmem as i32,
15304 )?;
15305 let cfg = LaunchConfig {
15306 grid_dim: (
15307 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15308 n_head as u32,
15309 1,
15310 ),
15311 block_dim: (32, 4, 1),
15312 shared_mem_bytes: shmem,
15313 };
15314 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15315 head_dim as i32,
15316 n_head as i32,
15317 n_head_kv as i32,
15318 t as i32,
15319 t_kv as i32,
15320 causal as i32,
15321 window as i32,
15322 );
15323 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15324 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15325 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15326 let __s_b = self.gpu.stream();
15327 let mut b = __s_b.launch_builder(&f);
15328 b.arg(&qb)
15329 .arg(&kb)
15330 .arg(&vb)
15331 .arg(o)
15332 .arg(&hd)
15333 .arg(&nh)
15334 .arg(&nhkv)
15335 .arg(&ti)
15336 .arg(&tkvi)
15337 .arg(&scale)
15338 .arg(&cz)
15339 .arg(&wi);
15340 unsafe {
15341 b.launch(cfg)?;
15342 }
15343 return Ok(());
15344 }
15345 static G4_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15348 let g4 = !floor
15349 && !f32_stage
15350 && n_head_kv == 1
15351 && n_head % 4 == 0
15352 && *G4_ON.get_or_init(|| {
15353 std::env::var("MEMRA_FAW_G4")
15354 .map(|v| v != "0")
15355 .unwrap_or(true)
15356 });
15357 if g4 {
15358 const SP_M: usize = 16;
15359 static O2_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15362 let o2 = *O2_ON.get_or_init(|| {
15363 std::env::var("MEMRA_FAW_O2")
15364 .map(|v| v != "0")
15365 .unwrap_or(true)
15366 });
15367 let f = self.func(if o2 {
15368 "fa_prefill_w_bf16_g4o2"
15369 } else {
15370 "fa_prefill_w_bf16_g4"
15371 });
15372 let shmem = if o2 {
15373 (2 * (4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M)) as u32
15374 } else {
15375 (2 * (2 * BK * head_dim + 4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M))
15376 as u32
15377 };
15378 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15379 f.set_attribute(
15380 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15381 shmem as i32,
15382 )?;
15383 let cfg = LaunchConfig {
15384 grid_dim: ((t as u32).div_ceil(SP_M as u32), (n_head / 4) as u32, 1),
15385 block_dim: (32, 4, 1),
15386 shared_mem_bytes: shmem,
15387 };
15388 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15389 head_dim as i32,
15390 n_head as i32,
15391 n_head_kv as i32,
15392 t as i32,
15393 t_kv as i32,
15394 causal as i32,
15395 window as i32,
15396 );
15397 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15398 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15399 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15400 let __s_b = self.gpu.stream();
15401 let mut b = __s_b.launch_builder(&f);
15402 b.arg(&qb)
15403 .arg(&kb)
15404 .arg(&vb)
15405 .arg(o)
15406 .arg(&hd)
15407 .arg(&nh)
15408 .arg(&nhkv)
15409 .arg(&ti)
15410 .arg(&tkvi)
15411 .arg(&scale)
15412 .arg(&cz)
15413 .arg(&wi);
15414 unsafe {
15415 b.launch(cfg)?;
15416 }
15417 return Ok(());
15418 }
15419 let f = self.func(if floor {
15420 "fa_prefill_w_f32"
15421 } else if f32_stage {
15422 "fa_prefill_w_f32_pp"
15423 } else {
15424 "fa_prefill_w_bf16_pp"
15425 });
15426 let shmem =
15427 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15428 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15429 f.set_attribute(
15430 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15431 shmem as i32,
15432 )?;
15433 let cfg = LaunchConfig {
15434 grid_dim: (
15435 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15436 n_head as u32,
15437 1,
15438 ),
15439 block_dim: (32, 4, 1),
15440 shared_mem_bytes: shmem,
15441 };
15442 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15443 head_dim as i32,
15444 n_head as i32,
15445 n_head_kv as i32,
15446 t as i32,
15447 t_kv as i32,
15448 causal as i32,
15449 window as i32,
15450 );
15451 if f32_stage {
15452 let __s_b = self.gpu.stream();
15453 let mut b = __s_b.launch_builder(&f);
15454 b.arg(q)
15455 .arg(k)
15456 .arg(v)
15457 .arg(o)
15458 .arg(&hd)
15459 .arg(&nh)
15460 .arg(&nhkv)
15461 .arg(&ti)
15462 .arg(&tkvi)
15463 .arg(&scale)
15464 .arg(&cz)
15465 .arg(&wi);
15466 unsafe {
15467 b.launch(cfg)?;
15468 }
15469 } else {
15470 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15471 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15472 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15473 let __s_b = self.gpu.stream();
15474 let mut b = __s_b.launch_builder(&f);
15475 b.arg(&qb)
15476 .arg(&kb)
15477 .arg(&vb)
15478 .arg(o)
15479 .arg(&hd)
15480 .arg(&nh)
15481 .arg(&nhkv)
15482 .arg(&ti)
15483 .arg(&tkvi)
15484 .arg(&scale)
15485 .arg(&cz)
15486 .arg(&wi);
15487 unsafe {
15488 b.launch(cfg)?;
15489 }
15490 }
15491 Ok(())
15492 }
15493
15494 #[allow(clippy::too_many_arguments)]
15498 pub fn fa_prefill_hd512(
15499 &self,
15500 q: &CudaSlice<f32>,
15501 k: &CudaSlice<f32>,
15502 v: &CudaSlice<f32>,
15503 o: &mut CudaSlice<f32>,
15504 head_dim: usize,
15505 n_head: usize,
15506 n_head_kv: usize,
15507 t: usize,
15508 t_kv: usize,
15509 scale: f32,
15510 causal: bool,
15511 ) -> Result<(), Box<dyn std::error::Error>> {
15512 if portable_mma_gated() {
15514 return self.sdpa_naive(
15515 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
15516 );
15517 }
15518 static F32_STAGE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15524 let f32_stage =
15525 *F32_STAGE.get_or_init(|| std::env::var("MEMRA_FA512_STAGE").as_deref() == Ok("f32"));
15526 static SP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15530 let sp = !f32_stage
15531 && *SP_ON.get_or_init(|| {
15532 std::env::var("MEMRA_FA512_SP")
15533 .map(|v| v != "0")
15534 .unwrap_or(true)
15535 });
15536 self.fa_prefill_hd512_arm(
15537 q,
15538 k,
15539 v,
15540 o,
15541 head_dim,
15542 n_head,
15543 n_head_kv,
15544 t,
15545 t_kv,
15546 scale,
15547 causal,
15548 f32_stage,
15549 sp,
15550 sp && fa_f16pv_on(),
15551 )
15552 }
15553
15554 #[allow(clippy::too_many_arguments)]
15556 pub fn fa_prefill_hd512_pre(
15557 &self,
15558 qb: &CudaSlice<u8>,
15559 kb: &CudaSlice<u8>,
15560 vb: &CudaSlice<u8>,
15561 o: &mut CudaSlice<f32>,
15562 head_dim: usize,
15563 n_head: usize,
15564 n_head_kv: usize,
15565 t: usize,
15566 t_kv: usize,
15567 scale: f32,
15568 causal: bool,
15569 v_f16: bool,
15570 ) -> Result<(), Box<dyn std::error::Error>> {
15571 debug_assert_eq!(head_dim, 512);
15572 const SP_M: usize = 16;
15573 const BKS: usize = 32;
15574 let f16pv = fa_f16pv_on();
15578 let nw = if f16pv { fa512_wide_warps() } else { 2 };
15579 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15580 debug_assert!(!v_f16 || f16pv, "f16 V emitted without the door on");
15581 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
15582 let vref: &CudaSlice<u8> = if f16pv && !v_f16 {
15583 let n = t_kv * n_head_kv * head_dim;
15585 let need = n * 2;
15586 if vguard.as_ref().map(|b| b.len() < need).unwrap_or(true) {
15587 *vguard = Some(self.alloc_uninit::<u8>(need)?);
15588 }
15589 let dst = vguard.as_mut().unwrap();
15590 self.bf16_to_f16_into(vb, n, dst)?;
15591 vguard.as_ref().unwrap()
15592 } else {
15593 vb
15594 };
15595 let f = self.func(if hp {
15596 "fa_prefill_bf16_hd512_sp16h2"
15597 } else {
15598 match (f16pv, nw) {
15599 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
15600 (true, _) => "fa_prefill_bf16_hd512_sp16",
15601 _ => "fa_prefill_bf16_hd512_sp",
15602 }
15603 });
15604 let (nwarp, npart) = if hp {
15605 (4usize, 4usize)
15606 } else if nw > 2 {
15607 (nw, nw)
15608 } else {
15609 (2, 1)
15610 };
15611 let shmem = if hp {
15613 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS) + 4 * (2 * npart * SP_M * BKS + 2 * SP_M))
15614 as u32
15615 } else {
15616 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
15617 + 4 * (npart * SP_M * BKS + SP_M)) as u32
15618 };
15619 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15620 f.set_attribute(
15621 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15622 shmem as i32,
15623 )?;
15624 let grid_y = if hp {
15625 (n_head / 2) as u32
15626 } else {
15627 n_head as u32
15628 };
15629 let cfg = LaunchConfig {
15630 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
15631 block_dim: (32, nwarp as u32, 1),
15632 shared_mem_bytes: shmem,
15633 };
15634 let (hd, nh, nhkv, ti, tkvi, cz) = (
15635 head_dim as i32,
15636 n_head as i32,
15637 n_head_kv as i32,
15638 t as i32,
15639 t_kv as i32,
15640 causal as i32,
15641 );
15642 let __s_b = self.gpu.stream();
15643 let mut b = __s_b.launch_builder(&f);
15644 b.arg(qb)
15645 .arg(kb)
15646 .arg(vref)
15647 .arg(o)
15648 .arg(&hd)
15649 .arg(&nh)
15650 .arg(&nhkv)
15651 .arg(&ti)
15652 .arg(&tkvi)
15653 .arg(&scale)
15654 .arg(&cz);
15655 unsafe {
15656 b.launch(cfg)?;
15657 }
15658 Ok(())
15659 }
15660
15661 #[allow(clippy::too_many_arguments)]
15664 pub fn fa_prefill_hd512_arm(
15665 &self,
15666 q: &CudaSlice<f32>,
15667 k: &CudaSlice<f32>,
15668 v: &CudaSlice<f32>,
15669 o: &mut CudaSlice<f32>,
15670 head_dim: usize,
15671 n_head: usize,
15672 n_head_kv: usize,
15673 t: usize,
15674 t_kv: usize,
15675 scale: f32,
15676 causal: bool,
15677 f32_stage: bool,
15678 sp: bool,
15679 f16pv: bool,
15680 ) -> Result<(), Box<dyn std::error::Error>> {
15681 debug_assert_eq!(head_dim, 512, "fa_prefill_hd512 is hd512 only");
15682 if sp && !f32_stage {
15683 const SP_M: usize = 16;
15687 const BKS: usize = 32;
15688 let nw = if f16pv { fa512_wide_warps() } else { 2 };
15689 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15690 let f = self.func(if hp {
15691 "fa_prefill_bf16_hd512_sp16h2"
15692 } else {
15693 match (f16pv, nw) {
15694 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
15695 (true, _) => "fa_prefill_bf16_hd512_sp16",
15696 _ => "fa_prefill_bf16_hd512_sp",
15697 }
15698 });
15699 let (nwarp, npart) = if hp {
15700 (4usize, 4usize)
15701 } else if nw > 2 {
15702 (nw, nw)
15703 } else {
15704 (2, 1)
15705 };
15706 let shmem = if hp {
15707 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS)
15708 + 4 * (2 * npart * SP_M * BKS + 2 * SP_M)) as u32
15709 } else {
15710 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
15711 + 4 * (npart * SP_M * BKS + SP_M)) as u32
15712 };
15713 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15714 f.set_attribute(
15715 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15716 shmem as i32,
15717 )?;
15718 let grid_y = if hp {
15719 (n_head / 2) as u32
15720 } else {
15721 n_head as u32
15722 };
15723 let cfg = LaunchConfig {
15724 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
15725 block_dim: (32, nwarp as u32, 1),
15726 shared_mem_bytes: shmem,
15727 };
15728 let (hd, nh, nhkv, ti, tkvi, cz) = (
15729 head_dim as i32,
15730 n_head as i32,
15731 n_head_kv as i32,
15732 t as i32,
15733 t_kv as i32,
15734 causal as i32,
15735 );
15736 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15737 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15738 let vb = if f16pv {
15739 self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?
15740 } else {
15741 self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?
15742 };
15743 let __s_b = self.gpu.stream();
15744 let mut b = __s_b.launch_builder(&f);
15745 b.arg(&qb)
15746 .arg(&kb)
15747 .arg(&vb)
15748 .arg(o)
15749 .arg(&hd)
15750 .arg(&nh)
15751 .arg(&nhkv)
15752 .arg(&ti)
15753 .arg(&tkvi)
15754 .arg(&scale)
15755 .arg(&cz);
15756 unsafe {
15757 b.launch(cfg)?;
15758 }
15759 return Ok(());
15760 }
15761 const BLOCK_Q: usize = 32;
15762 const BK: usize = 32;
15763 const HALF: usize = 256;
15764 let f = self.func(if f32_stage {
15765 "fa_prefill_f32_hd512"
15766 } else {
15767 "fa_prefill_bf16_hd512"
15768 });
15769 let shmem = (2 * (BLOCK_Q * head_dim + BK * head_dim + BK * HALF + BLOCK_Q * BK)
15771 + 4 * BLOCK_Q) as u32;
15772 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15773 f.set_attribute(
15774 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15775 shmem as i32,
15776 )?;
15777 let cfg = LaunchConfig {
15778 grid_dim: (
15779 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15780 n_head as u32,
15781 2,
15782 ),
15783 block_dim: (32, 2, 1),
15784 shared_mem_bytes: shmem,
15785 };
15786 let (hd, nh, nhkv, ti, tkvi, cz) = (
15787 head_dim as i32,
15788 n_head as i32,
15789 n_head_kv as i32,
15790 t as i32,
15791 t_kv as i32,
15792 causal as i32,
15793 );
15794 if f32_stage {
15795 let __s_b = self.gpu.stream();
15796 let mut b = __s_b.launch_builder(&f);
15797 b.arg(q)
15798 .arg(k)
15799 .arg(v)
15800 .arg(o)
15801 .arg(&hd)
15802 .arg(&nh)
15803 .arg(&nhkv)
15804 .arg(&ti)
15805 .arg(&tkvi)
15806 .arg(&scale)
15807 .arg(&cz);
15808 unsafe {
15809 b.launch(cfg)?;
15810 }
15811 } else {
15812 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15813 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15814 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15815 let __s_b = self.gpu.stream();
15816 let mut b = __s_b.launch_builder(&f);
15817 b.arg(&qb)
15818 .arg(&kb)
15819 .arg(&vb)
15820 .arg(o)
15821 .arg(&hd)
15822 .arg(&nh)
15823 .arg(&nhkv)
15824 .arg(&ti)
15825 .arg(&tkvi)
15826 .arg(&scale)
15827 .arg(&cz);
15828 unsafe {
15829 b.launch(cfg)?;
15830 }
15831 }
15832 Ok(())
15833 }
15834
15835 #[allow(clippy::too_many_arguments)]
15839 pub fn rope_neox2_bf16e(
15840 &self,
15841 q: &mut CudaSlice<f32>,
15842 k: &mut CudaSlice<f32>,
15843 qb: &mut CudaSlice<u8>,
15844 kb: &mut CudaSlice<u8>,
15845 pos: &CudaSlice<i32>,
15846 head_dim: usize,
15847 n_dims: usize,
15848 nh_q: usize,
15849 nh_k: usize,
15850 n_tokens: usize,
15851 base: f32,
15852 freq_scale: f32,
15853 ff: Option<&CudaSlice<f32>>,
15854 ) -> Result<(), Box<dyn std::error::Error>> {
15855 let f = self.func("rope_neox2_bf16e_f32");
15856 let rows = ((nh_q + nh_k) * n_tokens) as u32;
15857 let cfg = LaunchConfig {
15858 grid_dim: (rows, 1, 1),
15859 block_dim: ((head_dim / 2) as u32, 1, 1),
15860 shared_mem_bytes: 0,
15861 };
15862 let theta_scale = base.powf(-2.0 / n_dims as f32);
15863 let (hd, nd, nhq, nhk, nt) = (
15864 head_dim as i32,
15865 n_dims as i32,
15866 nh_q as i32,
15867 nh_k as i32,
15868 n_tokens as i32,
15869 );
15870 let __s_b = self.gpu.stream();
15871 let mut b = __s_b.launch_builder(&f);
15872 match ff {
15873 Some(t) => {
15874 b.arg(&mut *q)
15875 .arg(&mut *k)
15876 .arg(&mut *qb)
15877 .arg(&mut *kb)
15878 .arg(pos)
15879 .arg(&hd)
15880 .arg(&nd)
15881 .arg(&nhq)
15882 .arg(&nhk)
15883 .arg(&nt)
15884 .arg(&theta_scale)
15885 .arg(&freq_scale)
15886 .arg(t);
15887 unsafe {
15888 b.launch(cfg)?;
15889 }
15890 }
15891 None => {
15892 let null: u64 = 0;
15893 b.arg(&mut *q)
15894 .arg(&mut *k)
15895 .arg(&mut *qb)
15896 .arg(&mut *kb)
15897 .arg(pos)
15898 .arg(&hd)
15899 .arg(&nd)
15900 .arg(&nhq)
15901 .arg(&nhk)
15902 .arg(&nt)
15903 .arg(&theta_scale)
15904 .arg(&freq_scale)
15905 .arg(&null);
15906 unsafe {
15907 b.launch(cfg)?;
15908 }
15909 }
15910 }
15911 Ok(())
15912 }
15913
15914 pub fn f32_to_bf16(
15917 &self,
15918 x: &CudaSlice<f32>,
15919 n: usize,
15920 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
15921 assert!(n % 4 == 0, "f32_to_bf16 requires n % 4 == 0, got {n}");
15922 let mut y = self.alloc_uninit::<u8>(n * 2)?;
15923 let f = self.func("f32_to_bf16_flat");
15924 let n_i = n as i64;
15925 let cfg = LaunchConfig {
15926 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
15927 block_dim: (256, 1, 1),
15928 shared_mem_bytes: 0,
15929 };
15930 let __s_b = self.gpu.stream();
15931 let mut b = __s_b.launch_builder(&f);
15932 b.arg(x).arg(&mut y).arg(&n_i);
15933 unsafe {
15934 b.launch(cfg)?;
15935 }
15936 Ok(y)
15937 }
15938
15939 pub fn f32_to_f16(
15940 &self,
15941 x: &CudaSlice<f32>,
15942 n: usize,
15943 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
15944 assert!(n % 4 == 0, "f32_to_f16 requires n % 4 == 0, got {n}");
15945 let mut y = self.alloc_uninit::<u8>(n * 2)?;
15946 let f = self.func("f32_to_f16_flat");
15947 let n_i = n as i64;
15948 let cfg = LaunchConfig {
15949 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
15950 block_dim: (256, 1, 1),
15951 shared_mem_bytes: 0,
15952 };
15953 let __s_b = self.gpu.stream();
15954 let mut b = __s_b.launch_builder(&f);
15955 b.arg(x).arg(&mut y).arg(&n_i);
15956 unsafe {
15957 b.launch(cfg)?;
15958 }
15959 Ok(y)
15960 }
15961
15962 pub fn bf16_to_f16(
15964 &self,
15965 xb: &CudaSlice<u8>,
15966 n: usize,
15967 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
15968 let mut y = self.alloc_uninit::<u8>(n * 2)?;
15969 self.bf16_to_f16_into(xb, n, &mut y)?;
15970 Ok(y)
15971 }
15972
15973 pub fn bf16_to_f16_into(
15975 &self,
15976 xb: &CudaSlice<u8>,
15977 n: usize,
15978 y: &mut CudaSlice<u8>,
15979 ) -> Result<(), Box<dyn std::error::Error>> {
15980 assert!(n % 2 == 0, "bf16_to_f16 requires n % 2 == 0, got {n}");
15981 assert!(y.len() >= n * 2);
15982 let f = self.func("bf16_to_f16_flat");
15983 let n2 = (n / 2) as i64;
15984 let cfg = LaunchConfig {
15985 grid_dim: (((n / 2) as u32).div_ceil(256), 1, 1),
15986 block_dim: (256, 1, 1),
15987 shared_mem_bytes: 0,
15988 };
15989 let __s_b = self.gpu.stream();
15990 let mut b = __s_b.launch_builder(&f);
15991 b.arg(xb).arg(y).arg(&n2);
15992 unsafe {
15993 b.launch(cfg)?;
15994 }
15995 Ok(())
15996 }
15997
15998 #[allow(clippy::too_many_arguments)]
16003 pub fn fa_prefill_vl8(
16004 &self,
16005 seqs: &[FaSeqVl],
16006 head_dim: usize,
16007 n_head: usize,
16008 n_head_kv: usize,
16009 scale: f32,
16010 ) -> Result<(), Box<dyn std::error::Error>> {
16011 const BK: usize = 32;
16012 let b = seqs.len();
16013 assert!(b >= 1 && b <= 8);
16014 let mut packed = [FaSeqVl::default(); 8];
16015 packed[..b].copy_from_slice(seqs);
16016 let v = FaVl8(packed);
16017 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
16018 let ept = (n_head_kv * head_dim) as i32;
16019 {
16020 let f = self.func("fa_mirror_vl");
16021 let max_n = (max_t as i64) * ept as i64;
16022 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
16023 for which in 0..2i32 {
16024 let cfg = LaunchConfig {
16025 grid_dim: (blocks, 1, b as u32),
16026 block_dim: (256, 1, 1),
16027 shared_mem_bytes: 0,
16028 };
16029 let __s_lb = self.gpu.stream();
16030 let mut lb = __s_lb.launch_builder(&f);
16031 lb.arg(&v).arg(&ept).arg(&which);
16032 unsafe {
16033 lb.launch(cfg)?;
16034 }
16035 }
16036 }
16037 let hd_sfx = fa_hd_suffix(head_dim)?;
16038 let f = self.func(&format!("fa_prefill_bf16kv_vl{hd_sfx}"));
16039 let block_q = 64usize;
16040 let kv_stages = 2usize;
16041 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
16042 + 4 * (block_q * BK + 2 * block_q)) as u32;
16043 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16044 f.set_attribute(
16045 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16046 shmem as i32,
16047 )?;
16048 let cfg = LaunchConfig {
16049 grid_dim: (max_t.div_ceil(block_q as u32), n_head as u32, b as u32),
16050 block_dim: (32, 4, 1),
16051 shared_mem_bytes: shmem,
16052 };
16053 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
16054 let __s_lb = self.gpu.stream();
16055 let mut lb = __s_lb.launch_builder(&f);
16056 lb.arg(&v).arg(&hd).arg(&nh).arg(&nhkv).arg(&scale);
16057 unsafe {
16058 lb.launch(cfg)?;
16059 }
16060 Ok(())
16061 }
16062
16063 #[allow(clippy::too_many_arguments)]
16067 pub fn attn_pre_vl8(
16068 &self,
16069 seqs: &[AttnPreVl],
16070 wq: &CudaSlice<f32>,
16071 wk: &CudaSlice<f32>,
16072 head_dim: usize,
16073 rope_dims: usize,
16074 n_head: usize,
16075 n_head_kv: usize,
16076 eps: f32,
16077 freq_base: f32,
16078 freq_scale: f32,
16079 kv_dim_k: usize,
16080 kv_dim_v: usize,
16081 k_tok_bytes: usize,
16082 v_tok_bytes: usize,
16083 ) -> Result<(), Box<dyn std::error::Error>> {
16084 let b = seqs.len();
16085 assert!(b >= 1 && b <= 8);
16086 let mut packed = [AttnPreVl::default(); 8];
16087 packed[..b].copy_from_slice(seqs);
16088 let v = AttnPreVl8(packed);
16089 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
16090 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
16091 {
16092 let f = self.func("q_gate_split_vl");
16093 let n = max_t * (n_head * head_dim) as u32;
16094 let cfg = LaunchConfig {
16095 grid_dim: (n.div_ceil(256), 1, b as u32),
16096 block_dim: (256, 1, 1),
16097 shared_mem_bytes: 0,
16098 };
16099 let __s_lb = self.gpu.stream();
16100 let mut lb = __s_lb.launch_builder(&f);
16101 lb.arg(&v).arg(&hd).arg(&nh);
16102 unsafe {
16103 lb.launch(cfg)?;
16104 }
16105 }
16106 {
16107 let f = self.func("attn_rms_vl");
16108 let cfg = LaunchConfig {
16109 grid_dim: (max_t * n_head as u32, 2, b as u32),
16110 block_dim: (rms_block(), 1, 1),
16111 shared_mem_bytes: 0,
16112 };
16113 let __s_lb = self.gpu.stream();
16114 let mut lb = __s_lb.launch_builder(&f);
16115 lb.arg(&v)
16116 .arg(wq)
16117 .arg(wk)
16118 .arg(&hd)
16119 .arg(&nh)
16120 .arg(&nhkv)
16121 .arg(&eps);
16122 unsafe {
16123 lb.launch(cfg)?;
16124 }
16125 }
16126 {
16127 let f = self.func("attn_rope_vl");
16128 let theta_scale = freq_base.powf(-2.0 / rope_dims as f32);
16129 let nd = rope_dims as i32;
16130 let cfg = LaunchConfig {
16131 grid_dim: (max_t * n_head as u32, 2, b as u32),
16132 block_dim: ((head_dim / 2) as u32, 1, 1),
16133 shared_mem_bytes: 0,
16134 };
16135 let __s_lb = self.gpu.stream();
16136 let mut lb = __s_lb.launch_builder(&f);
16137 lb.arg(&v)
16138 .arg(&hd)
16139 .arg(&nd)
16140 .arg(&nh)
16141 .arg(&nhkv)
16142 .arg(&theta_scale)
16143 .arg(&freq_scale);
16144 unsafe {
16145 lb.launch(cfg)?;
16146 }
16147 }
16148 {
16149 let f = self.func("append_kv_vl");
16150 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
16151 let cfg = LaunchConfig {
16152 grid_dim: (nblk, max_t, b as u32),
16153 block_dim: (32, 1, 1),
16154 shared_mem_bytes: 0,
16155 };
16156 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
16157 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16158 let __s_lb = self.gpu.stream();
16159 let mut lb = __s_lb.launch_builder(&f);
16160 lb.arg(&v).arg(&kdk).arg(&kdv).arg(&ktb).arg(&vtb);
16161 unsafe {
16162 lb.launch(cfg)?;
16163 }
16164 }
16165 Ok(())
16166 }
16167
16168 pub fn fa_prefill_view(
16173 &self,
16174 q: &CudaSlice<f32>,
16175 k: &cudarc::driver::CudaView<u8>,
16176 v: &cudarc::driver::CudaView<u8>,
16177 o: &mut CudaSlice<f32>,
16178 head_dim: usize,
16179 n_head: usize,
16180 n_head_kv: usize,
16181 t: usize,
16182 t_kv: usize,
16183 scale: f32,
16184 causal: bool,
16185 k_tok_bytes: usize,
16186 v_tok_bytes: usize,
16187 g: bool,
16188 ) -> Result<(), Box<dyn std::error::Error>> {
16189 if portable_mma_gated() {
16190 return self.sdpa_naive_quantized_view(
16191 q,
16192 k,
16193 v,
16194 o,
16195 head_dim,
16196 n_head,
16197 n_head_kv,
16198 t,
16199 t_kv,
16200 scale,
16201 causal,
16202 k_tok_bytes,
16203 v_tok_bytes,
16204 );
16205 }
16206 const BLOCK_Q: usize = 64;
16207 const BK: usize = 32;
16208 let name = format!("fa_prefill_q{}", fa_hd_suffix(head_dim)?);
16211 let f = if g {
16212 self.func_g(&name)
16213 } else {
16214 self.func(&name)
16215 };
16216 let shmem =
16217 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
16218 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16219 f.set_attribute(
16220 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16221 shmem as i32,
16222 )?;
16223 let cfg = LaunchConfig {
16224 grid_dim: (
16225 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16226 n_head as u32,
16227 1,
16228 ),
16229 block_dim: (32, 4, 1),
16230 shared_mem_bytes: shmem,
16231 };
16232 let (hd, nh, nhkv, ti, tkvi, cz) = (
16233 head_dim as i32,
16234 n_head as i32,
16235 n_head_kv as i32,
16236 t as i32,
16237 t_kv as i32,
16238 causal as i32,
16239 );
16240 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16241 let __s_b = self.gpu.stream();
16242 let mut b = __s_b.launch_builder(&f);
16243 b.arg(q)
16244 .arg(k)
16245 .arg(v)
16246 .arg(o)
16247 .arg(&hd)
16248 .arg(&nh)
16249 .arg(&nhkv)
16250 .arg(&ti)
16251 .arg(&tkvi)
16252 .arg(&scale)
16253 .arg(&cz)
16254 .arg(&ktb)
16255 .arg(&vtb);
16256 unsafe {
16257 b.launch(cfg)?;
16258 }
16259 Ok(())
16260 }
16261
16262 #[allow(clippy::too_many_arguments)]
16272 pub fn fa_prefill_view_ws(
16273 &self,
16274 q: &CudaSlice<f32>,
16275 k: &cudarc::driver::CudaView<u8>,
16276 v: &cudarc::driver::CudaView<u8>,
16277 o: &mut CudaSlice<f32>,
16278 head_dim: usize,
16279 n_head: usize,
16280 n_head_kv: usize,
16281 t: usize,
16282 t_kv: usize,
16283 scale: f32,
16284 causal: bool,
16285 k_tok_bytes: usize,
16286 v_tok_bytes: usize,
16287 g: bool,
16288 ) -> Result<(), Box<dyn std::error::Error>> {
16289 if portable_mma_gated() {
16290 return self.sdpa_naive_quantized_view(
16291 q,
16292 k,
16293 v,
16294 o,
16295 head_dim,
16296 n_head,
16297 n_head_kv,
16298 t,
16299 t_kv,
16300 scale,
16301 causal,
16302 k_tok_bytes,
16303 v_tok_bytes,
16304 );
16305 }
16306 const BLOCK_Q: usize = 64;
16307 const BK: usize = 32;
16308 let kv_dim_k = n_head_kv * head_dim;
16309 let kv_dim_v = n_head_kv * head_dim;
16310 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
16312 let mut guard = self.prime_deqw_ws.lock().unwrap();
16314 let need_grow = match guard.as_ref() {
16315 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
16316 None => true,
16317 };
16318 if need_grow {
16319 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
16320 let (ck, cv) = guard
16321 .as_ref()
16322 .map(|(a, b)| (a.len(), b.len()))
16323 .unwrap_or((0, 0));
16324 *guard = Some((
16325 self.alloc_u8(grow(ck, k_ws_bytes))?,
16326 self.alloc_u8(grow(cv, v_ws_bytes))?,
16327 ));
16328 }
16329 let (kw, vw) = guard.as_mut().unwrap();
16330 {
16332 let f = if g {
16334 self.func_g("fa_dequant_kv_ws_bf16")
16335 } else {
16336 self.func("fa_dequant_kv_ws_bf16")
16337 };
16338 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
16339 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
16340 let cfg = LaunchConfig {
16341 grid_dim: (nblk.max(1), 1, 1),
16342 block_dim: (256, 1, 1),
16343 shared_mem_bytes: 0,
16344 };
16345 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
16346 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16347 let __s_b = self.gpu.stream();
16348 let mut b = __s_b.launch_builder(&f);
16349 b.arg(k)
16350 .arg(v)
16351 .arg(&mut *kw)
16352 .arg(&mut *vw)
16353 .arg(&kdk)
16354 .arg(&kdv)
16355 .arg(&tkvi)
16356 .arg(&ktb)
16357 .arg(&vtb);
16358 unsafe {
16359 b.launch(cfg)?;
16360 }
16361 }
16362 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
16370 .map(|v| v != "0")
16371 .unwrap_or(true);
16372 {
16373 let hd_sfx = fa_hd_suffix(head_dim)?;
16374 let f = self.func(&format!(
16375 "fa_prefill_qw{}{hd_sfx}",
16376 if db { "_db" } else { "" }
16377 ));
16378 let shmem = if db {
16379 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
16381 } else {
16382 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
16383 };
16384 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16385 f.set_attribute(
16386 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16387 shmem as i32,
16388 )?;
16389 let cfg = LaunchConfig {
16390 grid_dim: (
16391 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16392 n_head as u32,
16393 1,
16394 ),
16395 block_dim: (32, 4, 1),
16396 shared_mem_bytes: shmem,
16397 };
16398 let (hd, nh, nhkv, ti, tkvi, cz) = (
16399 head_dim as i32,
16400 n_head as i32,
16401 n_head_kv as i32,
16402 t as i32,
16403 t_kv as i32,
16404 causal as i32,
16405 );
16406 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
16407 let __s_b = self.gpu.stream();
16408 let mut b = __s_b.launch_builder(&f);
16409 b.arg(q)
16410 .arg(&*kw)
16411 .arg(&*vw)
16412 .arg(o)
16413 .arg(&hd)
16414 .arg(&nh)
16415 .arg(&nhkv)
16416 .arg(&ti)
16417 .arg(&tkvi)
16418 .arg(&scale)
16419 .arg(&cz)
16420 .arg(&kdk)
16421 .arg(&kdv);
16422 unsafe {
16423 b.launch(cfg)?;
16424 }
16425 }
16426 Ok(())
16427 }
16428
16429 #[allow(clippy::too_many_arguments)]
16445 pub fn fa_prefill_view_ws_w_hd128(
16446 &self,
16447 q: &CudaSlice<f32>,
16448 k: &cudarc::driver::CudaView<u8>,
16449 v: &cudarc::driver::CudaView<u8>,
16450 o: &mut CudaSlice<f32>,
16451 head_dim: usize,
16452 n_head: usize,
16453 n_head_kv: usize,
16454 t: usize,
16455 t_kv: usize,
16456 scale: f32,
16457 causal: bool,
16458 window: usize,
16459 k_tok_bytes: usize,
16460 v_tok_bytes: usize,
16461 ) -> Result<(), Box<dyn std::error::Error>> {
16462 assert_eq!(
16463 head_dim, 128,
16464 "fa_prefill_view_ws_w_hd128: only the hd128 twin is stamped"
16465 );
16466 if portable_mma_gated() {
16467 return self.sdpa_naive_w_quantized_view(
16468 q,
16469 k,
16470 v,
16471 o,
16472 head_dim,
16473 n_head,
16474 n_head_kv,
16475 t,
16476 t_kv,
16477 scale,
16478 causal,
16479 window,
16480 k_tok_bytes,
16481 v_tok_bytes,
16482 );
16483 }
16484 const BLOCK_Q: usize = 64;
16485 const BK: usize = 32;
16486 let kv_dim_k = n_head_kv * head_dim;
16487 let kv_dim_v = n_head_kv * head_dim;
16488 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
16490 let mut guard = self.prime_deqw_ws.lock().unwrap();
16491 let need_grow = match guard.as_ref() {
16492 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
16493 None => true,
16494 };
16495 if need_grow {
16496 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
16497 let (ck, cv) = guard
16498 .as_ref()
16499 .map(|(a, b)| (a.len(), b.len()))
16500 .unwrap_or((0, 0));
16501 *guard = Some((
16502 self.alloc_u8(grow(ck, k_ws_bytes))?,
16503 self.alloc_u8(grow(cv, v_ws_bytes))?,
16504 ));
16505 }
16506 let (kw, vw) = guard.as_mut().unwrap();
16507 {
16510 let f = self.func("fa_dequant_kv_ws_bf16");
16511 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
16512 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
16513 let cfg = LaunchConfig {
16514 grid_dim: (nblk.max(1), 1, 1),
16515 block_dim: (256, 1, 1),
16516 shared_mem_bytes: 0,
16517 };
16518 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
16519 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16520 let __s_b = self.gpu.stream();
16521 let mut b = __s_b.launch_builder(&f);
16522 b.arg(k)
16523 .arg(v)
16524 .arg(&mut *kw)
16525 .arg(&mut *vw)
16526 .arg(&kdk)
16527 .arg(&kdv)
16528 .arg(&tkvi)
16529 .arg(&ktb)
16530 .arg(&vtb);
16531 unsafe {
16532 b.launch(cfg)?;
16533 }
16534 }
16535 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
16537 .map(|v| v != "0")
16538 .unwrap_or(true);
16539 {
16540 let f = self.func(if db {
16541 "fa_prefill_qw_db_w_hd128"
16542 } else {
16543 "fa_prefill_qw_w_hd128"
16544 });
16545 let shmem = if db {
16546 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
16547 } else {
16548 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
16549 };
16550 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16551 f.set_attribute(
16552 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16553 shmem as i32,
16554 )?;
16555 let cfg = LaunchConfig {
16556 grid_dim: (
16557 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16558 n_head as u32,
16559 1,
16560 ),
16561 block_dim: (32, 4, 1),
16562 shared_mem_bytes: shmem,
16563 };
16564 let (hd, nh, nhkv, ti, tkvi, cz) = (
16565 head_dim as i32,
16566 n_head as i32,
16567 n_head_kv as i32,
16568 t as i32,
16569 t_kv as i32,
16570 causal as i32,
16571 );
16572 let (kdk, kdv, wnd) = (kv_dim_k as i32, kv_dim_v as i32, window as i32);
16573 let __s_b = self.gpu.stream();
16574 let mut b = __s_b.launch_builder(&f);
16575 b.arg(q)
16576 .arg(&*kw)
16577 .arg(&*vw)
16578 .arg(o)
16579 .arg(&hd)
16580 .arg(&nh)
16581 .arg(&nhkv)
16582 .arg(&ti)
16583 .arg(&tkvi)
16584 .arg(&scale)
16585 .arg(&cz)
16586 .arg(&kdk)
16587 .arg(&kdv)
16588 .arg(&wnd);
16589 unsafe {
16590 b.launch(cfg)?;
16591 }
16592 }
16593 Ok(())
16594 }
16595
16596 pub fn fa_decode(
16600 &self,
16601 q: &CudaSlice<f32>,
16602 k: &cudarc::driver::CudaView<u8>,
16603 v: &cudarc::driver::CudaView<u8>,
16604 o: &mut CudaSlice<f32>,
16605 head_dim: usize,
16606 n_head: usize,
16607 n_head_kv: usize,
16608 t_kv: usize,
16609 scale: f32,
16610 k_tok_bytes: usize,
16611 v_tok_bytes: usize,
16612 ) -> Result<(), Box<dyn std::error::Error>> {
16613 self.fa_decode_kvmod(
16614 q,
16615 k,
16616 v,
16617 o,
16618 head_dim,
16619 n_head,
16620 n_head_kv,
16621 t_kv,
16622 scale,
16623 k_tok_bytes,
16624 v_tok_bytes,
16625 false,
16626 )
16627 }
16628
16629 #[allow(clippy::too_many_arguments)]
16633 #[allow(clippy::too_many_arguments)]
16637 #[allow(clippy::too_many_arguments)]
16638 fn fa_decode_scalar_unified(
16639 &self,
16640 q: &cudarc::driver::CudaView<f32>,
16641 k: &cudarc::driver::CudaView<u8>,
16642 v: &cudarc::driver::CudaView<u8>,
16643 o: &mut cudarc::driver::CudaViewMut<f32>,
16644 head_dim: usize,
16645 n_head: usize,
16646 n_head_kv: usize,
16647 t_kv_host: usize,
16648 t_kv_dev: Option<&CudaSlice<i32>>,
16649 scale: f32,
16650 n_splits: usize,
16651 split_keys: usize,
16652 k_tok_bytes: usize,
16653 v_tok_bytes: usize,
16654 g: bool,
16655 part_o: &mut CudaSlice<f32>,
16656 part_m: &mut CudaSlice<f32>,
16657 part_l: &mut CudaSlice<f32>,
16658 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
16659 ) -> Result<(), Box<dyn std::error::Error>> {
16660 let f = if g {
16661 self.func_g("fa_decode_f32")
16662 } else {
16663 self.fa_func("fa_decode_f32", head_dim)
16664 };
16665 let cfg = LaunchConfig {
16666 grid_dim: (n_head as u32, n_splits as u32, 1),
16667 block_dim: (head_dim as u32, 1, 1),
16668 shared_mem_bytes: (4 * (head_dim + 32)) as u32,
16669 };
16670 let (hd, nh, nhkv, nsp) = (
16671 head_dim as i32,
16672 n_head as i32,
16673 n_head_kv as i32,
16674 n_splits as i32,
16675 );
16676 let (ktb, vtb, tkvi, ski) = (
16677 k_tok_bytes as i64,
16678 v_tok_bytes as i64,
16679 t_kv_host as i32,
16680 split_keys as i32,
16681 );
16682 let __s_b = self.gpu.stream();
16683 let mut b = __s_b.launch_builder(&f);
16684 match t_kv_dev {
16685 Some(d) => {
16686 b.arg(q)
16687 .arg(k)
16688 .arg(v)
16689 .arg(&mut *part_o)
16690 .arg(&mut *part_m)
16691 .arg(&mut *part_l)
16692 .arg(&hd)
16693 .arg(&nh)
16694 .arg(&nhkv)
16695 .arg(&tkvi)
16696 .arg(d)
16697 .arg(&scale)
16698 .arg(&nsp)
16699 .arg(&ski)
16700 .arg(&ktb)
16701 .arg(&vtb);
16702 unsafe {
16703 b.launch(cfg)?;
16704 }
16705 }
16706 None => {
16707 let null: u64 = 0;
16708 b.arg(q)
16709 .arg(k)
16710 .arg(v)
16711 .arg(&mut *part_o)
16712 .arg(&mut *part_m)
16713 .arg(&mut *part_l)
16714 .arg(&hd)
16715 .arg(&nh)
16716 .arg(&nhkv)
16717 .arg(&tkvi)
16718 .arg(&null)
16719 .arg(&scale)
16720 .arg(&nsp)
16721 .arg(&ski)
16722 .arg(&ktb)
16723 .arg(&vtb);
16724 unsafe {
16725 b.launch(cfg)?;
16726 }
16727 }
16728 }
16729 let cfg2 = LaunchConfig {
16730 grid_dim: (n_head as u32, 1, 1),
16731 block_dim: (head_dim as u32, 1, 1),
16732 shared_mem_bytes: 0,
16733 };
16734 if let Some((oq, od)) = q8_out {
16735 let fc = if g {
16737 self.func_g("fa_decode_combine_q8_1")
16738 } else {
16739 self.fa_func("fa_decode_combine_q8_1", head_dim)
16740 };
16741 let __s_b2 = self.gpu.stream();
16742 let mut b2 = __s_b2.launch_builder(&fc);
16743 b2.arg(&*part_o)
16744 .arg(&*part_m)
16745 .arg(&*part_l)
16746 .arg(oq)
16747 .arg(od)
16748 .arg(&hd)
16749 .arg(&nh)
16750 .arg(&nsp);
16751 unsafe {
16752 b2.launch(cfg2)?;
16753 }
16754 return Ok(());
16755 }
16756 let fc = if g {
16757 self.func_g("fa_decode_combine_f32")
16758 } else {
16759 self.fa_func("fa_decode_combine_f32", head_dim)
16760 };
16761 let __s_b2 = self.gpu.stream();
16762 let mut b2 = __s_b2.launch_builder(&fc);
16763 b2.arg(&*part_o)
16764 .arg(&*part_m)
16765 .arg(&*part_l)
16766 .arg(o)
16767 .arg(&hd)
16768 .arg(&nh)
16769 .arg(&nsp);
16770 unsafe {
16771 b2.launch(cfg2)?;
16772 }
16773 Ok(())
16774 }
16775
16776 pub fn fa_decode_kvmod(
16777 &self,
16778 q: &CudaSlice<f32>,
16779 k: &cudarc::driver::CudaView<u8>,
16780 v: &cudarc::driver::CudaView<u8>,
16781 o: &mut CudaSlice<f32>,
16782 head_dim: usize,
16783 n_head: usize,
16784 n_head_kv: usize,
16785 t_kv: usize,
16786 scale: f32,
16787 k_tok_bytes: usize,
16788 v_tok_bytes: usize,
16789 g: bool,
16790 ) -> Result<(), Box<dyn std::error::Error>> {
16791 let q_view = q.as_view();
16792 let mut o_view = o.as_view_mut();
16793 self.fa_decode_kvmod_view(
16794 &q_view,
16795 k,
16796 v,
16797 &mut o_view,
16798 head_dim,
16799 n_head,
16800 n_head_kv,
16801 t_kv,
16802 scale,
16803 k_tok_bytes,
16804 v_tok_bytes,
16805 g,
16806 )
16807 }
16808
16809 #[allow(clippy::too_many_arguments)]
16814 pub fn fa_decode_kvmod_view(
16815 &self,
16816 q: &cudarc::driver::CudaView<f32>,
16817 k: &cudarc::driver::CudaView<u8>,
16818 v: &cudarc::driver::CudaView<u8>,
16819 o: &mut cudarc::driver::CudaViewMut<f32>,
16820 head_dim: usize,
16821 n_head: usize,
16822 n_head_kv: usize,
16823 t_kv: usize,
16824 scale: f32,
16825 k_tok_bytes: usize,
16826 v_tok_bytes: usize,
16827 g: bool,
16828 ) -> Result<(), Box<dyn std::error::Error>> {
16829 let mut fa_vec = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
16850 if g && head_dim == 256 && !fa_v4_at(t_kv) {
16854 fa_vec = false;
16855 }
16856 let sp = fa_split_keys(t_kv, n_head_kv);
16857 let n_splits = if fa_vec {
16858 ((t_kv + sp - 1) / sp).max(1)
16859 } else {
16860 ((t_kv + 255) / 256).max(1)
16861 };
16862 let o_len = n_head * n_splits * head_dim;
16863 let ml_len = n_head * n_splits;
16864 let mut part_guard = self.fa_part_pool.lock().unwrap();
16865 if part_guard
16866 .as_ref()
16867 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
16868 .unwrap_or(true)
16869 {
16870 let old = part_guard.take();
16881 let (co, cm) = old
16882 .as_ref()
16883 .map(|pp| (pp.0.len(), pp.1.len()))
16884 .unwrap_or((0, 0));
16885 if let Some(old) = old {
16886 self.fa_part_retired.lock().unwrap().push(old);
16887 }
16888 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
16889 eprintln!(
16890 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
16891 co, o_len, cm, ml_len
16892 );
16893 }
16894 *part_guard = Some((
16895 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
16896 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
16897 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
16898 ));
16899 }
16900 let pg = part_guard.as_mut().unwrap();
16901 self.gpu
16902 .stream()
16903 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
16904 self.gpu
16905 .stream()
16906 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
16907 self.gpu
16908 .stream()
16909 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
16910 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
16911 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
16912 let (hd, nh, nhkv, tkvi, nsp) = (
16913 head_dim as i32,
16914 n_head as i32,
16915 n_head_kv as i32,
16916 t_kv as i32,
16917 n_splits as i32,
16918 );
16919 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16920 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
16924 let fa512_min = fa512_min_tkv();
16929 let deep = fa_vec
16932 && head_dim == 256
16933 && fa_v4_at(t_kv)
16934 && !g
16935 && fa_deep_at(t_kv)
16936 && !matches!(fa_v4_mode(), "noB3" | "stage");
16937 let (f, cfg) = if fa_vec && head_dim == 512 && t_kv >= fa512_min {
16938 let gqa = (n_head / n_head_kv).max(1) as u32;
16941 let fv = self.fa_func("fa_decode_vec_q_dpl16", head_dim);
16942 (
16943 fv,
16944 LaunchConfig {
16945 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16946 block_dim: (32, gqa, 1),
16947 shared_mem_bytes: 0,
16948 },
16949 )
16950 } else if fa_vec && head_dim <= 256 {
16951 let gqa = (n_head / n_head_kv).max(1) as u32;
16952 static SMEM_TKV: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
16963 let smem_tkv = *SMEM_TKV.get_or_init(|| {
16964 std::env::var("MEMRA_FA_SMEM_TKV")
16965 .ok()
16966 .and_then(|v| v.parse().ok())
16967 .unwrap_or_else(|| {
16968 FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
16969 })
16970 });
16971 if fa_v4_at(t_kv) && head_dim == 256 {
16972 let v4name = match fa_v4_mode() {
16976 "noB3" => "fa_decode_vec_q_v4_noB3", "stage" => "fa_decode_vec_q_v4_stage", _ if deep => "fa_decode_vec_q_v4_deep",
16979 _ => "fa_decode_vec_q_v4",
16980 };
16981 let fv = if g {
16982 self.func_g(v4name)
16983 } else {
16984 self.func(v4name)
16985 };
16986 let shmem = (if deep { 12160 } else { 11520 }
16989 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
16990 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16991 fv.set_attribute(
16992 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16993 shmem as i32,
16994 )?;
16995 (
16996 fv,
16997 LaunchConfig {
16998 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
16999 block_dim: (32, gqa, 1),
17000 shared_mem_bytes: shmem,
17001 },
17002 )
17003 } else if fa_v3_active(head_dim) {
17004 let fv = if g {
17007 self.func_g("fa_decode_vec_q_v3")
17008 } else {
17009 self.func("fa_decode_vec_q_v3")
17010 };
17011 let shmem = (32 * head_dim * 2) as u32; (
17013 fv,
17014 LaunchConfig {
17015 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17016 block_dim: (32, gqa, 1),
17017 shared_mem_bytes: shmem,
17018 },
17019 )
17020 } else if fa_v2_on() {
17021 let fv = if g {
17025 self.func_g("fa_decode_vec_q_v2")
17026 } else {
17027 self.func("fa_decode_vec_q_v2")
17028 };
17029 let shmem = (2 * 32 * head_dim * 2) as u32; (
17031 fv,
17032 LaunchConfig {
17033 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17034 block_dim: (32, gqa, 1),
17035 shared_mem_bytes: shmem,
17036 },
17037 )
17038 } else if smem_tkv > 0 && t_kv >= smem_tkv && !g && !(head_dim == 512 && Self::gkv_on())
17039 {
17040 let fv = if g {
17044 self.func_g("fa_decode_vec_q_smem")
17045 } else {
17046 self.func("fa_decode_vec_q_smem")
17047 };
17048 let shmem = (2 * 32 * head_dim * 2) as u32; use cudarc::driver::sys::CUfunction_attribute_enum as A;
17050 fv.set_attribute(
17051 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17052 shmem as i32,
17053 )?;
17054 (
17055 fv,
17056 LaunchConfig {
17057 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17058 block_dim: (32, gqa, 1),
17059 shared_mem_bytes: shmem,
17060 },
17061 )
17062 } else {
17063 let fv = if g {
17066 self.func_g("fa_decode_vec_q")
17067 } else {
17068 self.func("fa_decode_vec_q")
17069 };
17070 (
17071 fv,
17072 LaunchConfig {
17073 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17074 block_dim: (32, gqa, 1),
17075 shared_mem_bytes: 0,
17076 },
17077 )
17078 }
17079 } else {
17080 return self.fa_decode_scalar_unified(
17083 q,
17084 k,
17085 v,
17086 o,
17087 head_dim,
17088 n_head,
17089 n_head_kv,
17090 t_kv,
17091 None,
17092 scale,
17093 n_splits,
17094 if fa_vec { sp } else { 256 },
17095 k_tok_bytes,
17096 v_tok_bytes,
17097 g,
17098 part_o,
17099 part_m,
17100 part_l,
17101 None,
17102 );
17103 };
17104 let __s_b = self.gpu.stream();
17105 let mut b = __s_b.launch_builder(&f);
17106 b.arg(q)
17107 .arg(k)
17108 .arg(v)
17109 .arg(&mut *part_o)
17110 .arg(&mut *part_m)
17111 .arg(&mut *part_l)
17112 .arg(&hd)
17113 .arg(&nh)
17114 .arg(&nhkv)
17115 .arg(&tkvi)
17116 .arg(&scale)
17117 .arg(&nsp)
17118 .arg(&ktb)
17119 .arg(&vtb);
17120 unsafe {
17121 b.launch(cfg)?;
17122 }
17123 let (fc, cfg2) = (
17126 if g {
17127 self.func_g("fa_decode_combine_f32")
17128 } else {
17129 self.fa_func("fa_decode_combine_f32", head_dim)
17130 },
17131 LaunchConfig {
17132 grid_dim: (n_head as u32, 1, 1),
17133 block_dim: (head_dim as u32, 1, 1),
17134 shared_mem_bytes: 0,
17135 },
17136 );
17137 let __s_b2 = self.gpu.stream();
17138 let mut b2 = __s_b2.launch_builder(&fc);
17139 b2.arg(&*part_o)
17140 .arg(&*part_m)
17141 .arg(&*part_l)
17142 .arg(o)
17143 .arg(&hd)
17144 .arg(&nh)
17145 .arg(&nsp);
17146 unsafe {
17147 b2.launch(cfg2)?;
17148 }
17149 Ok(())
17150 }
17151
17152 #[allow(clippy::too_many_arguments)]
17163 pub fn fa_decode_batch_seqs_v4(
17164 &self,
17165 q: &CudaSlice<f32>,
17166 kv_ptrs: &cudarc::driver::CudaView<u64>,
17167 pos_seq: &CudaSlice<i32>,
17168 o: &mut CudaSlice<f32>,
17169 head_dim: usize,
17170 n_head: usize,
17171 n_head_kv: usize,
17172 b_n: usize,
17173 t_kv_max: usize,
17174 scale: f32,
17175 split_keys: usize,
17176 k_tok_bytes: usize,
17177 v_tok_bytes: usize,
17178 ) -> Result<(), Box<dyn std::error::Error>> {
17179 debug_assert!(head_dim == 256, "seqs twin is v4-stamped (hd256 only)");
17180 let n_splits_max = (t_kv_max + split_keys - 1) / split_keys;
17181 let o_len = b_n * n_head * n_splits_max * head_dim;
17182 let ml_len = b_n * n_head * n_splits_max;
17183 let mut part_guard = self.fa_part_pool.lock().unwrap();
17184 if part_guard
17185 .as_ref()
17186 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17187 .unwrap_or(true)
17188 {
17189 let old = part_guard.take();
17200 let (co, cm) = old
17201 .as_ref()
17202 .map(|pp| (pp.0.len(), pp.1.len()))
17203 .unwrap_or((0, 0));
17204 if let Some(old) = old {
17205 self.fa_part_retired.lock().unwrap().push(old);
17206 }
17207 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17208 eprintln!(
17209 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17210 co, o_len, cm, ml_len
17211 );
17212 }
17213 *part_guard = Some((
17214 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17215 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17216 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17217 ));
17218 }
17219 let pg = part_guard.as_mut().unwrap();
17220 self.gpu
17221 .stream()
17222 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17223 self.gpu
17224 .stream()
17225 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17226 self.gpu
17227 .stream()
17228 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17229 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17230 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17231 let (nspm, spk) = (n_splits_max as i32, split_keys as i32);
17232 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17233 let gqa = (n_head / n_head_kv).max(1) as u32;
17234 let f = self.func("fa_decode_vec_q_seqs_v4");
17235 let shmem = (11520 + 32 * head_dim * 2) as u32;
17237 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17238 f.set_attribute(
17239 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17240 shmem as i32,
17241 )?;
17242 let cfg = LaunchConfig {
17243 grid_dim: (n_head_kv as u32, n_splits_max as u32, b_n as u32),
17244 block_dim: (32, gqa, 1),
17245 shared_mem_bytes: shmem,
17246 };
17247 {
17248 let __s_b = self.gpu.stream();
17249 let mut b = __s_b.launch_builder(&f);
17250 b.arg(q)
17251 .arg(kv_ptrs)
17252 .arg(pos_seq)
17253 .arg(&mut *part_o)
17254 .arg(&mut *part_m)
17255 .arg(&mut *part_l)
17256 .arg(&hd)
17257 .arg(&nh)
17258 .arg(&nhkv)
17259 .arg(&scale)
17260 .arg(&nspm)
17261 .arg(&spk)
17262 .arg(&ktb)
17263 .arg(&vtb);
17264 unsafe {
17265 b.launch(cfg)?;
17266 }
17267 }
17268 let fc = self.func("fa_decode_combine_seqs");
17269 let cfg2 = LaunchConfig {
17270 grid_dim: (n_head as u32, b_n as u32, 1),
17271 block_dim: (head_dim as u32, 1, 1),
17272 shared_mem_bytes: 0,
17273 };
17274 let __s_b2 = self.gpu.stream();
17275 let mut b2 = __s_b2.launch_builder(&fc);
17276 b2.arg(&*part_o)
17277 .arg(&*part_m)
17278 .arg(&*part_l)
17279 .arg(o)
17280 .arg(&hd)
17281 .arg(&nh)
17282 .arg(pos_seq)
17283 .arg(&nspm)
17284 .arg(&spk);
17285 unsafe {
17286 b2.launch(cfg2)?;
17287 }
17288 Ok(())
17289 }
17290
17291 #[allow(clippy::too_many_arguments)]
17298 pub fn append_kv_quantized_seqs(
17299 &self,
17300 k_rows: &CudaSlice<f32>,
17301 v_rows: &CudaSlice<f32>,
17302 kv_ptrs: &cudarc::driver::CudaView<u64>,
17303 pos_seq: &CudaSlice<i32>,
17304 b_n: usize,
17305 kv_dim_k: usize,
17306 kv_dim_v: usize,
17307 k_tok_bytes: usize,
17308 v_tok_bytes: usize,
17309 ) -> Result<(), Box<dyn std::error::Error>> {
17310 let f = self.func("append_quantize_kv_q8_0_q5_1_seqs");
17311 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
17312 let cfg = LaunchConfig {
17313 grid_dim: (nblk, b_n as u32, 1),
17314 block_dim: (32, 1, 1),
17315 shared_mem_bytes: 0,
17316 };
17317 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
17318 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17319 let __s_b = self.gpu.stream();
17320 let mut b = __s_b.launch_builder(&f);
17321 b.arg(k_rows)
17322 .arg(v_rows)
17323 .arg(kv_ptrs)
17324 .arg(pos_seq)
17325 .arg(&kdk)
17326 .arg(&kdv)
17327 .arg(&ktb)
17328 .arg(&vtb);
17329 unsafe {
17330 b.launch(cfg)?;
17331 }
17332 Ok(())
17333 }
17334
17335 pub fn fa_rows_eligible(&self, base_len: usize, head_dim: usize) -> bool {
17341 std::env::var("MEMRA_NO_FA_VEC").is_err()
17342 && std::env::var("MEMRA_FA_ROWS_OFF").is_err()
17343 && base_len + 1 >= fa_vec_min_tkv()
17344 && head_dim <= 256
17345 && head_dim % 32 == 0
17346 }
17347
17348 #[allow(clippy::too_many_arguments)]
17357 pub fn fa_decode_rows(
17358 &self,
17359 q: &CudaSlice<f32>,
17360 k: &cudarc::driver::CudaView<u8>,
17361 v: &cudarc::driver::CudaView<u8>,
17362 o: &mut CudaSlice<f32>,
17363 head_dim: usize,
17364 n_head: usize,
17365 n_head_kv: usize,
17366 base_len: usize,
17367 t: usize,
17368 scale: f32,
17369 k_tok_bytes: usize,
17370 v_tok_bytes: usize,
17371 base_dev: Option<(&CudaSlice<i32>, i32)>,
17375 kv_shared: bool,
17378 g: bool,
17382 mut q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
17385 ) -> Result<(), Box<dyn std::error::Error>> {
17386 debug_assert!(base_len + 1 >= fa_vec_min_tkv() && head_dim <= 512 && head_dim % 32 == 0);
17387 let t_kv_max = base_len + t; let mut sp = fa_split_keys(t_kv_max, n_head_kv); if head_dim == 512 {
17394 static SP512: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17395 let v = *SP512.get_or_init(|| {
17398 std::env::var("MEMRA_FA_SP512")
17399 .ok()
17400 .and_then(|x| x.parse().ok())
17401 .unwrap_or(0)
17402 });
17403 sp = if v >= 8 {
17404 v
17405 } else {
17406 FA_SP512_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
17407 };
17408 }
17409 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17410 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17411 let gqa = (n_head / n_head_kv).max(1) as u32;
17412 let mut groups: Vec<(usize, usize, usize)> = Vec::new(); if head_dim == 512 || fa_split_keys(base_len + 1, n_head_kv) == sp {
17423 groups.push((0, t, sp));
17424 } else {
17425 let mut r0 = 0usize;
17426 while r0 < t {
17427 let sp_g = fa_split_keys(base_len + r0 + 1, n_head_kv);
17428 let mut r1 = r0 + 1;
17429 while r1 < t && fa_split_keys(base_len + r1 + 1, n_head_kv) == sp_g {
17430 r1 += 1;
17431 }
17432 groups.push((r0, r1 - r0, sp_g));
17433 r0 = r1;
17434 }
17435 }
17436 static SMEM_TKV_R: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17440 let smem_tkv = *SMEM_TKV_R.get_or_init(|| {
17441 std::env::var("MEMRA_FA_SMEM_TKV")
17442 .ok()
17443 .and_then(|v| v.parse().ok())
17444 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
17445 });
17446 let v4 = fa_v4_at(base_len + t) && head_dim == 256;
17447 let v3 = fa_v3_active(head_dim);
17448 let smem_rows =
17449 head_dim <= 256 && !v3 && !fa_v2_on() && smem_tkv > 0 && t_kv_max >= smem_tkv;
17450 let _ = kv_shared;
17455 let i2 = head_dim == 512 && std::env::var("MEMRA_FA_I2").as_deref() != Ok("0");
17458 static TB512: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
17472 let tb512 = head_dim == 512
17474 && sp <= 32
17475 && n_head / n_head_kv.max(1) <= 16
17476 && *TB512.get_or_init(|| std::env::var("MEMRA_FA_TB512").as_deref() != Ok("0"));
17477 let fname = if tb512 {
17478 "fa_decode_vec_q_rows_v4_512_tb"
17479 } else if i2 {
17480 "fa_decode_vec_q_rows_dpl16_i2"
17481 } else if head_dim == 512 {
17482 "fa_decode_vec_q_rows_dpl16"
17483 }
17484 else if v4 {
17486 "fa_decode_vec_q_rows_v4"
17487 } else if v3 {
17488 "fa_decode_vec_q_rows_v3"
17489 } else if fa_v2_on() {
17490 "fa_decode_vec_q_rows_v2"
17491 } else if smem_rows {
17492 "fa_decode_vec_q_rows_smem"
17493 } else {
17494 "fa_decode_vec_q_rows"
17495 };
17496 let f = if head_dim == 512 {
17497 self.fa_func(fname, head_dim)
17498 } else if g {
17499 self.func_g(if smem_rows {
17507 "fa_decode_vec_q_rows"
17508 } else {
17509 fname
17510 })
17511 } else {
17512 self.func(fname)
17513 };
17514 let shmem = if tb512 {
17515 let gk = Self::gkv_on();
17517 let sh =
17518 (8192 + 1024 + 32 * 512 + 32 * 64 + 32 * head_dim * if gk { 1 } else { 2 }) as u32;
17519 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17520 f.set_attribute(
17521 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17522 sh as i32,
17523 )?;
17524 sh
17525 } else if v4 || v3 || smem_rows || fa_v2_on() {
17526 let sh = (if v4 {
17528 11520 + 32 * head_dim * if g { 1 } else { 2 }
17529 } else if v3 {
17530 32 * head_dim * 2
17531 } else {
17532 2 * 32 * head_dim * 2
17533 }) as u32;
17534 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17535 f.set_attribute(
17536 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17537 sh as i32,
17538 )?;
17539 sh
17540 } else {
17541 0
17542 };
17543 for &(r0, t_g, sp_g) in &groups {
17547 let n_splits_g = (base_len + r0 + t_g).div_ceil(sp_g);
17548 let (nspm, spk) = (n_splits_g as i32, sp_g as i32);
17549 let base_i = (base_len + r0) as i32;
17550 let o_len = t_g * n_head * n_splits_g * head_dim;
17551 let ml_len = t_g * n_head * n_splits_g;
17552 let mut part_guard = self.fa_part_pool.lock().unwrap();
17553 if part_guard
17554 .as_ref()
17555 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17556 .unwrap_or(true)
17557 {
17558 let old = part_guard.take();
17569 let (co, cm) = old
17570 .as_ref()
17571 .map(|pp| (pp.0.len(), pp.1.len()))
17572 .unwrap_or((0, 0));
17573 if let Some(old) = old {
17574 self.fa_part_retired.lock().unwrap().push(old);
17575 }
17576 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17577 eprintln!(
17578 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17579 co, o_len, cm, ml_len
17580 );
17581 }
17582 *part_guard = Some((
17583 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17584 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17585 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17586 ));
17587 }
17588 let pg = part_guard.as_mut().unwrap();
17589 self.gpu
17590 .stream()
17591 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17592 self.gpu
17593 .stream()
17594 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17595 self.gpu
17596 .stream()
17597 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17598 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17599 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
17600 let qv = self.view(q, t * n_head * head_dim);
17601 let q_g = qv.slice(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
17602 let cfg = LaunchConfig {
17603 grid_dim: (n_head_kv as u32, n_splits_g as u32, t_g as u32),
17604 block_dim: (32, gqa, 1),
17605 shared_mem_bytes: shmem,
17606 };
17607 {
17608 let __s_b = self.gpu.stream();
17609 let mut b = __s_b.launch_builder(&f);
17610 if tb512 {
17611 let (bd, plus) =
17613 base_dev.expect("hd512 rows twin requires a device base counter");
17614 let plus_g = plus + r0 as i32;
17615 let nr = t_g as i32;
17616 if Self::pdl_on() && Self::pdl_wb_on() {
17617 use cudarc::driver::{DevicePtr, DevicePtrMut};
17619 let s = &self.gpu.stream();
17620 let (pq, _b0) = q_g.device_ptr(s);
17621 let (pk, _b1) = k.device_ptr(s);
17622 let (pv, _b2) = v.device_ptr(s);
17623 let (po, _b3) = part_o.device_ptr_mut(s);
17624 let (pm, _b4) = part_m.device_ptr_mut(s);
17625 let (pl, _b5) = part_l.device_ptr_mut(s);
17626 let (pb, _b6) = bd.device_ptr(s);
17627 let mut ps = [
17628 &pq as *const _ as *mut std::ffi::c_void,
17629 &pk as *const _ as *mut _,
17630 &pv as *const _ as *mut _,
17631 &po as *const _ as *mut _,
17632 &pm as *const _ as *mut _,
17633 &pl as *const _ as *mut _,
17634 &hd as *const _ as *mut _,
17635 &nh as *const _ as *mut _,
17636 &nhkv as *const _ as *mut _,
17637 &pb as *const _ as *mut _,
17638 &plus_g as *const _ as *mut _,
17639 &scale as *const _ as *mut _,
17640 &nspm as *const _ as *mut _,
17641 &spk as *const _ as *mut _,
17642 &ktb as *const _ as *mut _,
17643 &vtb as *const _ as *mut _,
17644 &nr as *const _ as *mut _,
17645 ];
17646 unsafe {
17647 self.launch_pdl_flash(
17648 Self::gkv_on(),
17649 "fa_decode_vec_q_rows_v4_512_tb",
17650 (n_head_kv as u32, n_splits_g as u32, 1),
17651 (32, gqa, 1),
17652 shmem,
17653 &mut ps,
17654 )?;
17655 }
17656 } else {
17657 let cfg_tb = LaunchConfig {
17658 grid_dim: (n_head_kv as u32, n_splits_g as u32, 1),
17659 block_dim: (32, gqa, 1),
17660 shared_mem_bytes: shmem,
17661 };
17662 b.arg(&q_g)
17663 .arg(k)
17664 .arg(v)
17665 .arg(&mut *part_o)
17666 .arg(&mut *part_m)
17667 .arg(&mut *part_l)
17668 .arg(&hd)
17669 .arg(&nh)
17670 .arg(&nhkv)
17671 .arg(bd)
17672 .arg(&plus_g)
17673 .arg(&scale)
17674 .arg(&nspm)
17675 .arg(&spk)
17676 .arg(&ktb)
17677 .arg(&vtb)
17678 .arg(&nr);
17679 unsafe {
17680 b.launch(cfg_tb)?;
17681 }
17682 }
17683 } else if head_dim == 512 {
17684 let (bd, plus) =
17685 base_dev.expect("hd512 rows twin requires a device base counter");
17686 let plus_g = plus + r0 as i32;
17687 b.arg(&q_g)
17688 .arg(k)
17689 .arg(v)
17690 .arg(&mut *part_o)
17691 .arg(&mut *part_m)
17692 .arg(&mut *part_l)
17693 .arg(&hd)
17694 .arg(&nh)
17695 .arg(&nhkv)
17696 .arg(bd)
17697 .arg(&plus_g)
17698 .arg(&scale)
17699 .arg(&nspm)
17700 .arg(&spk)
17701 .arg(&ktb)
17702 .arg(&vtb);
17703 unsafe {
17704 b.launch(cfg)?;
17705 }
17706 } else {
17707 b.arg(&q_g)
17708 .arg(k)
17709 .arg(v)
17710 .arg(&mut *part_o)
17711 .arg(&mut *part_m)
17712 .arg(&mut *part_l)
17713 .arg(&hd)
17714 .arg(&nh)
17715 .arg(&nhkv)
17716 .arg(&base_i)
17717 .arg(&scale)
17718 .arg(&nspm)
17719 .arg(&spk)
17720 .arg(&ktb)
17721 .arg(&vtb);
17722 unsafe {
17723 b.launch(cfg)?;
17724 }
17725 }
17726 }
17727 let cfg2 = LaunchConfig {
17728 grid_dim: (n_head as u32, t_g as u32, 1),
17729 block_dim: (head_dim as u32, 1, 1),
17730 shared_mem_bytes: 0,
17731 };
17732 let mut o_g = o.slice_mut(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
17733 if head_dim == 512 {
17734 let (bd, plus) = base_dev.unwrap();
17737 let plus_g = plus + r0 as i32;
17738 if let Some((oq, od)) = q8_out.as_mut() {
17739 debug_assert!(t == 1, "rows q8 emit is a t=1 decode arm");
17741 if Self::pdl_on() && Self::pdl_wb_on() {
17742 use cudarc::driver::{DevicePtr, DevicePtrMut};
17744 let s = &self.gpu.stream();
17745 let (po, _g0) = part_o.device_ptr(s);
17746 let (pm, _g1) = part_m.device_ptr(s);
17747 let (pl, _g2) = part_l.device_ptr(s);
17748 let (pq, _g3) = oq.device_ptr_mut(s);
17749 let (pd, _g4) = od.device_ptr_mut(s);
17750 let (pb, _g5) = bd.device_ptr(s);
17751 let mut ps = [
17752 &po as *const _ as *mut std::ffi::c_void,
17753 &pm as *const _ as *mut _,
17754 &pl as *const _ as *mut _,
17755 &pq as *const _ as *mut _,
17756 &pd as *const _ as *mut _,
17757 &hd as *const _ as *mut _,
17758 &nh as *const _ as *mut _,
17759 &pb as *const _ as *mut _,
17760 &plus_g as *const _ as *mut _,
17761 &nspm as *const _ as *mut _,
17762 &spk as *const _ as *mut _,
17763 ];
17764 unsafe {
17765 self.launch_pdl_flash(
17766 Self::gkv_on(),
17767 "fa_decode_combine_rows_dc_q8_1",
17768 cfg2.grid_dim,
17769 cfg2.block_dim,
17770 0,
17771 &mut ps,
17772 )?;
17773 }
17774 continue;
17775 }
17776 let fc = self.fa_func("fa_decode_combine_rows_dc_q8_1", head_dim);
17777 let __s_b2 = self.gpu.stream();
17778 let mut b2 = __s_b2.launch_builder(&fc);
17779 b2.arg(&*part_o)
17780 .arg(&*part_m)
17781 .arg(&*part_l)
17782 .arg(&mut **oq)
17783 .arg(&mut **od)
17784 .arg(&hd)
17785 .arg(&nh)
17786 .arg(bd)
17787 .arg(&plus_g)
17788 .arg(&nspm)
17789 .arg(&spk);
17790 unsafe {
17791 b2.launch(cfg2)?;
17792 }
17793 continue;
17794 }
17795 let fc = self.fa_func("fa_decode_combine_rows_dc", head_dim);
17796 let __s_b2 = self.gpu.stream();
17797 let mut b2 = __s_b2.launch_builder(&fc);
17798 b2.arg(&*part_o)
17799 .arg(&*part_m)
17800 .arg(&*part_l)
17801 .arg(&mut o_g)
17802 .arg(&hd)
17803 .arg(&nh)
17804 .arg(bd)
17805 .arg(&plus_g)
17806 .arg(&nspm)
17807 .arg(&spk);
17808 unsafe {
17809 b2.launch(cfg2)?;
17810 }
17811 } else {
17812 assert!(
17815 q8_out.is_none(),
17816 "rows q8 emit requires the hd512 dc combine"
17817 );
17818 let fc = self.func("fa_decode_combine_rows");
17819 let __s_b2 = self.gpu.stream();
17820 let mut b2 = __s_b2.launch_builder(&fc);
17821 b2.arg(&*part_o)
17822 .arg(&*part_m)
17823 .arg(&*part_l)
17824 .arg(&mut o_g)
17825 .arg(&hd)
17826 .arg(&nh)
17827 .arg(&base_i)
17828 .arg(&nspm)
17829 .arg(&spk);
17830 unsafe {
17831 b2.launch(cfg2)?;
17832 }
17833 }
17834 }
17835 Ok(())
17836 }
17837
17838 #[allow(clippy::too_many_arguments)]
17842 pub fn fa_decode_rows_w(
17843 &self,
17844 q: &CudaSlice<f32>,
17845 k: &cudarc::driver::CudaView<u8>,
17846 v: &cudarc::driver::CudaView<u8>,
17847 o: &mut CudaSlice<f32>,
17848 head_dim: usize,
17849 n_head: usize,
17850 n_head_kv: usize,
17851 base_dev: &CudaSlice<i32>,
17852 base_plus: i32,
17853 t: usize,
17854 scale: f32,
17855 window: usize,
17856 k_tok_bytes: usize,
17857 v_tok_bytes: usize,
17858 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
17859 ) -> Result<(), Box<dyn std::error::Error>> {
17860 debug_assert!(head_dim == 256);
17865 let sp = {
17873 static SPW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17874 let v = *SPW.get_or_init(|| {
17875 std::env::var("MEMRA_FA_SPW")
17876 .ok()
17877 .and_then(|x| x.parse().ok())
17878 .unwrap_or(0)
17879 });
17880 if v >= 8 {
17881 v
17882 } else {
17883 FA_SPW_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
17884 }
17885 };
17886 let n_splits_max = (window + sp - 1) / sp;
17887 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17888 let (nspm, spk, wini) = (n_splits_max as i32, sp as i32, window as i32);
17889 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17890 let gqa = (n_head / n_head_kv).max(1) as u32;
17891 let o_len = t * n_head * n_splits_max * head_dim;
17892 let ml_len = t * n_head * n_splits_max;
17893 let mut part_guard = self.fa_part_pool.lock().unwrap();
17894 if part_guard
17895 .as_ref()
17896 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17897 .unwrap_or(true)
17898 {
17899 let old = part_guard.take();
17910 let (co, cm) = old
17911 .as_ref()
17912 .map(|pp| (pp.0.len(), pp.1.len()))
17913 .unwrap_or((0, 0));
17914 if let Some(old) = old {
17915 self.fa_part_retired.lock().unwrap().push(old);
17916 }
17917 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17918 eprintln!(
17919 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17920 co, o_len, cm, ml_len
17921 );
17922 }
17923 *part_guard = Some((
17924 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17925 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17926 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17927 ));
17928 }
17929 let pg = part_guard.as_mut().unwrap();
17930 self.gpu
17931 .stream()
17932 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17933 self.gpu
17934 .stream()
17935 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17936 self.gpu
17937 .stream()
17938 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17939 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17940 static SMEM_TKV_W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17946 let smem_tkv = *SMEM_TKV_W.get_or_init(|| {
17947 std::env::var("MEMRA_FA_SMEM_TKV")
17948 .ok()
17949 .and_then(|v| v.parse().ok())
17950 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
17951 });
17952 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17958 let wg = Self::wkv_on();
17963 let sp2 =
17966 gqa <= 4 && fa_v4_at(window) && std::env::var("MEMRA_FA_SPW2").as_deref() != Ok("0");
17967 if sp2 {
17968 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
17969 if Self::pdl_on() && Self::pdl_wb_on() {
17970 use cudarc::driver::{DevicePtr, DevicePtrMut};
17972 let s = &self.gpu.stream();
17973 let (pq, _b0) = q.device_ptr(s);
17974 let (pk, _b1) = k.device_ptr(s);
17975 let (pv, _b2) = v.device_ptr(s);
17976 let (po, _b3) = part_o.device_ptr_mut(s);
17977 let (pm, _b4) = part_m.device_ptr_mut(s);
17978 let (pl, _b5) = part_l.device_ptr_mut(s);
17979 let (pb, _b6) = base_dev.device_ptr(s);
17980 let mut ps = [
17981 &pq as *const _ as *mut std::ffi::c_void,
17982 &pk as *const _ as *mut _,
17983 &pv as *const _ as *mut _,
17984 &po as *const _ as *mut _,
17985 &pm as *const _ as *mut _,
17986 &pl as *const _ as *mut _,
17987 &hd as *const _ as *mut _,
17988 &nh as *const _ as *mut _,
17989 &nhkv as *const _ as *mut _,
17990 &pb as *const _ as *mut _,
17991 &base_plus as *const _ as *mut _,
17992 &scale as *const _ as *mut _,
17993 &nspm as *const _ as *mut _,
17994 &spk as *const _ as *mut _,
17995 &ktb as *const _ as *mut _,
17996 &vtb as *const _ as *mut _,
17997 &wini as *const _ as *mut _,
17998 ];
17999 unsafe {
18000 self.launch_pdl_flash(
18001 wg,
18002 "fa_decode_vec_q_rows_v4_w_sp",
18003 (n_head_kv as u32, n_splits_max as u32, t as u32),
18004 (32, gqa + 1, 1),
18005 sh,
18006 &mut ps,
18007 )?;
18008 }
18009 } else {
18010 let f = if wg {
18011 self.func_g("fa_decode_vec_q_rows_v4_w_sp")
18012 } else {
18013 self.func("fa_decode_vec_q_rows_v4_w_sp")
18014 };
18015 f.set_attribute(
18016 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18017 sh as i32,
18018 )?;
18019 let cfg = LaunchConfig {
18020 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18021 block_dim: (32, gqa + 1, 1),
18022 shared_mem_bytes: sh,
18023 };
18024 let __s_b = self.gpu.stream();
18025 let mut b = __s_b.launch_builder(&f);
18026 b.arg(q)
18027 .arg(k)
18028 .arg(v)
18029 .arg(&mut *part_o)
18030 .arg(&mut *part_m)
18031 .arg(&mut *part_l)
18032 .arg(&hd)
18033 .arg(&nh)
18034 .arg(&nhkv)
18035 .arg(base_dev)
18036 .arg(&base_plus)
18037 .arg(&scale)
18038 .arg(&nspm)
18039 .arg(&spk)
18040 .arg(&ktb)
18041 .arg(&vtb)
18042 .arg(&wini);
18043 unsafe {
18044 b.launch(cfg)?;
18045 }
18046 }
18047 } else {
18048 if fa_v4_at(window) && Self::pdl_on() && Self::pdl_wb_on() {
18049 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
18051 use cudarc::driver::{DevicePtr, DevicePtrMut};
18052 let s = &self.gpu.stream();
18053 let (pq, _b0) = q.device_ptr(s);
18054 let (pk, _b1) = k.device_ptr(s);
18055 let (pv, _b2) = v.device_ptr(s);
18056 let (po, _b3) = part_o.device_ptr_mut(s);
18057 let (pm, _b4) = part_m.device_ptr_mut(s);
18058 let (pl, _b5) = part_l.device_ptr_mut(s);
18059 let (pb, _b6) = base_dev.device_ptr(s);
18060 let mut ps = [
18061 &pq as *const _ as *mut std::ffi::c_void,
18062 &pk as *const _ as *mut _,
18063 &pv as *const _ as *mut _,
18064 &po as *const _ as *mut _,
18065 &pm as *const _ as *mut _,
18066 &pl as *const _ as *mut _,
18067 &hd as *const _ as *mut _,
18068 &nh as *const _ as *mut _,
18069 &nhkv as *const _ as *mut _,
18070 &pb as *const _ as *mut _,
18071 &base_plus as *const _ as *mut _,
18072 &scale as *const _ as *mut _,
18073 &nspm as *const _ as *mut _,
18074 &spk as *const _ as *mut _,
18075 &ktb as *const _ as *mut _,
18076 &vtb as *const _ as *mut _,
18077 &wini as *const _ as *mut _,
18078 ];
18079 unsafe {
18080 self.launch_pdl_flash(
18081 wg,
18082 "fa_decode_vec_q_rows_v4_w",
18083 (n_head_kv as u32, n_splits_max as u32, t as u32),
18084 (32, gqa, 1),
18085 sh,
18086 &mut ps,
18087 )?;
18088 }
18089 } else {
18090 let pick = |name: &str| {
18091 if wg {
18092 self.func_g(name)
18093 } else {
18094 self.func(name)
18095 }
18096 };
18097 let (f, sh) = if fa_v4_at(window) {
18098 let f = pick("fa_decode_vec_q_rows_v4_w");
18099 (f, (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32)
18100 } else if smem_tkv > 0 && window >= smem_tkv {
18101 (
18104 pick("fa_decode_vec_q_rows_smem_w"),
18105 (2 * 32 * head_dim * 2) as u32,
18106 )
18107 } else {
18108 (pick("fa_decode_vec_q_rows_reg_w"), 0u32)
18109 };
18110 f.set_attribute(
18111 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18112 sh as i32,
18113 )?;
18114 let cfg = LaunchConfig {
18115 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18116 block_dim: (32, gqa, 1),
18117 shared_mem_bytes: sh,
18118 };
18119 let __s_b = self.gpu.stream();
18120 let mut b = __s_b.launch_builder(&f);
18121 b.arg(q)
18122 .arg(k)
18123 .arg(v)
18124 .arg(&mut *part_o)
18125 .arg(&mut *part_m)
18126 .arg(&mut *part_l)
18127 .arg(&hd)
18128 .arg(&nh)
18129 .arg(&nhkv)
18130 .arg(base_dev)
18131 .arg(&base_plus)
18132 .arg(&scale)
18133 .arg(&nspm)
18134 .arg(&spk)
18135 .arg(&ktb)
18136 .arg(&vtb)
18137 .arg(&wini);
18138 unsafe {
18139 b.launch(cfg)?;
18140 }
18141 }
18142 }
18143 let cfg2 = LaunchConfig {
18144 grid_dim: (n_head as u32, t as u32, 1),
18145 block_dim: (head_dim as u32, 1, 1),
18146 shared_mem_bytes: 0,
18147 };
18148 if let Some((oq, od)) = q8_out {
18149 if Self::pdl_on() && Self::pdl_wb_on() {
18152 use cudarc::driver::{DevicePtr, DevicePtrMut};
18154 let s = &self.gpu.stream();
18155 let (po, _g0) = part_o.device_ptr(s);
18156 let (pm, _g1) = part_m.device_ptr(s);
18157 let (pl, _g2) = part_l.device_ptr(s);
18158 let (pq, _g3) = oq.device_ptr_mut(s);
18159 let (pd, _g4) = od.device_ptr_mut(s);
18160 let mut ps = [
18161 &po as *const _ as *mut std::ffi::c_void,
18162 &pm as *const _ as *mut _,
18163 &pl as *const _ as *mut _,
18164 &pq as *const _ as *mut _,
18165 &pd as *const _ as *mut _,
18166 &hd as *const _ as *mut _,
18167 &nh as *const _ as *mut _,
18168 &nspm as *const _ as *mut _,
18169 &spk as *const _ as *mut _,
18170 &wini as *const _ as *mut _,
18171 ];
18172 unsafe {
18173 self.launch_pdl_flash(
18174 wg,
18175 "fa_decode_combine_rows_w_q8_1",
18176 cfg2.grid_dim,
18177 cfg2.block_dim,
18178 0,
18179 &mut ps,
18180 )?;
18181 }
18182 return Ok(());
18183 }
18184 let fc = if wg {
18185 self.func_g("fa_decode_combine_rows_w_q8_1")
18186 } else {
18187 self.func("fa_decode_combine_rows_w_q8_1")
18188 };
18189 let __s_b2 = self.gpu.stream();
18190 let mut b2 = __s_b2.launch_builder(&fc);
18191 b2.arg(&*part_o)
18192 .arg(&*part_m)
18193 .arg(&*part_l)
18194 .arg(oq)
18195 .arg(od)
18196 .arg(&hd)
18197 .arg(&nh)
18198 .arg(&nspm)
18199 .arg(&spk)
18200 .arg(&wini);
18201 unsafe {
18202 b2.launch(cfg2)?;
18203 }
18204 return Ok(());
18205 }
18206 let fc = if wg {
18207 self.func_g("fa_decode_combine_rows_w")
18208 } else {
18209 self.func("fa_decode_combine_rows_w")
18210 };
18211 let __s_b2 = self.gpu.stream();
18212 let mut b2 = __s_b2.launch_builder(&fc);
18213 b2.arg(&*part_o)
18214 .arg(&*part_m)
18215 .arg(&*part_l)
18216 .arg(o)
18217 .arg(&hd)
18218 .arg(&nh)
18219 .arg(&nspm)
18220 .arg(&spk)
18221 .arg(&wini);
18222 unsafe {
18223 b2.launch(cfg2)?;
18224 }
18225 Ok(())
18226 }
18227
18228 #[allow(clippy::too_many_arguments)]
18234 pub fn fa_decode_rows_dc(
18235 &self,
18236 q: &CudaSlice<f32>,
18237 k: &cudarc::driver::CudaView<u8>,
18238 v: &cudarc::driver::CudaView<u8>,
18239 o: &mut CudaSlice<f32>,
18240 head_dim: usize,
18241 n_head: usize,
18242 n_head_kv: usize,
18243 base_dev: &CudaSlice<i32>,
18244 t_kv_upper: usize,
18245 t: usize,
18246 scale: f32,
18247 k_tok_bytes: usize,
18248 v_tok_bytes: usize,
18249 base_plus: i32,
18250 g: bool,
18251 ) -> Result<(), Box<dyn std::error::Error>> {
18252 let v4 = head_dim == 256 && fa_v4_at(t_kv_upper);
18253 assert!(
18254 v4 || fa_v3_active(head_dim),
18255 "stream fa rows requires the v3 or v4 lane"
18256 );
18257 assert!(v4 || base_plus == 0, "v3_dc kernel takes no plus arg");
18258 if v4 {
18259 let sp = fa_split_keys(t_kv_upper, n_head_kv);
18260 let n_splits_max = (t_kv_upper + sp - 1) / sp;
18261 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18262 let (nspm, spk) = (n_splits_max as i32, sp as i32);
18263 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18264 let gqa = (n_head / n_head_kv).max(1) as u32;
18265 let o_len = t * n_head * n_splits_max * head_dim;
18266 let ml_len = t * n_head * n_splits_max;
18267 let mut part_guard = self.fa_part_pool.lock().unwrap();
18268 if part_guard
18269 .as_ref()
18270 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18271 .unwrap_or(true)
18272 {
18273 let old = part_guard.take();
18284 let (co, cm) = old
18285 .as_ref()
18286 .map(|pp| (pp.0.len(), pp.1.len()))
18287 .unwrap_or((0, 0));
18288 if let Some(old) = old {
18289 self.fa_part_retired.lock().unwrap().push(old);
18290 }
18291 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18292 eprintln!(
18293 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18294 co, o_len, cm, ml_len
18295 );
18296 }
18297 *part_guard = Some((
18298 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18299 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18300 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18301 ));
18302 }
18303 let pg = part_guard.as_mut().unwrap();
18304 self.gpu
18305 .stream()
18306 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18307 self.gpu
18308 .stream()
18309 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18310 self.gpu
18311 .stream()
18312 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18313 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18314 let f = if g {
18315 self.func_g("fa_decode_vec_q_rows_v4_dc")
18316 } else {
18317 self.func("fa_decode_vec_q_rows_v4_dc")
18318 };
18319 let sh = (11520 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
18320 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18321 f.set_attribute(
18322 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18323 sh as i32,
18324 )?;
18325 let cfg = LaunchConfig {
18326 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18327 block_dim: (32, gqa, 1),
18328 shared_mem_bytes: sh,
18329 };
18330 let __s_b = self.gpu.stream();
18331 let mut b = __s_b.launch_builder(&f);
18332 b.arg(q)
18333 .arg(k)
18334 .arg(v)
18335 .arg(&mut *part_o)
18336 .arg(&mut *part_m)
18337 .arg(&mut *part_l)
18338 .arg(&hd)
18339 .arg(&nh)
18340 .arg(&nhkv)
18341 .arg(base_dev)
18342 .arg(&base_plus)
18343 .arg(&scale)
18344 .arg(&nspm)
18345 .arg(&spk)
18346 .arg(&ktb)
18347 .arg(&vtb);
18348 unsafe {
18349 b.launch(cfg)?;
18350 }
18351 let fc = self.func("fa_decode_combine_rows_dc");
18352 let cfg2 = LaunchConfig {
18353 grid_dim: (n_head as u32, t as u32, 1),
18354 block_dim: (head_dim as u32, 1, 1),
18355 shared_mem_bytes: 0,
18356 };
18357 let __s_b2 = self.gpu.stream();
18358 let mut b2 = __s_b2.launch_builder(&fc);
18359 b2.arg(&*part_o)
18360 .arg(&*part_m)
18361 .arg(&*part_l)
18362 .arg(o)
18363 .arg(&hd)
18364 .arg(&nh)
18365 .arg(base_dev)
18366 .arg(&base_plus)
18367 .arg(&nspm)
18368 .arg(&spk);
18369 unsafe {
18370 b2.launch(cfg2)?;
18371 }
18372 return Ok(());
18373 }
18374 let sp = fa_split_keys(t_kv_upper, n_head_kv);
18375 let n_splits_max = (t_kv_upper + sp - 1) / sp;
18376 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18377 let (nspm, spk) = (n_splits_max as i32, sp as i32);
18378 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18379 let gqa = (n_head / n_head_kv).max(1) as u32;
18380 let o_len = t * n_head * n_splits_max * head_dim;
18381 let ml_len = t * n_head * n_splits_max;
18382 let mut part_guard = self.fa_part_pool.lock().unwrap();
18383 if part_guard
18384 .as_ref()
18385 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18386 .unwrap_or(true)
18387 {
18388 let old = part_guard.take();
18399 let (co, cm) = old
18400 .as_ref()
18401 .map(|pp| (pp.0.len(), pp.1.len()))
18402 .unwrap_or((0, 0));
18403 if let Some(old) = old {
18404 self.fa_part_retired.lock().unwrap().push(old);
18405 }
18406 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18407 eprintln!(
18408 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18409 co, o_len, cm, ml_len
18410 );
18411 }
18412 *part_guard = Some((
18413 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18414 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18415 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18416 ));
18417 }
18418 let pg = part_guard.as_mut().unwrap();
18419 self.gpu
18420 .stream()
18421 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18422 self.gpu
18423 .stream()
18424 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18425 self.gpu
18426 .stream()
18427 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18428 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18429 let f = self.func("fa_decode_vec_q_rows_v3_dc");
18430 let sh = (32 * head_dim * 2) as u32;
18431 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18432 f.set_attribute(
18433 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18434 sh as i32,
18435 )?;
18436 let cfg = LaunchConfig {
18437 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18438 block_dim: (32, gqa, 1),
18439 shared_mem_bytes: sh,
18440 };
18441 let __s_b = self.gpu.stream();
18442 let mut b = __s_b.launch_builder(&f);
18443 b.arg(q)
18444 .arg(k)
18445 .arg(v)
18446 .arg(&mut *part_o)
18447 .arg(&mut *part_m)
18448 .arg(&mut *part_l)
18449 .arg(&hd)
18450 .arg(&nh)
18451 .arg(&nhkv)
18452 .arg(base_dev)
18453 .arg(&scale)
18454 .arg(&nspm)
18455 .arg(&spk)
18456 .arg(&ktb)
18457 .arg(&vtb);
18458 unsafe {
18459 b.launch(cfg)?;
18460 }
18461 let fc = self.func("fa_decode_combine_rows_dc");
18462 let cfg2 = LaunchConfig {
18463 grid_dim: (n_head as u32, t as u32, 1),
18464 block_dim: (head_dim as u32, 1, 1),
18465 shared_mem_bytes: 0,
18466 };
18467 let plus0 = 0i32;
18468 let __s_b2 = self.gpu.stream();
18469 let mut b2 = __s_b2.launch_builder(&fc);
18470 b2.arg(&*part_o)
18471 .arg(&*part_m)
18472 .arg(&*part_l)
18473 .arg(o)
18474 .arg(&hd)
18475 .arg(&nh)
18476 .arg(base_dev)
18477 .arg(&plus0)
18478 .arg(&nspm)
18479 .arg(&spk);
18480 unsafe {
18481 b2.launch(cfg2)?;
18482 }
18483 Ok(())
18484 }
18485
18486 pub fn fa_decode_dc(
18497 &self,
18498 q: &CudaSlice<f32>,
18499 k: &cudarc::driver::CudaView<u8>,
18500 v: &cudarc::driver::CudaView<u8>,
18501 o: &mut CudaSlice<f32>,
18502 head_dim: usize,
18503 n_head: usize,
18504 n_head_kv: usize,
18505 t_kv_dev: &CudaSlice<i32>,
18506 bucket_max: usize,
18507 scale: f32,
18508 k_tok_bytes: usize,
18509 v_tok_bytes: usize,
18510 g: bool,
18511 ) -> Result<(), Box<dyn std::error::Error>> {
18512 self.fa_decode_dc_q8(
18513 q,
18514 k,
18515 v,
18516 o,
18517 head_dim,
18518 n_head,
18519 n_head_kv,
18520 t_kv_dev,
18521 bucket_max,
18522 scale,
18523 k_tok_bytes,
18524 v_tok_bytes,
18525 g,
18526 None,
18527 )
18528 }
18529
18530 #[allow(clippy::too_many_arguments)]
18533 pub fn fa_decode_dc_q8(
18534 &self,
18535 q: &CudaSlice<f32>,
18536 k: &cudarc::driver::CudaView<u8>,
18537 v: &cudarc::driver::CudaView<u8>,
18538 o: &mut CudaSlice<f32>,
18539 head_dim: usize,
18540 n_head: usize,
18541 n_head_kv: usize,
18542 t_kv_dev: &CudaSlice<i32>,
18543 bucket_max: usize,
18544 scale: f32,
18545 k_tok_bytes: usize,
18546 v_tok_bytes: usize,
18547 g: bool,
18548 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
18549 ) -> Result<(), Box<dyn std::error::Error>> {
18550 let mut fa_vec =
18558 std::env::var("MEMRA_NO_FA_VEC").is_err() && bucket_max >= fa_vec_min_tkv();
18559 if g && head_dim == 256 && !fa_v4_at(bucket_max) {
18560 fa_vec = false;
18561 } let sp = fa_split_keys(bucket_max, n_head_kv);
18563 let n_splits = if fa_vec {
18564 ((bucket_max + sp - 1) / sp).max(1)
18565 } else {
18566 ((bucket_max + 255) / 256).max(1)
18567 };
18568 let o_len = n_head * n_splits * head_dim;
18569 let ml_len = n_head * n_splits;
18570 let mut part_guard = self.fa_part_pool.lock().unwrap();
18571 if part_guard
18572 .as_ref()
18573 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18574 .unwrap_or(true)
18575 {
18576 let old = part_guard.take();
18587 let (co, cm) = old
18588 .as_ref()
18589 .map(|pp| (pp.0.len(), pp.1.len()))
18590 .unwrap_or((0, 0));
18591 if let Some(old) = old {
18592 self.fa_part_retired.lock().unwrap().push(old);
18593 }
18594 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18595 eprintln!(
18596 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18597 co, o_len, cm, ml_len
18598 );
18599 }
18600 *part_guard = Some((
18601 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18602 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18603 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18604 ));
18605 }
18606 let pg = part_guard.as_mut().unwrap();
18607 self.gpu
18608 .stream()
18609 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18610 self.gpu
18611 .stream()
18612 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18613 self.gpu
18614 .stream()
18615 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18616 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18617 let (hd, nh, nhkv, nsp) = (
18618 head_dim as i32,
18619 n_head as i32,
18620 n_head_kv as i32,
18621 n_splits as i32,
18622 );
18623 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18624 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
18625 let deep = fa_vec
18628 && head_dim == 256
18629 && fa_v4_at(bucket_max)
18630 && !g
18631 && fa_deep_at(bucket_max)
18632 && !matches!(fa_v4_mode(), "noB3" | "stage");
18633 let (f, cfg) = if fa_vec
18634 && head_dim == 512
18635 && bucket_max >= {
18636 static FA512_MIN_DC: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18637 *FA512_MIN_DC.get_or_init(|| {
18638 std::env::var("MEMRA_FA512_MIN")
18639 .ok()
18640 .and_then(|v| v.parse().ok())
18641 .unwrap_or(512)
18642 })
18643 } {
18644 let gqa = (n_head / n_head_kv).max(1) as u32;
18646 (
18647 self.fa_func("fa_decode_vec_q_dpl16_dc", head_dim),
18648 LaunchConfig {
18649 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18650 block_dim: (32, gqa, 1),
18651 shared_mem_bytes: 0,
18652 },
18653 )
18654 } else if fa_vec && head_dim == 512 {
18655 let q_view = q.as_view();
18658 let mut o_view = o.as_view_mut();
18659 return self.fa_decode_scalar_unified(
18660 &q_view,
18661 k,
18662 v,
18663 &mut o_view,
18664 head_dim,
18665 n_head,
18666 n_head_kv,
18667 0,
18668 Some(t_kv_dev),
18669 scale,
18670 n_splits,
18671 sp,
18672 k_tok_bytes,
18673 v_tok_bytes,
18674 g,
18675 &mut *part_o,
18676 &mut *part_m,
18677 &mut *part_l,
18678 q8_out,
18679 );
18680 } else if fa_vec && head_dim == 256 && fa_v4_at(bucket_max) {
18681 let gqa = (n_head / n_head_kv).max(1) as u32;
18684 let fv = if g {
18685 self.func_g("fa_decode_vec_q_v4_dc")
18686 } else if deep {
18687 self.func("fa_decode_vec_q_v4_deep_dc")
18688 } else {
18689 self.func("fa_decode_vec_q_v4_dc")
18690 };
18691 let shmem =
18692 (if deep { 12160 } else { 11520 } + 32 * head_dim * if g { 1 } else { 2 }) as u32;
18693 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18694 fv.set_attribute(
18695 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18696 shmem as i32,
18697 )?;
18698 (
18699 fv,
18700 LaunchConfig {
18701 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18702 block_dim: (32, gqa, 1),
18703 shared_mem_bytes: shmem,
18704 },
18705 )
18706 } else if fa_vec && fa_v3_active(head_dim) {
18707 let gqa = (n_head / n_head_kv).max(1) as u32;
18710 let fv = if g {
18711 self.func_g("fa_decode_vec_q_v3_dc")
18712 } else {
18713 self.func("fa_decode_vec_q_v3_dc")
18714 };
18715 let shmem = (32 * head_dim * 2) as u32; (
18717 fv,
18718 LaunchConfig {
18719 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18720 block_dim: (32, gqa, 1),
18721 shared_mem_bytes: shmem,
18722 },
18723 )
18724 } else if fa_vec && fa_v2_on() {
18725 let gqa = (n_head / n_head_kv).max(1) as u32;
18729 let fv = if g {
18730 self.func_g("fa_decode_vec_q_v2_dc")
18731 } else {
18732 self.func("fa_decode_vec_q_v2_dc")
18733 };
18734 let shmem = (2 * 32 * head_dim * 2) as u32; (
18736 fv,
18737 LaunchConfig {
18738 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18739 block_dim: (32, gqa, 1),
18740 shared_mem_bytes: shmem,
18741 },
18742 )
18743 } else if fa_vec {
18744 let gqa = (n_head / n_head_kv).max(1) as u32;
18745 let fv = if g {
18747 self.func_g("fa_decode_vec_q_dc")
18748 } else {
18749 self.func("fa_decode_vec_q_dc")
18750 };
18751 (
18752 fv,
18753 LaunchConfig {
18754 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
18755 block_dim: (32, gqa, 1),
18756 shared_mem_bytes: 0,
18757 },
18758 )
18759 } else {
18760 let q_view = q.as_view();
18761 let mut o_view = o.as_view_mut();
18762 return self.fa_decode_scalar_unified(
18763 &q_view,
18764 k,
18765 v,
18766 &mut o_view,
18767 head_dim,
18768 n_head,
18769 n_head_kv,
18770 0,
18771 Some(t_kv_dev),
18772 scale,
18773 n_splits,
18774 if fa_vec { sp } else { 256 },
18775 k_tok_bytes,
18776 v_tok_bytes,
18777 g,
18778 &mut *part_o,
18779 &mut *part_m,
18780 &mut *part_l,
18781 q8_out,
18782 );
18783 };
18784 let ski = sp as i32; let __s_b = self.gpu.stream();
18786 let mut b = __s_b.launch_builder(&f);
18787 b.arg(q)
18788 .arg(k)
18789 .arg(v)
18790 .arg(&mut *part_o)
18791 .arg(&mut *part_m)
18792 .arg(&mut *part_l)
18793 .arg(&hd)
18794 .arg(&nh)
18795 .arg(&nhkv)
18796 .arg(t_kv_dev)
18797 .arg(&scale)
18798 .arg(&nsp)
18799 .arg(&ski)
18800 .arg(&ktb)
18801 .arg(&vtb);
18802 unsafe {
18803 b.launch(cfg)?;
18804 }
18805 let cfg2 = LaunchConfig {
18806 grid_dim: (n_head as u32, 1, 1),
18807 block_dim: (head_dim as u32, 1, 1),
18808 shared_mem_bytes: 0,
18809 };
18810 if let Some((oq, od)) = q8_out {
18811 let fc = if g {
18812 self.func_g("fa_decode_combine_q8_1")
18813 } else {
18814 self.fa_func("fa_decode_combine_q8_1", head_dim)
18815 };
18816 let __s_b2 = self.gpu.stream();
18817 let mut b2 = __s_b2.launch_builder(&fc);
18818 b2.arg(&*part_o)
18819 .arg(&*part_m)
18820 .arg(&*part_l)
18821 .arg(oq)
18822 .arg(od)
18823 .arg(&hd)
18824 .arg(&nh)
18825 .arg(&nsp);
18826 unsafe {
18827 b2.launch(cfg2)?;
18828 }
18829 return Ok(());
18830 }
18831 let fc = if g {
18832 self.func_g("fa_decode_combine_f32")
18833 } else {
18834 self.fa_func("fa_decode_combine_f32", head_dim)
18835 };
18836 let __s_b2 = self.gpu.stream();
18837 let mut b2 = __s_b2.launch_builder(&fc);
18838 b2.arg(&*part_o)
18839 .arg(&*part_m)
18840 .arg(&*part_l)
18841 .arg(o)
18842 .arg(&hd)
18843 .arg(&nh)
18844 .arg(&nsp);
18845 unsafe {
18846 b2.launch(cfg2)?;
18847 }
18848 Ok(())
18849 }
18850
18851 pub fn fa_geom_eager(
18857 &self,
18858 t_kv: usize,
18859 head_dim: usize,
18860 n_head_kv: usize,
18861 g: bool,
18862 ) -> (bool, usize) {
18863 let fa_ok = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
18867 let vec512 = fa_ok && head_dim == 512 && t_kv >= fa512_min_tkv();
18873 let mut fa_vec = vec512 || (fa_ok && head_dim <= 256 && head_dim % 32 == 0);
18874 if g && head_dim == 256 && !fa_v4_at(t_kv) {
18880 fa_vec = false;
18881 }
18882 let sp = fa_split_keys(t_kv, n_head_kv);
18883 let n_splits = if fa_vec {
18884 ((t_kv + sp - 1) / sp).max(1)
18885 } else {
18886 ((t_kv + 255) / 256).max(1)
18887 };
18888 (fa_vec, n_splits)
18889 }
18890
18891 pub fn fa_bucket_key(
18897 &self,
18898 t_kv: usize,
18899 head_dim: usize,
18900 n_head_kv: usize,
18901 g: bool,
18902 ) -> (bool, usize) {
18903 self.fa_geom_eager(t_kv, head_dim, n_head_kv, g)
18904 }
18905
18906 pub fn capture_graph_retained<F>(
18918 &self,
18919 step: F,
18920 ) -> Result<
18921 (
18922 cudarc::driver::CudaGraph,
18923 Vec<Box<dyn std::any::Any + Send>>,
18924 ),
18925 Box<dyn std::error::Error>,
18926 >
18927 where
18928 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
18929 {
18930 use cudarc::driver::sys::CUgraphInstantiate_flags;
18931 self.capture_graph_retained_flags(
18932 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
18933 step,
18934 )
18935 }
18936
18937 pub fn capture_graph_retained_flags<F>(
18942 &self,
18943 flags: cudarc::driver::sys::CUgraphInstantiate_flags,
18944 mut step: F,
18945 ) -> Result<
18946 (
18947 cudarc::driver::CudaGraph,
18948 Vec<Box<dyn std::any::Any + Send>>,
18949 ),
18950 Box<dyn std::error::Error>,
18951 >
18952 where
18953 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
18954 {
18955 use cudarc::driver::sys::CUstreamCaptureMode;
18956 self.capture_keep.lock().unwrap().clear();
18964 let was_tracking = self.gpu.ctx.is_event_tracking();
18965 if was_tracking {
18966 unsafe {
18967 self.gpu.ctx.disable_event_tracking();
18968 }
18969 }
18970 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
18971 self.capture_keep_on
18972 .store(true, std::sync::atomic::Ordering::Relaxed);
18973 let w = (|| {
18974 step(self)?;
18975 step(self)
18976 })();
18977 self.capture_keep_on
18978 .store(false, std::sync::atomic::Ordering::Relaxed);
18979 w?;
18980 self.gpu.stream().synchronize()?;
18981 self.gpu
18982 .stream()
18983 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
18984 let r = step(self);
18985 let g = self.gpu.stream().end_capture(flags);
18986 r?;
18987 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
18988 graph.upload()?;
18989 Ok(graph)
18990 };
18991 let result = run();
18992 self.capture_keep_on
18993 .store(false, std::sync::atomic::Ordering::Relaxed);
18994 if was_tracking {
18995 unsafe {
18996 self.gpu.ctx.enable_event_tracking();
18997 }
18998 }
18999 let keeper = std::mem::take(&mut *self.capture_keep.lock().unwrap());
19000 Ok((result?, keeper))
19001 }
19002
19003 pub fn capture_graph<F>(
19004 &self,
19005 mut step: F,
19006 ) -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>>
19007 where
19008 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
19009 {
19010 use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
19011 let was_tracking = self.gpu.ctx.is_event_tracking();
19019 if was_tracking {
19020 unsafe {
19021 self.gpu.ctx.disable_event_tracking();
19022 }
19023 }
19024 let iflag = {
19031 static F: std::sync::OnceLock<CUgraphInstantiate_flags> = std::sync::OnceLock::new();
19032 *F.get_or_init(|| match std::env::var("MEMRA_GRAPH_IFLAG").as_deref() {
19033 Ok("upload") => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_UPLOAD,
19036 Ok("priority") => {
19037 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY
19038 }
19039 _ => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
19040 })
19041 };
19042 let ct = {
19049 static T: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19050 *T.get_or_init(|| std::env::var("MEMRA_GRAPH_CAPTIME").as_deref() == Ok("1"))
19051 };
19052 let warmups = {
19075 static W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
19076 *W.get_or_init(|| {
19077 std::env::var("MEMRA_GRAPH_WARMUPS")
19078 .ok()
19079 .and_then(|v| v.parse().ok())
19080 .filter(|n| *n >= 1)
19081 .unwrap_or(1)
19082 })
19083 };
19084 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
19085 let t_w = std::time::Instant::now();
19086 for _ in 0..warmups {
19088 step(self)?;
19089 }
19090 self.gpu.stream().synchronize()?;
19091 let ms_warm = t_w.elapsed().as_secs_f64() * 1e3;
19092 let t_c = std::time::Instant::now();
19094 self.gpu
19095 .stream()
19096 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
19097 let r = step(self);
19100 let ms_body = t_c.elapsed().as_secs_f64() * 1e3;
19101 let t_i = std::time::Instant::now();
19102 let g = self.gpu.stream().end_capture(iflag);
19103 let ms_inst = t_i.elapsed().as_secs_f64() * 1e3;
19104 r?;
19105 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
19106 let t_u = std::time::Instant::now();
19107 graph.upload()?;
19108 if ct {
19109 println!(
19110 "[graph-captime] warmup2x {ms_warm:.2} ms capture-body {ms_body:.2} ms \
19111 instantiate {ms_inst:.2} ms upload {:.2} ms",
19112 t_u.elapsed().as_secs_f64() * 1e3
19113 );
19114 }
19115 Ok(graph)
19116 };
19117 let result = run();
19118 if was_tracking {
19119 unsafe {
19120 self.gpu.ctx.enable_event_tracking();
19121 }
19122 }
19123 result
19124 }
19125
19126 pub fn gdn_scan_s128_view(
19128 &self,
19129 q: &CudaSlice<f32>,
19130 k: &CudaSlice<f32>,
19131 v: &CudaSlice<f32>,
19132 g: &CudaSlice<f32>,
19133 beta: &CudaSlice<f32>,
19134 state_in: &cudarc::driver::CudaView<f32>,
19135 state_out: &mut cudarc::driver::CudaViewMut<f32>,
19136 o: &mut CudaSlice<f32>,
19137 n_head: usize,
19138 t: usize,
19139 scale: f32,
19140 ) -> Result<(), Box<dyn std::error::Error>> {
19141 let f = self.func("gdn_scan_s128");
19142 const S_V: u32 = 128;
19143 const WARP: u32 = 32;
19144 const COLS: u32 = 4;
19145 let cfg = LaunchConfig {
19146 grid_dim: (n_head as u32, 1, S_V / COLS),
19147 block_dim: (WARP, COLS, 1),
19148 shared_mem_bytes: 0,
19149 };
19150 let (h, ti) = (n_head as i32, t as i32);
19151 let __s_b = self.gpu.stream();
19152 let mut b = __s_b.launch_builder(&f);
19153 b.arg(q)
19154 .arg(k)
19155 .arg(v)
19156 .arg(g)
19157 .arg(beta)
19158 .arg(state_in)
19159 .arg(state_out)
19160 .arg(o)
19161 .arg(&h)
19162 .arg(&ti)
19163 .arg(&scale);
19164 unsafe {
19165 b.launch(cfg)?;
19166 }
19167 Ok(())
19168 }
19169
19170 pub fn ssm_conv1d_view(
19172 &self,
19173 x: &cudarc::driver::CudaView<f32>,
19174 w: &CudaSlice<f32>,
19175 y: &mut CudaSlice<f32>,
19176 conv_dim: usize,
19177 t: usize,
19178 d_conv: usize,
19179 silu: bool,
19180 ) -> Result<(), Box<dyn std::error::Error>> {
19181 let f = self.func("ssm_conv1d_silu_f32");
19182 let cfg = LaunchConfig {
19184 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
19185 block_dim: (256, 1, 1),
19186 shared_mem_bytes: 0,
19187 };
19188 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
19189 let __s_b = self.gpu.stream();
19190 let mut b = __s_b.launch_builder(&f);
19191 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
19192 unsafe {
19193 b.launch(cfg)?;
19194 }
19195 Ok(())
19196 }
19197
19198 pub fn ssm_conv1d_tm(
19205 &self,
19206 qkv_tm: &CudaSlice<f32>,
19207 w: &CudaSlice<f32>,
19208 y: &mut CudaSlice<f32>,
19209 conv_dim: usize,
19210 t: usize,
19211 d_conv: usize,
19212 ) -> Result<(), Box<dyn std::error::Error>> {
19213 let f = self.func("ssm_conv1d_tm_f32");
19214 let cfg = LaunchConfig {
19215 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19216 block_dim: (256, 1, 1),
19217 shared_mem_bytes: 0,
19218 };
19219 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19220 let __s_b = self.gpu.stream();
19221 let mut b = __s_b.launch_builder(&f);
19222 b.arg(qkv_tm).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc);
19223 unsafe {
19224 b.launch(cfg)?;
19225 }
19226 Ok(())
19227 }
19228
19229 pub fn ssm_conv1d_tm_state(
19237 &self,
19238 qkv_tm: &CudaSlice<f32>,
19239 conv_state: &mut CudaSlice<f32>,
19240 w: &CudaSlice<f32>,
19241 y: &mut CudaSlice<f32>,
19242 conv_dim: usize,
19243 t: usize,
19244 d_conv: usize,
19245 ) -> Result<(), Box<dyn std::error::Error>> {
19246 self.ssm_conv1d_tm_state_pad(qkv_tm, conv_state, w, y, conv_dim, t, d_conv, None)
19247 }
19248
19249 #[allow(clippy::too_many_arguments)]
19252 pub fn ssm_conv1d_tm_state_pad(
19253 &self,
19254 qkv_tm: &CudaSlice<f32>,
19255 conv_state: &mut CudaSlice<f32>,
19256 w: &CudaSlice<f32>,
19257 y: &mut CudaSlice<f32>,
19258 conv_dim: usize,
19259 t: usize,
19260 d_conv: usize,
19261 pad_len: Option<&CudaSlice<i32>>,
19262 ) -> Result<(), Box<dyn std::error::Error>> {
19263 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
19264 let ring_old = if t < d_conv - 1 {
19268 Some(self.clone_dtod(conv_state)?)
19269 } else {
19270 None
19271 };
19272 {
19273 let f = self.func("ssm_conv1d_tm_state_f32");
19274 let cfg = LaunchConfig {
19275 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19276 block_dim: (256, 1, 1),
19277 shared_mem_bytes: 0,
19278 };
19279 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19280 let __s_b = self.gpu.stream();
19281 let mut b = __s_b.launch_builder(&f);
19282 b.arg(qkv_tm)
19283 .arg(&*conv_state)
19284 .arg(w)
19285 .arg(y)
19286 .arg(&cd)
19287 .arg(&ti)
19288 .arg(&dc);
19289 unsafe {
19290 b.launch(cfg)?;
19291 }
19292 }
19293 match (ring_old, pad_len) {
19294 (None, Some(len_d)) => {
19295 let f = self.func("ssm_conv_ring_update_dev_f32");
19296 let n = conv_dim * (d_conv - 1);
19297 let cfg = LaunchConfig::for_num_elems(n as u32);
19298 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19299 let __s_b = self.gpu.stream();
19300 let mut b = __s_b.launch_builder(&f);
19301 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
19302 unsafe {
19303 b.launch(cfg)?;
19304 }
19305 }
19306 (None, None) => {
19307 let f = self.func("ssm_conv_ring_update_f32");
19308 let n = conv_dim * (d_conv - 1);
19309 let cfg = LaunchConfig::for_num_elems(n as u32);
19310 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19311 let __s_b = self.gpu.stream();
19312 let mut b = __s_b.launch_builder(&f);
19313 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
19314 unsafe {
19315 b.launch(cfg)?;
19316 }
19317 }
19318 (Some(old), _) => {
19319 self.ssm_conv_ring_rebuild(qkv_tm, &old, conv_state, conv_dim, t, d_conv)?
19320 }
19321 }
19322 Ok(())
19323 }
19324
19325 pub fn ssm_conv1d_tm_state_pad_v(
19327 &self,
19328 qkv_tm: &cudarc::driver::CudaView<f32>,
19329 conv_state: &mut CudaSlice<f32>,
19330 w: &CudaSlice<f32>,
19331 y: &mut CudaSlice<f32>,
19332 conv_dim: usize,
19333 t: usize,
19334 d_conv: usize,
19335 pad_len: Option<&CudaSlice<i32>>,
19336 ) -> Result<(), Box<dyn std::error::Error>> {
19337 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
19338 let ring_old = if t < d_conv - 1 {
19342 Some(self.clone_dtod(conv_state)?)
19343 } else {
19344 None
19345 };
19346 {
19347 let f = self.func("ssm_conv1d_tm_state_f32");
19348 let cfg = LaunchConfig {
19349 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19350 block_dim: (256, 1, 1),
19351 shared_mem_bytes: 0,
19352 };
19353 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19354 let __s_b = self.gpu.stream();
19355 let mut b = __s_b.launch_builder(&f);
19356 b.arg(qkv_tm)
19357 .arg(&*conv_state)
19358 .arg(w)
19359 .arg(y)
19360 .arg(&cd)
19361 .arg(&ti)
19362 .arg(&dc);
19363 unsafe {
19364 b.launch(cfg)?;
19365 }
19366 }
19367 match (ring_old, pad_len) {
19368 (None, Some(len_d)) => {
19369 let f = self.func("ssm_conv_ring_update_dev_f32");
19370 let n = conv_dim * (d_conv - 1);
19371 let cfg = LaunchConfig::for_num_elems(n as u32);
19372 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19373 let __s_b = self.gpu.stream();
19374 let mut b = __s_b.launch_builder(&f);
19375 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
19376 unsafe {
19377 b.launch(cfg)?;
19378 }
19379 }
19380 (None, None) => {
19381 let f = self.func("ssm_conv_ring_update_f32");
19382 let n = conv_dim * (d_conv - 1);
19383 let cfg = LaunchConfig::for_num_elems(n as u32);
19384 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19385 let __s_b = self.gpu.stream();
19386 let mut b = __s_b.launch_builder(&f);
19387 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
19388 unsafe {
19389 b.launch(cfg)?;
19390 }
19391 }
19392 (Some(_), _) => unreachable!(
19393 "ssm_conv1d_tm_state_pad_v: T < d_conv-1 has no view path (PRIME_MIN_T gates it)"
19394 ),
19395 }
19396 Ok(())
19397 }
19398
19399 pub fn ssm_conv_ring_rebuild(
19404 &self,
19405 qkv_tm: &CudaSlice<f32>,
19406 ring_old: &CudaSlice<f32>,
19407 conv_state: &mut CudaSlice<f32>,
19408 conv_dim: usize,
19409 tc: usize,
19410 d_conv: usize,
19411 ) -> Result<(), Box<dyn std::error::Error>> {
19412 let f = self.func("ssm_conv_ring_rebuild_f32");
19413 let n = conv_dim * (d_conv - 1);
19414 let cfg = LaunchConfig::for_num_elems(n as u32);
19415 let (cd, ti, dc) = (conv_dim as i32, tc as i32, d_conv as i32);
19416 let __s_b = self.gpu.stream();
19417 let mut b = __s_b.launch_builder(&f);
19418 b.arg(qkv_tm)
19419 .arg(ring_old)
19420 .arg(conv_state)
19421 .arg(&cd)
19422 .arg(&ti)
19423 .arg(&dc);
19424 unsafe {
19425 b.launch(cfg)?;
19426 }
19427 Ok(())
19428 }
19429
19430 #[allow(clippy::too_many_arguments)]
19435 pub fn gdn_prep_decode(
19436 &self,
19437 conv_out: &CudaSlice<f32>,
19438 beta_raw: &CudaSlice<f32>,
19439 alpha: &CudaSlice<f32>,
19440 dt_bias: &CudaSlice<f32>,
19441 a: &CudaSlice<f32>,
19442 q_l2: &mut CudaSlice<f32>,
19443 k_l2: &mut CudaSlice<f32>,
19444 v_g: &mut CudaSlice<f32>,
19445 beta: &mut CudaSlice<f32>,
19446 g_log: &mut CudaSlice<f32>,
19447 d_state: usize,
19448 num_v: usize,
19449 num_k: usize,
19450 key_dim: usize,
19451 eps: f32,
19452 ) -> Result<(), Box<dyn std::error::Error>> {
19453 let f = self.func("gdn_prep_decode_f32");
19454 let cfg = LaunchConfig {
19455 grid_dim: (num_v as u32, 1, 1),
19456 block_dim: (32, 4, 1),
19457 shared_mem_bytes: 0,
19458 };
19459 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
19460 let __s_b = self.gpu.stream();
19461 let mut b = __s_b.launch_builder(&f);
19462 b.arg(conv_out)
19463 .arg(beta_raw)
19464 .arg(alpha)
19465 .arg(dt_bias)
19466 .arg(a)
19467 .arg(q_l2)
19468 .arg(k_l2)
19469 .arg(v_g)
19470 .arg(beta)
19471 .arg(g_log)
19472 .arg(&ds)
19473 .arg(&nv)
19474 .arg(&nk)
19475 .arg(&kd)
19476 .arg(&eps);
19477 unsafe {
19478 b.launch(cfg)?;
19479 }
19480 Ok(())
19481 }
19482
19483 #[allow(clippy::too_many_arguments)]
19487 pub fn ssm_conv1d_gdn(
19488 &self,
19489 qkv_tm: &CudaSlice<f32>,
19490 w: &CudaSlice<f32>,
19491 q_g: &mut CudaSlice<f32>,
19492 k_g: &mut CudaSlice<f32>,
19493 v_g: &mut CudaSlice<f32>,
19494 conv_dim: usize,
19495 t: usize,
19496 d_conv: usize,
19497 d_state: usize,
19498 num_v: usize,
19499 num_k: usize,
19500 key_dim: usize,
19501 ) -> Result<(), Box<dyn std::error::Error>> {
19502 let f = self.func("ssm_conv1d_gdn_f32");
19503 let cfg = LaunchConfig {
19504 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19505 block_dim: (256, 1, 1),
19506 shared_mem_bytes: 0,
19507 };
19508 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19509 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
19510 let __s_b = self.gpu.stream();
19511 let mut b = __s_b.launch_builder(&f);
19512 b.arg(qkv_tm)
19513 .arg(w)
19514 .arg(q_g)
19515 .arg(k_g)
19516 .arg(v_g)
19517 .arg(&cd)
19518 .arg(&ti)
19519 .arg(&dc)
19520 .arg(&ds)
19521 .arg(&nv)
19522 .arg(&nk)
19523 .arg(&kd);
19524 unsafe {
19525 b.launch(cfg)?;
19526 }
19527 Ok(())
19528 }
19529
19530 pub fn ssm_conv1d(
19531 &self,
19532 x: &CudaSlice<f32>,
19533 w: &CudaSlice<f32>,
19534 y: &mut CudaSlice<f32>,
19535 conv_dim: usize,
19536 t: usize,
19537 d_conv: usize,
19538 silu: bool,
19539 ) -> Result<(), Box<dyn std::error::Error>> {
19540 let f = self.func("ssm_conv1d_silu_f32");
19541 let cfg = LaunchConfig {
19542 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
19543 block_dim: (256, 1, 1),
19544 shared_mem_bytes: 0,
19545 };
19546 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
19547 let __s_b = self.gpu.stream();
19548 let mut b = __s_b.launch_builder(&f);
19549 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
19550 unsafe {
19551 b.launch(cfg)?;
19552 }
19553 Ok(())
19554 }
19555
19556 pub fn gdn_scan_s128(
19559 &self,
19560 q: &CudaSlice<f32>,
19561 k: &CudaSlice<f32>,
19562 v: &CudaSlice<f32>,
19563 g: &CudaSlice<f32>,
19564 beta: &CudaSlice<f32>,
19565 state_in: &CudaSlice<f32>,
19566 state_out: &mut CudaSlice<f32>,
19567 o: &mut CudaSlice<f32>,
19568 n_head: usize,
19569 t: usize,
19570 scale: f32,
19571 ) -> Result<(), Box<dyn std::error::Error>> {
19572 let f = self.func("gdn_scan_s128");
19573 const S_V: u32 = 128;
19574 const WARP: u32 = 32;
19575 const COLS_PER_BLOCK: u32 = 4;
19576 let cfg = LaunchConfig {
19577 grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
19578 block_dim: (WARP, COLS_PER_BLOCK, 1),
19579 shared_mem_bytes: 0,
19580 };
19581 let (h, ti) = (n_head as i32, t as i32);
19582 let __s_b = self.gpu.stream();
19583 let mut b = __s_b.launch_builder(&f);
19584 b.arg(q)
19585 .arg(k)
19586 .arg(v)
19587 .arg(g)
19588 .arg(beta)
19589 .arg(state_in)
19590 .arg(state_out)
19591 .arg(o)
19592 .arg(&h)
19593 .arg(&ti)
19594 .arg(&scale);
19595 unsafe {
19596 b.launch(cfg)?;
19597 }
19598 Ok(())
19599 }
19600
19601 #[allow(clippy::too_many_arguments)]
19606 pub fn ssm_conv1d_fused_decode_b(
19607 &self,
19608 qkv_cols: &CudaSlice<f32>,
19609 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
19610 w: &CudaSlice<f32>,
19611 conv_outs: &mut CudaSlice<f32>,
19612 conv_dim: usize,
19613 d_conv: usize,
19614 b_n: usize,
19615 ) -> Result<(), Box<dyn std::error::Error>> {
19616 let f = self.func("ssm_conv1d_fused_decode_b_f32");
19617 let cfg = LaunchConfig {
19618 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
19619 block_dim: (256, 1, 1),
19620 shared_mem_bytes: 0,
19621 };
19622 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19623 let __s_b = self.gpu.stream();
19624 let mut b = __s_b.launch_builder(&f);
19625 b.arg(qkv_cols)
19626 .arg(conv_state_ptrs)
19627 .arg(w)
19628 .arg(conv_outs)
19629 .arg(&cd)
19630 .arg(&dc);
19631 unsafe {
19632 b.launch(cfg)?;
19633 }
19634 Ok(())
19635 }
19636
19637 #[allow(clippy::too_many_arguments)]
19638 pub fn gdn_prep_decode_b(
19639 &self,
19640 conv_outs: &CudaSlice<f32>,
19641 beta_raws: &CudaSlice<f32>,
19642 alphas: &CudaSlice<f32>,
19643 dt_bias: &CudaSlice<f32>,
19644 a: &CudaSlice<f32>,
19645 q_l2: &mut CudaSlice<f32>,
19646 k_l2: &mut CudaSlice<f32>,
19647 v_g: &mut CudaSlice<f32>,
19648 beta: &mut CudaSlice<f32>,
19649 g_log: &mut CudaSlice<f32>,
19650 d_state: usize,
19651 num_v: usize,
19652 num_k: usize,
19653 key_dim: usize,
19654 eps: f32,
19655 conv_dim: usize,
19656 b_n: usize,
19657 ) -> Result<(), Box<dyn std::error::Error>> {
19658 let f = self.func("gdn_prep_decode_b_f32");
19659 let cfg = LaunchConfig {
19660 grid_dim: (num_v as u32, 1, b_n as u32),
19661 block_dim: (32, 4, 1),
19662 shared_mem_bytes: 0,
19663 };
19664 let (ds, nv, nk, kd, cd) = (
19665 d_state as i32,
19666 num_v as i32,
19667 num_k as i32,
19668 key_dim as i32,
19669 conv_dim as i32,
19670 );
19671 let __s_b = self.gpu.stream();
19672 let mut b = __s_b.launch_builder(&f);
19673 b.arg(conv_outs)
19674 .arg(beta_raws)
19675 .arg(alphas)
19676 .arg(dt_bias)
19677 .arg(a)
19678 .arg(q_l2)
19679 .arg(k_l2)
19680 .arg(v_g)
19681 .arg(beta)
19682 .arg(g_log)
19683 .arg(&ds)
19684 .arg(&nv)
19685 .arg(&nk)
19686 .arg(&kd)
19687 .arg(&eps)
19688 .arg(&cd);
19689 unsafe {
19690 b.launch(cfg)?;
19691 }
19692 Ok(())
19693 }
19694
19695 #[allow(clippy::too_many_arguments)]
19696 pub fn gdn_scan_s128_batched(
19697 &self,
19698 q: &CudaSlice<f32>,
19699 k: &CudaSlice<f32>,
19700 v: &CudaSlice<f32>,
19701 g: &CudaSlice<f32>,
19702 beta: &CudaSlice<f32>,
19703 state_in_ptrs: &cudarc::driver::CudaView<u64>,
19704 state_out_ptrs: &cudarc::driver::CudaView<u64>,
19705 o: &mut CudaSlice<f32>,
19706 n_head: usize,
19707 b_n: usize,
19708 scale: f32,
19709 ) -> Result<(), Box<dyn std::error::Error>> {
19710 let f = self.func("gdn_scan_s128_b");
19711 const S_V: u32 = 128;
19712 const WARP: u32 = 32;
19713 const COLS_PER_BLOCK: u32 = 4;
19714 let cfg = LaunchConfig {
19715 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
19716 block_dim: (WARP, COLS_PER_BLOCK, 1),
19717 shared_mem_bytes: 0,
19718 };
19719 let h = n_head as i32;
19720 let __s_b = self.gpu.stream();
19721 let mut b = __s_b.launch_builder(&f);
19722 b.arg(q)
19723 .arg(k)
19724 .arg(v)
19725 .arg(g)
19726 .arg(beta)
19727 .arg(state_in_ptrs)
19728 .arg(state_out_ptrs)
19729 .arg(o)
19730 .arg(&h)
19731 .arg(&scale);
19732 unsafe {
19733 b.launch(cfg)?;
19734 }
19735 Ok(())
19736 }
19737
19738 #[allow(clippy::too_many_arguments)]
19744 pub fn ssm_conv1d_fused_decode_b_view(
19745 &self,
19746 qkv_cols: &cudarc::driver::CudaView<f32>,
19747 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
19748 w: &CudaSlice<f32>,
19749 conv_outs: &mut CudaSlice<f32>,
19750 conv_dim: usize,
19751 d_conv: usize,
19752 b_n: usize,
19753 ) -> Result<(), Box<dyn std::error::Error>> {
19754 let f = self.func("ssm_conv1d_fused_decode_b_f32");
19755 let cfg = LaunchConfig {
19756 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
19757 block_dim: (256, 1, 1),
19758 shared_mem_bytes: 0,
19759 };
19760 let (cd, dc) = (conv_dim as i32, d_conv as i32);
19761 let __s_b = self.gpu.stream();
19762 let mut b = __s_b.launch_builder(&f);
19763 b.arg(qkv_cols)
19764 .arg(conv_state_ptrs)
19765 .arg(w)
19766 .arg(conv_outs)
19767 .arg(&cd)
19768 .arg(&dc);
19769 unsafe {
19770 b.launch(cfg)?;
19771 }
19772 Ok(())
19773 }
19774
19775 #[allow(clippy::too_many_arguments)]
19776 pub fn gdn_prep_decode_b_view(
19777 &self,
19778 conv_outs: &CudaSlice<f32>,
19779 beta_raws: &cudarc::driver::CudaView<f32>,
19780 alphas: &cudarc::driver::CudaView<f32>,
19781 dt_bias: &CudaSlice<f32>,
19782 a: &CudaSlice<f32>,
19783 q_l2: &mut CudaSlice<f32>,
19784 k_l2: &mut CudaSlice<f32>,
19785 v_g: &mut CudaSlice<f32>,
19786 beta: &mut CudaSlice<f32>,
19787 g_log: &mut CudaSlice<f32>,
19788 d_state: usize,
19789 num_v: usize,
19790 num_k: usize,
19791 key_dim: usize,
19792 eps: f32,
19793 conv_dim: usize,
19794 b_n: usize,
19795 ) -> Result<(), Box<dyn std::error::Error>> {
19796 let f = self.func("gdn_prep_decode_b_f32");
19797 let cfg = LaunchConfig {
19798 grid_dim: (num_v as u32, 1, b_n as u32),
19799 block_dim: (32, 4, 1),
19800 shared_mem_bytes: 0,
19801 };
19802 let (ds, nv, nk, kd, cd) = (
19803 d_state as i32,
19804 num_v as i32,
19805 num_k as i32,
19806 key_dim as i32,
19807 conv_dim as i32,
19808 );
19809 let __s_b = self.gpu.stream();
19810 let mut b = __s_b.launch_builder(&f);
19811 b.arg(conv_outs)
19812 .arg(beta_raws)
19813 .arg(alphas)
19814 .arg(dt_bias)
19815 .arg(a)
19816 .arg(q_l2)
19817 .arg(k_l2)
19818 .arg(v_g)
19819 .arg(beta)
19820 .arg(g_log)
19821 .arg(&ds)
19822 .arg(&nv)
19823 .arg(&nk)
19824 .arg(&kd)
19825 .arg(&eps)
19826 .arg(&cd);
19827 unsafe {
19828 b.launch(cfg)?;
19829 }
19830 Ok(())
19831 }
19832
19833 #[allow(clippy::too_many_arguments)]
19834 pub fn gdn_scan_s128_batched_view(
19835 &self,
19836 q: &CudaSlice<f32>,
19837 k: &CudaSlice<f32>,
19838 v: &CudaSlice<f32>,
19839 g: &CudaSlice<f32>,
19840 beta: &CudaSlice<f32>,
19841 state_in_ptrs: &cudarc::driver::CudaView<u64>,
19842 state_out_ptrs: &cudarc::driver::CudaView<u64>,
19843 o: &mut cudarc::driver::CudaViewMut<f32>,
19844 n_head: usize,
19845 b_n: usize,
19846 scale: f32,
19847 ) -> Result<(), Box<dyn std::error::Error>> {
19848 let f = self.func("gdn_scan_s128_b");
19849 const S_V: u32 = 128;
19850 const WARP: u32 = 32;
19851 const COLS_PER_BLOCK: u32 = 4;
19852 let cfg = LaunchConfig {
19853 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
19854 block_dim: (WARP, COLS_PER_BLOCK, 1),
19855 shared_mem_bytes: 0,
19856 };
19857 let h = n_head as i32;
19858 let __s_b = self.gpu.stream();
19859 let mut b = __s_b.launch_builder(&f);
19860 b.arg(q)
19861 .arg(k)
19862 .arg(v)
19863 .arg(g)
19864 .arg(beta)
19865 .arg(state_in_ptrs)
19866 .arg(state_out_ptrs)
19867 .arg(o)
19868 .arg(&h)
19869 .arg(&scale);
19870 unsafe {
19871 b.launch(cfg)?;
19872 }
19873 Ok(())
19874 }
19875
19876 pub fn gdn_chunked_enabled() -> bool {
19885 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19886 *E.get_or_init(|| {
19887 std::env::var("MEMRA_GDN_CHUNKED")
19888 .map(|v| v != "0")
19889 .unwrap_or(true)
19890 })
19891 }
19892
19893 pub fn gdn_chunk_size() -> usize {
19898 static C: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
19899 *C.get_or_init(|| {
19900 let c: usize = std::env::var("MEMRA_GDN_CHUNK")
19901 .ok()
19902 .and_then(|v| v.parse().ok())
19903 .unwrap_or(32);
19904 c.clamp(32, 128) / 32 * 32
19905 })
19906 }
19907
19908 #[allow(clippy::too_many_arguments)]
19913 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
19916 #[allow(clippy::too_many_arguments)]
19917 pub fn gdn_chunk_k123(
19918 &self,
19919 q: &CudaSlice<f32>,
19920 k: &CudaSlice<f32>,
19921 v: &CudaSlice<f32>,
19922 g: &CudaSlice<f32>,
19923 beta: &CudaSlice<f32>,
19924 wb16: Option<&mut CudaSlice<u8>>,
19925 n_head: usize,
19926 t: usize,
19927 c: usize,
19928 hk: usize,
19929 k2w: Option<(&CudaSlice<u8>, &CudaSlice<u8>, &mut CudaSlice<u8>)>,
19930 ) -> Result<
19931 (
19932 CudaSlice<f32>,
19933 CudaSlice<f32>,
19934 CudaSlice<f32>,
19935 CudaSlice<f32>,
19936 ),
19937 Box<dyn std::error::Error>,
19938 > {
19939 const D: usize = 128;
19940 let h = n_head;
19941 let nc = (t + c - 1) / c;
19942 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
19943 let mut gcum = self.uninit(t * h)?;
19944 let mut a = self.uninit(nc * h * c * c)?;
19945 let mut p = self.uninit(nc * h * c * c)?;
19946 let mut u = self.uninit(nc * h * c * D)?;
19947 let mut w = self.uninit(nc * h * c * D)?;
19948 {
19949 let f = self.func("gdn_chunk_cumgate_f32");
19951 let cfg = LaunchConfig {
19952 grid_dim: (nc as u32, h as u32, 1),
19953 block_dim: (32, 1, 1),
19954 shared_mem_bytes: 0,
19955 };
19956 let __s_b = self.gpu.stream();
19957 let mut b = __s_b.launch_builder(&f);
19958 b.arg(g).arg(&mut gcum).arg(&hi).arg(&ti).arg(&ci);
19959 unsafe {
19960 b.launch(cfg)?;
19961 }
19962 }
19963 if let Some((qb, kb, pb)) = k2w {
19964 assert!(c == 32, "gdn_k2_wgmma is a C==32 tile");
19967 let f = self.func("gdn_k2_wgmma");
19968 let cfg = LaunchConfig {
19969 grid_dim: (nc as u32, h as u32, 1),
19970 block_dim: (128, 1, 1),
19971 shared_mem_bytes: 0,
19972 };
19973 let hki = hk as i32;
19974 let __s_b = self.gpu.stream();
19975 let mut b = __s_b.launch_builder(&f);
19976 b.arg(qb)
19977 .arg(kb)
19978 .arg(&gcum)
19979 .arg(beta)
19980 .arg(&mut a)
19981 .arg(&mut *pb)
19982 .arg(&hi)
19983 .arg(&ti)
19984 .arg(&ci)
19985 .arg(&hki);
19986 unsafe {
19987 b.launch(cfg)?;
19988 }
19989 } else if c <= 64 && !portable_mma_gated() {
19990 let f = self.func("gdn_chunk_attn_f32");
19992 let jt = ((c + 31) / 32) as u32;
19993 let cfg = LaunchConfig {
19994 grid_dim: (nc as u32, h as u32, jt),
19995 block_dim: (256, 1, 1),
19996 shared_mem_bytes: 0,
19997 };
19998 let hki = hk as i32;
19999 let __s_b = self.gpu.stream();
20000 let mut b = __s_b.launch_builder(&f);
20001 b.arg(q)
20002 .arg(k)
20003 .arg(&gcum)
20004 .arg(beta)
20005 .arg(&mut a)
20006 .arg(&mut p)
20007 .arg(&hi)
20008 .arg(&ti)
20009 .arg(&ci)
20010 .arg(&hki);
20011 unsafe {
20012 b.launch(cfg)?;
20013 }
20014 } else {
20015 assert!(
20017 hk == h,
20018 "generic K2 is broadcast-only (de-broadcast rides C==32)"
20019 );
20020 let f = self.func("gdn_chunk_attn_g_f32");
20021 let cfg = LaunchConfig {
20022 grid_dim: (nc as u32, h as u32, 1),
20023 block_dim: (32, 8, 1),
20024 shared_mem_bytes: 0,
20025 };
20026 let __s_b = self.gpu.stream();
20027 let mut b = __s_b.launch_builder(&f);
20028 b.arg(q)
20029 .arg(k)
20030 .arg(&gcum)
20031 .arg(beta)
20032 .arg(&mut a)
20033 .arg(&mut p)
20034 .arg(&hi)
20035 .arg(&ti)
20036 .arg(&ci);
20037 unsafe {
20038 b.launch(cfg)?;
20039 }
20040 }
20041 {
20042 let cfg = LaunchConfig {
20044 grid_dim: (nc as u32, h as u32, 1),
20045 block_dim: (256, 1, 1),
20046 shared_mem_bytes: 0,
20047 };
20048 match c {
20049 32 | 64 => {
20050 let f = self.func(if c == 32 {
20051 "gdn_chunk_solve32_f32"
20052 } else {
20053 "gdn_chunk_solve64_f32"
20054 });
20055 let wb: u64 = match wb16 {
20057 Some(d) => self.addr_u8(d),
20058 None => 0,
20059 };
20060 let hki = hk as i32;
20061 let __s_b = self.gpu.stream();
20062 let mut b = __s_b.launch_builder(&f);
20063 b.arg(v)
20064 .arg(k)
20065 .arg(&a)
20066 .arg(&gcum)
20067 .arg(&mut u)
20068 .arg(&mut w)
20069 .arg(&wb)
20070 .arg(&hi)
20071 .arg(&ti)
20072 .arg(&hki);
20073 unsafe {
20074 b.launch(cfg)?;
20075 }
20076 }
20077 _ => {
20078 assert!(hk == h, "generic K3 is broadcast-only");
20079 let f = self.func("gdn_chunk_solve_f32");
20080 let __s_b = self.gpu.stream();
20081 let mut b = __s_b.launch_builder(&f);
20082 b.arg(v)
20083 .arg(k)
20084 .arg(&a)
20085 .arg(&gcum)
20086 .arg(&mut u)
20087 .arg(&mut w)
20088 .arg(&hi)
20089 .arg(&ti)
20090 .arg(&ci);
20091 unsafe {
20092 b.launch(cfg)?;
20093 }
20094 }
20095 }
20096 }
20097 Ok((gcum, p, u, w))
20098 }
20099
20100 pub fn gdn_db_on() -> bool {
20104 std::env::var("MEMRA_GDN_DB").as_deref() != Ok("0")
20105 }
20106
20107 pub fn gdn_mma_enabled(&self, c: usize) -> bool {
20110 !portable_mma_gated()
20111 && c == 32
20112 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
20113 Ok("1") => true,
20114 Ok("0") => false,
20115 _ => cfg!(memra_hopper_mma),
20116 }
20117 }
20118
20119 pub fn gdn_wgmma_on(&self, c: usize) -> bool {
20122 self.gdn_mma_enabled(c)
20123 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
20124 Ok("0") => false,
20125 Ok("1") => true,
20126 _ => cfg!(memra_hopper_mma),
20127 }
20128 }
20129
20130 #[allow(clippy::too_many_arguments)]
20135 pub fn ssm_conv1d_gdn_state_pad(
20136 &self,
20137 qkv_tm: &cudarc::driver::CudaView<f32>,
20138 conv_state: &mut CudaSlice<f32>,
20139 w: &CudaSlice<f32>,
20140 q_g: &mut CudaSlice<f32>,
20141 k_g: &mut CudaSlice<f32>,
20142 v_g: &mut CudaSlice<f32>,
20143 conv_dim: usize,
20144 t: usize,
20145 d_conv: usize,
20146 d_state: usize,
20147 num_v: usize,
20148 num_k: usize,
20149 key_dim: usize,
20150 hk: usize,
20151 pad_len: Option<&CudaSlice<i32>>,
20152 ) -> Result<(), Box<dyn std::error::Error>> {
20153 assert!(
20154 t >= d_conv - 1,
20155 "fused state conv requires T >= pad (PRIME_MIN_T gates)"
20156 );
20157 {
20158 let f = self.func("ssm_conv1d_gdn_state_f32");
20159 let cfg = LaunchConfig {
20160 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
20161 block_dim: (256, 1, 1),
20162 shared_mem_bytes: 0,
20163 };
20164 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20165 let (ds, nv, nk, kd, hki) = (
20166 d_state as i32,
20167 num_v as i32,
20168 num_k as i32,
20169 key_dim as i32,
20170 hk as i32,
20171 );
20172 let __s_b = self.gpu.stream();
20173 let mut b = __s_b.launch_builder(&f);
20174 b.arg(qkv_tm)
20175 .arg(&*conv_state)
20176 .arg(w)
20177 .arg(q_g)
20178 .arg(k_g)
20179 .arg(v_g)
20180 .arg(&cd)
20181 .arg(&ti)
20182 .arg(&dc)
20183 .arg(&ds)
20184 .arg(&nv)
20185 .arg(&nk)
20186 .arg(&kd)
20187 .arg(&hki);
20188 unsafe {
20189 b.launch(cfg)?;
20190 }
20191 }
20192 match pad_len {
20193 Some(len_d) => {
20194 let f = self.func("ssm_conv_ring_update_dev_f32");
20195 let n = conv_dim * (d_conv - 1);
20196 let cfg = LaunchConfig::for_num_elems(n as u32);
20197 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20198 let __s_b = self.gpu.stream();
20199 let mut b = __s_b.launch_builder(&f);
20200 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
20201 unsafe {
20202 b.launch(cfg)?;
20203 }
20204 }
20205 None => {
20206 let f = self.func("ssm_conv_ring_update_f32");
20207 let n = conv_dim * (d_conv - 1);
20208 let cfg = LaunchConfig::for_num_elems(n as u32);
20209 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20210 let __s_b = self.gpu.stream();
20211 let mut b = __s_b.launch_builder(&f);
20212 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
20213 unsafe {
20214 b.launch(cfg)?;
20215 }
20216 }
20217 }
20218 Ok(())
20219 }
20220
20221 pub fn gdn_chunk_alloc(
20225 &self,
20226 n_head: usize,
20227 t: usize,
20228 c: usize,
20229 hk: usize,
20230 ) -> Result<GdnChunkBufs, Box<dyn std::error::Error>> {
20231 const D: usize = 128;
20232 assert!(
20233 c == 32,
20234 "gdn_chunk_alloc: varlen chain is the C==32 mma pair"
20235 );
20236 let h = n_head;
20237 let nc = (t + c - 1) / c;
20238 Ok(GdnChunkBufs {
20239 gcum: self.uninit(t * h)?,
20240 a: self.uninit(nc * h * c * c)?,
20241 p: self.uninit(nc * h * c * c)?,
20242 u: self.uninit(nc * h * c * D)?,
20243 w: self.uninit(nc * h * c * D)?,
20244 kb16: self.alloc_u8_uninit(t * hk * D * 2)?,
20245 wb16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
20246 y16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
20247 ssnap16: self.alloc_u8_uninit(nc * h * D * D * 2)?,
20248 qb16: self.alloc_u8_uninit(t * hk * D * 2)?,
20249 pb16: self.alloc_u8_uninit(nc * h * c * c * 2)?,
20250 o: self.uninit(D * h * t)?,
20251 t,
20252 nc,
20253 })
20254 }
20255
20256 pub fn f32_to_bf16_v(
20258 &self,
20259 x: &cudarc::driver::CudaView<f32>,
20260 dst: &mut CudaSlice<u8>,
20261 n: usize,
20262 ) -> Result<(), Box<dyn std::error::Error>> {
20263 let f = self.func("f32_to_bf16_bulk");
20264 let ni = n as i64;
20265 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
20266 let __s_b = self.gpu.stream();
20267 let mut b = __s_b.launch_builder(&f);
20268 b.arg(x).arg(dst).arg(&ni);
20269 unsafe {
20270 b.launch(cfg)?;
20271 }
20272 Ok(())
20273 }
20274
20275 pub fn f32_to_bf16_into(
20277 &self,
20278 x: &CudaSlice<f32>,
20279 dst: &mut CudaSlice<u8>,
20280 n: usize,
20281 ) -> Result<(), Box<dyn std::error::Error>> {
20282 let f = self.func("f32_to_bf16_bulk");
20283 let ni = n as i64;
20284 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
20285 let __s_b = self.gpu.stream();
20286 let mut b = __s_b.launch_builder(&f);
20287 b.arg(x).arg(dst).arg(&ni);
20288 unsafe {
20289 b.launch(cfg)?;
20290 }
20291 Ok(())
20292 }
20293
20294 pub fn gdn_chunk_k123_vl8(
20297 &self,
20298 seqs: &[GdnSeqVl],
20299 n_head: usize,
20300 hk: usize,
20301 wq: Option<&GdnWVl8>,
20302 ) -> Result<(), Box<dyn std::error::Error>> {
20303 let b = seqs.len();
20304 assert!(b >= 1 && b <= 8, "gdn_chunk_k123_vl8: 1..=8 sequences");
20305 let mut packed = [GdnSeqVl::default(); 8];
20306 packed[..b].copy_from_slice(seqs);
20307 let v = GdnVl8(packed);
20308 let (hi, ci) = (n_head as i32, 32i32);
20309 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
20310 {
20311 let f = self.func("gdn_chunk_cumgate_vl");
20312 let cfg = LaunchConfig {
20313 grid_dim: (max_nc, n_head as u32, b as u32),
20314 block_dim: (32, 1, 1),
20315 shared_mem_bytes: 0,
20316 };
20317 let __s_lb = self.gpu.stream();
20318 let mut lb = __s_lb.launch_builder(&f);
20319 lb.arg(&v).arg(&hi).arg(&ci);
20320 unsafe {
20321 lb.launch(cfg)?;
20322 }
20323 }
20324 let hki = hk as i32;
20325 if let Some(w) = wq {
20326 let f = self.func("gdn_k2_wgmma_vl");
20328 let cfg = LaunchConfig {
20329 grid_dim: (max_nc, n_head as u32, b as u32),
20330 block_dim: (128, 1, 1),
20331 shared_mem_bytes: 0,
20332 };
20333 let __s_lb = self.gpu.stream();
20334 let mut lb = __s_lb.launch_builder(&f);
20335 lb.arg(&v).arg(w).arg(&hi).arg(&ci).arg(&hki);
20336 unsafe {
20337 lb.launch(cfg)?;
20338 }
20339 } else {
20340 let f = self.func("gdn_chunk_attn_vl");
20341 let cfg = LaunchConfig {
20342 grid_dim: (max_nc, n_head as u32, b as u32),
20343 block_dim: (256, 1, 1),
20344 shared_mem_bytes: 0,
20345 };
20346 let __s_lb = self.gpu.stream();
20347 let mut lb = __s_lb.launch_builder(&f);
20348 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
20349 unsafe {
20350 lb.launch(cfg)?;
20351 }
20352 }
20353 {
20354 let f = self.func("gdn_chunk_solve32_vl");
20355 let cfg = LaunchConfig {
20356 grid_dim: (max_nc, n_head as u32, b as u32),
20357 block_dim: (256, 1, 1),
20358 shared_mem_bytes: 0,
20359 };
20360 let __s_lb = self.gpu.stream();
20361 let mut lb = __s_lb.launch_builder(&f);
20362 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
20363 unsafe {
20364 lb.launch(cfg)?;
20365 }
20366 }
20367 Ok(())
20368 }
20369
20370 #[allow(clippy::too_many_arguments)]
20374 pub fn gdn_prep_vl8(
20375 &self,
20376 seqs: &[GdnPrepVl],
20377 conv_w: &CudaSlice<f32>,
20378 dt_bias: &CudaSlice<f32>,
20379 a: &CudaSlice<f32>,
20380 conv_dim: usize,
20381 d_conv: usize,
20382 d_state: usize,
20383 num_v: usize,
20384 num_k: usize,
20385 key_dim: usize,
20386 hk: usize,
20387 eps: f32,
20388 ) -> Result<(), Box<dyn std::error::Error>> {
20389 let b = seqs.len();
20390 assert!(b >= 1 && b <= 8);
20391 let mut packed = [GdnPrepVl::default(); 8];
20392 packed[..b].copy_from_slice(seqs);
20393 let v = GdnPrepVl8(packed);
20394 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
20395 let (cdi, dci) = (conv_dim as i32, d_conv as i32);
20396 let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
20397 assert!(
20398 conv_fuse || hk == num_v,
20399 "de-broadcast requires the fused conv"
20400 );
20401 if conv_fuse {
20402 let f = self.func("ssm_conv1d_gdn_state_vl");
20403 let cfg = LaunchConfig {
20404 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
20405 block_dim: (256, 1, 1),
20406 shared_mem_bytes: 0,
20407 };
20408 let (dsi, nvi, nki, kdi, hki) = (
20409 d_state as i32,
20410 num_v as i32,
20411 num_k as i32,
20412 key_dim as i32,
20413 hk as i32,
20414 );
20415 let __s_lb = self.gpu.stream();
20416 let mut lb = __s_lb.launch_builder(&f);
20417 lb.arg(&v)
20418 .arg(conv_w)
20419 .arg(&cdi)
20420 .arg(&dci)
20421 .arg(&dsi)
20422 .arg(&nvi)
20423 .arg(&nki)
20424 .arg(&kdi)
20425 .arg(&hki);
20426 unsafe {
20427 lb.launch(cfg)?;
20428 }
20429 } else {
20430 let f = self.func("ssm_conv1d_tm_state_vl");
20431 let cfg = LaunchConfig {
20432 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
20433 block_dim: (256, 1, 1),
20434 shared_mem_bytes: 0,
20435 };
20436 let __s_lb = self.gpu.stream();
20437 let mut lb = __s_lb.launch_builder(&f);
20438 lb.arg(&v).arg(conv_w).arg(&cdi).arg(&dci);
20439 unsafe {
20440 lb.launch(cfg)?;
20441 }
20442 }
20443 {
20444 let f = self.func("ssm_conv_ring_update_vl");
20445 let n = (conv_dim * (d_conv - 1)) as u32;
20446 let cfg = LaunchConfig {
20447 grid_dim: (n.div_ceil(256), 1, b as u32),
20448 block_dim: (256, 1, 1),
20449 shared_mem_bytes: 0,
20450 };
20451 let __s_lb = self.gpu.stream();
20452 let mut lb = __s_lb.launch_builder(&f);
20453 lb.arg(&v).arg(&cdi).arg(&dci);
20454 unsafe {
20455 lb.launch(cfg)?;
20456 }
20457 }
20458 if !conv_fuse {
20459 let f = self.func("qkv_to_gdn_repack_vl");
20460 let n = max_t * (num_v * d_state) as u32;
20461 let cfg = LaunchConfig {
20462 grid_dim: (n.div_ceil(256), 1, b as u32),
20463 block_dim: (256, 1, 1),
20464 shared_mem_bytes: 0,
20465 };
20466 let (dsi, nvi, nki, kdi) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
20467 let __s_lb = self.gpu.stream();
20468 let mut lb = __s_lb.launch_builder(&f);
20469 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&nki).arg(&kdi);
20470 unsafe {
20471 lb.launch(cfg)?;
20472 }
20473 }
20474 if Self::l2_v2_on(d_state) {
20475 let f = self.func("gdn_l2_v2_vl");
20476 let cfg = LaunchConfig {
20477 grid_dim: ((max_t * hk as u32).div_ceil(8), 2, b as u32),
20478 block_dim: (256, 1, 1),
20479 shared_mem_bytes: 0,
20480 };
20481 let (dsi, nvi) = (d_state as i32, hk as i32);
20482 let __s_lb = self.gpu.stream();
20483 let mut lb = __s_lb.launch_builder(&f);
20484 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
20485 unsafe {
20486 lb.launch(cfg)?;
20487 }
20488 } else {
20489 let f = self.func("gdn_l2_vl");
20490 let cfg = LaunchConfig {
20491 grid_dim: (max_t * hk as u32, 2, b as u32),
20492 block_dim: (256, 1, 1),
20493 shared_mem_bytes: 0,
20494 };
20495 let (dsi, nvi) = (d_state as i32, hk as i32);
20496 let __s_lb = self.gpu.stream();
20497 let mut lb = __s_lb.launch_builder(&f);
20498 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
20499 unsafe {
20500 lb.launch(cfg)?;
20501 }
20502 }
20503 {
20504 let f = self.func("gdn_gate_prep_vl");
20505 let n = max_t * num_v as u32;
20506 let cfg = LaunchConfig {
20507 grid_dim: (n.div_ceil(256), 1, b as u32),
20508 block_dim: (256, 1, 1),
20509 shared_mem_bytes: 0,
20510 };
20511 let nvi = num_v as i32;
20512 let __s_lb = self.gpu.stream();
20513 let mut lb = __s_lb.launch_builder(&f);
20514 lb.arg(&v).arg(dt_bias).arg(a).arg(&nvi);
20515 unsafe {
20516 lb.launch(cfg)?;
20517 }
20518 }
20519 Ok(())
20520 }
20521
20522 pub fn gdn_mirror_vl8(
20524 &self,
20525 seqs: &[GdnSeqVl],
20526 n_head: usize,
20527 which: i32,
20528 hk: usize,
20529 ) -> Result<(), Box<dyn std::error::Error>> {
20530 let b = seqs.len();
20531 assert!(b >= 1 && b <= 8);
20532 let mut packed = [GdnSeqVl::default(); 8];
20533 packed[..b].copy_from_slice(seqs);
20534 let v = GdnVl8(packed);
20535 let ept = (if which == 0 { hk } else { n_head } * 128) as i32;
20536 let max_n = seqs
20537 .iter()
20538 .map(|s| {
20539 if which == 0 {
20540 s.t as i64 * ept as i64
20541 } else {
20542 s.nc as i64 * ept as i64 * 32
20543 }
20544 })
20545 .max()
20546 .unwrap();
20547 let f = self.func("gdn_mirror_vl");
20548 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
20549 let cfg = LaunchConfig {
20550 grid_dim: (blocks, 1, b as u32),
20551 block_dim: (256, 1, 1),
20552 shared_mem_bytes: 0,
20553 };
20554 let __s_lb = self.gpu.stream();
20555 let mut lb = __s_lb.launch_builder(&f);
20556 lb.arg(&v).arg(&ept).arg(&which);
20557 unsafe {
20558 lb.launch(cfg)?;
20559 }
20560 Ok(())
20561 }
20562
20563 pub fn gdn_tail_vl8(
20565 &self,
20566 seqs: &[GdnPrepVl],
20567 norm_w: &CudaSlice<f32>,
20568 d_state: usize,
20569 num_v: usize,
20570 eps: f32,
20571 ) -> Result<(), Box<dyn std::error::Error>> {
20572 let b = seqs.len();
20573 assert!(b >= 1 && b <= 8);
20574 let mut packed = [GdnPrepVl::default(); 8];
20575 packed[..b].copy_from_slice(seqs);
20576 let v = GdnPrepVl8(packed);
20577 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
20578 let f = self.func("gated_rmsnorm_f16out_vl");
20579 let cfg = LaunchConfig {
20581 grid_dim: (max_t * num_v as u32, 1, b as u32),
20582 block_dim: (128, 1, 1),
20583 shared_mem_bytes: 0,
20584 };
20585 let (dsi, nvi) = (d_state as i32, num_v as i32);
20586 let __s_lb = self.gpu.stream();
20587 let mut lb = __s_lb.launch_builder(&f);
20588 lb.arg(&v).arg(norm_w).arg(&dsi).arg(&nvi).arg(&eps);
20589 unsafe {
20590 lb.launch(cfg)?;
20591 }
20592 Ok(())
20593 }
20594
20595 pub fn addr_f32(&self, x: &CudaSlice<f32>) -> u64 {
20598 use cudarc::driver::DevicePtr;
20599 let s = self.gpu.stream();
20600 let (p, _g) = x.device_ptr(&s);
20601 p as u64
20602 }
20603 pub fn addr_f32_mut(&self, x: &mut CudaSlice<f32>) -> u64 {
20604 use cudarc::driver::DevicePtrMut;
20605 let s = self.gpu.stream();
20606 let (p, _g) = x.device_ptr_mut(&s);
20607 p as u64
20608 }
20609 pub fn addr_f32v(&self, x: &cudarc::driver::CudaView<f32>) -> u64 {
20610 use cudarc::driver::DevicePtr;
20611 let s = self.gpu.stream();
20612 let (p, _g) = x.device_ptr(&s);
20613 p as u64
20614 }
20615 pub fn addr_u8(&self, x: &CudaSlice<u8>) -> u64 {
20616 use cudarc::driver::DevicePtr;
20617 let s = self.gpu.stream();
20618 let (p, _g) = x.device_ptr(&s);
20619 p as u64
20620 }
20621
20622 pub fn gdn_chunk_vl8(
20626 &self,
20627 seqs: &[GdnSeqVl],
20628 n_head: usize,
20629 scale: f32,
20630 hk: usize,
20631 wq: Option<&GdnWVl8>,
20632 ) -> Result<(), Box<dyn std::error::Error>> {
20633 const NSPLIT: u32 = 4;
20634 let b = seqs.len();
20635 assert!(b >= 1 && b <= 8, "gdn_chunk_vl8: 1..=8 sequences");
20636 let mut packed = [GdnSeqVl::default(); 8];
20637 packed[..b].copy_from_slice(seqs);
20638 let v = GdnVl8(packed);
20639 let (hi, ci) = (n_head as i32, 32i32);
20640 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
20641 let hki = hk as i32;
20642 if let Some(w) = wq {
20643 let f = self.func("gdn_k45_wgmma_vl");
20645 let cfg = LaunchConfig {
20646 grid_dim: (n_head as u32, NSPLIT, b as u32),
20647 block_dim: (256, 1, 1),
20648 shared_mem_bytes: 0,
20649 };
20650 let __s_lb = self.gpu.stream();
20651 let mut lb = __s_lb.launch_builder(&f);
20652 lb.arg(&v).arg(w).arg(&scale).arg(&hi).arg(&ci).arg(&hki);
20653 unsafe {
20654 lb.launch(cfg)?;
20655 }
20656 let _ = max_nc;
20657 return Ok(());
20658 }
20659 {
20660 let f = self.func("gdn_chunk_state_mma_vl");
20661 let cfg = LaunchConfig {
20662 grid_dim: (n_head as u32, NSPLIT, b as u32),
20663 block_dim: (256, 1, 1),
20664 shared_mem_bytes: 0,
20665 };
20666 let __s_lb = self.gpu.stream();
20667 let mut lb = __s_lb.launch_builder(&f);
20668 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
20669 unsafe {
20670 lb.launch(cfg)?;
20671 }
20672 }
20673 {
20674 let f = self.func("gdn_chunk_output_mma_vl");
20675 let cfg = LaunchConfig {
20676 grid_dim: (max_nc, n_head as u32, b as u32),
20677 block_dim: (256, 1, 1),
20678 shared_mem_bytes: 0,
20679 };
20680 let __s_lb = self.gpu.stream();
20681 let mut lb = __s_lb.launch_builder(&f);
20682 lb.arg(&v).arg(&hi).arg(&ci).arg(&scale).arg(&hki);
20683 unsafe {
20684 lb.launch(cfg)?;
20685 }
20686 }
20687 Ok(())
20688 }
20689 pub fn gdn_scan_chunked(
20690 &self,
20691 q: &CudaSlice<f32>,
20692 k: &CudaSlice<f32>,
20693 v: &CudaSlice<f32>,
20694 g: &CudaSlice<f32>,
20695 beta: &CudaSlice<f32>,
20696 kb16_pre: Option<&CudaSlice<u8>>,
20697 qb16_pre: Option<&CudaSlice<u8>>,
20698 state_in: &CudaSlice<f32>,
20699 state_out: &mut CudaSlice<f32>,
20700 o: &mut CudaSlice<f32>,
20701 n_head: usize,
20702 t: usize,
20703 scale: f32,
20704 c: usize,
20705 hk: usize,
20706 ) -> Result<(), Box<dyn std::error::Error>> {
20707 const D: usize = 128;
20708 const NSPLIT: u32 = 4;
20709 assert!(c >= 1 && c <= 128, "gdn_scan_chunked: C must be in 1..=128");
20710 let h = n_head;
20711 let nc = (t + c - 1) / c;
20712 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
20713 let gdn_mma_pre = !portable_mma_gated()
20717 && c == 32
20718 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
20719 Ok("1") => true,
20720 Ok("0") => false,
20721 _ => cfg!(memra_hopper_mma),
20722 };
20723 let mut wb16_pre: Option<CudaSlice<u8>> = if gdn_mma_pre {
20724 Some(self.alloc_u8_uninit(nc * h * c * D * 2)?)
20725 } else {
20726 None
20727 };
20728 let gdn_wgmma_pre = gdn_mma_pre
20732 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
20733 Ok("0") => false,
20734 Ok("1") => true,
20735 _ => cfg!(memra_hopper_mma),
20736 };
20737 let nk = t * hk * D;
20738 let mut kb16_local: Option<CudaSlice<u8>> = None;
20739 if gdn_mma_pre && kb16_pre.is_none() {
20740 let mut kb = self.alloc_u8_uninit(nk * 2)?;
20741 let f = self.func("f32_to_bf16_bulk");
20742 let n2 = nk as i64;
20743 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
20744 let __s_b = self.gpu.stream();
20745 let mut b = __s_b.launch_builder(&f);
20746 b.arg(k).arg(&mut kb).arg(&n2);
20747 unsafe {
20748 b.launch(cfg2)?;
20749 }
20750 kb16_local = Some(kb);
20751 }
20752 let kb16_ref0: Option<&CudaSlice<u8>> = kb16_local.as_ref().or(kb16_pre);
20753 if let Some(kb) = kb16_pre {
20754 assert!(kb.len() >= nk * 2, "kb16_pre too small");
20755 }
20756 let mut qb16: Option<CudaSlice<u8>> = None;
20757 let mut pb16: Option<CudaSlice<u8>> = None;
20758 if gdn_wgmma_pre {
20759 if qb16_pre.is_none() {
20762 let mut qb = self.alloc_u8_uninit(nk * 2)?;
20763 let f = self.func("f32_to_bf16_bulk");
20764 let n2 = nk as i64;
20765 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
20766 let __s_b = self.gpu.stream();
20767 let mut b = __s_b.launch_builder(&f);
20768 b.arg(q).arg(&mut qb).arg(&n2);
20769 unsafe {
20770 b.launch(cfg2)?;
20771 }
20772 qb16 = Some(qb);
20773 } else if let Some(qb) = qb16_pre {
20774 assert!(qb.len() >= nk * 2, "qb16_pre too small");
20775 }
20776 pb16 = Some(self.alloc_u8_uninit(nc * h * c * c * 2)?);
20777 }
20778 let qb16_ref0: Option<&CudaSlice<u8>> = qb16.as_ref().or(qb16_pre);
20779 let k2w = if gdn_wgmma_pre {
20780 Some((
20781 *qb16_ref0.as_ref().unwrap(),
20782 *kb16_ref0.as_ref().unwrap(),
20783 pb16.as_mut().unwrap(),
20784 ))
20785 } else {
20786 None
20787 };
20788 let (gcum, p, u, w) =
20789 self.gdn_chunk_k123(q, k, v, g, beta, wb16_pre.as_mut(), n_head, t, c, hk, k2w)?;
20790 let _ = &w;
20791 let mut y = self.uninit(nc * h * c * D)?;
20792 let mut ssnap = self.uninit(nc * h * D * D)?; let gdn_mma = !portable_mma_gated()
20804 && c == 32
20805 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
20806 Ok("1") => true,
20807 Ok("0") => false,
20808 _ => cfg!(memra_hopper_mma),
20809 };
20810 if gdn_mma {
20811 let wb16 = wb16_pre
20812 .take()
20813 .expect("mma path pre-allocates wb16 (K3 store fold)");
20814 let kb16_ref: &CudaSlice<u8> = kb16_ref0.expect("mma path pre-builds kb16 above K123");
20815 if gdn_wgmma_pre {
20827 let qb16 = qb16_ref0.unwrap();
20829 let pb16 = pb16.as_ref().unwrap();
20830 {
20831 let f = self.func("gdn_k45_wgmma");
20832 let cfg = LaunchConfig {
20833 grid_dim: (h as u32, 4, 1),
20834 block_dim: (256, 1, 1),
20835 shared_mem_bytes: 0,
20836 };
20837 let hki = hk as i32;
20838 let __s_b = self.gpu.stream();
20839 let mut b = __s_b.launch_builder(&f);
20840 b.arg(kb16_ref)
20841 .arg(&gcum)
20842 .arg(beta)
20843 .arg(&u)
20844 .arg(&wb16)
20845 .arg(qb16)
20846 .arg(pb16)
20847 .arg(o)
20848 .arg(&scale)
20849 .arg(state_in)
20850 .arg(&mut *state_out)
20851 .arg(&hi)
20852 .arg(&ti)
20853 .arg(&ci)
20854 .arg(&hki);
20855 unsafe {
20856 b.launch(cfg)?;
20857 }
20858 }
20859 return Ok(());
20860 }
20861 let mut y16 = self.alloc_u8_uninit(nc * h * c * D * 2)?;
20865 let mut ssnap16 = self.alloc_u8_uninit(nc * h * D * D * 2)?;
20866 {
20867 let f = self.func("gdn_chunk_state_mma");
20868 let cfg = LaunchConfig {
20869 grid_dim: (h as u32, NSPLIT, 1),
20870 block_dim: (256, 1, 1),
20871 shared_mem_bytes: 0,
20872 };
20873 let hki = hk as i32;
20874 let __s_b = self.gpu.stream();
20875 let mut b = __s_b.launch_builder(&f);
20876 b.arg(kb16_ref)
20877 .arg(&gcum)
20878 .arg(beta)
20879 .arg(&u)
20880 .arg(&wb16)
20881 .arg(&mut y16)
20882 .arg(&mut ssnap16)
20883 .arg(state_in)
20884 .arg(&mut *state_out)
20885 .arg(&hi)
20886 .arg(&ti)
20887 .arg(&ci)
20888 .arg(&hki);
20889 unsafe {
20890 b.launch(cfg)?;
20891 }
20892 }
20893 {
20894 let f = self.func("gdn_chunk_output_mma");
20896 let jt = ((c + 31) / 32) as u32;
20897 let cfg = LaunchConfig {
20898 grid_dim: (nc as u32, h as u32, jt),
20899 block_dim: (256, 1, 1),
20900 shared_mem_bytes: 0,
20901 };
20902 let hki = hk as i32;
20903 let __s_b = self.gpu.stream();
20904 let mut b = __s_b.launch_builder(&f);
20905 b.arg(q)
20906 .arg(&gcum)
20907 .arg(&p)
20908 .arg(&y16)
20909 .arg(&ssnap16)
20910 .arg(o)
20911 .arg(&hi)
20912 .arg(&ti)
20913 .arg(&ci)
20914 .arg(&scale)
20915 .arg(&hki);
20916 unsafe {
20917 b.launch(cfg)?;
20918 }
20919 }
20920 return Ok(());
20921 }
20922 {
20923 let f = self.func("gdn_chunk_state_f32");
20925 let cfg = LaunchConfig {
20926 grid_dim: (h as u32, NSPLIT, 1),
20927 block_dim: (256, 1, 1),
20928 shared_mem_bytes: 0,
20929 };
20930 let __s_b = self.gpu.stream();
20931 let mut b = __s_b.launch_builder(&f);
20932 b.arg(k)
20933 .arg(&gcum)
20934 .arg(beta)
20935 .arg(&u)
20936 .arg(&w)
20937 .arg(&mut y)
20938 .arg(&mut ssnap)
20939 .arg(state_in)
20940 .arg(&mut *state_out)
20941 .arg(&hi)
20942 .arg(&ti)
20943 .arg(&ci);
20944 unsafe {
20945 b.launch(cfg)?;
20946 }
20947 }
20948 {
20949 let f = self.func("gdn_chunk_output_f32");
20951 let jt = ((c + 31) / 32) as u32;
20952 let cfg = LaunchConfig {
20953 grid_dim: (nc as u32, h as u32, jt),
20954 block_dim: (256, 1, 1),
20955 shared_mem_bytes: 0,
20956 };
20957 let __s_b = self.gpu.stream();
20958 let mut b = __s_b.launch_builder(&f);
20959 b.arg(q)
20960 .arg(&gcum)
20961 .arg(&p)
20962 .arg(&y)
20963 .arg(&ssnap)
20964 .arg(o)
20965 .arg(&hi)
20966 .arg(&ti)
20967 .arg(&ci)
20968 .arg(&scale);
20969 unsafe {
20970 b.launch(cfg)?;
20971 }
20972 }
20973 Ok(())
20974 }
20975
20976 #[allow(clippy::too_many_arguments)]
20985 #[allow(clippy::too_many_arguments)]
20986 pub fn gdn_scan_prefill(
20987 &self,
20988 q: &CudaSlice<f32>,
20989 k: &CudaSlice<f32>,
20990 v: &CudaSlice<f32>,
20991 g: &CudaSlice<f32>,
20992 beta: &CudaSlice<f32>,
20993 kb16_pre: Option<&CudaSlice<u8>>,
20994 qb16_pre: Option<&CudaSlice<u8>>,
20995 state_in: &CudaSlice<f32>,
20996 state_out: &mut CudaSlice<f32>,
20997 o: &mut CudaSlice<f32>,
20998 n_head: usize,
20999 t: usize,
21000 scale: f32,
21001 hk: usize,
21002 ) -> Result<(), Box<dyn std::error::Error>> {
21003 if std::env::var("MEMRA_GDN_DIFF").is_ok() && t >= 16 {
21004 assert!(hk == n_head, "GDN_DIFF oracle is broadcast-only");
21005 return self.gdn_scan_diff(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale);
21006 }
21007 if Self::gdn_chunked_enabled() && t >= 16 {
21008 self.gdn_scan_chunked(
21009 q,
21010 k,
21011 v,
21012 g,
21013 beta,
21014 kb16_pre,
21015 qb16_pre,
21016 state_in,
21017 state_out,
21018 o,
21019 n_head,
21020 t,
21021 scale,
21022 Self::gdn_chunk_size(),
21023 hk,
21024 )
21025 } else {
21026 assert!(
21027 hk == n_head,
21028 "s128 scan is broadcast-only (prep guarantees by predicate)"
21029 );
21030 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)
21031 }
21032 }
21033
21034 #[allow(clippy::too_many_arguments)]
21036 fn gdn_scan_diff(
21037 &self,
21038 q: &CudaSlice<f32>,
21039 k: &CudaSlice<f32>,
21040 v: &CudaSlice<f32>,
21041 g: &CudaSlice<f32>,
21042 beta: &CudaSlice<f32>,
21043 state_in: &CudaSlice<f32>,
21044 state_out: &mut CudaSlice<f32>,
21045 o: &mut CudaSlice<f32>,
21046 n_head: usize,
21047 t: usize,
21048 scale: f32,
21049 ) -> Result<(), Box<dyn std::error::Error>> {
21050 static CALL: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
21051 let call = CALL.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
21052 let mut o_c = self.uninit(o.len())?;
21053 let mut st_c = self.uninit(state_out.len())?;
21054 self.gdn_scan_chunked(
21055 q,
21056 k,
21057 v,
21058 g,
21059 beta,
21060 None,
21061 None,
21062 state_in,
21063 &mut st_c,
21064 &mut o_c,
21065 n_head,
21066 t,
21067 scale,
21068 Self::gdn_chunk_size(),
21069 n_head,
21070 )?;
21071 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)?;
21072 let (oh_s, oh_c) = (self.dtoh(o)?, self.dtoh(&o_c)?);
21073 let (sh_s, sh_c) = (self.dtoh(state_out)?, self.dtoh(&st_c)?);
21074 let stats = |a: &[f32], b: &[f32]| -> (f32, f32, f64) {
21075 let mut max_abs = 0f32;
21076 let mut max_rel = 0f32;
21077 let mut sum_rel = 0f64;
21078 for (x, y) in a.iter().zip(b) {
21079 let ad = (x - y).abs();
21080 let rel = ad / x.abs().max(y.abs()).max(1e-3);
21081 if ad > max_abs {
21082 max_abs = ad;
21083 }
21084 if rel > max_rel {
21085 max_rel = rel;
21086 }
21087 sum_rel += rel as f64;
21088 }
21089 (max_abs, max_rel, sum_rel / a.len() as f64)
21090 };
21091 let (o_ma, o_mr, o_mean) = stats(&oh_s, &oh_c);
21092 let (s_ma, s_mr, s_mean) = stats(&sh_s, &sh_c);
21093 println!(
21094 "[gdn-diff call {call:3} T={t} C={}] out: max_abs={o_ma:.3e} max_rel={o_mr:.3e} mean_rel={o_mean:.3e} | \
21095 state: max_abs={s_ma:.3e} max_rel={s_mr:.3e} mean_rel={s_mean:.3e}",
21096 Self::gdn_chunk_size()
21097 );
21098 Ok(())
21099 }
21100
21101 pub fn gdn_glog(
21103 &self,
21104 alpha: &CudaSlice<f32>,
21105 dt_bias: &CudaSlice<f32>,
21106 a: &CudaSlice<f32>,
21107 g_log: &mut CudaSlice<f32>,
21108 n_head: usize,
21109 t: usize,
21110 ) -> Result<(), Box<dyn std::error::Error>> {
21111 let f = self.func("gdn_glog_f32");
21112 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
21113 let (h, ti) = (n_head as i32, t as i32);
21114 let __s_b = self.gpu.stream();
21115 let mut b = __s_b.launch_builder(&f);
21116 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
21117 unsafe {
21118 b.launch(cfg)?;
21119 }
21120 Ok(())
21121 }
21122
21123 pub fn sigmoid_v(
21126 &self,
21127 x: &cudarc::driver::CudaView<f32>,
21128 y: &mut CudaSlice<f32>,
21129 n: usize,
21130 ) -> Result<(), Box<dyn std::error::Error>> {
21131 let f = self.func("sigmoid_f32");
21132 let cfg = LaunchConfig::for_num_elems(n as u32);
21133 let ni = n as i32;
21134 let __s_b = self.gpu.stream();
21135 let mut b = __s_b.launch_builder(&f);
21136 b.arg(x).arg(y).arg(&ni);
21137 unsafe {
21138 b.launch(cfg)?;
21139 }
21140 Ok(())
21141 }
21142
21143 pub fn gdn_glog_v(
21144 &self,
21145 alpha: &cudarc::driver::CudaView<f32>,
21146 dt_bias: &CudaSlice<f32>,
21147 a: &CudaSlice<f32>,
21148 g_log: &mut CudaSlice<f32>,
21149 n_head: usize,
21150 t: usize,
21151 ) -> Result<(), Box<dyn std::error::Error>> {
21152 let f = self.func("gdn_glog_f32");
21153 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
21154 let (h, ti) = (n_head as i32, t as i32);
21155 let __s_b = self.gpu.stream();
21156 let mut b = __s_b.launch_builder(&f);
21157 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
21158 unsafe {
21159 b.launch(cfg)?;
21160 }
21161 Ok(())
21162 }
21163
21164 pub fn sigmoid(
21165 &self,
21166 x: &CudaSlice<f32>,
21167 y: &mut CudaSlice<f32>,
21168 n: usize,
21169 ) -> Result<(), Box<dyn std::error::Error>> {
21170 let f = self.func("sigmoid_f32");
21171 let cfg = LaunchConfig::for_num_elems(n as u32);
21172 let ni = n as i32;
21173 let __s_b = self.gpu.stream();
21174 let mut b = __s_b.launch_builder(&f);
21175 b.arg(x).arg(y).arg(&ni);
21176 unsafe {
21177 b.launch(cfg)?;
21178 }
21179 Ok(())
21180 }
21181
21182 pub fn sig_mul_f16out(
21185 &self,
21186 a: &CudaSlice<f32>,
21187 g: &CudaSlice<f32>,
21188 dst: &mut CudaSlice<f32>,
21189 dst16: &mut CudaSlice<u8>,
21190 n: usize,
21191 ) -> Result<(), Box<dyn std::error::Error>> {
21192 let f = self.func("sig_mul_f16out_f32");
21193 let cfg = LaunchConfig::for_num_elems(n as u32);
21194 let ni = n as i32;
21195 let __s_b = self.gpu.stream();
21196 let mut b = __s_b.launch_builder(&f);
21197 b.arg(a).arg(g).arg(dst).arg(dst16).arg(&ni);
21198 unsafe {
21199 b.launch(cfg)?;
21200 }
21201 Ok(())
21202 }
21203
21204 #[allow(clippy::too_many_arguments)]
21213 pub fn attn_head_gate(
21214 &self,
21215 a: &CudaSlice<f32>,
21216 g: &CudaSlice<f32>,
21217 dst: &mut CudaSlice<f32>,
21218 dst16: Option<&mut CudaSlice<u8>>,
21219 head_dim: usize,
21220 n_head: usize,
21221 t: usize,
21222 ) -> Result<(), Box<dyn std::error::Error>> {
21223 let f = self.func("attn_head_gate_f32");
21224 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
21225 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
21226 let d16: u64 = match dst16 {
21228 Some(d) => self.addr_u8(d),
21229 None => 0,
21230 };
21231 let __s_b = self.gpu.stream();
21232 let mut b = __s_b.launch_builder(&f);
21233 b.arg(a)
21234 .arg(g)
21235 .arg(dst)
21236 .arg(&d16)
21237 .arg(&hd)
21238 .arg(&nh)
21239 .arg(&ti);
21240 unsafe {
21241 b.launch(cfg)?;
21242 }
21243 Ok(())
21244 }
21245
21246 #[allow(clippy::too_many_arguments)]
21255 pub fn swiglu_clamped_mul_scaled(
21256 &self,
21257 gate: &CudaSlice<f32>,
21258 up: &CudaSlice<f32>,
21259 gs: f32,
21260 us: f32,
21261 limit: f32,
21262 dst: &mut CudaSlice<f32>,
21263 n: usize,
21264 ) -> Result<(), Box<dyn std::error::Error>> {
21265 debug_assert!(
21266 limit > 1e-6,
21267 "swiglu_clamped needs a live limit; use silu_mul_scaled"
21268 );
21269 let f = self.func("swiglu_clamped_mul_scaled_f32");
21270 let cfg = LaunchConfig::for_num_elems(n as u32);
21271 let ni = n as i32;
21272 let __s_b = self.gpu.stream();
21273 let mut b = __s_b.launch_builder(&f);
21274 b.arg(gate)
21275 .arg(up)
21276 .arg(&gs)
21277 .arg(&us)
21278 .arg(&limit)
21279 .arg(dst)
21280 .arg(&ni);
21281 unsafe {
21282 b.launch(cfg)?;
21283 }
21284 Ok(())
21285 }
21286
21287 pub fn gated_rmsnorm(
21289 &self,
21290 o: &CudaSlice<f32>,
21291 w: &CudaSlice<f32>,
21292 z: &CudaSlice<f32>,
21293 dst: &mut CudaSlice<f32>,
21294 ncols: usize,
21295 nrows: usize,
21296 eps: f32,
21297 ) -> Result<(), Box<dyn std::error::Error>> {
21298 let f = self.func("gated_rmsnorm_f32");
21299 let cfg = LaunchConfig {
21300 grid_dim: (nrows as u32, 1, 1),
21301 block_dim: (128, 1, 1),
21302 shared_mem_bytes: 0,
21303 };
21304 let (nc, e) = (ncols as i32, eps);
21305 let __s_b = self.gpu.stream();
21306 let mut b = __s_b.launch_builder(&f);
21307 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
21308 unsafe {
21309 b.launch(cfg)?;
21310 }
21311 Ok(())
21312 }
21313
21314 pub fn gated_rmsnorm_f16out(
21317 &self,
21318 o: &CudaSlice<f32>,
21319 w: &CudaSlice<f32>,
21320 z: &CudaSlice<f32>,
21321 dst: &mut CudaSlice<f32>,
21322 dst16: &mut CudaSlice<u8>,
21323 ncols: usize,
21324 nrows: usize,
21325 eps: f32,
21326 ) -> Result<(), Box<dyn std::error::Error>> {
21327 let f = self.func("gated_rmsnorm_f16out_f32");
21328 let cfg = LaunchConfig {
21330 grid_dim: (nrows as u32, 1, 1),
21331 block_dim: (128, 1, 1),
21332 shared_mem_bytes: 0,
21333 };
21334 let (nc, e) = (ncols as i32, eps);
21335 let __s_b = self.gpu.stream();
21336 let mut b = __s_b.launch_builder(&f);
21337 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
21338 unsafe {
21339 b.launch(cfg)?;
21340 }
21341 Ok(())
21342 }
21343
21344 #[allow(clippy::too_many_arguments)]
21348 pub fn add_rms_norm_zq8(
21349 &self,
21350 a: &CudaSlice<f32>,
21351 b_in: &CudaSlice<f32>,
21352 w: &CudaSlice<f32>,
21353 res: &mut CudaSlice<f32>,
21354 z: &mut CudaSlice<f32>,
21355 ncols: usize,
21356 nrows: usize,
21357 eps: f32,
21358 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
21359 assert!(ncols % 32 == 0);
21360 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
21361 let mut d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
21362 let f = self.func("add_rms_norm_zq8");
21363 let cfg = LaunchConfig {
21364 grid_dim: (nrows as u32, 1, 1),
21365 block_dim: (1024, 1, 1),
21366 shared_mem_bytes: 0,
21367 };
21368 let (nc, ep) = (ncols as i32, eps);
21369 let __s_b = self.gpu.stream();
21370 let mut b = __s_b.launch_builder(&f);
21371 b.arg(a)
21372 .arg(b_in)
21373 .arg(w)
21374 .arg(res)
21375 .arg(z)
21376 .arg(&mut q)
21377 .arg(&mut d)
21378 .arg(&nc)
21379 .arg(&ep);
21380 unsafe {
21381 b.launch(cfg)?;
21382 }
21383 Ok((q, d))
21384 }
21385
21386 pub fn gated_rmsnorm_zv(
21391 &self,
21392 o: &CudaSlice<f32>,
21393 w: &CudaSlice<f32>,
21394 z: &cudarc::driver::CudaView<f32>,
21395 dst: &mut CudaSlice<f32>,
21396 ncols: usize,
21397 nrows: usize,
21398 eps: f32,
21399 ) -> Result<(), Box<dyn std::error::Error>> {
21400 let f = self.func("gated_rmsnorm_f32");
21401 let cfg = LaunchConfig {
21402 grid_dim: (nrows as u32, 1, 1),
21403 block_dim: (128, 1, 1),
21404 shared_mem_bytes: 0,
21405 };
21406 let (nc, e) = (ncols as i32, eps);
21407 let __s_b = self.gpu.stream();
21408 let mut b = __s_b.launch_builder(&f);
21409 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
21410 unsafe {
21411 b.launch(cfg)?;
21412 }
21413 Ok(())
21414 }
21415
21416 pub fn gated_rmsnorm_f16out_zv(
21417 &self,
21418 o: &CudaSlice<f32>,
21419 w: &CudaSlice<f32>,
21420 z: &cudarc::driver::CudaView<f32>,
21421 dst: &mut CudaSlice<f32>,
21422 dst16: &mut CudaSlice<u8>,
21423 ncols: usize,
21424 nrows: usize,
21425 eps: f32,
21426 ) -> Result<(), Box<dyn std::error::Error>> {
21427 let f = self.func("gated_rmsnorm_f16out_f32");
21428 let cfg = LaunchConfig {
21430 grid_dim: (nrows as u32, 1, 1),
21431 block_dim: (128, 1, 1),
21432 shared_mem_bytes: 0,
21433 };
21434 let (nc, e) = (ncols as i32, eps);
21435 let __s_b = self.gpu.stream();
21436 let mut b = __s_b.launch_builder(&f);
21437 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
21438 unsafe {
21439 b.launch(cfg)?;
21440 }
21441 Ok(())
21442 }
21443
21444 pub fn gated_rmsnorm_q8_1(
21445 &self,
21446 o: &CudaSlice<f32>,
21447 w: &CudaSlice<f32>,
21448 z: &CudaSlice<f32>,
21449 ncols: usize,
21450 nrows: usize,
21451 eps: f32,
21452 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
21453 assert!(ncols % 32 == 0);
21454 let f = self.func("gated_rmsnorm_q8_1");
21455 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
21456 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
21457 let cfg = LaunchConfig {
21458 grid_dim: (nrows as u32, 1, 1),
21459 block_dim: (128, 1, 1),
21460 shared_mem_bytes: 0,
21461 };
21462 let (nc, ep) = (ncols as i32, eps);
21463 let __s_b = self.gpu.stream();
21464 let mut b = __s_b.launch_builder(&f);
21465 b.arg(o)
21466 .arg(w)
21467 .arg(z)
21468 .arg(&mut out_q)
21469 .arg(&mut out_d)
21470 .arg(&nc)
21471 .arg(&ep);
21472 unsafe {
21473 b.launch(cfg)?;
21474 }
21475 Ok((out_q, out_d))
21476 }
21477
21478 pub fn transpose(
21480 &self,
21481 inp: &CudaSlice<f32>,
21482 rows: usize,
21483 cols: usize,
21484 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21485 let f = self.func("transpose_f32");
21486 let mut out = self.zeros(rows * cols)?;
21487 let cfg = LaunchConfig::for_num_elems((rows * cols) as u32);
21488 let (r, c) = (rows as i32, cols as i32);
21489 let __s_b = self.gpu.stream();
21490 let mut b = __s_b.launch_builder(&f);
21491 b.arg(inp).arg(&mut out).arg(&r).arg(&c);
21492 unsafe {
21493 b.launch(cfg)?;
21494 }
21495 Ok(out)
21496 }
21497
21498 pub fn repeat_heads(
21500 &self,
21501 inp: &CudaSlice<f32>,
21502 out: &mut CudaSlice<f32>,
21503 head_dim: usize,
21504 n_in: usize,
21505 n_out: usize,
21506 t: usize,
21507 ) -> Result<(), Box<dyn std::error::Error>> {
21508 let f = self.func("repeat_heads_f32");
21509 let cfg = LaunchConfig::for_num_elems((head_dim * n_out * t) as u32);
21510 let (hd, ni, no, ti) = (head_dim as i32, n_in as i32, n_out as i32, t as i32);
21511 let __s_b = self.gpu.stream();
21512 let mut b = __s_b.launch_builder(&f);
21513 b.arg(inp).arg(out).arg(&hd).arg(&ni).arg(&no).arg(&ti);
21514 unsafe {
21515 b.launch(cfg)?;
21516 }
21517 Ok(())
21518 }
21519
21520 pub fn q_gate_split(
21523 &self,
21524 qf: &CudaSlice<f32>,
21525 q_out: &mut CudaSlice<f32>,
21526 gate_out: &mut CudaSlice<f32>,
21527 head_dim: usize,
21528 n_head: usize,
21529 t: usize,
21530 ) -> Result<(), Box<dyn std::error::Error>> {
21531 let f = self.func("q_gate_split_f32");
21532 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
21533 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
21534 let __s_b = self.gpu.stream();
21535 let mut b = __s_b.launch_builder(&f);
21536 b.arg(qf)
21537 .arg(q_out)
21538 .arg(gate_out)
21539 .arg(&hd)
21540 .arg(&nh)
21541 .arg(&ti);
21542 unsafe {
21543 b.launch(cfg)?;
21544 }
21545 Ok(())
21546 }
21547
21548 pub fn qkv_to_gdn_repack(
21552 &self,
21553 conv_out: &CudaSlice<f32>,
21554 q_g: &mut CudaSlice<f32>,
21555 k_g: &mut CudaSlice<f32>,
21556 v_g: &mut CudaSlice<f32>,
21557 d_state: usize,
21558 num_v: usize,
21559 num_k: usize,
21560 key_dim: usize,
21561 t: usize,
21562 ) -> Result<(), Box<dyn std::error::Error>> {
21563 let f = self.func("qkv_to_gdn_repack_f32");
21564 let cfg = LaunchConfig::for_num_elems((d_state * num_v * t) as u32);
21565 let (ds, nv, nk, kd, ti) = (
21566 d_state as i32,
21567 num_v as i32,
21568 num_k as i32,
21569 key_dim as i32,
21570 t as i32,
21571 );
21572 let __s_b = self.gpu.stream();
21573 let mut b = __s_b.launch_builder(&f);
21574 b.arg(conv_out)
21575 .arg(q_g)
21576 .arg(k_g)
21577 .arg(v_g)
21578 .arg(&ds)
21579 .arg(&nv)
21580 .arg(&nk)
21581 .arg(&kd)
21582 .arg(&ti);
21583 unsafe {
21584 b.launch(cfg)?;
21585 }
21586 Ok(())
21587 }
21588
21589 pub fn conv_left_pad(
21592 &self,
21593 src: &CudaSlice<f32>,
21594 dst: &mut CudaSlice<f32>,
21595 conv_dim: usize,
21596 t: usize,
21597 pad: usize,
21598 ) -> Result<(), Box<dyn std::error::Error>> {
21599 let f = self.func("conv_left_pad_f32");
21600 let cfg = LaunchConfig::for_num_elems((conv_dim * t) as u32);
21601 let (cd, ti, p) = (conv_dim as i32, t as i32, pad as i32);
21602 let __s_b = self.gpu.stream();
21603 let mut b = __s_b.launch_builder(&f);
21604 b.arg(src).arg(dst).arg(&cd).arg(&ti).arg(&p);
21605 unsafe {
21606 b.launch(cfg)?;
21607 }
21608 Ok(())
21609 }
21610
21611 pub fn conv_assemble_and_roll(
21615 &self,
21616 qkv_col: &CudaSlice<f32>,
21617 conv_state: &mut CudaSlice<f32>,
21618 conv_in: &mut CudaSlice<f32>,
21619 conv_dim: usize,
21620 pad: usize,
21621 ) -> Result<(), Box<dyn std::error::Error>> {
21622 let f = self.func("conv_assemble_and_roll_f32");
21623 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
21624 let (cd, p) = (conv_dim as i32, pad as i32);
21625 let __s_b = self.gpu.stream();
21626 let mut b = __s_b.launch_builder(&f);
21627 b.arg(qkv_col).arg(conv_state).arg(conv_in).arg(&cd).arg(&p);
21628 unsafe {
21629 b.launch(cfg)?;
21630 }
21631 Ok(())
21632 }
21633
21634 pub fn ssm_conv1d_fused_decode(
21640 &self,
21641 qkv_col: &CudaSlice<f32>,
21642 conv_state: &mut CudaSlice<f32>,
21643 w: &CudaSlice<f32>,
21644 conv_out: &mut CudaSlice<f32>,
21645 conv_dim: usize,
21646 d_conv: usize,
21647 ) -> Result<(), Box<dyn std::error::Error>> {
21648 let f = self.func("ssm_conv1d_fused_decode_f32");
21649 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
21650 let (cd, dc) = (conv_dim as i32, d_conv as i32);
21651 let __s_b = self.gpu.stream();
21652 let mut b = __s_b.launch_builder(&f);
21653 b.arg(qkv_col)
21654 .arg(conv_state)
21655 .arg(w)
21656 .arg(conv_out)
21657 .arg(&cd)
21658 .arg(&dc);
21659 unsafe {
21660 b.launch(cfg)?;
21661 }
21662 Ok(())
21663 }
21664
21665 pub fn slice_range(
21668 &self,
21669 src: &CudaSlice<f32>,
21670 start: usize,
21671 len: usize,
21672 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21673 let host = self.gpu.stream().clone_dtoh(src)?;
21674 self.gpu.stream().synchronize()?;
21675 Ok(self.htod(&host[start..start + len])?)
21676 }
21677}
21678
21679#[cfg(test)]
21680mod target_dispatch_tests {
21681 use super::legacy_quant_gemm_allowed;
21682
21683 #[test]
21684 fn legacy_quant_gemm_arch_policy_honors_the_escape_hatch() {
21685 assert!(legacy_quant_gemm_allowed(false, false, false));
21687 assert!(!legacy_quant_gemm_allowed(false, false, true));
21688 assert!(!legacy_quant_gemm_allowed(true, false, false));
21690 assert!(!legacy_quant_gemm_allowed(true, false, true));
21691 assert!(legacy_quant_gemm_allowed(true, true, false));
21693 assert!(!legacy_quant_gemm_allowed(true, true, true));
21694 }
21695
21696 #[cfg(all(memra_portable_cuda, not(memra_hopper_mma)))]
21697 #[test]
21698 fn portable_build_disables_legacy_quant_gemm_without_an_env_override() {
21699 assert!(!legacy_quant_gemm_allowed(
21700 cfg!(memra_portable_cuda),
21701 cfg!(memra_hopper_mma),
21702 false
21703 ));
21704 }
21705
21706 #[cfg(memra_hopper_mma)]
21707 #[test]
21708 fn hopper_mma_build_re_admits_legacy_quant_gemm() {
21709 assert!(legacy_quant_gemm_allowed(
21710 cfg!(memra_portable_cuda),
21711 cfg!(memra_hopper_mma),
21712 false
21713 ));
21714 assert!(super::portable_mma_gated() == false);
21715 }
21716}
21717
21718impl memra_kv::KvDev for Engine {
21721 fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21722 Engine::zeros(self, n)
21723 }
21724 fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21725 Engine::uninit(self, n)
21726 }
21727 fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
21728 Engine::alloc_u8(self, n)
21729 }
21730 fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
21731 Engine::htod_i32(self, v)
21732 }
21733 fn clone_dtod(
21734 &self,
21735 src: &CudaSlice<f32>,
21736 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
21737 Engine::clone_dtod(self, src)
21738 }
21739 fn copy_into(
21740 &self,
21741 dst: &mut CudaSlice<f32>,
21742 off: usize,
21743 src: &CudaSlice<f32>,
21744 len: usize,
21745 ) -> Result<(), Box<dyn std::error::Error>> {
21746 Engine::copy_into(self, dst, off, src, len)
21747 }
21748 fn set_i32_one(
21749 &self,
21750 d: &mut CudaSlice<i32>,
21751 v: i32,
21752 ) -> Result<(), Box<dyn std::error::Error>> {
21753 Engine::set_i32_one(self, d, v)
21754 }
21755}