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_gemma;
33pub mod vision_pre;
34pub mod cache {
37 pub use memra_kv::*;
38}
39pub mod decode;
40pub mod decode_batch;
41pub mod dflash;
42pub mod eagle;
43pub mod gemma_spec;
44pub mod graph_update;
45pub mod mla;
49pub mod moesd;
50pub mod parallel;
51pub mod pp;
52pub mod round_stream;
53pub mod spec;
54pub use memra_sampling as sampler;
55
56pub fn moe_f16g_mode() -> u8 {
100 static M: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
101 *M.get_or_init(|| match std::env::var("MEMRA_MOE_F16G").as_deref() {
102 Ok("0") => 0,
103 Ok("2") => 2,
104 Ok("3") => 3,
105 Ok(_) => 1,
106 Err(_) => 2,
109 })
110}
111pub fn moe_f16g_sk_params() -> (i32, i32) {
125 static P: std::sync::OnceLock<(i32, i32)> = std::sync::OnceLock::new();
126 *P.get_or_init(|| match std::env::var("MEMRA_F16G_SK").as_deref() {
127 Ok("0") => (-1, 0),
128 Ok("32") => (0, i32::MAX),
129 Ok("128") => (0, 1),
130 _ => {
131 let cross = std::env::var("MEMRA_F16G_SK_CROSS")
132 .ok()
133 .and_then(|v| v.parse().ok())
134 .unwrap_or(64);
135 (0, cross)
136 }
137 })
138}
139pub fn moe_f16g_direct_on(qtype: i32) -> bool {
150 static M: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
151 let m = *M.get_or_init(|| match std::env::var("MEMRA_F16G_DIRECT").as_deref() {
152 Ok("0") => 0,
153 Ok("kq") => 1,
154 _ => 2,
155 });
156 match m {
157 0 => false,
158 1 => qtype == QT_Q4_K || qtype == QT_Q6_K,
159 _ => true,
160 }
161}
162pub fn moe_f16g_tail_on() -> bool {
171 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
172 *ON.get_or_init(|| std::env::var("MEMRA_F16G_TAIL").as_deref() != Ok("0"))
173}
174
175pub fn moe_f16g_gemma_on() -> bool {
182 static M: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
183 *M.get_or_init(|| !matches!(std::env::var("MEMRA_MOE_F16G").as_deref(), Ok("0") | Err(_)))
184}
185
186pub fn moe_fuse_actq_on() -> bool {
190 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
191 *ON.get_or_init(|| std::env::var("MEMRA_MOE_FUSE_ACTQ").as_deref() != Ok("0"))
192}
193
194pub fn router_prefill_exact_on() -> bool {
204 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
205 *ON.get_or_init(|| std::env::var("MEMRA_ROUTER_PREFILL_EXACT").as_deref() != Ok("0"))
206}
207
208pub fn router_kernel_on() -> bool {
209 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
210 *ON.get_or_init(|| {
211 let on = std::env::var("MEMRA_ROUTER_KERNEL").as_deref() != Ok("0");
212 if !on {
213 eprintln!("[memra] router kernel OFF (rollback: per-column cuBLAS gemv)");
214 }
215 on
216 })
217}
218
219pub const ROUTER_BATCH_MIN_T: usize = 8;
234pub fn router_batch_on() -> bool {
235 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
236 *ON.get_or_init(|| std::env::var("MEMRA_ROUTER_BATCH").as_deref() != Ok("0"))
237}
238mod cpu_experts;
239#[cfg(memra_cutlass)]
240pub mod cutlass_ffi;
241pub mod dsv4_ffi;
242pub mod dsv4_gpu;
243pub mod f16_ffi;
244pub mod fp8_ffi;
245pub mod mmq_ffi;
246pub mod moe_cache;
247pub mod prime_graph;
248pub mod spill;
249mod spill_pread;
250
251const FATBIN: &[u8] = include_bytes!(env!("MEMRA_ENGINE_FATBIN"));
258const HYBRID_FATBIN: &[u8] = include_bytes!(env!("MEMRA_HYBRID_FATBIN"));
259const QMATVEC_FATBIN: &[u8] = include_bytes!(env!("MEMRA_QMATVEC_FATBIN"));
260const FLASH_FATBIN: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN"));
261const GEMM_FATBIN: &[u8] = include_bytes!(env!("MEMRA_GEMM_FATBIN"));
262const ROUTER_FATBIN: &[u8] = include_bytes!(env!("MEMRA_ROUTER_FATBIN"));
263const SAMPLE_FATBIN: &[u8] = include_bytes!(env!("MEMRA_SAMPLE_FATBIN"));
265
266fn gemm_fatbin_bytes() -> std::borrow::Cow<'static, [u8]> {
272 assert!(
273 !(portable_mma_gated() && std::env::var_os("MEMRA_GEMM_FATBIN").is_some()),
274 "MEMRA_GEMM_FATBIN overrides are not allowed in the portable CUDA lane"
275 );
276 match std::env::var("MEMRA_GEMM_FATBIN") {
277 Ok(path) => std::borrow::Cow::Owned(
278 std::fs::read(&path).unwrap_or_else(|e| panic!("MEMRA_GEMM_FATBIN read {path}: {e}")),
279 ),
280 Err(_) => std::borrow::Cow::Borrowed(GEMM_FATBIN),
281 }
282}
283
284pub(crate) const fn portable_mma_gated() -> bool {
291 cfg!(memra_portable_cuda) && !cfg!(memra_hopper_mma)
292}
293
294const fn legacy_quant_gemm_allowed(portable_cuda: bool, hopper_mma: bool, no_gemm: bool) -> bool {
299 (!portable_cuda || hopper_mma) && !no_gemm
300}
301
302const FLASH_FATBIN_VQ4: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_VQ4"));
310const FLASH_FATBIN_VF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_VF8"));
311const FLASH_FATBIN_KF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8"));
312const FLASH_FATBIN_KF8VQ4: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8VQ4"));
313const FLASH_FATBIN_KF8VF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8VF8"));
314
315pub use memra_kv::{kv_blk_bytes, kv_cache_formats};
318
319fn flash_fatbin_bytes() -> &'static [u8] {
321 match kv_cache_formats() {
322 ("q8_0", "q5_1") => FLASH_FATBIN,
323 ("q8_0", "q4_0") => FLASH_FATBIN_VQ4,
324 ("q8_0", "fp8") => FLASH_FATBIN_VF8,
325 ("fp8", "q5_1") => FLASH_FATBIN_KF8,
326 ("fp8", "q4_0") => FLASH_FATBIN_KF8VQ4,
327 ("fp8", "fp8") => FLASH_FATBIN_KF8VF8,
328 other => unreachable!("kv_cache_formats returned {other:?}"),
329 }
330}
331
332fn k1_launch_override() -> Option<(u32, u32, u32)> {
339 static K1: std::sync::OnceLock<Option<(u32, u32, u32)>> = std::sync::OnceLock::new();
340 *K1.get_or_init(|| {
341 let v = std::env::var("MEMRA_GEMM_K1_LAUNCH").ok()?;
342 let p: Vec<u32> = v.split(',').filter_map(|s| s.trim().parse().ok()).collect();
343 match p.as_slice() {
344 [bm, bn, w] => Some((*bm, *bn, *w)),
345 _ => None,
346 }
347 })
348}
349
350pub(crate) fn wgmma_gemm_enabled() -> bool {
357 static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
358 *V.get_or_init(|| std::env::var("MEMRA_WGMMA").as_deref() == Ok("1"))
359}
360
361pub const FA_VEC_MIN_TKV: usize = 96;
376pub fn fa_vec_min_tkv() -> usize {
380 static V: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
381 *V.get_or_init(|| {
382 std::env::var("MEMRA_FA_VEC_MIN")
383 .ok()
384 .and_then(|v| v.parse().ok())
385 .unwrap_or_else(|| FA_VEC_MIN_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
386 })
387}
388
389pub fn fa_f16pv_on() -> bool {
400 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
401 *ON.get_or_init(|| {
402 std::env::var("MEMRA_FA_F16PV")
403 .map(|v| v != "0")
404 .unwrap_or_else(|_| std::env::var("MEMRA_DRAFT").is_err())
405 })
406}
407
408pub fn fa512_hp_on() -> bool {
412 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
413 *ON.get_or_init(|| std::env::var("MEMRA_FA512_HP").as_deref() != Ok("0"))
414}
415
416pub fn faw_hp_on() -> bool {
420 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
421 *ON.get_or_init(|| std::env::var("MEMRA_FAW_HP").as_deref() != Ok("0"))
422}
423
424pub fn fa512_wide_warps() -> usize {
428 static N: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
429 *N.get_or_init(|| match std::env::var("MEMRA_FA512_W4").as_deref() {
430 Ok("1") => 4,
431 _ => 2,
432 })
433}
434
435pub fn fa512_min_tkv() -> usize {
438 static FA512_MIN: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
439 *FA512_MIN.get_or_init(|| {
440 std::env::var("MEMRA_FA512_MIN")
441 .ok()
442 .and_then(|v| v.parse().ok())
443 .unwrap_or(512)
444 })
445}
446pub static FA_VEC_MIN_DEFAULT: std::sync::atomic::AtomicUsize =
450 std::sync::atomic::AtomicUsize::new(FA_VEC_MIN_TKV);
451pub static FA_SPW_DEFAULT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(32);
455pub static FUSED_MR1_DEFAULT: std::sync::atomic::AtomicBool =
461 std::sync::atomic::AtomicBool::new(false);
462pub static ROUTER_W8_DEFAULT: std::sync::atomic::AtomicBool =
469 std::sync::atomic::AtomicBool::new(true);
470pub static FA_SP512_DEFAULT: std::sync::atomic::AtomicUsize =
471 std::sync::atomic::AtomicUsize::new(16);
472pub static RMS_BLOCK_DEFAULT: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(256);
477pub static FA_SP_GEMMA: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
479pub static MMQ_SK_FORCE: std::sync::atomic::AtomicI8 = std::sync::atomic::AtomicI8::new(-1);
484pub use memra_kv::KV_FP8_FORCE;
487pub(crate) fn rms_block() -> u32 {
488 static V: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
489 *V.get_or_init(|| {
490 std::env::var("MEMRA_RMS_BLOCK")
491 .ok()
492 .and_then(|v| v.parse().ok())
493 .unwrap_or_else(|| RMS_BLOCK_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
494 })
495}
496
497pub(crate) fn fa_split_keys(t_kv: usize, n_head_kv: usize) -> usize {
498 static S: std::sync::OnceLock<Option<usize>> = std::sync::OnceLock::new();
499 if let Some(forced) = *S.get_or_init(|| {
500 std::env::var("MEMRA_FA_SPLIT")
501 .ok()
502 .and_then(|v| v.parse().ok())
503 .filter(|&s: &usize| s >= 8 && s % 8 == 0)
504 }) {
505 return forced;
506 }
507 if FA_SP_GEMMA.load(std::sync::atomic::Ordering::Relaxed)
525 && std::env::var("MEMRA_FA_SP16").as_deref() == Ok("1")
526 {
527 return if t_kv <= 8192 {
528 16
529 } else if t_kv <= 16384 {
530 64
531 } else {
532 128
533 };
534 }
535 let big_rig = fa_sm_count() >= 128;
536 if big_rig {
537 let _ = n_head_kv;
538 if t_kv <= 2048 {
539 16
540 } else if t_kv <= 16384 {
541 64
542 } else {
543 128
544 }
545 } else if n_head_kv <= 4 {
546 if t_kv <= 512 {
567 8
568 } else if t_kv <= 16384 {
569 64
570 } else {
571 128
572 }
573 } else {
574 if t_kv <= 8192 {
575 32
576 } else if t_kv <= 16384 {
577 64
578 } else {
579 128
580 }
581 }
582}
583
584fn fa_sm_count() -> i32 {
587 static N: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
588 *N.get_or_init(|| {
589 cudarc::driver::result::init().ok();
590 cudarc::driver::result::device::get(0)
591 .and_then(|d| unsafe { cudarc::driver::result::device::get_attribute(
592 d, cudarc::driver::sys::CUdevice_attribute_enum::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT) })
593 .unwrap_or(82)
594 })
595}
596
597fn fa_hd_suffix(head_dim: usize) -> Result<&'static str, Box<dyn std::error::Error>> {
601 match head_dim {
602 256 => Ok(""),
603 128 => Ok("_hd128"),
604 d => Err(format!(
605 "fa_prefill: no kernel stamped for head_dim={d} (only 256/128); \
606 callers must gate to sdpa_naive"
607 )
608 .into()),
609 }
610}
611
612pub const QT_Q8_0: i32 = 0;
614pub const QT_Q4_K: i32 = 1;
615pub const QT_Q6_K: i32 = 2;
616pub const QT_Q5_K: i32 = 3;
617pub const QT_Q3_K: i32 = 4;
618pub const QT_IQ4_XS: i32 = 5;
619pub const QT_IQ3_S: i32 = 6;
620pub const QT_NVFP4: i32 = 7;
621pub const QT_F8_E4M3: i32 = 10;
627pub const QT_NVFP4_RP: i32 = 9;
630pub const QT_F32: i32 = 8;
632pub const QT_BF16: i32 = 11;
633pub const QT_Q4_0: i32 = 12; pub const QT_Q2_K: i32 = 13;
638pub const QT_F8_E4M3_BLK: i32 = 14;
654
655pub struct Engine {
657 pub gpu: memra_runtime::Gpu,
658 module: Arc<CudaModule>,
659 hybrid: Arc<CudaModule>,
660 qmatvec: Arc<CudaModule>,
661 flash: Arc<CudaModule>,
662 flash_g: std::sync::OnceLock<Arc<CudaModule>>,
666 gemm: Arc<CudaModule>,
667 router: Arc<CudaModule>,
668 sample: Arc<CudaModule>,
670 moe_cache: Mutex<Option<crate::moe_cache::MoeSlotCache>>,
674 moe_cache_layout: Mutex<Option<Vec<usize>>>,
678 capture_keep_on: std::sync::atomic::AtomicBool,
684 verify_exact: std::sync::atomic::AtomicBool,
689 capture_keep: Mutex<Vec<Box<dyn std::any::Any + Send>>>,
690 pub copy_stream: Arc<CudaStream>,
692 #[cfg(memra_cutlass)]
699 cutlass_scratch: Mutex<Option<crate::cutlass_ffi::CutlassScratch>>,
700 fp8_scratch: Mutex<Option<crate::fp8_ffi::Fp8Scratch>>,
704 fa_vf16_scratch: Mutex<Option<CudaSlice<u8>>>,
707 fa_part_pool: Mutex<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>>,
711 fa_part_retired: Mutex<Vec<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>>,
715 fn_cache: Mutex<std::collections::HashMap<String, CudaFunction>>,
717 f16_scratch: Mutex<Option<crate::f16_ffi::F16Scratch>>,
718 argmax_partials: Mutex<Option<(CudaSlice<f32>, CudaSlice<i32>)>>,
723 prime_deqw_ws: Mutex<Option<(CudaSlice<u8>, CudaSlice<u8>)>>,
728 router_stage: Mutex<Option<PinnedStage>>,
732}
733
734fn fa_v2_on() -> bool {
744 std::env::var("MEMRA_FA_V2")
750 .map(|v| v != "0")
751 .unwrap_or(true)
752}
753
754fn fa_v3_on() -> bool {
762 std::env::var("MEMRA_FA_V3")
766 .map(|v| v != "0")
767 .unwrap_or(true)
768}
769
770fn fa_v4_mode() -> &'static str {
775 static M: std::sync::OnceLock<String> = std::sync::OnceLock::new();
776 M.get_or_init(|| std::env::var("MEMRA_FA_V4").unwrap_or_default())
777}
778fn fa_v4_on() -> bool {
779 fa_v4_mode() != "0"
780} pub static FA_SMEM_TKV_DEFAULT: std::sync::atomic::AtomicUsize =
789 std::sync::atomic::AtomicUsize::new(1024);
790pub static FA_V4_MAX_DEFAULT: std::sync::atomic::AtomicUsize =
791 std::sync::atomic::AtomicUsize::new(usize::MAX);
792pub fn fa_v4_at_pub(t_kv: usize) -> bool {
793 fa_v4_at(t_kv)
794}
795fn fa_v4_at(t_kv: usize) -> bool {
796 static M: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
797 let mx = *M.get_or_init(|| {
798 std::env::var("MEMRA_FA_V4_MAX")
799 .ok()
800 .and_then(|v| v.parse().ok())
801 .unwrap_or_else(|| FA_V4_MAX_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
802 });
803 fa_v4_on() && t_kv < mx
804}
805pub const FA_DEEP_MIN_DEFAULT: usize = 0;
819fn fa_deep_at(t_kv: usize) -> bool {
820 if std::env::var("MEMRA_FA_DEEP").as_deref() == Ok("0") {
821 return false;
822 }
823 let min = std::env::var("MEMRA_FA_DEEP_MIN")
824 .ok()
825 .and_then(|v| v.parse().ok())
826 .unwrap_or(FA_DEEP_MIN_DEFAULT);
827 t_kv >= min
828}
829pub fn fa_deep_at_pub(t_kv: usize) -> bool {
831 fa_deep_at(t_kv)
832}
833
834fn fa_v3_active(head_dim: usize) -> bool {
835 fa_v3_on()
838 && head_dim % 128 == 0
839 && kv_cache_formats() == ("q8_0", "q5_1")
840 && !Engine::kv_fp8_on()
841}
842
843pub fn fa_seqs_eligible(t_kv: usize, head_dim: usize) -> bool {
851 std::env::var("MEMRA_NO_FA_VEC").is_err()
852 && t_kv >= fa_vec_min_tkv()
853 && head_dim == 256
854 && fa_v4_at(t_kv)
855 && !matches!(fa_v4_mode(), "noB3" | "stage")
856 && !Engine::kv_fp8_on()
857}
858pub fn fa_split_keys_pub(t_kv: usize, n_head_kv: usize) -> usize {
860 fa_split_keys(t_kv, n_head_kv)
861}
862
863struct PinnedStage {
868 ptr: *mut u8,
869 cap: usize,
870}
871unsafe impl Send for PinnedStage {}
872impl PinnedStage {
873 fn new(cap: usize) -> Result<Self, Box<dyn std::error::Error>> {
874 let ptr = unsafe { cudarc::driver::result::malloc_host(cap, 0)? } as *mut u8;
875 Ok(PinnedStage { ptr, cap })
876 }
877}
878impl Drop for PinnedStage {
879 fn drop(&mut self) {
880 let _ = unsafe { cudarc::driver::result::free_host(self.ptr as _) };
881 }
882}
883
884pub const ARGMAX_NB: usize = 256;
887
888pub(crate) use memra_fa3_vl as fa3_vl_raw;
890
891unsafe extern "C" {
892 fn memra_fa3_prefill(
894 q16: *const core::ffi::c_void,
895 k16: *const core::ffi::c_void,
896 v16: *const core::ffi::c_void,
897 o: *mut f32,
898 t: i32,
899 h: i32,
900 hkv: i32,
901 d: i32,
902 scale: f32,
903 stream: *mut core::ffi::c_void,
904 ) -> i32;
905 pub(crate) fn memra_fa3_vl(
907 q16s: *const *const core::ffi::c_void,
908 k16s: *const *const core::ffi::c_void,
909 v16s: *const *const core::ffi::c_void,
910 os: *const *mut f32,
911 ts: *const i32,
912 b: i32,
913 h: i32,
914 hkv: i32,
915 d: i32,
916 scale: f32,
917 stream: *mut core::ffi::c_void,
918 ) -> i32;
919}
920
921#[repr(C)]
926#[derive(Clone, Copy)]
927pub struct WPtr8(pub [u64; 8]);
928unsafe impl cudarc::driver::DeviceRepr for WPtr8 {}
929
930#[repr(C)]
935#[derive(Clone, Copy, Default)]
936pub struct GdnSeqVl {
937 pub kb16: u64,
938 pub gcum: u64,
939 pub beta: u64,
940 pub u: u64,
941 pub wb16: u64,
942 pub y: u64,
943 pub ssnap: u64,
944 pub state_in: u64,
945 pub state_out: u64,
946 pub q: u64,
947 pub p: u64,
948 pub o: u64,
949 pub k: u64,
950 pub v: u64,
951 pub g: u64,
952 pub a: u64,
953 pub w: u64,
954 pub t: i32,
955 pub nc: i32,
956}
957unsafe impl cudarc::driver::DeviceRepr for GdnSeqVl {}
958#[repr(C)]
959#[derive(Clone, Copy)]
960pub struct GdnVl8(pub [GdnSeqVl; 8]);
961unsafe impl cudarc::driver::DeviceRepr for GdnVl8 {}
962
963#[repr(C)]
966#[derive(Clone, Copy, Default)]
967pub struct GdnWVl {
968 pub qb16: u64,
969 pub pb16: u64,
970}
971unsafe impl cudarc::driver::DeviceRepr for GdnWVl {}
972#[repr(C)]
973#[derive(Clone, Copy)]
974pub struct GdnWVl8(pub [GdnWVl; 8]);
975unsafe impl cudarc::driver::DeviceRepr for GdnWVl8 {}
976
977#[repr(C)]
979#[derive(Clone, Copy, Default)]
980pub struct GdnPrepVl {
981 pub qkv: u64,
982 pub conv_state: u64,
983 pub conv_out: u64,
984 pub q_g: u64,
985 pub k_g: u64,
986 pub v_g: u64,
987 pub q_l2: u64,
988 pub k_l2: u64,
989 pub beta_raw: u64,
990 pub alpha: u64,
991 pub beta: u64,
992 pub g_log: u64,
993 pub o: u64,
994 pub z: u64,
995 pub gn: u64,
996 pub gn16: u64,
997 pub kb16: u64,
998 pub qb16: u64,
999 pub t: i32,
1000 pub pad: i32,
1001}
1002unsafe impl cudarc::driver::DeviceRepr for GdnPrepVl {}
1003#[repr(C)]
1004#[derive(Clone, Copy)]
1005pub struct GdnPrepVl8(pub [GdnPrepVl; 8]);
1006unsafe impl cudarc::driver::DeviceRepr for GdnPrepVl8 {}
1007
1008#[repr(C)]
1010#[derive(Clone, Copy, Default)]
1011pub struct FaSeqVl {
1012 pub q: u64,
1013 pub k16: u64,
1014 pub v16: u64,
1015 pub o: u64,
1016 pub kf: u64,
1017 pub vf: u64,
1018 pub t: i32,
1019 pub pad: i32,
1020}
1021unsafe impl cudarc::driver::DeviceRepr for FaSeqVl {}
1022#[repr(C)]
1023#[derive(Clone, Copy)]
1024pub struct FaVl8(pub [FaSeqVl; 8]);
1025unsafe impl cudarc::driver::DeviceRepr for FaVl8 {}
1026
1027#[repr(C)]
1029#[derive(Clone, Copy, Default)]
1030pub struct AttnPreVl {
1031 pub qf: u64,
1032 pub kf: u64,
1033 pub vf: u64,
1034 pub q: u64,
1035 pub gate: u64,
1036 pub qn: u64,
1037 pub kn: u64,
1038 pub kc: u64,
1039 pub vc: u64,
1040 pub t: i32,
1041 pub pad: i32,
1042}
1043unsafe impl cudarc::driver::DeviceRepr for AttnPreVl {}
1044#[repr(C)]
1045#[derive(Clone, Copy)]
1046pub struct AttnPreVl8(pub [AttnPreVl; 8]);
1047unsafe impl cudarc::driver::DeviceRepr for AttnPreVl8 {}
1048
1049pub struct GdnChunkBufs {
1052 pub gcum: CudaSlice<f32>,
1053 pub a: CudaSlice<f32>,
1054 pub p: CudaSlice<f32>,
1055 pub u: CudaSlice<f32>,
1056 pub w: CudaSlice<f32>,
1057 pub kb16: CudaSlice<u8>,
1058 pub wb16: CudaSlice<u8>,
1059 pub y16: CudaSlice<u8>,
1060 pub ssnap16: CudaSlice<u8>,
1061 pub qb16: CudaSlice<u8>,
1062 pub pb16: CudaSlice<u8>,
1063 pub o: CudaSlice<f32>,
1064 pub t: usize,
1065 pub nc: usize,
1066}
1067
1068#[repr(C)]
1070#[derive(Clone, Copy)]
1071pub struct F32x8(pub [f32; 8]);
1072unsafe impl cudarc::driver::DeviceRepr for F32x8 {}
1073
1074pub static PRIME_NANOS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1078
1079impl Engine {
1080 pub fn new(ordinal: usize) -> Result<Self, Box<dyn std::error::Error>> {
1081 let gpu = memra_runtime::Gpu::new(ordinal)?;
1082 if std::env::var("MEMRA_ARCH_CHECK").as_deref() != Ok("0") {
1086 use cudarc::driver::sys::CUdevice_attribute_enum as A;
1087 let (maj, min) = cudarc::driver::result::device::get(ordinal as i32)
1088 .and_then(|d| unsafe {
1089 Ok((
1090 cudarc::driver::result::device::get_attribute(
1091 d,
1092 A::CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR,
1093 )?,
1094 cudarc::driver::result::device::get_attribute(
1095 d,
1096 A::CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR,
1097 )?,
1098 ))
1099 })
1100 .unwrap_or((0, 0));
1101 let built = env!("MEMRA_BUILT_CUDA_ARCH");
1102 let ok = matches!(
1103 (built, maj, min),
1104 ("120a", 12, 0) | ("120a", 12, 1) | ("100a", 10, 0) | ("90a", 9, 0) | ("89", 8, 9)
1105 );
1106 if !ok {
1107 return Err(format!(
1108 "memra was built for sm_{built} but device {ordinal} reports compute \
1109 capability {maj}.{min}. Rebuild on this machine (MEMRA_CUDA_ARCH \
1110 auto-detects the GPU) or set MEMRA_ARCH_CHECK=0 to bypass."
1111 )
1112 .into());
1113 }
1114 }
1115 unsafe {
1120 use cudarc::driver::sys;
1121 let dev: sys::CUdevice = ordinal as sys::CUdevice;
1122 let mut pool: sys::CUmemoryPool = std::ptr::null_mut();
1123 if sys::cuDeviceGetDefaultMemPool(&mut pool, dev) == sys::CUresult::CUDA_SUCCESS {
1124 let mut thresh: u64 = u64::MAX;
1125 let _ = sys::cuMemPoolSetAttribute(
1126 pool,
1127 sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_RELEASE_THRESHOLD,
1128 &mut thresh as *mut u64 as *mut core::ffi::c_void,
1129 );
1130 }
1131 }
1132 let module = gpu.ctx.load_module(Ptx::from_binary(FATBIN.to_vec()))?;
1133 let hybrid = gpu
1134 .ctx
1135 .load_module(Ptx::from_binary(HYBRID_FATBIN.to_vec()))?;
1136 let qmatvec = gpu
1137 .ctx
1138 .load_module(Ptx::from_binary(QMATVEC_FATBIN.to_vec()))?;
1139 let flash = gpu
1140 .ctx
1141 .load_module(Ptx::from_binary(flash_fatbin_bytes().to_vec()))?;
1142 let gemm = gpu
1143 .ctx
1144 .load_module(Ptx::from_binary(gemm_fatbin_bytes().into_owned()))?;
1145 let router = gpu
1146 .ctx
1147 .load_module(Ptx::from_binary(ROUTER_FATBIN.to_vec()))?;
1148 let sample = gpu
1149 .ctx
1150 .load_module(Ptx::from_binary(SAMPLE_FATBIN.to_vec()))?;
1151 let copy_stream = gpu.ctx.new_stream()?;
1152 if std::env::var("MEMRA_EVT")
1168 .map(|v| v == "1")
1169 .unwrap_or(false)
1170 {
1171 } else {
1173 unsafe {
1174 gpu.ctx.disable_event_tracking();
1175 }
1176 }
1177 Ok(Self {
1178 gpu,
1179 module,
1180 hybrid,
1181 qmatvec,
1182 flash,
1183 flash_g: std::sync::OnceLock::new(),
1184 gemm,
1185 router,
1186 sample,
1187 moe_cache: Mutex::new(None),
1188 moe_cache_layout: Mutex::new(None),
1189 copy_stream,
1190 capture_keep_on: std::sync::atomic::AtomicBool::new(false),
1191 verify_exact: std::sync::atomic::AtomicBool::new(false),
1192 capture_keep: Mutex::new(Vec::new()),
1193 argmax_partials: Mutex::new(None),
1194 prime_deqw_ws: Mutex::new(None),
1195 router_stage: Mutex::new(None),
1196 fp8_scratch: Mutex::new(None),
1197 fa_vf16_scratch: Mutex::new(None),
1198 fa_part_pool: Mutex::new(None),
1199 fa_part_retired: Mutex::new(Vec::new()),
1200 fn_cache: Mutex::new(Default::default()),
1201 f16_scratch: Mutex::new(None),
1202 #[cfg(memra_cutlass)]
1203 cutlass_scratch: Mutex::new(None),
1204 })
1205 }
1206
1207 pub fn ctx(&self) -> &Arc<CudaContext> {
1208 &self.gpu.ctx
1209 }
1210
1211 pub fn pool_cached_bytes(&self) -> usize {
1229 let (reserved, used) = self.pool_reserved_used();
1230 reserved.saturating_sub(used)
1231 }
1232
1233 pub fn pool_reserved_used(&self) -> (usize, usize) {
1240 use cudarc::driver::sys;
1241 unsafe {
1242 let mut pool: sys::CUmemoryPool = std::ptr::null_mut();
1243 if sys::cuDeviceGetDefaultMemPool(&mut pool, self.gpu.ctx.ordinal() as sys::CUdevice)
1244 != sys::CUresult::CUDA_SUCCESS
1245 {
1246 return (0, 0);
1247 }
1248 let (mut reserved, mut used) = (0u64, 0u64);
1249 if sys::cuMemPoolGetAttribute(
1250 pool,
1251 sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_RESERVED_MEM_CURRENT,
1252 &mut reserved as *mut u64 as *mut core::ffi::c_void,
1253 ) != sys::CUresult::CUDA_SUCCESS
1254 {
1255 return (0, 0);
1256 }
1257 if sys::cuMemPoolGetAttribute(
1258 pool,
1259 sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_USED_MEM_CURRENT,
1260 &mut used as *mut u64 as *mut core::ffi::c_void,
1261 ) != sys::CUresult::CUDA_SUCCESS
1262 {
1263 return (0, 0);
1264 }
1265 (reserved as usize, used as usize)
1266 }
1267 }
1268
1269 pub fn stream(&self) -> Arc<CudaStream> {
1272 self.gpu.stream()
1273 }
1274 pub fn gkv_on() -> bool {
1277 memra_kv::gkv_on()
1278 }
1279
1280 pub fn wkv_on() -> bool {
1292 memra_kv::wkv_on()
1293 }
1294
1295 pub fn kv_fp8_on() -> bool {
1301 memra_kv::kv_fp8_on()
1302 }
1303
1304 fn fa_func(&self, name: &str, head_dim: usize) -> CudaFunction {
1307 if head_dim == 512 && Self::gkv_on() {
1308 self.func_g(name)
1309 } else {
1310 self.func(name)
1311 }
1312 }
1313
1314 fn func_g(&self, name: &str) -> CudaFunction {
1318 let m = self.flash_g.get_or_init(|| {
1319 self.gpu
1320 .ctx
1321 .load_module(cudarc::nvrtc::Ptx::from_binary(
1322 FLASH_FATBIN_KF8VF8.to_vec(),
1323 ))
1324 .expect("load kf8vf8 flash fatbin (fp8-globals arm)")
1325 });
1326 let key = format!("g:{name}");
1327 if let Some(f) = self.fn_cache.lock().unwrap().get(&key) {
1328 return f.clone();
1329 }
1330 let f = match m.load_function(name) {
1331 Ok(f) => f,
1332 Err(_) => self.func(name),
1333 };
1334 self.fn_cache.lock().unwrap().insert(key, f.clone());
1335 f
1336 }
1337
1338 fn func(&self, name: &str) -> CudaFunction {
1339 if let Some(f) = self.fn_cache.lock().unwrap().get(name) {
1342 return f.clone();
1343 }
1344 let f = self
1345 .module
1346 .load_function(name)
1347 .or_else(|_| self.hybrid.load_function(name))
1348 .or_else(|_| self.qmatvec.load_function(name))
1349 .or_else(|_| self.flash.load_function(name))
1350 .or_else(|_| self.gemm.load_function(name))
1351 .or_else(|_| self.router.load_function(name))
1352 .or_else(|_| self.sample.load_function(name))
1353 .unwrap_or_else(|_| panic!("kernel {name} not in any fatbin"));
1354 self.fn_cache
1355 .lock()
1356 .unwrap()
1357 .insert(name.to_string(), f.clone());
1358 f
1359 }
1360
1361 pub fn scatter_trim_logits(
1364 &self,
1365 src: &CudaSlice<f32>,
1366 d2t: &CudaSlice<u32>,
1367 dst: &mut CudaSlice<f32>,
1368 d_vocab: usize,
1369 n_vocab: usize,
1370 ) -> Result<(), Box<dyn std::error::Error>> {
1371 let f1 = self.func("scatter_trim_logits_f32");
1372 let f2 = self.func("scatter_trim_logits_pass2_f32");
1373 let (dv, nv) = (d_vocab as i32, n_vocab as i32);
1374 let cfg1 = LaunchConfig {
1375 grid_dim: (256, 1, 1),
1376 block_dim: (256, 1, 1),
1377 shared_mem_bytes: 0,
1378 };
1379 let __s_b1 = self.gpu.stream();
1380 let mut b1 = __s_b1.launch_builder(&f1);
1381 b1.arg(src).arg(d2t).arg(&mut *dst).arg(&dv).arg(&nv);
1382 unsafe {
1383 b1.launch(cfg1)?;
1384 }
1385 let cfg2 = LaunchConfig {
1386 grid_dim: (d_vocab.div_ceil(256) as u32, 1, 1),
1387 block_dim: (256, 1, 1),
1388 shared_mem_bytes: 0,
1389 };
1390 let __s_b2 = self.gpu.stream();
1391 let mut b2 = __s_b2.launch_builder(&f2);
1392 b2.arg(src).arg(d2t).arg(&mut *dst).arg(&dv);
1393 unsafe {
1394 b2.launch(cfg2)?;
1395 }
1396 Ok(())
1397 }
1398
1399 #[allow(clippy::too_many_arguments)]
1405 pub fn filter_stats(
1406 &self,
1407 x: &CudaSlice<f32>,
1408 row_stride: usize,
1409 rows: &CudaSlice<i32>,
1410 out_th: &mut CudaSlice<f32>,
1411 out_z: &mut CudaSlice<f32>,
1412 out_max: &mut CudaSlice<f32>,
1413 n: usize,
1414 nrow: usize,
1415 temp: f32,
1416 top_k: i32,
1417 top_p: f32,
1418 min_p: f32,
1419 ) -> Result<(), Box<dyn std::error::Error>> {
1420 let f = self.func("filter_stats_f32");
1427 let (ni, nr, rs) = (n as i32, nrow as i32, row_stride as i64);
1428 let cfg = LaunchConfig {
1429 grid_dim: (nrow as u32, 1, 1),
1430 block_dim: (1024, 1, 1),
1431 shared_mem_bytes: 0,
1432 };
1433 let __s_b = self.gpu.stream();
1434 let mut b = __s_b.launch_builder(&f);
1435 b.arg(x)
1436 .arg(&rs)
1437 .arg(rows)
1438 .arg(&mut *out_th)
1439 .arg(&mut *out_z)
1440 .arg(&mut *out_max)
1441 .arg(&ni)
1442 .arg(&nr)
1443 .arg(&temp)
1444 .arg(&top_k)
1445 .arg(&top_p)
1446 .arg(&min_p);
1447 unsafe {
1448 b.launch(cfg)?;
1449 }
1450 Ok(())
1451 }
1452
1453 #[allow(clippy::too_many_arguments)]
1455 pub fn softmax_gather_filtered(
1456 &self,
1457 x: &CudaSlice<f32>,
1458 row_stride: usize,
1459 ids: &CudaSlice<u32>,
1460 rows: &CudaSlice<i32>,
1461 th: &CudaSlice<f32>,
1462 z: &CudaSlice<f32>,
1463 out: &mut CudaSlice<f32>,
1464 n: usize,
1465 npair: usize,
1466 temp: f32,
1467 ) -> Result<(), Box<dyn std::error::Error>> {
1468 let f = self.func("softmax_gather_filtered_f32");
1469 let (ni, np, rs) = (n as i32, npair as i32, row_stride as i64);
1470 let cfg = LaunchConfig {
1471 grid_dim: (npair as u32, 1, 1),
1472 block_dim: (256, 1, 1),
1473 shared_mem_bytes: 0,
1474 };
1475 let __s_b = self.gpu.stream();
1476 let mut b = __s_b.launch_builder(&f);
1477 b.arg(x)
1478 .arg(&rs)
1479 .arg(ids)
1480 .arg(rows)
1481 .arg(th)
1482 .arg(z)
1483 .arg(&mut *out)
1484 .arg(&ni)
1485 .arg(&np)
1486 .arg(&temp);
1487 unsafe {
1488 b.launch(cfg)?;
1489 }
1490 Ok(())
1491 }
1492
1493 #[allow(clippy::too_many_arguments)]
1495 pub fn residual_sample_filtered(
1496 &self,
1497 p: &CudaSlice<f32>,
1498 q: Option<&CudaSlice<f32>>,
1499 n: usize,
1500 temp: f32,
1501 seed: u64,
1502 stream_pos: u32,
1503 p_stats: (f32, f32, f32),
1504 q_stats: (f32, f32, f32),
1505 out_tok: &mut CudaSlice<u32>,
1506 ) -> Result<(), Box<dyn std::error::Error>> {
1507 let f = self.func("residual_sample_filtered_f32");
1508 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
1509 let has_q: i32 = q.is_some() as i32;
1510 let qbuf = q.unwrap_or(p);
1511 let (pm, pth, pz) = p_stats;
1512 let (qm, qth, qz) = q_stats;
1513 let cfg = LaunchConfig {
1514 grid_dim: (1, 1, 1),
1515 block_dim: (1024, 1, 1),
1516 shared_mem_bytes: 0,
1517 };
1518 let __s_b = self.gpu.stream();
1519 let mut b = __s_b.launch_builder(&f);
1520 b.arg(p)
1521 .arg(qbuf)
1522 .arg(&has_q)
1523 .arg(&ni)
1524 .arg(&temp)
1525 .arg(&slo)
1526 .arg(&shi)
1527 .arg(&stream_pos)
1528 .arg(&pm)
1529 .arg(&pth)
1530 .arg(&pz)
1531 .arg(&qm)
1532 .arg(&qth)
1533 .arg(&qz)
1534 .arg(&mut *out_tok);
1535 unsafe {
1536 b.launch(cfg)?;
1537 }
1538 Ok(())
1539 }
1540
1541 #[allow(clippy::too_many_arguments)]
1543 pub fn gumbel_perturb_filtered(
1544 &self,
1545 x: &CudaSlice<f32>,
1546 y: &mut CudaSlice<f32>,
1547 n: usize,
1548 seed: u64,
1549 stream_pos: u32,
1550 temp: f32,
1551 row_max: f32,
1552 th: f32,
1553 ) -> Result<(), Box<dyn std::error::Error>> {
1554 let f = self.func("gumbel_perturb_filtered_f32");
1555 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
1556 let cfg = LaunchConfig {
1557 grid_dim: (n.div_ceil(256) as u32, 1, 1),
1558 block_dim: (256, 1, 1),
1559 shared_mem_bytes: 0,
1560 };
1561 let __s_b = self.gpu.stream();
1562 let mut b = __s_b.launch_builder(&f);
1563 b.arg(x)
1564 .arg(&mut *y)
1565 .arg(&ni)
1566 .arg(&slo)
1567 .arg(&shi)
1568 .arg(&stream_pos)
1569 .arg(&temp)
1570 .arg(&row_max)
1571 .arg(&th);
1572 unsafe {
1573 b.launch(cfg)?;
1574 }
1575 Ok(())
1576 }
1577
1578 #[allow(clippy::too_many_arguments)]
1582 pub fn penalize_logits(
1583 &self,
1584 x: &mut CudaSlice<f32>,
1585 hist: &CudaSlice<u32>,
1586 n_hist: usize,
1587 rep: f32,
1588 freq: f32,
1589 present: f32,
1590 n: usize,
1591 ) -> Result<(), Box<dyn std::error::Error>> {
1592 if n_hist == 0 {
1593 return Ok(());
1594 }
1595 let f = self.func("penalize_logits_f32");
1596 let (nh, ni) = (n_hist as i32, n as i32);
1597 let cfg = LaunchConfig {
1598 grid_dim: (n_hist.div_ceil(128) as u32, 1, 1),
1599 block_dim: (128, 1, 1),
1600 shared_mem_bytes: 0,
1601 };
1602 let __s_b = self.gpu.stream();
1603 let mut b = __s_b.launch_builder(&f);
1604 b.arg(&mut *x)
1605 .arg(hist)
1606 .arg(&nh)
1607 .arg(&rep)
1608 .arg(&freq)
1609 .arg(&present)
1610 .arg(&ni);
1611 unsafe {
1612 b.launch(cfg)?;
1613 }
1614 Ok(())
1615 }
1616
1617 #[allow(clippy::too_many_arguments)]
1619 pub fn penalize_logits_rows(
1620 &self,
1621 x: &mut CudaSlice<f32>,
1622 hist: &CudaSlice<u32>,
1623 n_hist: usize,
1624 rep: f32,
1625 freq: f32,
1626 present: f32,
1627 n: usize,
1628 nrow: usize,
1629 ) -> Result<(), Box<dyn std::error::Error>> {
1630 if n_hist == 0 || nrow == 0 {
1631 return Ok(());
1632 }
1633 let f = self.func("penalize_logits_rows_f32");
1634 let (nh, ni, nr) = (n_hist as i32, n as i32, nrow as i32);
1635 let cfg = LaunchConfig {
1636 grid_dim: (n_hist.div_ceil(128) as u32, nrow as u32, 1),
1637 block_dim: (128, 1, 1),
1638 shared_mem_bytes: 0,
1639 };
1640 let __s_b = self.gpu.stream();
1641 let mut b = __s_b.launch_builder(&f);
1642 b.arg(&mut *x)
1643 .arg(hist)
1644 .arg(&nh)
1645 .arg(&rep)
1646 .arg(&freq)
1647 .arg(&present)
1648 .arg(&ni)
1649 .arg(&nr);
1650 unsafe {
1651 b.launch(cfg)?;
1652 }
1653 Ok(())
1654 }
1655
1656 pub fn wpf_level() -> u32 {
1664 static ON: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
1665 *ON.get_or_init(|| {
1666 std::env::var("MEMRA_WPF")
1667 .ok()
1668 .and_then(|v| v.parse().ok())
1669 .unwrap_or(1)
1670 })
1671 }
1672
1673 pub fn set_verify_exact(&self, on: bool) {
1685 self.verify_exact
1686 .store(on, std::sync::atomic::Ordering::Relaxed);
1687 }
1688 pub(crate) fn verify_exact_on(&self) -> bool {
1689 self.verify_exact.load(std::sync::atomic::Ordering::Relaxed)
1690 }
1691
1692 pub fn qkv_append_on() -> bool {
1695 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1696 *ON.get_or_init(|| {
1697 std::env::var("MEMRA_QKV_APPEND")
1698 .map(|v| v != "0")
1699 .unwrap_or(true)
1700 })
1701 }
1702
1703 pub fn pdl_wb_on() -> bool {
1706 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1707 *ON.get_or_init(|| {
1708 std::env::var("MEMRA_PDL_WB")
1709 .map(|v| v != "0")
1710 .unwrap_or(true)
1711 })
1712 }
1713
1714 pub fn pdl_mmvq_on() -> bool {
1718 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1719 *ON.get_or_init(|| {
1720 std::env::var("MEMRA_PDL_MMVQ")
1721 .map(|v| v != "0")
1722 .unwrap_or(true)
1723 })
1724 }
1725
1726 pub fn pdl_on() -> bool {
1727 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1728 *ON.get_or_init(|| std::env::var("MEMRA_PDL").map(|v| v != "0").unwrap_or(true))
1729 }
1730
1731 pub fn pdl_nvfp4q8_on() -> bool {
1737 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1738 *ON.get_or_init(|| {
1739 std::env::var("MEMRA_PDL_NVFP4")
1740 .map(|v| v != "0")
1741 .unwrap_or(true)
1742 })
1743 }
1744
1745 fn q40_mr1_on() -> bool {
1751 static Q40MR: std::sync::OnceLock<Option<u32>> = std::sync::OnceLock::new();
1752 match *Q40MR.get_or_init(|| {
1753 std::env::var("MEMRA_Q40_MR")
1754 .ok()
1755 .and_then(|v| v.parse().ok())
1756 }) {
1757 Some(v) => v == 1,
1758 None => crate::FUSED_MR1_DEFAULT.load(std::sync::atomic::Ordering::Relaxed),
1759 }
1760 }
1761
1762 fn pdl_func_flash(
1767 &self,
1768 g: bool,
1769 name: &'static str,
1770 ) -> Result<cudarc::driver::sys::CUfunction, Box<dyn std::error::Error>> {
1771 use cudarc::driver::sys as cu;
1772 static MODS: std::sync::Mutex<Option<std::collections::HashMap<(usize, bool), usize>>> =
1779 std::sync::Mutex::new(None);
1780 static FNS: std::sync::Mutex<
1781 Option<std::collections::HashMap<(usize, bool, &'static str), usize>>,
1782 > = std::sync::Mutex::new(None);
1783 let ctx_key = self.ctx().cu_ctx() as usize;
1784 if let Some(&f) = FNS
1785 .lock()
1786 .unwrap()
1787 .get_or_insert_with(Default::default)
1788 .get(&(ctx_key, g, name))
1789 {
1790 return Ok(f as cu::CUfunction);
1791 }
1792 let module = {
1793 let mut mods = MODS.lock().unwrap();
1794 let map = mods.get_or_insert_with(Default::default);
1795 match map.get(&(ctx_key, g)) {
1796 Some(&m) => m,
1797 None => {
1798 let m = self.pdl_load_module_in_ctx(if g {
1799 FLASH_FATBIN_KF8VF8
1800 } else {
1801 FLASH_FATBIN
1802 })?;
1803 map.insert((ctx_key, g), m);
1804 m
1805 }
1806 }
1807 };
1808 let cname = std::ffi::CString::new(name)?;
1809 let mut f: cu::CUfunction = std::ptr::null_mut();
1810 let r = unsafe { cu::cuModuleGetFunction(&mut f, module as cu::CUmodule, cname.as_ptr()) };
1811 if r != cu::CUresult::CUDA_SUCCESS {
1812 return Err(format!("pdl_func_flash {name} (g={g}): {r:?}").into());
1813 }
1814 FNS.lock()
1815 .unwrap()
1816 .get_or_insert_with(Default::default)
1817 .insert((ctx_key, g, name), f as usize);
1818 Ok(f)
1819 }
1820
1821 fn pdl_load_module_in_ctx(&self, bytes: &[u8]) -> Result<usize, Box<dyn std::error::Error>> {
1826 use cudarc::driver::sys as cu;
1827 let mut prev: cu::CUcontext = std::ptr::null_mut();
1828 unsafe {
1829 cu::cuCtxGetCurrent(&mut prev).result()?;
1830 }
1831 self.ctx().bind_to_thread()?;
1832 let mut m: cu::CUmodule = std::ptr::null_mut();
1833 let r = unsafe { cu::cuModuleLoadData(&mut m, bytes.as_ptr() as *const std::ffi::c_void) };
1834 let restore = if prev.is_null() {
1835 cu::CUresult::CUDA_SUCCESS
1836 } else {
1837 unsafe { cu::cuCtxSetCurrent(prev) }
1838 };
1839 if r != cu::CUresult::CUDA_SUCCESS {
1840 return Err(format!("pdl module load: {r:?}").into());
1841 }
1842 if restore != cu::CUresult::CUDA_SUCCESS {
1843 return Err(format!("pdl module load: ctx restore {restore:?}").into());
1844 }
1845 Ok(m as usize)
1846 }
1847
1848 fn pdl_func(
1849 &self,
1850 name: &'static str,
1851 ) -> Result<cudarc::driver::sys::CUfunction, Box<dyn std::error::Error>> {
1852 use cudarc::driver::sys as cu;
1853 static MODULES: std::sync::Mutex<Option<std::collections::HashMap<usize, usize>>> =
1856 std::sync::Mutex::new(None);
1857 static QMODULES: std::sync::Mutex<Option<std::collections::HashMap<usize, usize>>> =
1860 std::sync::Mutex::new(None);
1861 static FNS: std::sync::Mutex<
1862 Option<std::collections::HashMap<(usize, &'static str), usize>>,
1863 > = std::sync::Mutex::new(None);
1864 let ctx_key = self.ctx().cu_ctx() as usize;
1865 if let Some(&f) = FNS
1866 .lock()
1867 .unwrap()
1868 .get_or_insert_with(Default::default)
1869 .get(&(ctx_key, name))
1870 {
1871 return Ok(f as cu::CUfunction);
1872 }
1873 let module = {
1874 let mut mods = MODULES.lock().unwrap();
1875 let map = mods.get_or_insert_with(Default::default);
1876 match map.get(&ctx_key) {
1877 Some(&m) => m,
1878 None => {
1879 let m = self.pdl_load_module_in_ctx(FATBIN)?;
1880 map.insert(ctx_key, m);
1881 m
1882 }
1883 }
1884 };
1885 let cname = std::ffi::CString::new(name)?;
1886 let mut f: cu::CUfunction = std::ptr::null_mut();
1887 let mut r =
1888 unsafe { cu::cuModuleGetFunction(&mut f, module as cu::CUmodule, cname.as_ptr()) };
1889 if r == cu::CUresult::CUDA_ERROR_NOT_FOUND {
1890 let qmodule = {
1891 let mut mods = QMODULES.lock().unwrap();
1892 let map = mods.get_or_insert_with(Default::default);
1893 match map.get(&ctx_key) {
1894 Some(&m) => m,
1895 None => {
1896 let m = self.pdl_load_module_in_ctx(QMATVEC_FATBIN)?;
1897 map.insert(ctx_key, m);
1898 m
1899 }
1900 }
1901 };
1902 r = unsafe { cu::cuModuleGetFunction(&mut f, qmodule as cu::CUmodule, cname.as_ptr()) };
1903 }
1904 if r != cu::CUresult::CUDA_SUCCESS {
1905 return Err(format!("pdl_func {name}: {r:?}").into());
1906 }
1907 FNS.lock()
1908 .unwrap()
1909 .get_or_insert_with(Default::default)
1910 .insert((ctx_key, name), f as usize);
1911 Ok(f)
1912 }
1913
1914 unsafe fn launch_pdl_flash(
1926 &self,
1927 g: bool,
1928 name: &'static str,
1929 grid: (u32, u32, u32),
1930 block: (u32, u32, u32),
1931 smem: u32,
1932 params: &mut [*mut std::ffi::c_void],
1933 ) -> Result<(), Box<dyn std::error::Error>> {
1934 use cudarc::driver::sys as cu;
1935 let f = self.pdl_func_flash(g, name)?;
1936 if smem > 0 {
1937 let r =
1939 unsafe {
1940 cu::cuFuncSetAttribute(f,
1941 cu::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
1942 smem as i32)
1943 };
1944 if r != cu::CUresult::CUDA_SUCCESS {
1945 return Err(format!("pdl smem attr {name}: {r:?}").into());
1946 }
1947 }
1948 let mut attr = cu::CUlaunchAttribute {
1949 id: cu::CUlaunchAttributeID::CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION,
1950 pad: [0; 4],
1951 value: cu::CUlaunchAttributeValue {
1952 programmaticStreamSerializationAllowed: 1,
1953 },
1954 };
1955 let cfg = cu::CUlaunchConfig {
1956 gridDimX: grid.0,
1957 gridDimY: grid.1,
1958 gridDimZ: grid.2,
1959 blockDimX: block.0,
1960 blockDimY: block.1,
1961 blockDimZ: block.2,
1962 sharedMemBytes: smem,
1963 hStream: self.gpu.stream().cu_stream(),
1964 attrs: &mut attr,
1965 numAttrs: 1,
1966 };
1967 let r = unsafe { cu::cuLaunchKernelEx(&cfg, f, params.as_mut_ptr(), std::ptr::null_mut()) };
1968 if r != cu::CUresult::CUDA_SUCCESS {
1969 return Err(format!("launch_pdl_flash {name}: {r:?}").into());
1970 }
1971 Ok(())
1972 }
1973
1974 unsafe fn launch_pdl(
1975 &self,
1976 name: &'static str,
1977 grid: (u32, u32, u32),
1978 block: (u32, u32, u32),
1979 params: &mut [*mut std::ffi::c_void],
1980 ) -> Result<(), Box<dyn std::error::Error>> {
1981 use cudarc::driver::sys as cu;
1982 let f = self.pdl_func(name)?;
1983 let mut attr = cu::CUlaunchAttribute {
1984 id: cu::CUlaunchAttributeID::CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION,
1985 pad: [0; 4],
1986 value: cu::CUlaunchAttributeValue {
1987 programmaticStreamSerializationAllowed: 1,
1988 },
1989 };
1990 let cfg = cu::CUlaunchConfig {
1991 gridDimX: grid.0,
1992 gridDimY: grid.1,
1993 gridDimZ: grid.2,
1994 blockDimX: block.0,
1995 blockDimY: block.1,
1996 blockDimZ: block.2,
1997 sharedMemBytes: 0,
1998 hStream: self.gpu.stream().cu_stream(),
1999 attrs: &mut attr,
2000 numAttrs: 1,
2001 };
2002 let r = unsafe { cu::cuLaunchKernelEx(&cfg, f, params.as_mut_ptr(), std::ptr::null_mut()) };
2003 if r != cu::CUresult::CUDA_SUCCESS {
2004 return Err(format!("launch_pdl {name}: {r:?}").into());
2005 }
2006 Ok(())
2007 }
2008
2009 pub fn prefetch_weight_l2(
2012 &self,
2013 w: &crate::model::GpuTensor,
2014 ) -> Result<(), Box<dyn std::error::Error>> {
2015 if let crate::model::GpuTensor::Quant { bytes, rp4, .. } = w {
2016 let p = rp4.as_ref().unwrap_or(bytes);
2017 self.prefetch_l2(p, p.len())?;
2018 }
2019 Ok(())
2020 }
2021
2022 pub fn gather_row_bf16(
2025 &self,
2026 table: &CudaSlice<u8>,
2027 tok: &CudaSlice<u32>,
2028 idx: usize,
2029 dst: &mut CudaSlice<f32>,
2030 ncols: usize,
2031 ) -> Result<(), Box<dyn std::error::Error>> {
2032 let f = self.func("gather_row_bf16_f32");
2033 let cfg = LaunchConfig {
2034 grid_dim: (ncols.div_ceil(256) as u32, 1, 1),
2035 block_dim: (256, 1, 1),
2036 shared_mem_bytes: 0,
2037 };
2038 let (nc, ix) = (ncols as i32, idx as i32);
2039 let __s_b = self.gpu.stream();
2040 let mut b = __s_b.launch_builder(&f);
2041 b.arg(table).arg(tok).arg(&ix).arg(dst).arg(&nc);
2042 unsafe {
2043 b.launch(cfg)?;
2044 }
2045 Ok(())
2046 }
2047
2048 pub fn add_row_inplace(
2050 &self,
2051 logits: &mut CudaSlice<f32>,
2052 bias: &CudaSlice<f32>,
2053 n: usize,
2054 row_off: usize,
2055 ) -> Result<(), Box<dyn std::error::Error>> {
2056 let f = self.func("add_row_inplace_f32");
2057 let cfg = LaunchConfig {
2058 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2059 block_dim: (256, 1, 1),
2060 shared_mem_bytes: 0,
2061 };
2062 let (ni, off) = (n as i32, row_off as i64);
2063 let __s_b = self.gpu.stream();
2064 let mut b = __s_b.launch_builder(&f);
2065 b.arg(logits).arg(bias).arg(&ni).arg(&off);
2066 unsafe {
2067 b.launch(cfg)?;
2068 }
2069 Ok(())
2070 }
2071
2072 pub fn prefetch_l2(
2074 &self,
2075 p: &CudaSlice<u8>,
2076 n: usize,
2077 ) -> Result<(), Box<dyn std::error::Error>> {
2078 let f = self.func("prefetch_l2_bytes");
2079 let lines = n.div_ceil(128);
2080 let ni = n as i64;
2081 let cfg = LaunchConfig {
2082 grid_dim: (lines.div_ceil(256) as u32, 1, 1),
2083 block_dim: (256, 1, 1),
2084 shared_mem_bytes: 0,
2085 };
2086 let __s_b = self.gpu.stream();
2087 let mut b = __s_b.launch_builder(&f);
2088 b.arg(p).arg(&ni);
2089 unsafe {
2090 b.launch(cfg)?;
2091 }
2092 Ok(())
2093 }
2094
2095 pub fn router_gemv(
2098 &self,
2099 w: &CudaSlice<f32>,
2100 x: &CudaSlice<f32>,
2101 n_embd: usize,
2102 n_experts: usize,
2103 t: usize,
2104 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2105 let w8 = match std::env::var("MEMRA_ROUTER_V2").as_deref() {
2111 Ok("0") => false,
2112 Ok(_) => true,
2113 Err(_) => ROUTER_W8_DEFAULT.load(std::sync::atomic::Ordering::Relaxed),
2114 };
2115 let batch = w8 && t >= ROUTER_BATCH_MIN_T && router_batch_on();
2125 self.router_gemv_form(w, x, n_embd, n_experts, t, w8, batch)
2126 }
2127
2128 pub fn router_gemv_form(
2131 &self,
2132 w: &CudaSlice<f32>,
2133 x: &CudaSlice<f32>,
2134 n_embd: usize,
2135 n_experts: usize,
2136 t: usize,
2137 w8: bool,
2138 batch: bool,
2139 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2140 debug_assert!(!batch || w8, "batch twin exists for the w8 form only");
2141 let mut y = self.alloc_uninit::<f32>(t * n_experts)?;
2142 let f = if batch {
2143 self.func("router_gemv_f32_w8_batch")
2144 } else if w8 {
2145 self.func("router_gemv_f32_w8")
2146 } else {
2147 self.func("router_gemv_f32")
2148 };
2149 let (ne, nx, ti) = (n_embd as i32, n_experts as i32, t as i32);
2150 let cfg = if batch {
2151 LaunchConfig {
2152 grid_dim: (n_experts.div_ceil(8) as u32, t.div_ceil(8) as u32, 1),
2153 block_dim: (32, 8, 1),
2154 shared_mem_bytes: 0,
2155 }
2156 } else {
2157 LaunchConfig {
2158 grid_dim: (n_experts as u32, t as u32, 1),
2159 block_dim: (32, if w8 { 8 } else { 1 }, 1),
2160 shared_mem_bytes: 0,
2161 }
2162 };
2163 let __s_b = self.gpu.stream();
2164 let mut b = __s_b.launch_builder(&f);
2165 b.arg(w).arg(x).arg(&mut y).arg(&ne).arg(&nx).arg(&ti);
2166 unsafe {
2167 b.launch(cfg)?;
2168 }
2169 Ok(y)
2170 }
2171
2172 pub fn rows_permute(
2174 &self,
2175 src: &CudaSlice<f32>,
2176 idx: &CudaSlice<i32>,
2177 nrows: usize,
2178 ncols: usize,
2179 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2180 let mut dst = self.alloc_uninit::<f32>(nrows * ncols)?;
2181 let f = self.func("rows_permute_f32");
2182 let (nc, nr) = (ncols as i32, nrows as i32);
2183 let cfg = LaunchConfig {
2184 grid_dim: (nrows as u32, 1, 1),
2185 block_dim: (256, 1, 1),
2186 shared_mem_bytes: 0,
2187 };
2188 let __s_b = self.gpu.stream();
2189 let mut b = __s_b.launch_builder(&f);
2190 b.arg(src).arg(idx).arg(&mut dst).arg(&nc).arg(&nr);
2191 unsafe {
2192 b.launch(cfg)?;
2193 }
2194 Ok(dst)
2195 }
2196
2197 pub fn sigmoid_dot_rows(
2202 &self,
2203 x: &CudaSlice<f32>,
2204 w: &CudaSlice<f32>,
2205 n_embd: usize,
2206 t: usize,
2207 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2208 static OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2211 if *OFF.get_or_init(|| std::env::var("MEMRA_SHEXP_DOT").as_deref() == Ok("0")) {
2212 let gs = self.linear(x, w, t, n_embd, 1)?;
2213 let mut g = self.uninit(t)?;
2214 self.sigmoid(&gs, &mut g, t)?;
2215 return Ok(g);
2216 }
2217 let mut g = self.alloc_uninit::<f32>(t)?;
2223 let f = self.func("sigmoid_dot_rows_f32");
2224 let (ne, ti) = (n_embd as i32, t as i32);
2225 let cfg = LaunchConfig {
2226 grid_dim: (t as u32, 1, 1),
2227 block_dim: (32, 8, 1),
2228 shared_mem_bytes: 0,
2229 };
2230 let __s_b = self.gpu.stream();
2231 let mut b = __s_b.launch_builder(&f);
2232 b.arg(x).arg(w).arg(&mut g).arg(&ne).arg(&ti);
2233 unsafe {
2234 b.launch(cfg)?;
2235 }
2236 Ok(g)
2237 }
2238
2239 pub fn spec_rollback_stream(
2241 &self,
2242 len_ptrs: &CudaSlice<u64>,
2243 pos_start: &CudaSlice<i32>,
2244 acc: &CudaSlice<u32>,
2245 base: usize,
2246 n_rows: usize,
2247 ) -> Result<(), Box<dyn std::error::Error>> {
2248 let f = self.func("spec_rollback_stream");
2249 let (b, nr) = (base as i32, n_rows as i32);
2250 let cfg = LaunchConfig {
2251 grid_dim: (n_rows.div_ceil(64) as u32, 1, 1),
2252 block_dim: (64, 1, 1),
2253 shared_mem_bytes: 0,
2254 };
2255 let __s_bl = self.gpu.stream();
2256 let mut bl = __s_bl.launch_builder(&f);
2257 bl.arg(len_ptrs).arg(pos_start).arg(acc).arg(&b).arg(&nr);
2258 unsafe {
2259 bl.launch(cfg)?;
2260 }
2261 Ok(())
2262 }
2263
2264 pub fn plain_tok_ring(
2266 &self,
2267 vam: &CudaSlice<u32>,
2268 pos_start: &CudaSlice<i32>,
2269 base: usize,
2270 ring: &mut CudaSlice<u32>,
2271 ) -> Result<(), Box<dyn std::error::Error>> {
2272 let f = self.func("plain_tok_ring");
2273 let (b, cap) = (base as i32, ring.len() as i32);
2274 let cfg = LaunchConfig {
2275 grid_dim: (1, 1, 1),
2276 block_dim: (32, 1, 1),
2277 shared_mem_bytes: 0,
2278 };
2279 let __s_bl = self.gpu.stream();
2280 let mut bl = __s_bl.launch_builder(&f);
2281 bl.arg(vam).arg(pos_start).arg(&b).arg(&mut *ring).arg(&cap);
2282 unsafe {
2283 bl.launch(cfg)?;
2284 }
2285 Ok(())
2286 }
2287
2288 pub fn spec_ring_commit(
2290 &self,
2291 vtok: &CudaSlice<u32>,
2292 acc: &CudaSlice<u32>,
2293 brk: &CudaSlice<u32>,
2294 ring: &mut CudaSlice<u32>,
2295 pend: &mut CudaSlice<u32>,
2296 ) -> Result<(), Box<dyn std::error::Error>> {
2297 let f = self.func("spec_ring_commit");
2298 let cfg = LaunchConfig {
2299 grid_dim: (1, 1, 1),
2300 block_dim: (32, 1, 1),
2301 shared_mem_bytes: 0,
2302 };
2303 let __s_b = self.gpu.stream();
2304 let mut b = __s_b.launch_builder(&f);
2305 b.arg(vtok).arg(acc).arg(brk).arg(ring).arg(pend);
2306 unsafe {
2307 b.launch(cfg)?;
2308 }
2309 Ok(())
2310 }
2311 pub fn i32_copy_add(
2312 &self,
2313 src: &CudaSlice<i32>,
2314 dst: &mut CudaSlice<i32>,
2315 delta: i32,
2316 ) -> Result<(), Box<dyn std::error::Error>> {
2317 let f = self.func("i32_copy_add");
2318 let cfg = LaunchConfig {
2319 grid_dim: (1, 1, 1),
2320 block_dim: (32, 1, 1),
2321 shared_mem_bytes: 0,
2322 };
2323 let __s_b = self.gpu.stream();
2324 let mut b = __s_b.launch_builder(&f);
2325 b.arg(src).arg(dst).arg(&delta);
2326 unsafe {
2327 b.launch(cfg)?;
2328 }
2329 Ok(())
2330 }
2331 pub fn u32_copy(
2332 &self,
2333 src: &CudaSlice<u32>,
2334 dst: &mut CudaSlice<u32>,
2335 ) -> Result<(), Box<dyn std::error::Error>> {
2336 let f = self.func("u32_copy");
2337 let cfg = LaunchConfig {
2338 grid_dim: (1, 1, 1),
2339 block_dim: (32, 1, 1),
2340 shared_mem_bytes: 0,
2341 };
2342 let __s_b = self.gpu.stream();
2343 let mut b = __s_b.launch_builder(&f);
2344 b.arg(src).arg(dst);
2345 unsafe {
2346 b.launch(cfg)?;
2347 }
2348 Ok(())
2349 }
2350
2351 pub fn spec_adapt_k(
2355 &self,
2356 acc: &CudaSlice<u32>,
2357 brk: &mut CudaSlice<u32>,
2358 floor: usize,
2359 cap: usize,
2360 ) -> Result<(), Box<dyn std::error::Error>> {
2361 let f = self.func("spec_adapt_k");
2362 let (fl, cp) = (floor as i32, cap as i32);
2363 let cfg = LaunchConfig {
2364 grid_dim: (1, 1, 1),
2365 block_dim: (32, 1, 1),
2366 shared_mem_bytes: 0,
2367 };
2368 let __s_b = self.gpu.stream();
2369 let mut b = __s_b.launch_builder(&f);
2370 b.arg(acc).arg(brk).arg(&fl).arg(&cp);
2371 unsafe {
2372 b.launch(cfg)?;
2373 }
2374 Ok(())
2375 }
2376
2377 pub fn spec_accept_greedy_dc(
2379 &self,
2380 preds: &CudaSlice<u32>,
2381 vtok: &CudaSlice<u32>,
2382 last_pred: &CudaSlice<u32>,
2383 brk: &CudaSlice<u32>,
2384 out: &mut CudaSlice<u32>,
2385 ) -> Result<(), Box<dyn std::error::Error>> {
2386 let f = self.func("spec_accept_greedy_dc");
2387 let cfg = LaunchConfig {
2388 grid_dim: (1, 1, 1),
2389 block_dim: (32, 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(preds).arg(vtok).arg(last_pred).arg(brk).arg(out);
2395 unsafe {
2396 b.launch(cfg)?;
2397 }
2398 Ok(())
2399 }
2400
2401 pub fn pos_iota(
2403 &self,
2404 pos0: &CudaSlice<i32>,
2405 out: &mut CudaSlice<i32>,
2406 t: usize,
2407 ) -> Result<(), Box<dyn std::error::Error>> {
2408 let f = self.func("pos_iota_i32");
2409 let ti = t as i32;
2410 let cfg = LaunchConfig {
2411 grid_dim: (1, 1, 1),
2412 block_dim: (t.max(1) as u32, 1, 1),
2413 shared_mem_bytes: 0,
2414 };
2415 let __s_b = self.gpu.stream();
2416 let mut b = __s_b.launch_builder(&f);
2417 b.arg(pos0).arg(out).arg(&ti);
2418 unsafe {
2419 b.launch(cfg)?;
2420 }
2421 Ok(())
2422 }
2423 #[allow(clippy::too_many_arguments)]
2424 pub fn append_kv_quantized_rows_dc(
2425 &self,
2426 k_rows: &CudaSlice<f32>,
2427 v_rows: &CudaSlice<f32>,
2428 kc: &mut CudaSlice<u8>,
2429 vc: &mut CudaSlice<u8>,
2430 t0_dev: &CudaSlice<i32>,
2431 t: usize,
2432 kv_dim_k: usize,
2433 kv_dim_v: usize,
2434 k_tok_bytes: usize,
2435 v_tok_bytes: usize,
2436 g: bool,
2437 ) -> Result<(), Box<dyn std::error::Error>> {
2438 let f = if g {
2439 self.func_g("append_quantize_kv_q8_0_q5_1_rows_dc")
2440 } else {
2441 self.func("append_quantize_kv_q8_0_q5_1_rows_dc")
2442 };
2443 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
2444 let cfg = LaunchConfig {
2445 grid_dim: (nblk, t as u32, 1),
2446 block_dim: (32, 1, 1),
2447 shared_mem_bytes: 0,
2448 };
2449 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
2450 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
2451 let __s_b = self.gpu.stream();
2452 let mut b = __s_b.launch_builder(&f);
2453 b.arg(k_rows)
2454 .arg(v_rows)
2455 .arg(kc)
2456 .arg(vc)
2457 .arg(t0_dev)
2458 .arg(&kdk)
2459 .arg(&kdv)
2460 .arg(&ktb)
2461 .arg(&vtb);
2462 unsafe {
2463 b.launch(cfg)?;
2464 }
2465 Ok(())
2466 }
2467
2468 #[allow(clippy::too_many_arguments)]
2471 pub fn append_kv_quantized_row_dc_inc(
2472 &self,
2473 k_row: &CudaSlice<f32>,
2474 v_row: &CudaSlice<f32>,
2475 kc: &mut CudaSlice<u8>,
2476 vc: &mut CudaSlice<u8>,
2477 t0_dev: &mut CudaSlice<i32>,
2478 kv_dim_k: usize,
2479 kv_dim_v: usize,
2480 k_tok_bytes: usize,
2481 v_tok_bytes: usize,
2482 g: bool,
2483 ) -> Result<(), Box<dyn std::error::Error>> {
2484 let f = if g {
2485 self.func_g("append_quantize_kv_q8_0_q5_1_dc_inc")
2486 } else {
2487 self.func("append_quantize_kv_q8_0_q5_1_dc_inc")
2488 };
2489 let nthreads = ((kv_dim_k.max(kv_dim_v) / 32) * 32).min(1024) as u32;
2490 let cfg = LaunchConfig {
2491 grid_dim: (1, 1, 1),
2492 block_dim: (nthreads, 1, 1),
2493 shared_mem_bytes: 0,
2494 };
2495 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
2496 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
2497 let __s_b = self.gpu.stream();
2498 let mut b = __s_b.launch_builder(&f);
2499 b.arg(k_row)
2500 .arg(v_row)
2501 .arg(kc)
2502 .arg(vc)
2503 .arg(t0_dev)
2504 .arg(&kdk)
2505 .arg(&kdv)
2506 .arg(&ktb)
2507 .arg(&vtb);
2508 unsafe {
2509 b.launch(cfg)?;
2510 }
2511 Ok(())
2512 }
2513
2514 pub fn pack_tok_p(
2516 &self,
2517 tok: &CudaSlice<u32>,
2518 p: &CudaSlice<f32>,
2519 out: &mut CudaSlice<u32>,
2520 slot: usize,
2521 ) -> Result<(), Box<dyn std::error::Error>> {
2522 let f = self.func("pack_tok_p");
2523 let sl = slot as i32;
2524 let cfg = LaunchConfig {
2525 grid_dim: (1, 1, 1),
2526 block_dim: (32, 1, 1),
2527 shared_mem_bytes: 0,
2528 };
2529 let __s_b = self.gpu.stream();
2530 let mut b = __s_b.launch_builder(&f);
2531 b.arg(tok).arg(p).arg(out).arg(&sl);
2532 unsafe {
2533 b.launch(cfg)?;
2534 }
2535 Ok(())
2536 }
2537 pub fn tok_map_u32(
2538 &self,
2539 tok: &mut CudaSlice<u32>,
2540 map: &CudaSlice<u32>,
2541 ) -> Result<(), Box<dyn std::error::Error>> {
2542 let f = self.func("tok_map_u32");
2543 let cfg = LaunchConfig {
2544 grid_dim: (1, 1, 1),
2545 block_dim: (32, 1, 1),
2546 shared_mem_bytes: 0,
2547 };
2548 let __s_b = self.gpu.stream();
2549 let mut b = __s_b.launch_builder(&f);
2550 b.arg(tok).arg(map);
2551 unsafe {
2552 b.launch(cfg)?;
2553 }
2554 Ok(())
2555 }
2556
2557 #[allow(clippy::too_many_arguments)]
2559 pub fn spec_assemble_verify(
2560 &self,
2561 tokp: &CudaSlice<u32>,
2562 pend: &CudaSlice<u32>,
2563 d2t: Option<&CudaSlice<u32>>,
2564 vtok: &mut CudaSlice<u32>,
2565 brk: &mut CudaSlice<u32>,
2566 p_min: f32,
2567 k: usize,
2568 pmin0: bool,
2569 ) -> Result<(), Box<dyn std::error::Error>> {
2570 let f = self.func("spec_assemble_verify");
2571 let (ki, pm) = (k as i32, if pmin0 { 1i32 } else { 0i32 });
2572 let cfg = LaunchConfig {
2573 grid_dim: (1, 1, 1),
2574 block_dim: (32, 1, 1),
2575 shared_mem_bytes: 0,
2576 };
2577 let __s_b = self.gpu.stream();
2578 let mut b = __s_b.launch_builder(&f);
2579 match d2t {
2580 Some(m) => {
2581 b.arg(tokp)
2582 .arg(pend)
2583 .arg(m)
2584 .arg(vtok)
2585 .arg(brk)
2586 .arg(&p_min)
2587 .arg(&ki)
2588 .arg(&pm);
2589 unsafe {
2590 b.launch(cfg)?;
2591 }
2592 }
2593 None => {
2594 let null: u64 = 0;
2595 b.arg(tokp)
2596 .arg(pend)
2597 .arg(&null)
2598 .arg(vtok)
2599 .arg(brk)
2600 .arg(&p_min)
2601 .arg(&ki)
2602 .arg(&pm);
2603 unsafe {
2604 b.launch(cfg)?;
2605 }
2606 }
2607 }
2608 Ok(())
2609 }
2610
2611 #[allow(clippy::too_many_arguments)]
2613 pub fn ssm_conv_ring_rebuild_dc(
2614 &self,
2615 qkv_tm: &CudaSlice<f32>,
2616 ring_old: &CudaSlice<f32>,
2617 conv_state: &mut CudaSlice<f32>,
2618 conv_dim: usize,
2619 acc: &CudaSlice<u32>,
2620 base: usize,
2621 t_v: usize,
2622 d_conv: usize,
2623 ) -> Result<(), Box<dyn std::error::Error>> {
2624 let f = self.func("ssm_conv_ring_rebuild_f32_dc");
2625 let n = conv_dim * (d_conv - 1);
2626 let cfg = LaunchConfig::for_num_elems(n as u32);
2627 let (cd, b0, tv, dc) = (conv_dim as i32, base as i32, t_v as i32, d_conv as i32);
2628 let __s_b = self.gpu.stream();
2629 let mut b = __s_b.launch_builder(&f);
2630 b.arg(qkv_tm)
2631 .arg(ring_old)
2632 .arg(conv_state)
2633 .arg(&cd)
2634 .arg(acc)
2635 .arg(&b0)
2636 .arg(&tv)
2637 .arg(&dc);
2638 unsafe {
2639 b.launch(cfg)?;
2640 }
2641 Ok(())
2642 }
2643 #[allow(clippy::too_many_arguments)]
2644 pub fn gdn_scan_s128_dc(
2645 &self,
2646 q: &CudaSlice<f32>,
2647 k: &CudaSlice<f32>,
2648 v: &CudaSlice<f32>,
2649 g: &CudaSlice<f32>,
2650 beta: &CudaSlice<f32>,
2651 state_in: &CudaSlice<f32>,
2652 state_out: &mut CudaSlice<f32>,
2653 o: &mut CudaSlice<f32>,
2654 n_head: usize,
2655 acc: &CudaSlice<u32>,
2656 base: usize,
2657 t_v: usize,
2658 scale: f32,
2659 ) -> Result<(), Box<dyn std::error::Error>> {
2660 let f = self.func("gdn_scan_s128_dc");
2661 const S_V: u32 = 128;
2662 const WARP: u32 = 32;
2663 const COLS_PER_BLOCK: u32 = 4;
2664 let cfg = LaunchConfig {
2665 grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
2666 block_dim: (WARP, COLS_PER_BLOCK, 1),
2667 shared_mem_bytes: 0,
2668 };
2669 let (h, b0, tv) = (n_head as i32, base as i32, t_v as i32);
2670 let __s_b = self.gpu.stream();
2671 let mut b = __s_b.launch_builder(&f);
2672 b.arg(q)
2673 .arg(k)
2674 .arg(v)
2675 .arg(g)
2676 .arg(beta)
2677 .arg(state_in)
2678 .arg(state_out)
2679 .arg(o)
2680 .arg(&h)
2681 .arg(acc)
2682 .arg(&b0)
2683 .arg(&tv)
2684 .arg(&scale);
2685 unsafe {
2686 b.launch(cfg)?;
2687 }
2688 Ok(())
2689 }
2690
2691 pub fn spec_rollback_kv(
2693 &self,
2694 len_ptrs: &CudaSlice<u64>,
2695 saved: &CudaSlice<i32>,
2696 acc: &CudaSlice<u32>,
2697 base: usize,
2698 n_layer: usize,
2699 ) -> Result<(), Box<dyn std::error::Error>> {
2700 let f = self.func("spec_rollback_kv");
2701 let (b, nl) = (base as i32, n_layer as i32);
2702 let cfg = LaunchConfig {
2703 grid_dim: (n_layer.div_ceil(64) as u32, 1, 1),
2704 block_dim: (64, 1, 1),
2705 shared_mem_bytes: 0,
2706 };
2707 let __s_bl = self.gpu.stream();
2708 let mut bl = __s_bl.launch_builder(&f);
2709 bl.arg(len_ptrs).arg(saved).arg(acc).arg(&b).arg(&nl);
2710 unsafe {
2711 bl.launch(cfg)?;
2712 }
2713 Ok(())
2714 }
2715
2716 pub fn spec_fork_valid(
2718 &self,
2719 acc: &CudaSlice<u32>,
2720 optimistic_pending: u32,
2721 valid: &mut CudaSlice<u32>,
2722 ) -> Result<(), Box<dyn std::error::Error>> {
2723 let f = self.func("spec_fork_valid");
2724 let cfg = LaunchConfig {
2725 grid_dim: (1, 1, 1),
2726 block_dim: (1, 1, 1),
2727 shared_mem_bytes: 0,
2728 };
2729 let __s_bl = self.gpu.stream();
2730 let mut bl = __s_bl.launch_builder(&f);
2731 bl.arg(acc).arg(&optimistic_pending).arg(valid);
2732 unsafe {
2733 bl.launch(cfg)?;
2734 }
2735 Ok(())
2736 }
2737
2738 pub fn spec_fork_reconcile_kv(
2740 &self,
2741 len_ptrs: &CudaSlice<u64>,
2742 saved: &CudaSlice<i32>,
2743 acc: &CudaSlice<u32>,
2744 valid: &CudaSlice<u32>,
2745 base: usize,
2746 n_layer: usize,
2747 ) -> Result<(), Box<dyn std::error::Error>> {
2748 let f = self.func("spec_fork_reconcile_kv");
2749 let (b, nl) = (base as i32, n_layer as i32);
2750 let cfg = LaunchConfig {
2751 grid_dim: (n_layer.div_ceil(64) as u32, 1, 1),
2752 block_dim: (64, 1, 1),
2753 shared_mem_bytes: 0,
2754 };
2755 let __s_bl = self.gpu.stream();
2756 let mut bl = __s_bl.launch_builder(&f);
2757 bl.arg(len_ptrs)
2758 .arg(saved)
2759 .arg(acc)
2760 .arg(valid)
2761 .arg(&b)
2762 .arg(&nl);
2763 unsafe {
2764 bl.launch(cfg)?;
2765 }
2766 Ok(())
2767 }
2768
2769 pub fn spec_fork_restore_f32(
2771 &self,
2772 snapshot: &CudaSlice<f32>,
2773 state: &mut CudaSlice<f32>,
2774 valid: &CudaSlice<u32>,
2775 ) -> Result<(), Box<dyn std::error::Error>> {
2776 assert_eq!(
2777 snapshot.len(),
2778 state.len(),
2779 "fork recurrent snapshot shape mismatch"
2780 );
2781 let f = self.func("spec_fork_restore_f32");
2782 let n = state.len() as i32;
2783 let blocks = state.len().div_ceil(256).min(65535).max(1) as u32;
2784 let cfg = LaunchConfig {
2785 grid_dim: (blocks, 1, 1),
2786 block_dim: (256, 1, 1),
2787 shared_mem_bytes: 0,
2788 };
2789 let __s_bl = self.gpu.stream();
2790 let mut bl = __s_bl.launch_builder(&f);
2791 bl.arg(snapshot).arg(state).arg(valid).arg(&n);
2792 unsafe {
2793 bl.launch(cfg)?;
2794 }
2795 Ok(())
2796 }
2797
2798 pub fn spec_seed_gather(
2801 &self,
2802 vx: &CudaSlice<f32>,
2803 fill_prev: &CudaSlice<f32>,
2804 acc: &CudaSlice<u32>,
2805 h_seed: &mut CudaSlice<f32>,
2806 base: usize,
2807 n_embd: usize,
2808 ) -> Result<(), Box<dyn std::error::Error>> {
2809 let f = self.func("spec_seed_gather");
2810 let (b, ne) = (base as i32, n_embd as i32);
2811 let cfg = LaunchConfig {
2812 grid_dim: (n_embd.div_ceil(256) as u32, 1, 1),
2813 block_dim: (256, 1, 1),
2814 shared_mem_bytes: 0,
2815 };
2816 let __s_bl = self.gpu.stream();
2817 let mut bl = __s_bl.launch_builder(&f);
2818 bl.arg(vx)
2819 .arg(fill_prev)
2820 .arg(acc)
2821 .arg(h_seed)
2822 .arg(&b)
2823 .arg(&ne);
2824 unsafe {
2825 bl.launch(cfg)?;
2826 }
2827 Ok(())
2828 }
2829
2830 pub fn spec_accept_greedy(
2832 &self,
2833 preds: &CudaSlice<u32>,
2834 draft: &CudaSlice<u32>,
2835 last_pred: u32,
2836 base: usize,
2837 k_round: usize,
2838 out: &mut CudaSlice<u32>,
2839 ) -> Result<(), Box<dyn std::error::Error>> {
2840 let f = self.func("spec_accept_greedy");
2841 let (b, k) = (base as i32, k_round as i32);
2842 let cfg = LaunchConfig {
2843 grid_dim: (1, 1, 1),
2844 block_dim: (32, 1, 1),
2845 shared_mem_bytes: 0,
2846 };
2847 let __s_bl = self.gpu.stream();
2848 let mut bl = __s_bl.launch_builder(&f);
2849 bl.arg(preds)
2850 .arg(draft)
2851 .arg(&last_pred)
2852 .arg(&b)
2853 .arg(&k)
2854 .arg(out);
2855 unsafe {
2856 bl.launch(cfg)?;
2857 }
2858 Ok(())
2859 }
2860
2861 pub fn gumbel_perturb(
2868 &self,
2869 x: &CudaSlice<f32>,
2870 y: &mut CudaSlice<f32>,
2871 n: usize,
2872 seed: u64,
2873 stream_pos: u32,
2874 temp: f32,
2875 ) -> Result<(), Box<dyn std::error::Error>> {
2876 let f = self.func("gumbel_perturb_f32");
2877 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2878 let cfg = LaunchConfig {
2879 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2880 block_dim: (256, 1, 1),
2881 shared_mem_bytes: 0,
2882 };
2883 let __s_b = self.gpu.stream();
2884 let mut b = __s_b.launch_builder(&f);
2885 b.arg(x)
2886 .arg(&mut *y)
2887 .arg(&ni)
2888 .arg(&slo)
2889 .arg(&shi)
2890 .arg(&stream_pos)
2891 .arg(&temp);
2892 unsafe {
2893 b.launch(cfg)?;
2894 }
2895 Ok(())
2896 }
2897
2898 pub fn mask_logits_col(
2906 &self,
2907 logits: &mut CudaSlice<f32>,
2908 mask: &CudaSlice<u32>,
2909 col: usize,
2910 n: usize,
2911 mask_words: usize,
2912 ) -> Result<(), Box<dyn std::error::Error>> {
2913 let f = self.func("mask_logits_f32");
2914 let (ci, ni, mw) = (col as i32, n as i32, mask_words as i32);
2915 let cfg = LaunchConfig {
2916 grid_dim: (n.div_ceil(256).min(1024) as u32, 1, 1),
2917 block_dim: (256, 1, 1),
2918 shared_mem_bytes: 0,
2919 };
2920 let __s_b = self.gpu.stream();
2921 let mut b = __s_b.launch_builder(&f);
2922 b.arg(&mut *logits).arg(mask).arg(&ci).arg(&ni).arg(&mw);
2923 unsafe {
2924 b.launch(cfg)?;
2925 }
2926 Ok(())
2927 }
2928
2929 pub fn gumbel_perturb_col(
2936 &self,
2937 x: &CudaSlice<f32>,
2938 col: usize,
2939 y: &mut CudaSlice<f32>,
2940 n: usize,
2941 seed: u64,
2942 stream_pos: u32,
2943 temp: f32,
2944 ) -> Result<(), Box<dyn std::error::Error>> {
2945 let f = self.func("gumbel_perturb_f32");
2946 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2947 let col_view = x.slice(col * n..(col + 1) * n);
2948 let cfg = LaunchConfig {
2949 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2950 block_dim: (256, 1, 1),
2951 shared_mem_bytes: 0,
2952 };
2953 let __s_b = self.gpu.stream();
2954 let mut b = __s_b.launch_builder(&f);
2955 b.arg(&col_view)
2956 .arg(&mut *y)
2957 .arg(&ni)
2958 .arg(&slo)
2959 .arg(&shi)
2960 .arg(&stream_pos)
2961 .arg(&temp);
2962 unsafe {
2963 b.launch(cfg)?;
2964 }
2965 Ok(())
2966 }
2967
2968 #[allow(clippy::too_many_arguments)]
2974 pub fn gumbel_perturb_filtered_col(
2975 &self,
2976 x: &CudaSlice<f32>,
2977 col: usize,
2978 y: &mut CudaSlice<f32>,
2979 n: usize,
2980 seed: u64,
2981 stream_pos: u32,
2982 temp: f32,
2983 stat_max: &CudaSlice<f32>,
2984 stat_th: &CudaSlice<f32>,
2985 stat_idx: usize,
2986 ) -> Result<(), Box<dyn std::error::Error>> {
2987 let f = self.func("gumbel_perturb_filtered_col_f32");
2988 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2989 let (ci, si) = (col as i32, stat_idx as i32);
2990 let cfg = LaunchConfig {
2991 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2992 block_dim: (256, 1, 1),
2993 shared_mem_bytes: 0,
2994 };
2995 let __s_b = self.gpu.stream();
2996 let mut b = __s_b.launch_builder(&f);
2997 b.arg(x)
2998 .arg(&ci)
2999 .arg(&mut *y)
3000 .arg(&ni)
3001 .arg(&slo)
3002 .arg(&shi)
3003 .arg(&stream_pos)
3004 .arg(&temp)
3005 .arg(stat_max)
3006 .arg(stat_th)
3007 .arg(&si);
3008 unsafe {
3009 b.launch(cfg)?;
3010 }
3011 Ok(())
3012 }
3013
3014 pub fn sctr_inc(&self, ctr: &mut CudaSlice<u32>) -> Result<(), Box<dyn std::error::Error>> {
3019 let f = self.func("memra_sctr_inc");
3020 let cfg = LaunchConfig {
3021 grid_dim: (1, 1, 1),
3022 block_dim: (1, 1, 1),
3023 shared_mem_bytes: 0,
3024 };
3025 let __s_b = self.gpu.stream();
3026 let mut b = __s_b.launch_builder(&f);
3027 b.arg(&mut *ctr);
3028 unsafe {
3029 b.launch(cfg)?;
3030 }
3031 Ok(())
3032 }
3033
3034 pub fn gumbel_perturb_ctr(
3039 &self,
3040 x: &CudaSlice<f32>,
3041 y: &mut CudaSlice<f32>,
3042 n: usize,
3043 seed: u64,
3044 ctr: &CudaSlice<u32>,
3045 temp: f32,
3046 ) -> Result<(), Box<dyn std::error::Error>> {
3047 let f = self.func("gumbel_perturb_ctr_f32");
3048 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3049 let cfg = LaunchConfig {
3050 grid_dim: (n.div_ceil(256) as u32, 1, 1),
3051 block_dim: (256, 1, 1),
3052 shared_mem_bytes: 0,
3053 };
3054 let __s_b = self.gpu.stream();
3055 let mut b = __s_b.launch_builder(&f);
3056 b.arg(x)
3057 .arg(&mut *y)
3058 .arg(&ni)
3059 .arg(&slo)
3060 .arg(&shi)
3061 .arg(ctr)
3062 .arg(&temp);
3063 unsafe {
3064 b.launch(cfg)?;
3065 }
3066 Ok(())
3067 }
3068
3069 pub fn softmax_gather(
3073 &self,
3074 x: &CudaSlice<f32>,
3075 row_stride: usize,
3076 ids: &CudaSlice<u32>,
3077 rows: &CudaSlice<i32>,
3078 out: &mut CudaSlice<f32>,
3079 n: usize,
3080 npair: usize,
3081 temp: f32,
3082 ) -> Result<(), Box<dyn std::error::Error>> {
3083 let f = self.func("softmax_gather_f32");
3084 let (ni, rs) = (n as i32, row_stride as i64);
3085 let np = npair as i32;
3086 let cfg = LaunchConfig {
3087 grid_dim: (npair as u32, 1, 1),
3088 block_dim: (256, 1, 1),
3089 shared_mem_bytes: 0,
3090 };
3091 let __s_b = self.gpu.stream();
3092 let mut b = __s_b.launch_builder(&f);
3093 b.arg(x)
3094 .arg(&rs)
3095 .arg(ids)
3096 .arg(rows)
3097 .arg(&mut *out)
3098 .arg(&ni)
3099 .arg(&np)
3100 .arg(&temp);
3101 unsafe {
3102 b.launch(cfg)?;
3103 }
3104 Ok(())
3105 }
3106
3107 pub fn residual_sample(
3111 &self,
3112 p: &CudaSlice<f32>,
3113 q: Option<&CudaSlice<f32>>,
3114 n: usize,
3115 temp: f32,
3116 seed: u64,
3117 stream_pos: u32,
3118 out_tok: &mut CudaSlice<u32>,
3119 ) -> Result<(), Box<dyn std::error::Error>> {
3120 let f = self.func("residual_sample_f32");
3121 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3122 let nth = 1024u32;
3123 let cfg = LaunchConfig {
3124 grid_dim: (1, 1, 1),
3125 block_dim: (nth, 1, 1),
3126 shared_mem_bytes: 0,
3127 };
3128 let has_q: i32 = q.is_some() as i32;
3129 let qbuf = q.unwrap_or(p); let __s_b = self.gpu.stream();
3131 let mut b = __s_b.launch_builder(&f);
3132 b.arg(p)
3133 .arg(qbuf)
3134 .arg(&has_q)
3135 .arg(&ni)
3136 .arg(&temp)
3137 .arg(&slo)
3138 .arg(&shi)
3139 .arg(&stream_pos)
3140 .arg(&mut *out_tok);
3141 unsafe {
3142 b.launch(cfg)?;
3143 }
3144 Ok(())
3145 }
3146
3147 pub fn with_moe_cache<R>(
3152 &self,
3153 max_block_bytes: usize,
3154 f: impl FnOnce(
3155 &mut crate::moe_cache::MoeSlotCache,
3156 &Engine,
3157 ) -> Result<R, Box<dyn std::error::Error>>,
3158 ) -> Result<R, Box<dyn std::error::Error>> {
3159 let mut guard = self.moe_cache.lock().unwrap();
3160 if guard.is_none() {
3161 *guard = Some(crate::moe_cache::MoeSlotCache::new(self, max_block_bytes)?);
3162 }
3163 let cache = guard.as_mut().unwrap();
3164 f(cache, self)
3165 }
3166
3167 pub fn freeze_moe_cache(&self) {
3170 if let Some(cache) = self.moe_cache.lock().unwrap().as_mut() {
3171 cache.freeze();
3172 }
3173 }
3174
3175 pub fn export_moe_residency(&self) -> Option<Vec<(u16, u8, u16)>> {
3178 self.moe_cache
3179 .lock()
3180 .unwrap()
3181 .as_ref()
3182 .map(crate::moe_cache::MoeSlotCache::export_residency)
3183 }
3184
3185 pub(crate) fn moe_cache_frozen(&self) -> bool {
3186 self.moe_cache
3187 .lock()
3188 .unwrap()
3189 .as_ref()
3190 .is_some_and(crate::moe_cache::MoeSlotCache::is_frozen)
3191 }
3192
3193 pub fn frozen_cpu_experts_prefer_tokenwise_prime(&self) -> bool {
3200 crate::cpu_experts::configured()
3201 && self.moe_cache_frozen()
3202 && std::env::var("MEMRA_CPU_EXPERT_BATCHED_PRIME").as_deref() != Ok("1")
3203 }
3204
3205 pub(crate) fn configure_moe_cache_layout(&self, block_bytes: Vec<usize>) {
3207 assert!(
3208 self.moe_cache.lock().unwrap().is_none(),
3209 "MoE cache layout configured after cache construction"
3210 );
3211 *self.moe_cache_layout.lock().unwrap() = Some(block_bytes);
3212 }
3213
3214 pub(crate) fn moe_cache_layout(&self) -> Option<Vec<usize>> {
3215 self.moe_cache_layout.lock().unwrap().clone()
3216 }
3217
3218 pub fn moe_cache_enabled() -> bool {
3220 std::env::var("MEMRA_MOE_CACHE").as_deref() != Ok("0")
3221 }
3222
3223 pub fn moe_cache_stats(&self) -> Option<(u64, u64, u64, usize)> {
3226 let guard = self.moe_cache.lock().unwrap();
3227 guard
3228 .as_ref()
3229 .map(|c| (c.hits, c.misses, c.staged_bytes, c.n_slots()))
3230 }
3231
3232 pub fn cpu_expert_stats(
3236 &self,
3237 ) -> Option<(u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64)> {
3238 crate::cpu_experts::configured().then(crate::cpu_experts::stats)
3239 }
3240
3241 pub fn cpu_expert_predictor_stats(&self) -> (u64, u64) {
3244 crate::cpu_experts::predictor_stats()
3245 }
3246
3247 pub fn cpu_expert_exposed_wait_ns(&self) -> Option<u64> {
3248 crate::cpu_experts::configured().then(crate::cpu_experts::exposed_wait_ns)
3249 }
3250
3251 pub fn cpu_expert_gpu_residency_stats(&self) -> Option<(u64, u64, u64)> {
3254 crate::cpu_experts::configured().then(crate::cpu_experts::incomplete_gpu_residency_stats)
3255 }
3256
3257 pub fn moe_pread_stats(&self) -> Option<(u64, u64, u64, u64, u64, u64, u64)> {
3260 let guard = self.moe_cache.lock().unwrap();
3261 guard
3262 .as_ref()
3263 .and_then(|cache| cache.pread_stats())
3264 .map(|stats| {
3265 (
3266 stats.reads,
3267 stats.bytes,
3268 stats.read_errors,
3269 stats.short_reads,
3270 stats.fallbacks,
3271 stats.buffer_waits,
3272 stats.ring_full,
3273 )
3274 })
3275 }
3276
3277 pub fn spill_config_fallbacks(&self) -> u64 {
3279 crate::spill_pread::config_fallbacks()
3280 }
3281
3282 pub fn moe_cache_reset_counters(&self) {
3284 if let Some(c) = self.moe_cache.lock().unwrap().as_mut() {
3285 c.reset_counters();
3286 }
3287 }
3288
3289 pub fn htod_bytes(&self, v: &[u8]) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3290 Ok(self.gpu.stream().clone_htod(v)?)
3291 }
3292
3293 pub fn htod_bytes_padded(
3297 &self,
3298 v: &[u8],
3299 pad: usize,
3300 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3301 let mut d = self.alloc_u8_uninit(v.len() + pad)?;
3302 {
3303 let mut view = d.slice_mut(0..v.len());
3304 self.gpu.stream().memcpy_htod(v, &mut view)?;
3305 }
3306 Ok(d)
3307 }
3308
3309 pub fn copy_into(
3311 &self,
3312 dst: &mut CudaSlice<f32>,
3313 off: usize,
3314 src: &CudaSlice<f32>,
3315 len: usize,
3316 ) -> Result<(), Box<dyn std::error::Error>> {
3317 let mut view = dst.slice_mut(off..off + len);
3318 self.gpu
3319 .stream()
3320 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3321 Ok(())
3322 }
3323
3324 pub fn copy_u8_into(
3327 &self,
3328 dst: &mut CudaSlice<u8>,
3329 off: usize,
3330 src: &CudaSlice<u8>,
3331 len: usize,
3332 ) -> Result<(), Box<dyn std::error::Error>> {
3333 let mut view = dst.slice_mut(off..off + len);
3334 self.gpu
3335 .stream()
3336 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3337 Ok(())
3338 }
3339
3340 pub fn copy_u8_range_into(
3342 &self,
3343 dst: &mut CudaSlice<u8>,
3344 dst_off: usize,
3345 src: &CudaSlice<u8>,
3346 src_off: usize,
3347 len: usize,
3348 ) -> Result<(), Box<dyn std::error::Error>> {
3349 let mut dst_view = dst.slice_mut(dst_off..dst_off + len);
3350 self.gpu
3351 .stream()
3352 .memcpy_dtod(&src.slice(src_off..src_off + len), &mut dst_view)?;
3353 Ok(())
3354 }
3355
3356 pub fn prepare_kv_append(
3360 &self,
3361 kv: &mut crate::cache::KvLayer,
3362 retain_from: usize,
3363 append_rows: usize,
3364 ) -> Result<usize, Box<dyn std::error::Error>> {
3365 let Some(plan) = kv
3366 .ring
3367 .as_ref()
3368 .map(|ring| ring.append_plan(kv.len, retain_from, append_rows))
3369 .transpose()?
3370 else {
3371 return Ok(kv.len);
3372 };
3373 match plan {
3374 crate::cache::KvRingAppend::Contiguous { write_row } => Ok(write_row),
3375 crate::cache::KvRingAppend::Rebase {
3376 src_row,
3377 keep_rows,
3378 new_base,
3379 write_row,
3380 } => {
3381 if keep_rows > 0 {
3382 let k_len = keep_rows * kv.k_tok_bytes;
3383 let v_len = keep_rows * kv.v_tok_bytes;
3384 let mut k_tmp = self.alloc_u8_uninit(k_len)?;
3385 let mut v_tmp = self.alloc_u8_uninit(v_len)?;
3386 self.copy_u8_range_into(&mut k_tmp, 0, &kv.k, src_row * kv.k_tok_bytes, k_len)?;
3387 self.copy_u8_range_into(&mut v_tmp, 0, &kv.v, src_row * kv.v_tok_bytes, v_len)?;
3388 self.copy_u8_into(&mut kv.k, 0, &k_tmp, k_len)?;
3389 self.copy_u8_into(&mut kv.v, 0, &v_tmp, v_len)?;
3390 }
3391 kv.ring.as_mut().unwrap().apply_rebase(new_base);
3392 Ok(write_row)
3393 }
3394 }
3395 }
3396
3397 pub fn htod_u8_into(
3400 &self,
3401 dst: &mut CudaSlice<u8>,
3402 off: usize,
3403 src: &[u8],
3404 ) -> Result<(), Box<dyn std::error::Error>> {
3405 let mut view = dst.slice_mut(off..off + src.len());
3406 self.gpu.stream().memcpy_htod(src, &mut view)?;
3407 Ok(())
3408 }
3409
3410 pub fn view<'a>(&self, b: &'a CudaSlice<f32>, len: usize) -> cudarc::driver::CudaView<'a, f32> {
3411 b.slice(0..len)
3412 }
3413
3414 pub fn view_u8_range<'a>(
3417 &self,
3418 b: &'a CudaSlice<u8>,
3419 start: usize,
3420 end: usize,
3421 ) -> cudarc::driver::CudaView<'a, u8> {
3422 b.slice(start..end)
3423 }
3424 pub fn view_u8<'a>(
3425 &self,
3426 b: &'a CudaSlice<u8>,
3427 len: usize,
3428 ) -> cudarc::driver::CudaView<'a, u8> {
3429 b.slice(0..len)
3430 }
3431
3432 pub fn append_kv_quantized(
3436 &self,
3437 k_row: &CudaSlice<f32>,
3438 v_row: &CudaSlice<f32>,
3439 kc: &mut CudaSlice<u8>,
3440 vc: &mut CudaSlice<u8>,
3441 t: usize,
3442 kv_dim_k: usize,
3443 kv_dim_v: usize,
3444 k_tok_bytes: usize,
3445 v_tok_bytes: usize,
3446 g: bool,
3447 ) -> Result<(), Box<dyn std::error::Error>> {
3448 let f = if g {
3449 self.func_g("append_quantize_kv_q8_0_q5_1")
3450 } else {
3451 self.func("append_quantize_kv_q8_0_q5_1")
3452 };
3453 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3454 let cfg = LaunchConfig {
3455 grid_dim: (nblk, 1, 1),
3456 block_dim: (32, 1, 1),
3457 shared_mem_bytes: 0,
3458 };
3459 let (ti, kdk, kdv) = (t as i32, kv_dim_k as i32, kv_dim_v as i32);
3460 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3461 let __s_b = self.gpu.stream();
3462 let mut b = __s_b.launch_builder(&f);
3463 b.arg(k_row)
3464 .arg(v_row)
3465 .arg(kc)
3466 .arg(vc)
3467 .arg(&ti)
3468 .arg(&kdk)
3469 .arg(&kdv)
3470 .arg(&ktb)
3471 .arg(&vtb);
3472 unsafe {
3473 b.launch(cfg)?;
3474 }
3475 Ok(())
3476 }
3477
3478 pub fn append_kv_quantized_dc(
3482 &self,
3483 k_row: &CudaSlice<f32>,
3484 v_row: &CudaSlice<f32>,
3485 kc: &mut CudaSlice<u8>,
3486 vc: &mut CudaSlice<u8>,
3487 t_dev: &CudaSlice<i32>,
3488 kv_dim_k: usize,
3489 kv_dim_v: usize,
3490 k_tok_bytes: usize,
3491 v_tok_bytes: usize,
3492 g: bool,
3493 ) -> Result<(), Box<dyn std::error::Error>> {
3494 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3495 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
3496 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3497 if Self::pdl_on() && Self::pdl_wb_on() {
3499 use cudarc::driver::{DevicePtr, DevicePtrMut};
3500 let s = &self.gpu.stream();
3501 let (pk, _g0) = k_row.device_ptr(s);
3502 let (pv, _g1) = v_row.device_ptr(s);
3503 let (pkc, _g2) = kc.device_ptr_mut(s);
3504 let (pvc, _g3) = vc.device_ptr_mut(s);
3505 let (pt, _g4) = t_dev.device_ptr(s);
3506 let mut ps = [
3507 &pk as *const _ as *mut std::ffi::c_void,
3508 &pv as *const _ as *mut _,
3509 &pkc as *const _ as *mut _,
3510 &pvc as *const _ as *mut _,
3511 &pt as *const _ as *mut _,
3512 &kdk as *const _ as *mut _,
3513 &kdv as *const _ as *mut _,
3514 &ktb as *const _ as *mut _,
3515 &vtb as *const _ as *mut _,
3516 ];
3517 unsafe {
3518 self.launch_pdl_flash(
3519 g,
3520 "append_quantize_kv_q8_0_q5_1_dc",
3521 (nblk, 1, 1),
3522 (32, 1, 1),
3523 0,
3524 &mut ps,
3525 )?;
3526 }
3527 return Ok(());
3528 }
3529 let f = if g {
3530 self.func_g("append_quantize_kv_q8_0_q5_1_dc")
3531 } else {
3532 self.func("append_quantize_kv_q8_0_q5_1_dc")
3533 };
3534 let cfg = LaunchConfig {
3535 grid_dim: (nblk, 1, 1),
3536 block_dim: (32, 1, 1),
3537 shared_mem_bytes: 0,
3538 };
3539 let __s_b = self.gpu.stream();
3540 let mut b = __s_b.launch_builder(&f);
3541 b.arg(k_row)
3542 .arg(v_row)
3543 .arg(kc)
3544 .arg(vc)
3545 .arg(t_dev)
3546 .arg(&kdk)
3547 .arg(&kdv)
3548 .arg(&ktb)
3549 .arg(&vtb);
3550 unsafe {
3551 b.launch(cfg)?;
3552 }
3553 Ok(())
3554 }
3555
3556 #[allow(clippy::too_many_arguments)]
3563 pub fn append_kv_quantized_rows(
3564 &self,
3565 k_rows: &CudaSlice<f32>,
3566 v_rows: &CudaSlice<f32>,
3567 kc: &mut CudaSlice<u8>,
3568 vc: &mut CudaSlice<u8>,
3569 t0: usize,
3570 t: usize,
3571 kv_dim_k: usize,
3572 kv_dim_v: usize,
3573 k_tok_bytes: usize,
3574 v_tok_bytes: usize,
3575 g: bool,
3576 ) -> Result<(), Box<dyn std::error::Error>> {
3577 if std::env::var("MEMRA_PRIME_APPEND_LOOP").is_ok() {
3578 for i in 0..t {
3579 let k_row = k_rows.slice(i * kv_dim_k..(i + 1) * kv_dim_k);
3580 let v_row = v_rows.slice(i * kv_dim_v..(i + 1) * kv_dim_v);
3581 self.append_kv_quantized_view(
3582 &k_row,
3583 &v_row,
3584 kc,
3585 vc,
3586 t0 + i,
3587 kv_dim_k,
3588 kv_dim_v,
3589 k_tok_bytes,
3590 v_tok_bytes,
3591 g,
3592 )?;
3593 }
3594 return Ok(());
3595 }
3596 let f = if g {
3597 self.func_g("append_quantize_kv_q8_0_q5_1_rows")
3598 } else {
3599 self.func("append_quantize_kv_q8_0_q5_1_rows")
3600 };
3601 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3602 let cfg = LaunchConfig {
3603 grid_dim: (nblk, t as u32, 1),
3604 block_dim: (32, 1, 1),
3605 shared_mem_bytes: 0,
3606 };
3607 let (t0i, kdk, kdv) = (t0 as i32, kv_dim_k as i32, kv_dim_v as i32);
3608 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3609 let __s_b = self.gpu.stream();
3610 let mut b = __s_b.launch_builder(&f);
3611 b.arg(k_rows)
3612 .arg(v_rows)
3613 .arg(kc)
3614 .arg(vc)
3615 .arg(&t0i)
3616 .arg(&kdk)
3617 .arg(&kdv)
3618 .arg(&ktb)
3619 .arg(&vtb);
3620 unsafe {
3621 b.launch(cfg)?;
3622 }
3623 Ok(())
3624 }
3625
3626 pub fn inc_seqlen(&self, p: &mut CudaSlice<i32>) -> Result<(), Box<dyn std::error::Error>> {
3630 let f = self.func("inc_i32");
3631 let cfg = LaunchConfig {
3632 grid_dim: (1, 1, 1),
3633 block_dim: (1, 1, 1),
3634 shared_mem_bytes: 0,
3635 };
3636 let __s_b = self.gpu.stream();
3637 let mut b = __s_b.launch_builder(&f);
3638 b.arg(p);
3639 unsafe {
3640 b.launch(cfg)?;
3641 }
3642 Ok(())
3643 }
3644
3645 pub fn append_kv_quantized_view(
3648 &self,
3649 k_row: &cudarc::driver::CudaView<f32>,
3650 v_row: &cudarc::driver::CudaView<f32>,
3651 kc: &mut CudaSlice<u8>,
3652 vc: &mut CudaSlice<u8>,
3653 t: usize,
3654 kv_dim_k: usize,
3655 kv_dim_v: usize,
3656 k_tok_bytes: usize,
3657 v_tok_bytes: usize,
3658 g: bool,
3659 ) -> Result<(), Box<dyn std::error::Error>> {
3660 let f = if g {
3661 self.func_g("append_quantize_kv_q8_0_q5_1")
3662 } else {
3663 self.func("append_quantize_kv_q8_0_q5_1")
3664 };
3665 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3666 let cfg = LaunchConfig {
3667 grid_dim: (nblk, 1, 1),
3668 block_dim: (32, 1, 1),
3669 shared_mem_bytes: 0,
3670 };
3671 let (ti, kdk, kdv) = (t as i32, kv_dim_k as i32, kv_dim_v as i32);
3672 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3673 let __s_b = self.gpu.stream();
3674 let mut b = __s_b.launch_builder(&f);
3675 b.arg(k_row)
3676 .arg(v_row)
3677 .arg(kc)
3678 .arg(vc)
3679 .arg(&ti)
3680 .arg(&kdk)
3681 .arg(&kdv)
3682 .arg(&ktb)
3683 .arg(&vtb);
3684 unsafe {
3685 b.launch(cfg)?;
3686 }
3687 Ok(())
3688 }
3689
3690 pub fn copy_view_into(
3693 &self,
3694 dst: &mut CudaSlice<f32>,
3695 off: usize,
3696 src: &cudarc::driver::CudaView<f32>,
3697 len: usize,
3698 ) -> Result<(), Box<dyn std::error::Error>> {
3699 let mut view = dst.slice_mut(off..off + len);
3700 self.gpu
3701 .stream()
3702 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3703 Ok(())
3704 }
3705
3706 pub fn clone_dtod(
3710 &self,
3711 src: &CudaSlice<f32>,
3712 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3713 let mut dst = self.gpu.stream().alloc_zeros::<f32>(src.len())?;
3714 self.gpu.stream().memcpy_dtod(src, &mut dst)?;
3715 Ok(dst)
3716 }
3717
3718 pub fn dtod_copy_view(
3721 &self,
3722 src: &cudarc::driver::CudaView<f32>,
3723 dst: &mut CudaSlice<f32>,
3724 ) -> Result<(), Box<dyn std::error::Error>> {
3725 self.gpu.stream().memcpy_dtod(src, dst)?;
3726 Ok(())
3727 }
3728
3729 pub fn dtod_copy_view_i8(
3731 &self,
3732 src: &cudarc::driver::CudaView<i8>,
3733 dst: &mut CudaSlice<i8>,
3734 ) -> Result<(), Box<dyn std::error::Error>> {
3735 self.gpu.stream().memcpy_dtod(src, dst)?;
3736 Ok(())
3737 }
3738
3739 pub fn dtod_copy_into(
3741 &self,
3742 src: &CudaSlice<f32>,
3743 dst: &mut CudaSlice<f32>,
3744 offset: usize,
3745 ) -> Result<(), Box<dyn std::error::Error>> {
3746 let n = src.len();
3747 let mut dv = dst.slice_mut(offset..offset + n);
3748 self.gpu.stream().memcpy_dtod(src, &mut dv)?;
3749 Ok(())
3750 }
3751
3752 pub fn copy_batch_uniform_f32(
3758 &self,
3759 table: &CudaSlice<u64>,
3760 n: usize,
3761 words: usize,
3762 ) -> Result<(), Box<dyn std::error::Error>> {
3763 if n == 0 || words == 0 {
3764 return Ok(());
3765 }
3766 debug_assert!(
3767 table.len() >= 2 * n,
3768 "pointer table must hold n srcs + n dsts"
3769 );
3770 let f = self.func("copy_batch_uniform_f32");
3771 let chunks = (words / 4).max(1).div_ceil(256).min(48) as u32;
3774 let (ni, wi) = (n as i32, words as i32);
3775 let cfg = LaunchConfig {
3776 grid_dim: (chunks, n as u32, 1),
3777 block_dim: (256, 1, 1),
3778 shared_mem_bytes: 0,
3779 };
3780 let __s = self.gpu.stream();
3781 let mut b = __s.launch_builder(&f);
3782 b.arg(table).arg(&ni).arg(&wi);
3783 unsafe {
3784 b.launch(cfg)?;
3785 }
3786 Ok(())
3787 }
3788
3789 pub fn htod_u64_into(
3792 &self,
3793 v: &[u64],
3794 dst: &mut CudaSlice<u64>,
3795 ) -> Result<(), Box<dyn std::error::Error>> {
3796 let mut view = dst.slice_mut(0..v.len());
3797 self.gpu.stream().memcpy_htod(v, &mut view)?;
3798 Ok(())
3799 }
3800
3801 pub fn copy_indirect_src_f32(
3806 &self,
3807 src_entry: &cudarc::driver::CudaView<u64>,
3808 dst: &mut CudaSlice<f32>,
3809 dst_off: usize,
3810 words: usize,
3811 ) -> Result<(), Box<dyn std::error::Error>> {
3812 let f = self.func("copy_indirect_src_f32");
3813 let chunks = (words / 4).max(1).div_ceil(256).min(48) as u32;
3814 let wi = words as i32;
3815 let cfg = LaunchConfig {
3816 grid_dim: (chunks, 1, 1),
3817 block_dim: (256, 1, 1),
3818 shared_mem_bytes: 0,
3819 };
3820 let mut dv = dst.slice_mut(dst_off..dst_off + words);
3821 let __s = self.gpu.stream();
3822 let mut b = __s.launch_builder(&f);
3823 b.arg(src_entry).arg(&mut dv).arg(&wi);
3824 unsafe {
3825 b.launch(cfg)?;
3826 }
3827 Ok(())
3828 }
3829
3830 pub fn uninit_i8(&self, n: usize) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
3832 self.alloc_uninit::<i8>(n)
3833 }
3834
3835 pub fn qmatvec(
3837 &self,
3838 w: &CudaSlice<u8>,
3839 x: &CudaSlice<f32>,
3840 m: usize,
3841 in_f: usize,
3842 out_f: usize,
3843 qtype: i32,
3844 row_bytes: usize,
3845 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3846 let f = self.func("qmatvec_f32");
3847 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
3849 grid_dim: (out_f as u32, m as u32, 1),
3850 block_dim: (256, 1, 1),
3851 shared_mem_bytes: 0,
3852 };
3853 let (inf, outf, mi, qt, rb) =
3854 (in_f as i32, out_f as i32, m as i32, qtype, row_bytes as i64);
3855 let __s_b = self.gpu.stream();
3856 let mut b = __s_b.launch_builder(&f);
3857 b.arg(w)
3858 .arg(x)
3859 .arg(&mut y)
3860 .arg(&inf)
3861 .arg(&outf)
3862 .arg(&mi)
3863 .arg(&qt)
3864 .arg(&rb);
3865 unsafe {
3866 b.launch(cfg)?;
3867 }
3868 Ok(y)
3869 }
3870
3871 pub fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3873 let s = self.gpu.stream().alloc_zeros::<u8>(n)?;
3874 self.keep_if_capturing(&s);
3875 Ok(s)
3876 }
3877
3878 pub fn alloc_u8_uninit(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3882 let s = unsafe { self.gpu.stream().alloc::<u8>(n)? };
3883 self.keep_if_capturing(&s);
3884 Ok(s)
3885 }
3886
3887 pub fn memset_zeros_view(
3890 &self,
3891 dst: &mut cudarc::driver::CudaViewMut<f32>,
3892 ) -> Result<(), Box<dyn std::error::Error>> {
3893 self.gpu.stream().memset_zeros(dst)?;
3894 Ok(())
3895 }
3896
3897 pub fn stage_expert(
3903 &self,
3904 host_bytes: &[u8],
3905 scratch: &mut CudaSlice<u8>,
3906 off: usize,
3907 ) -> Result<(), Box<dyn std::error::Error>> {
3908 let mut dst = scratch.slice_mut(off..off + host_bytes.len()); self.gpu.stream().memcpy_htod(host_bytes, &mut dst)?; Ok(())
3911 }
3912
3913 pub fn moe_router_topk(
3919 &self,
3920 logits: &CudaSlice<f32>,
3921 t: usize,
3922 n_expert: usize,
3923 n_used: usize,
3924 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
3925 let f = self.func("moe_router_topk_f32");
3926 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 {
3929 grid_dim: (t as u32, 1, 1),
3930 block_dim: (n_expert as u32, 1, 1),
3931 shared_mem_bytes: 0,
3932 };
3933 let (ne, nu) = (n_expert as i32, n_used as i32);
3934 let __s_b = self.gpu.stream();
3935 let mut b = __s_b.launch_builder(&f);
3936 b.arg(logits)
3937 .arg(&mut sel_idx)
3938 .arg(&mut sel_w)
3939 .arg(&ne)
3940 .arg(&nu);
3941 unsafe {
3942 b.launch(cfg)?;
3943 }
3944 Ok((sel_idx, sel_w))
3945 }
3946
3947 pub fn moe_router_topk_scaled(
3950 &self,
3951 logits: &CudaSlice<f32>,
3952 t: usize,
3953 n_expert: usize,
3954 n_used: usize,
3955 ex_scale: &CudaSlice<f32>,
3956 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
3957 let f = self.func("moe_router_topk_scaled_f32");
3962 let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?;
3963 let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?;
3964 let cfg = LaunchConfig {
3965 grid_dim: (t as u32, 1, 1),
3966 block_dim: (n_expert as u32, 1, 1),
3967 shared_mem_bytes: 0,
3968 };
3969 let (ne, nu) = (n_expert as i32, n_used as i32);
3970 let __s_b = self.gpu.stream();
3971 let mut b = __s_b.launch_builder(&f);
3972 b.arg(logits)
3973 .arg(&mut sel_idx)
3974 .arg(&mut sel_w)
3975 .arg(&ne)
3976 .arg(&nu)
3977 .arg(ex_scale);
3978 unsafe {
3979 b.launch(cfg)?;
3980 }
3981 Ok((sel_idx, sel_w))
3982 }
3983
3984 pub fn moe_router_topk_host(
3992 &self,
3993 logits: &CudaSlice<f32>,
3994 t: usize,
3995 n_expert: usize,
3996 n_used: usize,
3997 ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
3998 let f = self.func("moe_router_topk_f32");
3999 let n = t * n_used;
4000 let mut sel_idx = self.alloc_uninit::<i32>(n)?;
4001 let mut sel_w = self.alloc_uninit::<f32>(n)?;
4002 let cfg = LaunchConfig {
4003 grid_dim: (t as u32, 1, 1),
4004 block_dim: (n_expert as u32, 1, 1),
4005 shared_mem_bytes: 0,
4006 };
4007 let (ne, nu) = (n_expert as i32, n_used as i32);
4008 let __s_b = self.gpu.stream();
4009 let mut b = __s_b.launch_builder(&f);
4010 b.arg(logits)
4011 .arg(&mut sel_idx)
4012 .arg(&mut sel_w)
4013 .arg(&ne)
4014 .arg(&nu);
4015 unsafe {
4016 b.launch(cfg)?;
4017 }
4018 let bytes = n * 8;
4020 let mut guard = self.router_stage.lock().unwrap();
4021 if guard.as_ref().map(|p| p.cap < bytes).unwrap_or(true) {
4022 *guard = Some(PinnedStage::new(bytes.max(4096))?);
4023 }
4024 let stage = guard.as_mut().unwrap();
4025 let (si, sw) = unsafe {
4026 (
4027 std::slice::from_raw_parts_mut(stage.ptr as *mut i32, n),
4028 std::slice::from_raw_parts_mut(stage.ptr.add(n * 4) as *mut f32, n),
4029 )
4030 };
4031 self.gpu.stream().memcpy_dtoh(&sel_idx, si)?; self.gpu.stream().memcpy_dtoh(&sel_w, sw)?; self.gpu.stream().synchronize()?; Ok((si.iter().map(|&i| i as u32).collect(), sw.to_vec()))
4035 }
4036
4037 #[allow(clippy::too_many_arguments)]
4041 pub fn moe_router_sigmoid_topk(
4042 &self,
4043 logits: &CudaSlice<f32>,
4044 t: usize,
4045 n_expert: usize,
4046 n_used: usize,
4047 active_count: usize,
4048 correction_bias: &CudaSlice<f32>,
4049 active: &CudaSlice<u8>,
4050 scaling_factor: f32,
4051 route_norm: bool,
4052 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
4053 crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
4054 if n_expert == 0 || n_expert > 1024 || n_used == 0 || n_used > n_expert {
4055 return Err(format!(
4056 "sigmoid router shape unsupported: n_expert={n_expert}, n_used={n_used}",
4057 )
4058 .into());
4059 }
4060 if logits.len() < t * n_expert
4061 || correction_bias.len() != n_expert
4062 || active.len() != n_expert
4063 {
4064 return Err(format!(
4065 "sigmoid router buffer mismatch: logits={} bias={} active={} expected logits>={} row={}",
4066 logits.len(), correction_bias.len(), active.len(), t * n_expert, n_expert,
4067 ).into());
4068 }
4069 let f = self.func("moe_router_sigmoid_topk_f32");
4070 let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?;
4071 let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?;
4072 let threads = n_expert.div_ceil(32) * 32;
4073 let cfg = LaunchConfig {
4074 grid_dim: (t as u32, 1, 1),
4075 block_dim: (threads as u32, 1, 1),
4076 shared_mem_bytes: 0,
4077 };
4078 let (ne, nu, rn) = (n_expert as i32, n_used as i32, i32::from(route_norm));
4079 let __s_b = self.gpu.stream();
4080 let mut b = __s_b.launch_builder(&f);
4081 b.arg(logits)
4082 .arg(correction_bias)
4083 .arg(active)
4084 .arg(&mut sel_idx)
4085 .arg(&mut sel_w)
4086 .arg(&ne)
4087 .arg(&nu)
4088 .arg(&scaling_factor)
4089 .arg(&rn);
4090 unsafe {
4091 b.launch(cfg)?;
4092 }
4093 Ok((sel_idx, sel_w))
4094 }
4095
4096 #[allow(clippy::too_many_arguments)]
4099 pub fn moe_router_sigmoid_topk_host(
4100 &self,
4101 logits: &CudaSlice<f32>,
4102 t: usize,
4103 n_expert: usize,
4104 n_used: usize,
4105 active_count: usize,
4106 correction_bias: &CudaSlice<f32>,
4107 active: &CudaSlice<u8>,
4108 scaling_factor: f32,
4109 route_norm: bool,
4110 ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
4111 let (sel_idx, sel_w) = self.moe_router_sigmoid_topk(
4112 logits,
4113 t,
4114 n_expert,
4115 n_used,
4116 active_count,
4117 correction_bias,
4118 active,
4119 scaling_factor,
4120 route_norm,
4121 )?;
4122 let n = t * n_used;
4123 let bytes = n * 8;
4124 let mut guard = self.router_stage.lock().unwrap();
4125 if guard.as_ref().map(|p| p.cap < bytes).unwrap_or(true) {
4126 *guard = Some(PinnedStage::new(bytes.max(4096))?);
4127 }
4128 let stage = guard.as_mut().unwrap();
4129 let (si, sw) = unsafe {
4130 (
4131 std::slice::from_raw_parts_mut(stage.ptr as *mut i32, n),
4132 std::slice::from_raw_parts_mut(stage.ptr.add(n * 4) as *mut f32, n),
4133 )
4134 };
4135 self.gpu.stream().memcpy_dtoh(&sel_idx, si)?;
4136 self.gpu.stream().memcpy_dtoh(&sel_w, sw)?;
4137 self.gpu.stream().synchronize()?;
4138 Ok((si.iter().map(|&i| i as u32).collect(), sw.to_vec()))
4139 }
4140
4141 pub fn stage_expert_async(
4145 &self,
4146 host_bytes: &[u8],
4147 scratch: &mut CudaSlice<u8>,
4148 off: usize,
4149 ) -> Result<cudarc::driver::CudaEvent, Box<dyn std::error::Error>> {
4150 let mut dst = scratch.slice_mut(off..off + host_bytes.len());
4151 self.copy_stream.memcpy_htod(host_bytes, &mut dst)?;
4152 Ok(self.copy_stream.record_event(None)?)
4153 }
4154
4155 pub fn compute_wait(
4157 &self,
4158 ev: &cudarc::driver::CudaEvent,
4159 ) -> Result<(), Box<dyn std::error::Error>> {
4160 self.gpu.stream().wait(ev)?;
4161 Ok(())
4162 }
4163
4164 pub fn qmatvec_view(
4169 &self,
4170 w: &CudaSlice<u8>,
4171 range: std::ops::Range<usize>,
4172 x: &cudarc::driver::CudaView<f32>,
4173 m: usize,
4174 in_f: usize,
4175 out_f: usize,
4176 qtype: i32,
4177 row_bytes: usize,
4178 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4179 let f = self.func("qmatvec_f32");
4180 let wv = w.slice(range); let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
4183 grid_dim: (out_f as u32, m as u32, 1),
4184 block_dim: (256, 1, 1),
4185 shared_mem_bytes: 0,
4186 };
4187 let (inf, outf, mi, qt, rb) =
4188 (in_f as i32, out_f as i32, m as i32, qtype, row_bytes as i64);
4189 let __s_b = self.gpu.stream();
4190 let mut b = __s_b.launch_builder(&f);
4191 b.arg(&wv)
4192 .arg(x)
4193 .arg(&mut y)
4194 .arg(&inf)
4195 .arg(&outf)
4196 .arg(&mi)
4197 .arg(&qt)
4198 .arg(&rb);
4199 unsafe {
4200 b.launch(cfg)?;
4201 }
4202 Ok(y)
4203 }
4204
4205 #[allow(clippy::too_many_arguments)]
4212 pub fn moe_gate_up_silu8_q8(
4216 &self,
4217 gp: WPtr8,
4218 up: WPtr8,
4219 aq: &CudaSlice<i8>,
4220 ad: &CudaSlice<f32>,
4221 in_f: usize,
4222 n_ff: usize,
4223 n_used: usize,
4224 qt_g: i32,
4225 qt_u: i32,
4226 rb_g: usize,
4227 rb_u: usize,
4228 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4229 let f = self.func("moe_gate_up_silu8_q8");
4230 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
4231 let cfg = LaunchConfig {
4232 grid_dim: (n_ff as u32, n_used as u32, 1),
4233 block_dim: (32, 1, 1),
4234 shared_mem_bytes: 0,
4235 };
4236 let (inf, nff, rbg, rbu) = (in_f as i32, n_ff as i32, rb_g as i64, rb_u as i64);
4237 let __s_b = self.gpu.stream();
4238 let mut b = __s_b.launch_builder(&f);
4239 b.arg(&gp)
4240 .arg(&up)
4241 .arg(aq)
4242 .arg(ad)
4243 .arg(&mut act)
4244 .arg(&inf)
4245 .arg(&nff)
4246 .arg(&qt_g)
4247 .arg(&qt_u)
4248 .arg(&rbg)
4249 .arg(&rbu);
4250 unsafe {
4251 b.launch(cfg)?;
4252 }
4253 Ok(act)
4254 }
4255
4256 #[allow(clippy::too_many_arguments)]
4257 pub fn moe_down8_fma_q8(
4258 &self,
4259 dp: WPtr8,
4260 w: F32x8,
4261 aq2: &CudaSlice<i8>,
4262 ad2: &CudaSlice<f32>,
4263 dst: &mut cudarc::driver::CudaViewMut<f32>,
4264 in_f: usize,
4265 out_f: usize,
4266 n_used: usize,
4267 qt: i32,
4268 rb: usize,
4269 ) -> Result<(), Box<dyn std::error::Error>> {
4270 let f = self.func("moe_down8_fma_q8");
4271 let cfg = LaunchConfig {
4272 grid_dim: (out_f as u32, 1, 1),
4273 block_dim: (32, 1, 1),
4274 shared_mem_bytes: 0,
4275 };
4276 let (inf, outf, nu, rbi) = (in_f as i32, out_f as i32, n_used as i32, rb as i64);
4277 let __s_b = self.gpu.stream();
4278 let mut b = __s_b.launch_builder(&f);
4279 b.arg(&dp)
4280 .arg(&w)
4281 .arg(aq2)
4282 .arg(ad2)
4283 .arg(dst)
4284 .arg(&inf)
4285 .arg(&outf)
4286 .arg(&nu)
4287 .arg(&qt)
4288 .arg(&rbi);
4289 unsafe {
4290 b.launch(cfg)?;
4291 }
4292 Ok(())
4293 }
4294
4295 pub fn qmatvec_expert_q8(
4297 &self,
4298 w: &CudaSlice<u8>,
4299 range: std::ops::Range<usize>,
4300 aq: &CudaSlice<i8>,
4301 ad: &CudaSlice<f32>,
4302 m: usize,
4303 in_f: usize,
4304 out_f: usize,
4305 qtype: i32,
4306 row_bytes: usize,
4307 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4308 let f = self.func("qmatvec_expert_q8");
4309 let wv = w.slice(range);
4310 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
4311 const ROWS: u32 = 4; let cfg = LaunchConfig {
4313 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, m as u32, 1),
4314 block_dim: (32, ROWS, 1),
4315 shared_mem_bytes: 0,
4316 };
4317 let (inf, outf, mi, rbi) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
4318 let __s_b = self.gpu.stream();
4319 let mut b = __s_b.launch_builder(&f);
4320 b.arg(&wv)
4321 .arg(aq)
4322 .arg(ad)
4323 .arg(&mut y)
4324 .arg(&inf)
4325 .arg(&outf)
4326 .arg(&mi)
4327 .arg(&qtype)
4328 .arg(&rbi);
4329 unsafe {
4330 b.launch(cfg)?;
4331 }
4332 Ok(y)
4333 }
4334
4335 pub fn moe_gate_up_silu8(
4336 &self,
4337 gp: WPtr8,
4338 up: WPtr8,
4339 x: &cudarc::driver::CudaView<f32>,
4340 in_f: usize,
4341 n_ff: usize,
4342 n_used: usize,
4343 qt_g: i32,
4344 qt_u: i32,
4345 rb_g: usize,
4346 rb_u: usize,
4347 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4348 let f = self.func("moe_gate_up_silu8_f32");
4349 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?; let cfg = LaunchConfig {
4351 grid_dim: (n_ff as u32, n_used as u32, 1),
4352 block_dim: (256, 1, 1),
4353 shared_mem_bytes: 0,
4354 };
4355 let (inf, nff, rbg, rbu) = (in_f as i32, n_ff as i32, rb_g as i64, rb_u as i64);
4356 let __s_b = self.gpu.stream();
4357 let mut b = __s_b.launch_builder(&f);
4358 b.arg(&gp)
4359 .arg(&up)
4360 .arg(x)
4361 .arg(&mut act)
4362 .arg(&inf)
4363 .arg(&nff)
4364 .arg(&qt_g)
4365 .arg(&qt_u)
4366 .arg(&rbg)
4367 .arg(&rbu);
4368 unsafe {
4369 b.launch(cfg)?;
4370 }
4371 Ok(act)
4372 }
4373
4374 #[allow(clippy::too_many_arguments)]
4380 pub fn moe_down8_fma_into(
4381 &self,
4382 dp: WPtr8,
4383 w: F32x8,
4384 act: &CudaSlice<f32>,
4385 dst: &mut cudarc::driver::CudaViewMut<f32>,
4386 in_f: usize,
4387 out_f: usize,
4388 n_used: usize,
4389 qt: i32,
4390 rb: usize,
4391 ) -> Result<(), Box<dyn std::error::Error>> {
4392 let f = self.func("moe_down8_fma_f32");
4393 let cfg = LaunchConfig {
4394 grid_dim: (out_f as u32, 1, 1),
4395 block_dim: (256, 1, 1),
4396 shared_mem_bytes: 0,
4397 };
4398 let (inf, outf, nu, rbv) = (in_f as i32, out_f as i32, n_used as i32, rb as i64);
4399 let __s_b = self.gpu.stream();
4400 let mut b = __s_b.launch_builder(&f);
4401 b.arg(&dp)
4402 .arg(&w)
4403 .arg(act)
4404 .arg(dst)
4405 .arg(&inf)
4406 .arg(&outf)
4407 .arg(&nu)
4408 .arg(&qt)
4409 .arg(&rbv);
4410 unsafe {
4411 b.launch(cfg)?;
4412 }
4413 Ok(())
4414 }
4415
4416 #[allow(clippy::too_many_arguments)]
4421 #[allow(clippy::too_many_arguments)]
4436 #[allow(clippy::too_many_arguments)]
4438 pub fn moe_pairs_matvec_q8(
4439 &self,
4440 table: &CudaSlice<u64>,
4441 proj: i32,
4442 pair_tok: &CudaSlice<i32>,
4443 pair_ex: &CudaSlice<i32>,
4444 aq: &CudaSlice<i8>,
4445 ad: &CudaSlice<f32>,
4446 in_f: usize,
4447 out_f: usize,
4448 n_expert: usize,
4449 n_pairs: usize,
4450 qtype: i32,
4451 row_bytes: usize,
4452 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4453 let f = self.func("moe_pairs_matvec_q8");
4454 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4455 const ROWS: u32 = 4;
4456 let cfg = LaunchConfig {
4457 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_pairs as u32, 1),
4458 block_dim: (32, ROWS, 1),
4459 shared_mem_bytes: 0,
4460 };
4461 let (inf, outf, ne, np, rbi) = (
4462 in_f as i32,
4463 out_f as i32,
4464 n_expert as i32,
4465 n_pairs as i32,
4466 row_bytes as i64,
4467 );
4468 let __s_b = self.gpu.stream();
4469 let mut b = __s_b.launch_builder(&f);
4470 b.arg(table)
4471 .arg(&proj)
4472 .arg(pair_tok)
4473 .arg(pair_ex)
4474 .arg(aq)
4475 .arg(ad)
4476 .arg(&mut y)
4477 .arg(&inf)
4478 .arg(&outf)
4479 .arg(&ne)
4480 .arg(&np)
4481 .arg(&qtype)
4482 .arg(&rbi);
4483 unsafe {
4484 b.launch(cfg)?;
4485 }
4486 Ok(y)
4487 }
4488
4489 #[allow(clippy::too_many_arguments)]
4491 pub fn moe_pairs_matvec_q8_em(
4492 &self,
4493 table: &CudaSlice<u64>,
4494 proj: i32,
4495 ex_ids: &CudaSlice<i32>,
4496 ex_off: &CudaSlice<i32>,
4497 ex_pairs: &CudaSlice<i32>,
4498 pair_tok: &CudaSlice<i32>,
4499 aq: &CudaSlice<i8>,
4500 ad: &CudaSlice<f32>,
4501 in_f: usize,
4502 out_f: usize,
4503 n_expert: usize,
4504 n_active: usize,
4505 n_pairs: usize,
4506 qtype: i32,
4507 row_bytes: usize,
4508 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4509 let f = self.func("moe_pairs_matvec_q8_em");
4510 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4511 const ROWS: u32 = 4;
4512 let cfg = LaunchConfig {
4513 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_active as u32, 1),
4514 block_dim: (32, ROWS, 1),
4515 shared_mem_bytes: 0,
4516 };
4517 let (inf, outf, ne, na, rbi) = (
4518 in_f as i32,
4519 out_f as i32,
4520 n_expert as i32,
4521 n_active as i32,
4522 row_bytes as i64,
4523 );
4524 let __s_b = self.gpu.stream();
4525 let mut b = __s_b.launch_builder(&f);
4526 b.arg(table)
4527 .arg(&proj)
4528 .arg(ex_ids)
4529 .arg(ex_off)
4530 .arg(ex_pairs)
4531 .arg(pair_tok)
4532 .arg(aq)
4533 .arg(ad)
4534 .arg(&mut y)
4535 .arg(&inf)
4536 .arg(&outf)
4537 .arg(&ne)
4538 .arg(&na)
4539 .arg(&qtype)
4540 .arg(&rbi);
4541 unsafe {
4542 b.launch(cfg)?;
4543 }
4544 Ok(y)
4545 }
4546
4547 #[allow(clippy::too_many_arguments)]
4550 pub fn moe_pairs_matvec_q8_dec(
4551 &self,
4552 table: &CudaSlice<u64>,
4553 proj: i32,
4554 ex_ids: &CudaSlice<i32>,
4555 ex_off: &CudaSlice<i32>,
4556 ex_pairs: &CudaSlice<i32>,
4557 pair_tok: &CudaSlice<i32>,
4558 aq: &CudaSlice<i8>,
4559 ad: &CudaSlice<f32>,
4560 in_f: usize,
4561 out_f: usize,
4562 n_expert: usize,
4563 n_active: usize,
4564 n_pairs: usize,
4565 qtype: i32,
4566 row_bytes: usize,
4567 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4568 let f = self.func("moe_pairs_matvec_q8_dec");
4569 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4570 const ROWS: u32 = 4;
4571 let cfg = LaunchConfig {
4572 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_active as u32, 1),
4573 block_dim: (32, ROWS, 1),
4574 shared_mem_bytes: 0,
4575 };
4576 let (inf, outf, ne, na, rbi) = (
4577 in_f as i32,
4578 out_f as i32,
4579 n_expert as i32,
4580 n_active as i32,
4581 row_bytes as i64,
4582 );
4583 let __s_b = self.gpu.stream();
4584 let mut b = __s_b.launch_builder(&f);
4585 b.arg(table)
4586 .arg(&proj)
4587 .arg(ex_ids)
4588 .arg(ex_off)
4589 .arg(ex_pairs)
4590 .arg(pair_tok)
4591 .arg(aq)
4592 .arg(ad)
4593 .arg(&mut y)
4594 .arg(&inf)
4595 .arg(&outf)
4596 .arg(&ne)
4597 .arg(&na)
4598 .arg(&qtype)
4599 .arg(&rbi);
4600 unsafe {
4601 b.launch(cfg)?;
4602 }
4603 Ok(y)
4604 }
4605
4606 pub fn moe_pairs_gelu_mul(
4607 &self,
4608 gate: &CudaSlice<f32>,
4609 up: &CudaSlice<f32>,
4610 n: usize,
4611 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4612 let f = self.func("moe_pairs_gelu_mul");
4613 let mut act = self.alloc_uninit::<f32>(n)?;
4614 let cfg = LaunchConfig::for_num_elems(n as u32);
4615 let nl = n as i64;
4616 let __s_b = self.gpu.stream();
4617 let mut b = __s_b.launch_builder(&f);
4618 b.arg(gate).arg(up).arg(&mut act).arg(&nl);
4619 unsafe {
4620 b.launch(cfg)?;
4621 }
4622 Ok(act)
4623 }
4624
4625 pub fn moe_pairs_silu_mul(
4626 &self,
4627 gate: &CudaSlice<f32>,
4628 up: &CudaSlice<f32>,
4629 n: usize,
4630 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4631 let f = self.func("moe_pairs_silu_mul");
4632 let mut act = self.alloc_uninit::<f32>(n)?;
4633 let cfg = LaunchConfig::for_num_elems(n as u32);
4634 let nl = n as i64;
4635 let __s_b = self.gpu.stream();
4636 let mut b = __s_b.launch_builder(&f);
4637 b.arg(gate).arg(up).arg(&mut act).arg(&nl);
4638 unsafe {
4639 b.launch(cfg)?;
4640 }
4641 Ok(act)
4642 }
4643
4644 #[allow(clippy::too_many_arguments)]
4645 pub fn moe_pairs_scatter(
4646 &self,
4647 y_down: &CudaSlice<f32>,
4648 pair_w: &CudaSlice<f32>,
4649 tok_pair_off: &CudaSlice<i32>,
4650 tok_pair_ids: &CudaSlice<i32>,
4651 moe_out: &mut CudaSlice<f32>,
4652 t: usize,
4653 n_embd: usize,
4654 ) -> Result<(), Box<dyn std::error::Error>> {
4655 let f = self.func("moe_pairs_scatter");
4656 let cfg = LaunchConfig {
4657 grid_dim: (((n_embd + 255) / 256) as u32, t as u32, 1),
4658 block_dim: (256, 1, 1),
4659 shared_mem_bytes: 0,
4660 };
4661 let ne = n_embd as i32;
4662 let __s_b = self.gpu.stream();
4663 let mut b = __s_b.launch_builder(&f);
4664 b.arg(y_down)
4665 .arg(pair_w)
4666 .arg(tok_pair_off)
4667 .arg(tok_pair_ids)
4668 .arg(moe_out)
4669 .arg(&ne);
4670 unsafe {
4671 b.launch(cfg)?;
4672 }
4673 Ok(())
4674 }
4675
4676 #[allow(clippy::too_many_arguments)]
4680 pub fn moe_gate_up_gelu8_dev_q8(
4681 &self,
4682 table: &CudaSlice<u64>,
4683 sel: &cudarc::driver::CudaView<i32>,
4684 aq: &CudaSlice<i8>,
4685 ad: &CudaSlice<f32>,
4686 in_f: usize,
4687 n_ff: usize,
4688 n_used: usize,
4689 n_expert: usize,
4690 qt_g: i32,
4691 qt_u: i32,
4692 rb_g: usize,
4693 rb_u: usize,
4694 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4695 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
4696 let (inf, nff, ne, rbg, rbu) = (
4697 in_f as i32,
4698 n_ff as i32,
4699 n_expert as i32,
4700 rb_g as i64,
4701 rb_u as i64,
4702 );
4703 let f = self.func("moe_gate_up_gelu8_dev_q8");
4704 let cfg = LaunchConfig {
4705 grid_dim: (n_ff as u32, n_used as u32, 1),
4706 block_dim: (32, 1, 1),
4707 shared_mem_bytes: 0,
4708 };
4709 let __s_b = self.gpu.stream();
4710 let mut b = __s_b.launch_builder(&f);
4711 b.arg(table)
4712 .arg(sel)
4713 .arg(aq)
4714 .arg(ad)
4715 .arg(&mut act)
4716 .arg(&inf)
4717 .arg(&nff)
4718 .arg(&ne)
4719 .arg(&qt_g)
4720 .arg(&qt_u)
4721 .arg(&rbg)
4722 .arg(&rbu);
4723 unsafe {
4724 b.launch(cfg)?;
4725 }
4726 Ok(act)
4727 }
4728
4729 #[allow(clippy::too_many_arguments)]
4731 pub fn moe_gate_up_gelu8_dev_q8_rows(
4732 &self,
4733 table: &CudaSlice<u64>,
4734 sel: &CudaSlice<i32>,
4735 aq: &CudaSlice<i8>,
4736 ad: &CudaSlice<f32>,
4737 t: usize,
4738 in_f: usize,
4739 n_ff: usize,
4740 n_used: usize,
4741 n_expert: usize,
4742 qt_g: i32,
4743 qt_u: i32,
4744 rb_g: usize,
4745 rb_u: usize,
4746 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4747 let mut act = self.alloc_uninit::<f32>(t * n_used * n_ff)?;
4748 let (inf, nff, ne, rbg, rbu, nu) = (
4749 in_f as i32,
4750 n_ff as i32,
4751 n_expert as i32,
4752 rb_g as i64,
4753 rb_u as i64,
4754 n_used as i32,
4755 );
4756 let f = self.func("moe_gate_up_gelu8_dev_q8_rows");
4757 let cfg = LaunchConfig {
4758 grid_dim: (n_ff as u32, n_used as u32, t as u32),
4759 block_dim: (32, 1, 1),
4760 shared_mem_bytes: 0,
4761 };
4762 let __s_b = self.gpu.stream();
4763 let mut b = __s_b.launch_builder(&f);
4764 b.arg(table)
4765 .arg(sel)
4766 .arg(aq)
4767 .arg(ad)
4768 .arg(&mut act)
4769 .arg(&inf)
4770 .arg(&nff)
4771 .arg(&ne)
4772 .arg(&qt_g)
4773 .arg(&qt_u)
4774 .arg(&rbg)
4775 .arg(&rbu)
4776 .arg(&nu);
4777 unsafe {
4778 b.launch(cfg)?;
4779 }
4780 Ok(act)
4781 }
4782
4783 #[allow(clippy::too_many_arguments)]
4785 pub fn moe_gate_up_gelu8_dev_q8_csr(
4786 &self,
4787 table: &CudaSlice<u64>,
4788 sel: &CudaSlice<i32>,
4789 aq: &CudaSlice<i8>,
4790 ad: &CudaSlice<f32>,
4791 n_pairs: usize,
4792 in_f: usize,
4793 n_ff: usize,
4794 n_used: usize,
4795 n_expert: usize,
4796 qt_g: i32,
4797 qt_u: i32,
4798 rb_g: usize,
4799 rb_u: usize,
4800 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4801 let mut act = self.alloc_uninit::<f32>(n_pairs * n_ff)?;
4802 let (inf, nff, ne, rbg, rbu, nu, npi) = (
4803 in_f as i32,
4804 n_ff as i32,
4805 n_expert as i32,
4806 rb_g as i64,
4807 rb_u as i64,
4808 n_used as i32,
4809 n_pairs as i32,
4810 );
4811 let f = self.func("moe_gate_up_gelu8_dev_q8_csr");
4812 let cfg = LaunchConfig {
4813 grid_dim: (n_ff as u32, n_pairs as u32, 1),
4814 block_dim: (32, 1, 1),
4815 shared_mem_bytes: 0,
4816 };
4817 let __s_b = self.gpu.stream();
4818 let mut b = __s_b.launch_builder(&f);
4819 b.arg(table)
4820 .arg(sel)
4821 .arg(aq)
4822 .arg(ad)
4823 .arg(&mut act)
4824 .arg(&inf)
4825 .arg(&nff)
4826 .arg(&ne)
4827 .arg(&qt_g)
4828 .arg(&qt_u)
4829 .arg(&rbg)
4830 .arg(&rbu)
4831 .arg(&nu)
4832 .arg(&npi);
4833 unsafe {
4834 b.launch(cfg)?;
4835 }
4836 Ok(act)
4837 }
4838
4839 #[allow(clippy::too_many_arguments)]
4841 pub fn moe_down8_fma_dev_q8_rows_g(
4842 &self,
4843 table: &CudaSlice<u64>,
4844 sel: &CudaSlice<i32>,
4845 w: &CudaSlice<f32>,
4846 aq2: &CudaSlice<i8>,
4847 ad2: &CudaSlice<f32>,
4848 dst: &mut CudaSlice<f32>,
4849 t: usize,
4850 in_f: usize,
4851 out_f: usize,
4852 n_used: usize,
4853 n_expert: usize,
4854 qt: i32,
4855 rb: usize,
4856 ) -> Result<(), Box<dyn std::error::Error>> {
4857 let (inf, outf, nu, ne, rbi) = (
4858 in_f as i32,
4859 out_f as i32,
4860 n_used as i32,
4861 n_expert as i32,
4862 rb as i64,
4863 );
4864 let step_b1_w8 = t == 1 && in_f == 1280 && out_f == 4096 && n_used == 8 && qt == QT_IQ4_XS;
4868 let f = self.func(if step_b1_w8 {
4869 "moe_down8_fma_dev_q8_rows_w8"
4870 } else {
4871 "moe_down8_fma_dev_q8_rows_g"
4872 });
4873 let cfg = LaunchConfig {
4874 grid_dim: (out_f as u32, 1, t as u32),
4875 block_dim: (32, if step_b1_w8 { 8 } else { 1 }, 1),
4876 shared_mem_bytes: 0,
4877 };
4878 let __s_b = self.gpu.stream();
4879 let mut b = __s_b.launch_builder(&f);
4880 b.arg(table)
4881 .arg(sel)
4882 .arg(w)
4883 .arg(aq2)
4884 .arg(ad2)
4885 .arg(dst)
4886 .arg(&inf)
4887 .arg(&outf)
4888 .arg(&nu)
4889 .arg(&ne)
4890 .arg(&qt)
4891 .arg(&rbi);
4892 unsafe {
4893 b.launch(cfg)?;
4894 }
4895 Ok(())
4896 }
4897
4898 pub fn rp_probe_q4(&self, m: usize) -> Result<(f64, f64), Box<dyn std::error::Error>> {
4902 let (out_f, in_f) = (2048usize, 2816usize);
4903 let nblk = in_f / 32;
4904 let mut seed = 0x9E3779B97F4A7C15u64;
4905 let mut rng = move || {
4906 seed = seed
4907 .wrapping_mul(6364136223846793005)
4908 .wrapping_add(1442695040888963407);
4909 (seed >> 33) as u8
4910 };
4911 let mut w = vec![0u8; out_f * nblk * 18];
4912 for b in w.iter_mut() {
4913 *b = rng();
4914 }
4915 for r in 0..out_f {
4916 for g in 0..nblk {
4917 let off = (r * nblk + g) * 18;
4918 w[off] = 0x00;
4919 w[off + 1] = 0x2C; }
4921 }
4922 let qplane = out_f * nblk * 16;
4923 let mut wrp = vec![0u8; w.len()];
4924 for r in 0..out_f {
4925 for g in 0..nblk {
4926 let src = &w[(r * nblk + g) * 18..(r * nblk + g) * 18 + 18];
4927 wrp[qplane + (r * nblk + g) * 2..qplane + (r * nblk + g) * 2 + 2]
4928 .copy_from_slice(&src[0..2]);
4929 wrp[(r * nblk + g) * 16..(r * nblk + g) * 16 + 16].copy_from_slice(&src[2..18]);
4930 }
4931 }
4932 let w_d = self.htod_bytes(&w)?;
4933 let wrp_d = self.htod_bytes(&wrp)?;
4934 let mut aq = vec![0i8; m * in_f];
4935 for v in aq.iter_mut() {
4936 *v = rng() as i8;
4937 }
4938 let aq_d = self.htod_i8(&aq)?;
4939 let ad_d = self.htod(&vec![0.03125f32; m * nblk])?;
4940 let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
4941 let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
4942 const RPB: u32 = 4;
4943 let cfg = LaunchConfig {
4944 grid_dim: ((out_f as u32).div_ceil(RPB), 1, 1),
4945 block_dim: (32, RPB, 1),
4946 shared_mem_bytes: 0,
4947 };
4948 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
4949 let (rb, qp) = ((nblk * 18) as i64, qplane as i64);
4950 let fb = self.func("qmatvec_q4_0_mmvq_b4");
4951 let fr = self.func("qmatvec_q4_0_mmvq_b4_rp");
4952 {
4953 let __s_b = self.gpu.stream();
4954 let mut b = __s_b.launch_builder(&fb);
4955 b.arg(&w_d)
4956 .arg(&aq_d)
4957 .arg(&ad_d)
4958 .arg(&mut y0)
4959 .arg(&inf)
4960 .arg(&outf)
4961 .arg(&mi)
4962 .arg(&rb);
4963 unsafe {
4964 b.launch(cfg)?;
4965 }
4966 let __s_b = self.gpu.stream();
4967 let mut b = __s_b.launch_builder(&fr);
4968 b.arg(&wrp_d)
4969 .arg(&aq_d)
4970 .arg(&ad_d)
4971 .arg(&mut y1)
4972 .arg(&inf)
4973 .arg(&outf)
4974 .arg(&mi)
4975 .arg(&qp);
4976 unsafe {
4977 b.launch(cfg)?;
4978 }
4979 }
4980 self.gpu.stream().synchronize()?;
4981 let (h0, h1) = (self.dtoh(&y0)?, self.dtoh(&y1)?);
4982 let nd = h0
4983 .iter()
4984 .zip(&h1)
4985 .filter(|(a, b)| a.to_bits() != b.to_bits())
4986 .count();
4987 if nd != 0 {
4988 return Err(format!("rp twin not bitwise: {nd}/{} diffs", h0.len()).into());
4989 }
4990 let mut time = |rp: bool| -> Result<f64, Box<dyn std::error::Error>> {
4991 self.gpu.stream().synchronize()?;
4992 let t0 = std::time::Instant::now();
4993 for _ in 0..500 {
4994 if rp {
4995 let __s_b = self.gpu.stream();
4996 let mut b = __s_b.launch_builder(&fr);
4997 b.arg(&wrp_d)
4998 .arg(&aq_d)
4999 .arg(&ad_d)
5000 .arg(&mut y1)
5001 .arg(&inf)
5002 .arg(&outf)
5003 .arg(&mi)
5004 .arg(&qp);
5005 unsafe {
5006 b.launch(cfg)?;
5007 }
5008 } else {
5009 let __s_b = self.gpu.stream();
5010 let mut b = __s_b.launch_builder(&fb);
5011 b.arg(&w_d)
5012 .arg(&aq_d)
5013 .arg(&ad_d)
5014 .arg(&mut y0)
5015 .arg(&inf)
5016 .arg(&outf)
5017 .arg(&mi)
5018 .arg(&rb);
5019 unsafe {
5020 b.launch(cfg)?;
5021 }
5022 }
5023 }
5024 self.gpu.stream().synchronize()?;
5025 Ok(t0.elapsed().as_secs_f64() * 1e6 / 500.0)
5026 };
5027 let _ = time(false)?;
5028 let _ = time(true)?; Ok((time(false)?, time(true)?))
5030 }
5031
5032 pub fn build_q4_rp4(
5037 &self,
5038 t: &mut crate::model::GpuTensor,
5039 ) -> Result<(), Box<dyn std::error::Error>> {
5040 use crate::model::GpuTensor;
5041 let GpuTensor::Quant {
5042 bytes,
5043 qtype,
5044 row_bytes,
5045 ne,
5046 rp4,
5047 ..
5048 } = t
5049 else {
5050 return Ok(());
5051 };
5052 if *qtype != QT_Q4_0 || rp4.is_some() || ne.len() != 2 {
5053 return Ok(());
5054 }
5055 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5056 if in_f % 32 != 0 || *row_bytes != (in_f / 32) * 18 {
5057 return Ok(());
5058 }
5059 let nblk = in_f / 32;
5060 let mut dst = self.alloc_uninit::<u8>(out_f * nblk * 18)?;
5061 let f = self.func("q4_0_split_rp_build");
5062 let n = (out_f * nblk) as i32;
5063 let cfg = LaunchConfig {
5064 grid_dim: (((out_f * nblk) as u32).div_ceil(256), 1, 1),
5065 block_dim: (256, 1, 1),
5066 shared_mem_bytes: 0,
5067 };
5068 let (of, nb) = (out_f as i32, nblk as i32);
5069 let _ = n;
5070 let __s_b = self.gpu.stream();
5071 let mut b = __s_b.launch_builder(&f);
5072 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
5073 unsafe {
5074 b.launch(cfg)?;
5075 }
5076 *rp4 = Some(dst);
5077 Ok(())
5078 }
5079
5080 pub fn build_q8_rp4(
5085 &self,
5086 t: &mut crate::model::GpuTensor,
5087 ) -> Result<(), Box<dyn std::error::Error>> {
5088 use crate::model::GpuTensor;
5089 let GpuTensor::Quant {
5090 bytes,
5091 qtype,
5092 row_bytes,
5093 ne,
5094 rp4,
5095 ..
5096 } = t
5097 else {
5098 return Ok(());
5099 };
5100 if *qtype != QT_Q8_0 || rp4.is_some() || ne.len() != 2 {
5101 return Ok(());
5102 }
5103 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5104 if in_f % 32 != 0 || *row_bytes != (in_f / 32) * 34 {
5105 return Ok(());
5106 }
5107 *rp4 = Some(self.build_q8_rp4_raw(bytes, in_f, out_f)?);
5108 Ok(())
5109 }
5110
5111 pub fn build_q8_rp4_raw(
5114 &self,
5115 bytes: &CudaSlice<u8>,
5116 in_f: usize,
5117 out_f: usize,
5118 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
5119 assert!(in_f % 32 == 0);
5120 let nblk = in_f / 32;
5121 let mut dst = self.alloc_uninit::<u8>(out_f * nblk * 34)?;
5122 let f = self.func("q8_0_split_rp_build");
5123 let cfg = LaunchConfig {
5124 grid_dim: (((out_f * nblk) as u32).div_ceil(256), 1, 1),
5125 block_dim: (256, 1, 1),
5126 shared_mem_bytes: 0,
5127 };
5128 let (of, nb) = (out_f as i32, nblk as i32);
5129 let __s_b = self.gpu.stream();
5130 let mut b = __s_b.launch_builder(&f);
5131 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
5132 unsafe {
5133 b.launch(cfg)?;
5134 }
5135 Ok(dst)
5136 }
5137
5138 pub fn build_q4k_rp4(
5146 &self,
5147 t: &mut crate::model::GpuTensor,
5148 ) -> Result<(), Box<dyn std::error::Error>> {
5149 use crate::model::GpuTensor;
5150 let GpuTensor::Quant {
5151 bytes,
5152 qtype,
5153 row_bytes,
5154 ne,
5155 rp4,
5156 ..
5157 } = t
5158 else {
5159 return Ok(());
5160 };
5161 if *qtype != QT_Q4_K || rp4.is_some() || ne.len() != 2 {
5162 return Ok(());
5163 }
5164 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5165 if in_f % 256 != 0 || *row_bytes != (in_f / 256) * 144 {
5166 return Ok(());
5167 }
5168 *rp4 = Some(self.build_kq_rp4_raw(bytes, in_f, out_f, QT_Q4_K)?);
5169 Ok(())
5170 }
5171
5172 pub fn build_q6k_rp4(
5173 &self,
5174 t: &mut crate::model::GpuTensor,
5175 ) -> Result<(), Box<dyn std::error::Error>> {
5176 use crate::model::GpuTensor;
5177 let GpuTensor::Quant {
5178 bytes,
5179 qtype,
5180 row_bytes,
5181 ne,
5182 rp4,
5183 ..
5184 } = t
5185 else {
5186 return Ok(());
5187 };
5188 if *qtype != QT_Q6_K || rp4.is_some() || ne.len() != 2 {
5189 return Ok(());
5190 }
5191 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5192 if in_f % 256 != 0 || *row_bytes != (in_f / 256) * 210 {
5193 return Ok(());
5194 }
5195 *rp4 = Some(self.build_kq_rp4_raw(bytes, in_f, out_f, QT_Q6_K)?);
5196 Ok(())
5197 }
5198
5199 pub fn build_kq_rp4_raw(
5201 &self,
5202 bytes: &CudaSlice<u8>,
5203 in_f: usize,
5204 out_f: usize,
5205 qtype: i32,
5206 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
5207 assert!(in_f % 256 == 0);
5208 let nsbk = in_f / 256;
5209 let (sb_bytes, kname) = match qtype {
5210 QT_Q4_K => (144usize, "q4_K_split_rp_build"),
5211 QT_Q6_K => (210usize, "q6_K_split_rp_build"),
5212 _ => return Err(format!("build_kq_rp4_raw: qtype {qtype} has no rp mirror").into()),
5213 };
5214 let mut dst = self.alloc_uninit::<u8>(out_f * nsbk * sb_bytes)?;
5215 let f = self.func(kname);
5216 let cfg = LaunchConfig {
5217 grid_dim: (((out_f * nsbk) as u32).div_ceil(256), 1, 1),
5218 block_dim: (256, 1, 1),
5219 shared_mem_bytes: 0,
5220 };
5221 let (of, nb) = (out_f as i32, nsbk as i32);
5222 let __s_b = self.gpu.stream();
5223 let mut b = __s_b.launch_builder(&f);
5224 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
5225 unsafe {
5226 b.launch(cfg)?;
5227 }
5228 Ok(dst)
5229 }
5230
5231 pub fn kqrp_enabled() -> bool {
5235 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5236 *ON.get_or_init(|| match std::env::var("MEMRA_KQRP").as_deref() {
5237 Ok("0") => false,
5238 Ok(_) => true,
5239 Err(_) => cfg!(memra_hopper_mma),
5240 })
5241 }
5242
5243 pub fn build_q4_rp_swap(
5249 &self,
5250 t: &mut crate::model::GpuTensor,
5251 ) -> Result<bool, Box<dyn std::error::Error>> {
5252 use crate::model::GpuTensor;
5253 if !matches!(t, GpuTensor::Quant { qtype, .. } if *qtype == QT_Q4_0) {
5263 return Ok(false);
5264 }
5265 self.build_q4_rp4(t)?;
5266 self.gpu.stream().synchronize()?; let GpuTensor::Quant { bytes, rp4, rp, .. } = t else {
5268 return Ok(false);
5269 };
5270 match rp4.take() {
5271 Some(split) => {
5272 *bytes = split; *rp = true;
5274 Ok(true)
5275 }
5276 None => Ok(false),
5277 }
5278 }
5279
5280 pub fn q4rp_enabled() -> bool {
5282 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5283 *ON.get_or_init(|| {
5284 std::env::var("MEMRA_Q4RP")
5285 .map(|v| v != "0")
5286 .unwrap_or(true)
5287 })
5288 }
5289
5290 pub fn copy_rows_strided(
5293 &self,
5294 src: &CudaSlice<f32>,
5295 dst: &mut CudaSlice<f32>,
5296 row_elems: usize,
5297 n_rows: usize,
5298 src_stride: usize,
5299 src_off: usize,
5300 ) -> Result<(), Box<dyn std::error::Error>> {
5301 let f = self.func("copy_rows_strided_f32");
5302 let cfg = LaunchConfig {
5303 grid_dim: (((row_elems as u32 + 255) / 256).max(1), n_rows as u32, 1),
5304 block_dim: (256, 1, 1),
5305 shared_mem_bytes: 0,
5306 };
5307 let (re, nr) = (row_elems as i32, n_rows as i32);
5308 let (st, off) = (src_stride as i64, src_off as i64);
5309 let __s_b = self.gpu.stream();
5310 let mut b = __s_b.launch_builder(&f);
5311 b.arg(src)
5312 .arg(&mut *dst)
5313 .arg(&re)
5314 .arg(&nr)
5315 .arg(&st)
5316 .arg(&off);
5317 unsafe {
5318 b.launch(cfg)?;
5319 }
5320 Ok(())
5321 }
5322
5323 pub fn u32_set_k(
5325 &self,
5326 dst: &mut CudaSlice<u32>,
5327 v: u32,
5328 idx: usize,
5329 ) -> Result<(), Box<dyn std::error::Error>> {
5330 let f = self.func("u32_set_k");
5331 let cfg = LaunchConfig {
5332 grid_dim: (1, 1, 1),
5333 block_dim: (1, 1, 1),
5334 shared_mem_bytes: 0,
5335 };
5336 let ii = idx as i32;
5337 let __s_b = self.gpu.stream();
5338 let mut b = __s_b.launch_builder(&f);
5339 b.arg(dst).arg(&v).arg(&ii);
5340 unsafe {
5341 b.launch(cfg)?;
5342 }
5343 Ok(())
5344 }
5345
5346 pub fn i32_add_k(
5348 &self,
5349 d: &mut CudaSlice<i32>,
5350 v: i32,
5351 ) -> Result<(), Box<dyn std::error::Error>> {
5352 let f = self.func("i32_add_k");
5353 let cfg = LaunchConfig {
5354 grid_dim: (1, 1, 1),
5355 block_dim: (32, 1, 1),
5356 shared_mem_bytes: 0,
5357 };
5358 let __s_b = self.gpu.stream();
5359 let mut b = __s_b.launch_builder(&f);
5360 b.arg(d).arg(&v);
5361 unsafe {
5362 b.launch(cfg)?;
5363 }
5364 Ok(())
5365 }
5366
5367 pub fn i32_iota_from(
5369 &self,
5370 ctr: &CudaSlice<i32>,
5371 dst: &mut CudaSlice<i32>,
5372 n: usize,
5373 ) -> Result<(), Box<dyn std::error::Error>> {
5374 let f = self.func("i32_iota_from");
5375 let cfg = LaunchConfig::for_num_elems(n as u32);
5376 let ni = n as i32;
5377 let __s_b = self.gpu.stream();
5378 let mut b = __s_b.launch_builder(&f);
5379 b.arg(ctr).arg(dst).arg(&ni);
5380 unsafe {
5381 b.launch(cfg)?;
5382 }
5383 Ok(())
5384 }
5385
5386 pub fn u32_map_k(
5388 &self,
5389 buf: &mut CudaSlice<u32>,
5390 map: &CudaSlice<u32>,
5391 idx: usize,
5392 ) -> Result<(), Box<dyn std::error::Error>> {
5393 let f = self.func("u32_map_k");
5394 let cfg = LaunchConfig {
5395 grid_dim: (1, 1, 1),
5396 block_dim: (1, 1, 1),
5397 shared_mem_bytes: 0,
5398 };
5399 let ii = idx as i32;
5400 let __s_b = self.gpu.stream();
5401 let mut b = __s_b.launch_builder(&f);
5402 b.arg(buf).arg(map).arg(&ii);
5403 unsafe {
5404 b.launch(cfg)?;
5405 }
5406 Ok(())
5407 }
5408
5409 #[allow(clippy::too_many_arguments)]
5411 pub fn u32_pack2(
5412 &self,
5413 a: &CudaSlice<u32>,
5414 off_a: usize,
5415 n1: usize,
5416 b_in: &CudaSlice<u32>,
5417 n2: usize,
5418 out: &mut CudaSlice<u32>,
5419 ) -> Result<(), Box<dyn std::error::Error>> {
5420 let f = self.func("u32_pack2");
5421 let cfg = LaunchConfig::for_num_elems((n1 + n2) as u32);
5422 let (oa, i1, i2) = (off_a as i32, n1 as i32, n2 as i32);
5423 let __s_b = self.gpu.stream();
5424 let mut b = __s_b.launch_builder(&f);
5425 b.arg(a).arg(&oa).arg(&i1).arg(b_in).arg(&i2).arg(out);
5426 unsafe {
5427 b.launch(cfg)?;
5428 }
5429 Ok(())
5430 }
5431
5432 pub fn moe_w_exscale(
5434 &self,
5435 w: &mut CudaSlice<f32>,
5436 sel: &CudaSlice<i32>,
5437 s: &CudaSlice<f32>,
5438 n: usize,
5439 ) -> Result<(), Box<dyn std::error::Error>> {
5440 let f = self.func("moe_w_exscale");
5441 let cfg = LaunchConfig::for_num_elems(n as u32);
5442 let ni = n as i32;
5443 let __s_b = self.gpu.stream();
5444 let mut b = __s_b.launch_builder(&f);
5445 b.arg(w).arg(sel).arg(s).arg(&ni);
5446 unsafe {
5447 b.launch(cfg)?;
5448 }
5449 Ok(())
5450 }
5451
5452 pub fn moe_w_scale_by_expert(
5455 &self,
5456 w: &mut CudaSlice<f32>,
5457 sel: &CudaSlice<i32>,
5458 macros: &CudaSlice<f32>,
5459 n_expert: usize,
5460 n: usize,
5461 ) -> Result<(), Box<dyn std::error::Error>> {
5462 let f = self.func("moe_w_scale_by_expert");
5463 let cfg = LaunchConfig {
5464 grid_dim: (n.div_ceil(64) as u32, 1, 1),
5465 block_dim: (64, 1, 1),
5466 shared_mem_bytes: 0,
5467 };
5468 let (ne, nn) = (n_expert as i32, n as i32);
5469 let __s_b = self.gpu.stream();
5470 let mut b = __s_b.launch_builder(&f);
5471 b.arg(w).arg(sel).arg(macros).arg(&ne).arg(&nn);
5472 unsafe {
5473 b.launch(cfg)?;
5474 }
5475 Ok(())
5476 }
5477
5478 pub fn moe_gate_up_silu8_dev_q8(
5479 &self,
5480 table: &CudaSlice<u64>,
5481 sel: &cudarc::driver::CudaView<i32>,
5482 aq: &CudaSlice<i8>,
5483 ad: &CudaSlice<f32>,
5484 in_f: usize,
5485 n_ff: usize,
5486 n_used: usize,
5487 n_expert: usize,
5488 qt_g: i32,
5489 qt_u: i32,
5490 rb_g: usize,
5491 rb_u: usize,
5492 macros: &CudaSlice<f32>,
5493 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5494 static GU: std::sync::OnceLock<(String, u32)> = std::sync::OnceLock::new();
5495 let (mode, wpb) = GU.get_or_init(|| {
5496 let mode = std::env::var("MEMRA_MOE_DEVQ8_GU").unwrap_or_default();
5497 let wpb = std::env::var("MEMRA_MOE_DEVQ8_WPB")
5498 .ok()
5499 .and_then(|v| v.parse().ok())
5500 .unwrap_or(4u32)
5501 .clamp(1, 16);
5502 (mode, wpb)
5503 });
5504 let (mode, wpb) = (mode.as_str(), *wpb);
5505 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
5506 let (inf, nff, ne, rbg, rbu) = (
5507 in_f as i32,
5508 n_ff as i32,
5509 n_expert as i32,
5510 rb_g as i64,
5511 rb_u as i64,
5512 );
5513 let (f, cfg) = match mode {
5514 "1" | "2" | "4" => {
5515 let rpw: u32 = mode.parse().unwrap();
5516 let f = self.func(match rpw {
5517 1 => "moe_gate_up_silu8_dev_q8_r1",
5518 2 => "moe_gate_up_silu8_dev_q8_r2",
5519 _ => "moe_gate_up_silu8_dev_q8_r4",
5520 });
5521 let rows_per_block = (rpw * wpb) as usize;
5522 let gx = n_ff.div_ceil(rows_per_block) as u32;
5523 (
5524 f,
5525 LaunchConfig {
5526 grid_dim: (gx, n_used as u32, 1),
5527 block_dim: (32, wpb, 1),
5528 shared_mem_bytes: 0,
5529 },
5530 )
5531 }
5532 "j8" if n_used <= 32 => (
5533 self.func("moe_gate_up_silu8_dev_q8_j8"),
5534 LaunchConfig {
5535 grid_dim: (n_ff as u32, 1, 1),
5536 block_dim: (32, n_used as u32, 1),
5537 shared_mem_bytes: 0,
5538 },
5539 ),
5540 "vsm2" => {
5542 let f = self.func("moe_gate_up_silu8_dev_q8_vsm2");
5543 let sh = (rb_g + rb_u) as u32;
5544 use cudarc::driver::sys::CUfunction_attribute_enum as A;
5545 f.set_attribute(
5546 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
5547 sh as i32,
5548 )?;
5549 (
5550 f,
5551 LaunchConfig {
5552 grid_dim: (n_ff as u32, n_used as u32, 1),
5553 block_dim: (32, 1, 1),
5554 shared_mem_bytes: sh,
5555 },
5556 )
5557 }
5558 "vsm" => {
5559 let f = self.func("moe_gate_up_silu8_dev_q8_vsm");
5560 let sh = (rb_g + rb_u) as u32;
5561 use cudarc::driver::sys::CUfunction_attribute_enum as A;
5562 f.set_attribute(
5563 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
5564 sh as i32,
5565 )?;
5566 (
5567 f,
5568 LaunchConfig {
5569 grid_dim: (n_ff as u32, n_used as u32, 1),
5570 block_dim: (32, 1, 1),
5571 shared_mem_bytes: sh,
5572 },
5573 )
5574 }
5575 "sg" => (
5576 self.func("moe_gate_up_silu8_dev_q8_sg"),
5577 LaunchConfig {
5578 grid_dim: (n_ff as u32, n_used as u32, 1),
5579 block_dim: (32, 1, 1),
5580 shared_mem_bytes: 0,
5581 },
5582 ),
5583 "j8sg" if n_used <= 32 => (
5584 self.func("moe_gate_up_silu8_dev_q8_j8sg"),
5585 LaunchConfig {
5586 grid_dim: (n_ff as u32, 1, 1),
5587 block_dim: (32, n_used as u32, 1),
5588 shared_mem_bytes: 0,
5589 },
5590 ),
5591 "u64" if in_f == 2048 => (
5592 self.func("moe_gate_up_silu8_dev_q8_u64"),
5593 LaunchConfig {
5594 grid_dim: (n_ff as u32, n_used as u32, 1),
5595 block_dim: (32, 1, 1),
5596 shared_mem_bytes: 0,
5597 },
5598 ),
5599 "gs4" if in_f == 2048 => (
5600 self.func("moe_gate_up_silu8_dev_q8_gs4"),
5601 LaunchConfig {
5602 grid_dim: (n_ff as u32, n_used as u32, 1),
5603 block_dim: (32, 4, 1),
5604 shared_mem_bytes: 0,
5605 },
5606 ),
5607 "v" | "" => (
5609 self.func("moe_gate_up_silu8_dev_q8_v"),
5610 LaunchConfig {
5611 grid_dim: (n_ff as u32, n_used as u32, 1),
5612 block_dim: (32, 1, 1),
5613 shared_mem_bytes: 0,
5614 },
5615 ),
5616 "s2" => (
5617 self.func("moe_gate_up_silu8_dev_q8_s2"),
5618 LaunchConfig {
5619 grid_dim: (n_ff as u32, n_used as u32, 1),
5620 block_dim: (32, 2, 1),
5621 shared_mem_bytes: 0,
5622 },
5623 ),
5624 "s2z" => {
5625 let rz = wpb.min(16); (
5627 self.func("moe_gate_up_silu8_dev_q8_s2z"),
5628 LaunchConfig {
5629 grid_dim: (n_ff.div_ceil(rz as usize) as u32, n_used as u32, 1),
5630 block_dim: (32, 2, rz),
5631 shared_mem_bytes: 0,
5632 },
5633 )
5634 }
5635 _ => (
5636 self.func("moe_gate_up_silu8_dev_q8"),
5637 LaunchConfig {
5638 grid_dim: (n_ff as u32, n_used as u32, 1),
5639 block_dim: (32, 1, 1),
5640 shared_mem_bytes: 0,
5641 },
5642 ),
5643 };
5644 let __s_b = self.gpu.stream();
5645 let mut b = __s_b.launch_builder(&f);
5646 b.arg(table)
5647 .arg(sel)
5648 .arg(aq)
5649 .arg(ad)
5650 .arg(&mut act)
5651 .arg(&inf)
5652 .arg(&nff)
5653 .arg(&ne)
5654 .arg(&qt_g)
5655 .arg(&qt_u)
5656 .arg(&rbg)
5657 .arg(&rbu)
5658 .arg(macros);
5659 unsafe {
5660 b.launch(cfg)?;
5661 }
5662 Ok(act)
5663 }
5664
5665 #[allow(clippy::too_many_arguments)]
5666 pub fn moe_down8_fma_dev_q8(
5667 &self,
5668 table: &CudaSlice<u64>,
5669 sel: &cudarc::driver::CudaView<i32>,
5670 w: &cudarc::driver::CudaView<f32>,
5671 aq2: &CudaSlice<i8>,
5672 ad2: &CudaSlice<f32>,
5673 dst: &mut cudarc::driver::CudaViewMut<f32>,
5674 in_f: usize,
5675 out_f: usize,
5676 n_used: usize,
5677 n_expert: usize,
5678 qt: i32,
5679 rb: usize,
5680 ) -> Result<(), Box<dyn std::error::Error>> {
5681 static DOWN: std::sync::OnceLock<String> = std::sync::OnceLock::new();
5682 let mode = DOWN.get_or_init(|| std::env::var("MEMRA_MOE_DEVQ8_DOWN").unwrap_or_default());
5683 let (inf, outf, nu, ne, rbi) = (
5684 in_f as i32,
5685 out_f as i32,
5686 n_used as i32,
5687 n_expert as i32,
5688 rb as i64,
5689 );
5690 let (f, cfg) = match mode.as_str() {
5693 m @ ("1" | "2" | "4") if n_used <= 8 => {
5694 let rpw: usize = m.parse().unwrap();
5695 let f = self.func(match rpw {
5696 1 => "moe_down8_fma_dev_q8_w8r1",
5697 2 => "moe_down8_fma_dev_q8_w8r2",
5698 _ => "moe_down8_fma_dev_q8_w8r4",
5699 });
5700 (
5701 f,
5702 LaunchConfig {
5703 grid_dim: (out_f.div_ceil(rpw) as u32, 1, 1),
5704 block_dim: (32, n_used as u32, 1),
5705 shared_mem_bytes: 0,
5706 },
5707 )
5708 }
5709 "h2" if in_f == 512 => (
5710 self.func("moe_down8_fma_dev_q8_h2"),
5711 LaunchConfig {
5712 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5713 block_dim: (32, 1, 1),
5714 shared_mem_bytes: 0,
5715 },
5716 ),
5717 "" if in_f == 704 && n_used <= 8 => (
5720 self.func("moe_down8_fma_dev_q8_w8r2"),
5721 LaunchConfig {
5722 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5723 block_dim: (32, n_used as u32, 1),
5724 shared_mem_bytes: 0,
5725 },
5726 ),
5727 "w8h2v" | "" if in_f == 512 && n_used <= 8 => (
5731 self.func("moe_down8_fma_dev_q8_w8h2v"),
5732 LaunchConfig {
5733 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5734 block_dim: (32, n_used as u32, 1),
5735 shared_mem_bytes: 0,
5736 },
5737 ),
5738 "w8h2r2v" if in_f == 512 && n_used <= 8 => (
5739 self.func("moe_down8_fma_dev_q8_w8h2r2v"),
5740 LaunchConfig {
5741 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
5742 block_dim: (32, n_used as u32, 1),
5743 shared_mem_bytes: 0,
5744 },
5745 ),
5746 "w8h2r2" if in_f == 512 && n_used <= 8 => (
5747 self.func("moe_down8_fma_dev_q8_w8h2r2"),
5748 LaunchConfig {
5749 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
5750 block_dim: (32, n_used as u32, 1),
5751 shared_mem_bytes: 0,
5752 },
5753 ),
5754 "w8h2" if in_f == 512 && n_used <= 8 => (
5755 self.func("moe_down8_fma_dev_q8_w8h2"),
5756 LaunchConfig {
5757 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5758 block_dim: (32, n_used as u32, 1),
5759 shared_mem_bytes: 0,
5760 },
5761 ),
5762 _ => (
5763 self.func("moe_down8_fma_dev_q8"),
5764 LaunchConfig {
5765 grid_dim: (out_f as u32, 1, 1),
5766 block_dim: (32, 1, 1),
5767 shared_mem_bytes: 0,
5768 },
5769 ),
5770 };
5771 let __s_b = self.gpu.stream();
5772 let mut b = __s_b.launch_builder(&f);
5773 b.arg(table)
5774 .arg(sel)
5775 .arg(w)
5776 .arg(aq2)
5777 .arg(ad2)
5778 .arg(dst)
5779 .arg(&inf)
5780 .arg(&outf)
5781 .arg(&nu)
5782 .arg(&ne)
5783 .arg(&qt)
5784 .arg(&rbi);
5785 unsafe {
5786 b.launch(cfg)?;
5787 }
5788 Ok(())
5789 }
5790
5791 #[allow(clippy::too_many_arguments)]
5798 pub fn moe_gate_up_silu8_dev_q8_rows(
5799 &self,
5800 table: &CudaSlice<u64>,
5801 sel: &CudaSlice<i32>,
5802 aq: &CudaSlice<i8>,
5803 ad: &CudaSlice<f32>,
5804 t: usize,
5805 in_f: usize,
5806 n_ff: usize,
5807 n_used: usize,
5808 n_expert: usize,
5809 qt_g: i32,
5810 qt_u: i32,
5811 rb_g: usize,
5812 rb_u: usize,
5813 macros: &CudaSlice<f32>,
5814 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5815 let f = self.func("moe_gate_up_silu8_dev_q8_v_rows");
5816 let mut act = self.alloc_uninit::<f32>(t * n_used * n_ff)?;
5817 let cfg = LaunchConfig {
5818 grid_dim: (n_ff as u32, n_used as u32, t as u32),
5819 block_dim: (32, 1, 1),
5820 shared_mem_bytes: 0,
5821 };
5822 let (inf, nff, ne, nu, rbg, rbu) = (
5823 in_f as i32,
5824 n_ff as i32,
5825 n_expert as i32,
5826 n_used as i32,
5827 rb_g as i64,
5828 rb_u as i64,
5829 );
5830 let __s_b = self.gpu.stream();
5831 let mut b = __s_b.launch_builder(&f);
5832 b.arg(table)
5833 .arg(sel)
5834 .arg(aq)
5835 .arg(ad)
5836 .arg(&mut act)
5837 .arg(&inf)
5838 .arg(&nff)
5839 .arg(&ne)
5840 .arg(&qt_g)
5841 .arg(&qt_u)
5842 .arg(&rbg)
5843 .arg(&rbu)
5844 .arg(&nu)
5845 .arg(macros);
5846 unsafe {
5847 b.launch(cfg)?;
5848 }
5849 Ok(act)
5850 }
5851
5852 #[allow(clippy::too_many_arguments)]
5857 pub fn moe_down8_fma_dev_q8_rows(
5858 &self,
5859 table: &CudaSlice<u64>,
5860 sel: &CudaSlice<i32>,
5861 w: &CudaSlice<f32>,
5862 aq2: &CudaSlice<i8>,
5863 ad2: &CudaSlice<f32>,
5864 dst: &mut CudaSlice<f32>,
5865 t: usize,
5866 in_f: usize,
5867 out_f: usize,
5868 n_used: usize,
5869 n_expert: usize,
5870 qt: i32,
5871 rb: usize,
5872 ) -> Result<(), Box<dyn std::error::Error>> {
5873 assert!(
5874 in_f == 512 && n_used <= 8,
5875 "down rows twin is w8h2v shape-gated"
5876 );
5877 let f = self.func("moe_down8_fma_dev_q8_w8h2v_rows");
5878 let cfg = LaunchConfig {
5879 grid_dim: (out_f.div_ceil(2) as u32, 1, t as u32),
5880 block_dim: (32, n_used as u32, 1),
5881 shared_mem_bytes: 0,
5882 };
5883 let (inf, outf, nu, ne, rbi) = (
5884 in_f as i32,
5885 out_f as i32,
5886 n_used as i32,
5887 n_expert as i32,
5888 rb as i64,
5889 );
5890 let __s_b = self.gpu.stream();
5891 let mut b = __s_b.launch_builder(&f);
5892 b.arg(table)
5893 .arg(sel)
5894 .arg(w)
5895 .arg(aq2)
5896 .arg(ad2)
5897 .arg(dst)
5898 .arg(&inf)
5899 .arg(&outf)
5900 .arg(&nu)
5901 .arg(&ne)
5902 .arg(&qt)
5903 .arg(&rbi);
5904 unsafe {
5905 b.launch(cfg)?;
5906 }
5907 Ok(())
5908 }
5909
5910 #[allow(clippy::too_many_arguments)]
5914 pub fn moe_gate_up_silu8_dev_q8_csr(
5915 &self,
5916 table: &CudaSlice<u64>,
5917 sel: &CudaSlice<i32>,
5918 aq: &CudaSlice<i8>,
5919 ad: &CudaSlice<f32>,
5920 n_pairs: usize,
5921 in_f: usize,
5922 n_ff: usize,
5923 n_used: usize,
5924 n_expert: usize,
5925 qt_g: i32,
5926 qt_u: i32,
5927 rb_g: usize,
5928 rb_u: usize,
5929 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5930 let f = if qt_g == crate::QT_NVFP4 {
5933 self.func("moe_gate_up_silu8_dev_q8_csr_nvfp4")
5934 } else {
5935 self.func("moe_gate_up_silu8_dev_q8_csr_iq4")
5936 };
5937 let mut act = self.alloc_uninit::<f32>(n_pairs * n_ff)?;
5938 let cfg = LaunchConfig {
5939 grid_dim: (n_ff as u32, n_pairs as u32, 1),
5940 block_dim: (32, 1, 1),
5941 shared_mem_bytes: 0,
5942 };
5943 let (inf, nff, ne, nu, npi, rbg, rbu) = (
5944 in_f as i32,
5945 n_ff as i32,
5946 n_expert as i32,
5947 n_used as i32,
5948 n_pairs as i32,
5949 rb_g as i64,
5950 rb_u as i64,
5951 );
5952 let __s_b = self.gpu.stream();
5953 let mut b = __s_b.launch_builder(&f);
5954 b.arg(table)
5955 .arg(sel)
5956 .arg(aq)
5957 .arg(ad)
5958 .arg(&mut act)
5959 .arg(&inf)
5960 .arg(&nff)
5961 .arg(&ne)
5962 .arg(&qt_g)
5963 .arg(&qt_u)
5964 .arg(&rbg)
5965 .arg(&rbu)
5966 .arg(&nu)
5967 .arg(&npi);
5968 unsafe {
5969 b.launch(cfg)?;
5970 }
5971 Ok(act)
5972 }
5973
5974 #[allow(clippy::too_many_arguments)]
5978 pub fn moe_down8_fma_dev_q8_variant(
5979 &self,
5980 variant: &str,
5981 table: &CudaSlice<u64>,
5982 sel: &cudarc::driver::CudaView<i32>,
5983 w: &cudarc::driver::CudaView<f32>,
5984 aq2: &CudaSlice<i8>,
5985 ad2: &CudaSlice<f32>,
5986 dst: &mut cudarc::driver::CudaViewMut<f32>,
5987 in_f: usize,
5988 out_f: usize,
5989 n_used: usize,
5990 n_expert: usize,
5991 qt: i32,
5992 rb: usize,
5993 ) -> Result<(), Box<dyn std::error::Error>> {
5994 let (inf, outf, nu, ne, rbi) = (
5995 in_f as i32,
5996 out_f as i32,
5997 n_used as i32,
5998 n_expert as i32,
5999 rb as i64,
6000 );
6001 let (f, cfg) = match variant {
6002 "w8h2" | "w8h2v" => (
6003 self.func(if variant == "w8h2" {
6004 "moe_down8_fma_dev_q8_w8h2"
6005 } else {
6006 "moe_down8_fma_dev_q8_w8h2v"
6007 }),
6008 LaunchConfig {
6009 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
6010 block_dim: (32, n_used as u32, 1),
6011 shared_mem_bytes: 0,
6012 },
6013 ),
6014 "w8h2r2" | "w8h2r2v" => (
6015 self.func(if variant == "w8h2r2" {
6016 "moe_down8_fma_dev_q8_w8h2r2"
6017 } else {
6018 "moe_down8_fma_dev_q8_w8h2r2v"
6019 }),
6020 LaunchConfig {
6021 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
6022 block_dim: (32, n_used as u32, 1),
6023 shared_mem_bytes: 0,
6024 },
6025 ),
6026 _ => (
6027 self.func("moe_down8_fma_dev_q8"),
6028 LaunchConfig {
6029 grid_dim: (out_f as u32, 1, 1),
6030 block_dim: (32, 1, 1),
6031 shared_mem_bytes: 0,
6032 },
6033 ),
6034 };
6035 let __s_b = self.gpu.stream();
6036 let mut b = __s_b.launch_builder(&f);
6037 b.arg(table)
6038 .arg(sel)
6039 .arg(w)
6040 .arg(aq2)
6041 .arg(ad2)
6042 .arg(dst)
6043 .arg(&inf)
6044 .arg(&outf)
6045 .arg(&nu)
6046 .arg(&ne)
6047 .arg(&qt)
6048 .arg(&rbi);
6049 unsafe {
6050 b.launch(cfg)?;
6051 }
6052 Ok(())
6053 }
6054
6055 #[allow(clippy::too_many_arguments)]
6057 pub fn moe_gate_up_silu8_dev_q8_variant(
6058 &self,
6059 variant: &str,
6060 table: &CudaSlice<u64>,
6061 sel: &cudarc::driver::CudaView<i32>,
6062 aq: &CudaSlice<i8>,
6063 ad: &CudaSlice<f32>,
6064 in_f: usize,
6065 n_ff: usize,
6066 n_used: usize,
6067 n_expert: usize,
6068 qt_g: i32,
6069 qt_u: i32,
6070 rb_g: usize,
6071 rb_u: usize,
6072 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6073 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
6074 let (inf, nff, ne, rbg, rbu) = (
6075 in_f as i32,
6076 n_ff as i32,
6077 n_expert as i32,
6078 rb_g as i64,
6079 rb_u as i64,
6080 );
6081 let f = self.func(if variant == "v" {
6082 "moe_gate_up_silu8_dev_q8_v"
6083 } else {
6084 "moe_gate_up_silu8_dev_q8"
6085 });
6086 let cfg = LaunchConfig {
6087 grid_dim: (n_ff as u32, n_used as u32, 1),
6088 block_dim: (32, 1, 1),
6089 shared_mem_bytes: 0,
6090 };
6091 let __s_b = self.gpu.stream();
6092 let mut b = __s_b.launch_builder(&f);
6093 b.arg(table)
6094 .arg(sel)
6095 .arg(aq)
6096 .arg(ad)
6097 .arg(&mut act)
6098 .arg(&inf)
6099 .arg(&nff)
6100 .arg(&ne)
6101 .arg(&qt_g)
6102 .arg(&qt_u)
6103 .arg(&rbg)
6104 .arg(&rbu);
6105 unsafe {
6106 b.launch(cfg)?;
6107 }
6108 Ok(act)
6109 }
6110
6111 pub fn moe_gate_up_silu8_dev(
6112 &self,
6113 table: &CudaSlice<u64>,
6114 sel: &cudarc::driver::CudaView<i32>,
6115 x: &cudarc::driver::CudaView<f32>,
6116 in_f: usize,
6117 n_ff: usize,
6118 n_used: usize,
6119 n_expert: usize,
6120 qt_g: i32,
6121 qt_u: i32,
6122 rb_g: usize,
6123 rb_u: usize,
6124 macros: &CudaSlice<f32>,
6125 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6126 let f = self.func("moe_gate_up_silu8_dev");
6127 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?; let cfg = LaunchConfig {
6129 grid_dim: (n_ff as u32, n_used as u32, 1),
6130 block_dim: (256, 1, 1),
6131 shared_mem_bytes: 0,
6132 };
6133 let (inf, nff, ne, rbg, rbu) = (
6134 in_f as i32,
6135 n_ff as i32,
6136 n_expert as i32,
6137 rb_g as i64,
6138 rb_u as i64,
6139 );
6140 let __s_b = self.gpu.stream();
6141 let mut b = __s_b.launch_builder(&f);
6142 b.arg(table)
6143 .arg(sel)
6144 .arg(x)
6145 .arg(&mut act)
6146 .arg(&inf)
6147 .arg(&nff)
6148 .arg(&ne)
6149 .arg(&qt_g)
6150 .arg(&qt_u)
6151 .arg(&rbg)
6152 .arg(&rbu)
6153 .arg(macros);
6154 unsafe {
6155 b.launch(cfg)?;
6156 }
6157 Ok(act)
6158 }
6159
6160 #[allow(clippy::too_many_arguments)]
6163 pub fn moe_down8_fma_dev(
6164 &self,
6165 table: &CudaSlice<u64>,
6166 sel: &cudarc::driver::CudaView<i32>,
6167 w: &cudarc::driver::CudaView<f32>,
6168 act: &CudaSlice<f32>,
6169 dst: &mut cudarc::driver::CudaViewMut<f32>,
6170 in_f: usize,
6171 out_f: usize,
6172 n_used: usize,
6173 n_expert: usize,
6174 qt: i32,
6175 rb: usize,
6176 ) -> Result<(), Box<dyn std::error::Error>> {
6177 let f = self.func("moe_down8_fma_dev");
6178 let cfg = LaunchConfig {
6179 grid_dim: (out_f as u32, 1, 1),
6180 block_dim: (256, 1, 1),
6181 shared_mem_bytes: 0,
6182 };
6183 let (inf, outf, nu, ne, rbv) = (
6184 in_f as i32,
6185 out_f as i32,
6186 n_used as i32,
6187 n_expert as i32,
6188 rb as i64,
6189 );
6190 let __s_b = self.gpu.stream();
6191 let mut b = __s_b.launch_builder(&f);
6192 b.arg(table)
6193 .arg(sel)
6194 .arg(w)
6195 .arg(act)
6196 .arg(dst)
6197 .arg(&inf)
6198 .arg(&outf)
6199 .arg(&nu)
6200 .arg(&ne)
6201 .arg(&qt)
6202 .arg(&rbv);
6203 unsafe {
6204 b.launch(cfg)?;
6205 }
6206 Ok(())
6207 }
6208
6209 pub fn axpy_into(
6211 &self,
6212 src: &CudaSlice<f32>,
6213 alpha: f32,
6214 dst: &mut cudarc::driver::CudaViewMut<f32>,
6215 n: usize,
6216 ) -> Result<(), Box<dyn std::error::Error>> {
6217 let f = self.func("axpy_f32");
6218 let cfg = LaunchConfig::for_num_elems(n as u32);
6219 let (a, ni) = (alpha, n as i32);
6220 let __s_b = self.gpu.stream();
6221 let mut b = __s_b.launch_builder(&f);
6222 b.arg(src).arg(dst).arg(&a).arg(&ni);
6223 unsafe {
6224 b.launch(cfg)?;
6225 }
6226 Ok(())
6227 }
6228
6229 pub fn add_scaled_rows(
6231 &self,
6232 src: &CudaSlice<f32>,
6233 scale: &CudaSlice<f32>,
6234 dst: &mut CudaSlice<f32>,
6235 ncols: usize,
6236 nrows: usize,
6237 ) -> Result<(), Box<dyn std::error::Error>> {
6238 let f = self.func("add_scaled_rows_f32");
6239 let cfg = LaunchConfig::for_num_elems((ncols * nrows) as u32);
6240 let (nc, nr) = (ncols as i32, nrows as i32);
6241 let __s_b = self.gpu.stream();
6242 let mut b = __s_b.launch_builder(&f);
6243 b.arg(src).arg(scale).arg(dst).arg(&nc).arg(&nr);
6244 unsafe {
6245 b.launch(cfg)?;
6246 }
6247 Ok(())
6248 }
6249
6250 pub fn gather_rows(
6254 &self,
6255 src: &CudaSlice<f32>,
6256 idx: &CudaSlice<i32>,
6257 dst: &mut CudaSlice<f32>,
6258 ncols: usize,
6259 m_e: usize,
6260 ) -> Result<(), Box<dyn std::error::Error>> {
6261 let f = self.func("gather_rows_f32");
6262 let cfg = LaunchConfig::for_num_elems((m_e * ncols) as u32);
6263 let (nc, me) = (ncols as i32, m_e as i32);
6264 let __s_b = self.gpu.stream();
6265 let mut b = __s_b.launch_builder(&f);
6266 b.arg(src).arg(idx).arg(dst).arg(&nc).arg(&me);
6267 unsafe {
6268 b.launch(cfg)?;
6269 }
6270 Ok(())
6271 }
6272
6273 pub fn scatter_slot(
6278 &self,
6279 src: &CudaSlice<f32>,
6280 tok_idx: &CudaSlice<i32>,
6281 slot_idx: &CudaSlice<i32>,
6282 weight: &CudaSlice<f32>,
6283 dst: &mut CudaSlice<f32>,
6284 wbuf: &mut CudaSlice<f32>,
6285 ncols: usize,
6286 n_used: usize,
6287 m_e: usize,
6288 ) -> Result<(), Box<dyn std::error::Error>> {
6289 let f = self.func("scatter_add_slot_f32");
6290 let cfg = LaunchConfig::for_num_elems((m_e * ncols) as u32);
6291 let (nc, nu, me) = (ncols as i32, n_used as i32, m_e as i32);
6292 let __s_b = self.gpu.stream();
6293 let mut b = __s_b.launch_builder(&f);
6294 b.arg(src)
6295 .arg(tok_idx)
6296 .arg(slot_idx)
6297 .arg(weight)
6298 .arg(dst)
6299 .arg(wbuf)
6300 .arg(&nc)
6301 .arg(&nu)
6302 .arg(&me);
6303 unsafe {
6304 b.launch(cfg)?;
6305 }
6306 Ok(())
6307 }
6308
6309 pub fn reduce_slots(
6313 &self,
6314 slots: &CudaSlice<f32>,
6315 wbuf: &CudaSlice<f32>,
6316 dst: &mut CudaSlice<f32>,
6317 ncols: usize,
6318 n_used: usize,
6319 t: usize,
6320 ) -> Result<(), Box<dyn std::error::Error>> {
6321 let f = self.func("reduce_slots_f32");
6322 let cfg = LaunchConfig::for_num_elems((t * ncols) as u32);
6323 let (nc, nu, ti) = (ncols as i32, n_used as i32, t as i32);
6324 let __s_b = self.gpu.stream();
6325 let mut b = __s_b.launch_builder(&f);
6326 b.arg(slots).arg(wbuf).arg(dst).arg(&nc).arg(&nu).arg(&ti);
6327 unsafe {
6328 b.launch(cfg)?;
6329 }
6330 Ok(())
6331 }
6332
6333 pub fn quantize_q8_1_view(
6340 &self,
6341 x: &cudarc::driver::CudaView<f32>,
6342 m: usize,
6343 in_f: usize,
6344 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
6345 let f = self.func("quantize_q8_1");
6346 let nblk = in_f / 32;
6347 let mut q = self.alloc_uninit::<i8>(m * in_f)?;
6348 let mut d = self.alloc_uninit::<f32>(m * nblk)?;
6349 let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
6350 let (inf, mi) = (in_f as i32, m as i32);
6351 let __s_b = self.gpu.stream();
6352 let mut b = __s_b.launch_builder(&f);
6353 b.arg(x).arg(&mut q).arg(&mut d).arg(&inf).arg(&mi);
6354 unsafe {
6355 b.launch(cfg)?;
6356 }
6357 Ok((q, d))
6358 }
6359
6360 pub fn quantize_q8_1(
6361 &self,
6362 x: &CudaSlice<f32>,
6363 m: usize,
6364 in_f: usize,
6365 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
6366 let nblk = in_f / 32;
6367 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);
6371 let (inf, mi) = (in_f as i32, m as i32);
6372 if Self::pdl_on() && Self::pdl_wb_on() {
6373 {
6374 use cudarc::driver::{DevicePtr, DevicePtrMut};
6375 let s = &self.gpu.stream();
6376 let (px, _g0) = x.device_ptr(s);
6377 let (pq, _g1) = q.device_ptr_mut(s);
6378 let (pd, _g2) = d.device_ptr_mut(s);
6379 let mut ps = [
6380 &px as *const _ as *mut std::ffi::c_void,
6381 &pq as *const _ as *mut _,
6382 &pd as *const _ as *mut _,
6383 &inf as *const _ as *mut _,
6384 &mi as *const _ as *mut _,
6385 ];
6386 unsafe {
6387 self.launch_pdl("quantize_q8_1", cfg.grid_dim, cfg.block_dim, &mut ps)?;
6388 }
6389 }
6390 return Ok((q, d));
6391 }
6392 let f = self.func("quantize_q8_1");
6393 let __s_b = self.gpu.stream();
6394 let mut b = __s_b.launch_builder(&f);
6395 b.arg(x).arg(&mut q).arg(&mut d).arg(&inf).arg(&mi);
6396 unsafe {
6397 b.launch(cfg)?;
6398 }
6399 Ok((q, d))
6400 }
6401
6402 pub fn quantize_fp4_act(
6406 &self,
6407 x: &CudaSlice<f32>,
6408 m: usize,
6409 in_f: usize,
6410 ) -> Result<(CudaSlice<u32>, CudaSlice<u8>), Box<dyn std::error::Error>> {
6411 let f = self.func("quantize_fp4_act");
6412 let nb16 = in_f / 16;
6413 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);
6416 let (inf, mi) = (in_f as i32, m as i32);
6417 let __s_b = self.gpu.stream();
6418 let mut b = __s_b.launch_builder(&f);
6419 b.arg(x).arg(&mut aq4).arg(&mut ad4).arg(&inf).arg(&mi);
6420 unsafe {
6421 b.launch(cfg)?;
6422 }
6423 Ok((aq4, ad4))
6424 }
6425
6426 pub fn qmatvec_gemm_nvfp4_fp4(
6431 &self,
6432 bytes: &CudaSlice<u8>,
6433 x: &CudaSlice<f32>,
6434 m: usize,
6435 in_f: usize,
6436 out_f: usize,
6437 row_bytes: usize,
6438 scale: f32,
6439 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6440 assert!(
6441 in_f % 64 == 0,
6442 "FP4 GEMM requires in_f % 64 == 0, got {in_f}"
6443 );
6444 let (aq4, ad4) = self.quantize_fp4_act(x, m, in_f)?;
6445 let mut y = self.fp4_gemm_launch(bytes, &aq4, &ad4, m, in_f, out_f, row_bytes)?;
6446 if scale != 1.0 {
6447 self.scale_inplace(&mut y, scale, m * out_f)?;
6448 }
6449 Ok(y)
6450 }
6451
6452 fn fp4_gemm_launch(
6455 &self,
6456 bytes: &CudaSlice<u8>,
6457 aq4: &CudaSlice<u32>,
6458 ad4: &CudaSlice<u8>,
6459 m: usize,
6460 in_f: usize,
6461 out_f: usize,
6462 row_bytes: usize,
6463 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6464 let f = self.func("qmatvec_gemm_nvfp4_fp4");
6465 let mut y = self.alloc_uninit::<f32>(m * out_f)?; const BM: u32 = 64;
6467 const BN: u32 = 256;
6468 let cfg = LaunchConfig {
6469 grid_dim: ((out_f as u32 + BM - 1) / BM, (m as u32 + BN - 1) / BN, 1),
6470 block_dim: (32, 4, 1),
6471 shared_mem_bytes: 0,
6472 };
6473 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6474 let __s_b = self.gpu.stream();
6475 let mut b = __s_b.launch_builder(&f);
6476 b.arg(bytes)
6477 .arg(aq4)
6478 .arg(ad4)
6479 .arg(&mut y)
6480 .arg(&inf)
6481 .arg(&outf)
6482 .arg(&mi)
6483 .arg(&rb);
6484 unsafe {
6485 b.launch(cfg)?;
6486 }
6487 Ok(y)
6488 }
6489
6490 pub fn qmatvec_gemm_nvfp4_fp4_raw(
6492 &self,
6493 bytes: &CudaSlice<u8>,
6494 x: &CudaSlice<f32>,
6495 m: usize,
6496 in_f: usize,
6497 out_f: usize,
6498 row_bytes: usize,
6499 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6500 assert!(
6501 in_f % 64 == 0,
6502 "FP4 GEMM requires in_f % 64 == 0, got {in_f}"
6503 );
6504 let (aq4, ad4) = self.quantize_fp4_act(x, m, in_f)?;
6505 self.fp4_gemm_launch(bytes, &aq4, &ad4, m, in_f, out_f, row_bytes)
6506 }
6507
6508 pub fn qmatvec_q8_0_fast(
6510 &self,
6511 w: &CudaSlice<u8>,
6512 x: &CudaSlice<f32>,
6513 m: usize,
6514 in_f: usize,
6515 out_f: usize,
6516 row_bytes: usize,
6517 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6518 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6519 let f = self.func("qmatvec_q8_0_dp4a");
6520 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6522 grid_dim: (out_f as u32, m as u32, 1),
6523 block_dim: (128, 1, 1),
6524 shared_mem_bytes: 0,
6525 };
6526 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6527 let __s_b = self.gpu.stream();
6528 let mut b = __s_b.launch_builder(&f);
6529 b.arg(w)
6530 .arg(&aq)
6531 .arg(&ad)
6532 .arg(&mut y)
6533 .arg(&inf)
6534 .arg(&outf)
6535 .arg(&mi)
6536 .arg(&rb);
6537 unsafe {
6538 b.launch(cfg)?;
6539 }
6540 Ok(y)
6541 }
6542
6543 #[allow(non_snake_case)] pub fn qmatvec_q4_K_fast(
6546 &self,
6547 w: &CudaSlice<u8>,
6548 x: &CudaSlice<f32>,
6549 m: usize,
6550 in_f: usize,
6551 out_f: usize,
6552 row_bytes: usize,
6553 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6554 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6555 let f = self.func("qmatvec_q4_K_dp4a");
6556 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6558 grid_dim: (out_f as u32, m as u32, 1),
6559 block_dim: (128, 1, 1),
6560 shared_mem_bytes: 0,
6561 };
6562 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6563 let __s_b = self.gpu.stream();
6564 let mut b = __s_b.launch_builder(&f);
6565 b.arg(w)
6566 .arg(&aq)
6567 .arg(&ad)
6568 .arg(&mut y)
6569 .arg(&inf)
6570 .arg(&outf)
6571 .arg(&mi)
6572 .arg(&rb);
6573 unsafe {
6574 b.launch(cfg)?;
6575 }
6576 Ok(y)
6577 }
6578
6579 #[allow(non_snake_case)] pub fn qmatvec_q6_K_fast(
6582 &self,
6583 w: &CudaSlice<u8>,
6584 x: &CudaSlice<f32>,
6585 m: usize,
6586 in_f: usize,
6587 out_f: usize,
6588 row_bytes: usize,
6589 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6590 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6591 let f = self.func("qmatvec_q6_K_dp4a");
6592 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6594 grid_dim: (out_f as u32, m as u32, 1),
6595 block_dim: (128, 1, 1),
6596 shared_mem_bytes: 0,
6597 };
6598 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6599 let __s_b = self.gpu.stream();
6600 let mut b = __s_b.launch_builder(&f);
6601 b.arg(w)
6602 .arg(&aq)
6603 .arg(&ad)
6604 .arg(&mut y)
6605 .arg(&inf)
6606 .arg(&outf)
6607 .arg(&mi)
6608 .arg(&rb);
6609 unsafe {
6610 b.launch(cfg)?;
6611 }
6612 Ok(y)
6613 }
6614
6615 #[allow(non_snake_case)] pub fn qmatvec_q5_K_fast(
6618 &self,
6619 w: &CudaSlice<u8>,
6620 x: &CudaSlice<f32>,
6621 m: usize,
6622 in_f: usize,
6623 out_f: usize,
6624 row_bytes: usize,
6625 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6626 self.qmatvec_dp4a_named("qmatvec_q5_K_dp4a", w, x, m, in_f, out_f, row_bytes)
6627 }
6628 #[allow(non_snake_case)] pub fn qmatvec_q3_K_fast(
6631 &self,
6632 w: &CudaSlice<u8>,
6633 x: &CudaSlice<f32>,
6634 m: usize,
6635 in_f: usize,
6636 out_f: usize,
6637 row_bytes: usize,
6638 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6639 self.qmatvec_dp4a_named("qmatvec_q3_K_dp4a", w, x, m, in_f, out_f, row_bytes)
6640 }
6641 pub fn qmatvec_nvfp4_fast_rp(
6643 &self,
6644 w: &CudaSlice<u8>,
6645 x: &CudaSlice<f32>,
6646 m: usize,
6647 in_f: usize,
6648 out_f: usize,
6649 row_bytes: usize,
6650 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6651 assert!(
6652 in_f % 64 == 0,
6653 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
6654 );
6655 self.qmatvec_dp4a_named("qmatvec_nvfp4_dp4a_rp", w, x, m, in_f, out_f, row_bytes)
6656 }
6657 pub fn qmatvec_nvfp4_fast(
6659 &self,
6660 w: &CudaSlice<u8>,
6661 x: &CudaSlice<f32>,
6662 m: usize,
6663 in_f: usize,
6664 out_f: usize,
6665 row_bytes: usize,
6666 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6667 assert!(
6670 in_f % 64 == 0,
6671 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
6672 );
6673 self.qmatvec_dp4a_named("qmatvec_nvfp4_dp4a", w, x, m, in_f, out_f, row_bytes)
6674 }
6675 #[allow(non_snake_case)] pub fn qmatvec_iq4_XS_fast(
6678 &self,
6679 w: &CudaSlice<u8>,
6680 x: &CudaSlice<f32>,
6681 m: usize,
6682 in_f: usize,
6683 out_f: usize,
6684 row_bytes: usize,
6685 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6686 self.qmatvec_dp4a_named("qmatvec_iq4_XS_dp4a", w, x, m, in_f, out_f, row_bytes)
6687 }
6688
6689 fn qmatvec_dp4a_named(
6691 &self,
6692 name: &str,
6693 w: &CudaSlice<u8>,
6694 x: &CudaSlice<f32>,
6695 m: usize,
6696 in_f: usize,
6697 out_f: usize,
6698 row_bytes: usize,
6699 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6700 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6701 let f = self.func(name);
6702 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6704 grid_dim: (out_f as u32, m as u32, 1),
6705 block_dim: (128, 1, 1),
6706 shared_mem_bytes: 0,
6707 };
6708 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6709 let __s_b = self.gpu.stream();
6710 let mut b = __s_b.launch_builder(&f);
6711 b.arg(w)
6712 .arg(&aq)
6713 .arg(&ad)
6714 .arg(&mut y)
6715 .arg(&inf)
6716 .arg(&outf)
6717 .arg(&mi)
6718 .arg(&rb);
6719 unsafe {
6720 b.launch(cfg)?;
6721 }
6722 Ok(y)
6723 }
6724
6725 pub fn htod(&self, v: &[f32]) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6726 Ok(self.gpu.stream().clone_htod(v)?)
6727 }
6728 pub fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
6729 Ok(self.gpu.stream().clone_htod(v)?)
6730 }
6731 pub fn htod_i8(&self, v: &[i8]) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
6733 Ok(self.gpu.stream().clone_htod(v)?)
6734 }
6735 pub fn htod_u64(&self, v: &[u64]) -> Result<CudaSlice<u64>, Box<dyn std::error::Error>> {
6736 Ok(self.gpu.stream().clone_htod(v)?)
6737 }
6738 pub fn dtoh_view(
6740 &self,
6741 d: &cudarc::driver::CudaView<f32>,
6742 ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6743 let v = self.gpu.stream().clone_dtoh(d)?;
6744 self.gpu.stream().synchronize()?;
6745 Ok(v)
6746 }
6747 pub fn dtoh(&self, d: &CudaSlice<f32>) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6748 let v = self.gpu.stream().clone_dtoh(d)?;
6749 self.gpu.stream().synchronize()?;
6750 Ok(v)
6751 }
6752 pub fn dtoh_pair(
6756 &self,
6757 a: &CudaSlice<f32>,
6758 b: &CudaSlice<f32>,
6759 ) -> Result<(Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
6760 let av = self.gpu.stream().clone_dtoh(a)?;
6761 let bv = self.gpu.stream().clone_dtoh(b)?;
6762 self.gpu.stream().synchronize()?;
6763 Ok((av, bv))
6764 }
6765 pub fn dtoh_i32(&self, d: &CudaSlice<i32>) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
6767 let v = self.gpu.stream().clone_dtoh(d)?;
6768 self.gpu.stream().synchronize()?;
6769 Ok(v)
6770 }
6771 pub fn dtoh_u8(&self, d: &CudaSlice<u8>) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
6773 let v = self.gpu.stream().clone_dtoh(d)?;
6774 self.gpu.stream().synchronize()?;
6775 Ok(v)
6776 }
6777 pub fn dtoh_u8_view(
6778 &self,
6779 d: &cudarc::driver::CudaView<u8>,
6780 ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
6781 let v = self.gpu.stream().clone_dtoh(d)?;
6782 self.gpu.stream().synchronize()?;
6783 Ok(v)
6784 }
6785 pub fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6786 let s = self.gpu.stream().alloc_zeros::<f32>(n)?;
6787 self.keep_if_capturing(&s);
6788 Ok(s)
6789 }
6790
6791 pub fn prob_of_token_device(
6800 &self,
6801 logits: &CudaSlice<f32>,
6802 tok: &CudaSlice<u32>,
6803 n_vocab: usize,
6804 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6805 let nb = ARGMAX_NB;
6806 let mut part = self.alloc_uninit::<f32>(nb)?;
6807 let mut p = self.alloc_uninit::<f32>(1)?;
6808 let f1 = self.func("prob_of_token_partial_f32");
6809 let cfg1 = LaunchConfig {
6810 grid_dim: (nb as u32, 1, 1),
6811 block_dim: (256, 1, 1),
6812 shared_mem_bytes: 0,
6813 };
6814 let nv = n_vocab as i32;
6815 let __s_b1 = self.gpu.stream();
6816 let mut b1 = __s_b1.launch_builder(&f1);
6817 b1.arg(logits).arg(tok).arg(&mut part).arg(&nv);
6818 unsafe {
6819 b1.launch(cfg1)?;
6820 }
6821 let f2 = self.func("prob_of_token_final_f32");
6822 let cfg2 = LaunchConfig {
6823 grid_dim: (1, 1, 1),
6824 block_dim: (256, 1, 1),
6825 shared_mem_bytes: 0,
6826 };
6827 let nbi = nb as i32;
6828 let __s_b2 = self.gpu.stream();
6829 let mut b2 = __s_b2.launch_builder(&f2);
6830 b2.arg(&part).arg(&mut p).arg(&nbi);
6831 unsafe {
6832 b2.launch(cfg2)?;
6833 }
6834 Ok(p)
6835 }
6836
6837 pub fn prob_of_token_device_col(
6844 &self,
6845 logits: &CudaSlice<f32>,
6846 tok_all: &CudaSlice<u32>,
6847 tok_idx: usize,
6848 p_out: &mut CudaSlice<f32>,
6849 p_idx: usize,
6850 n_vocab: usize,
6851 ) -> Result<(), Box<dyn std::error::Error>> {
6852 let tok_v = tok_all.slice(tok_idx..tok_idx + 1);
6853 let mut p_v = p_out.slice_mut(p_idx..p_idx + 1);
6854 let nb = ARGMAX_NB;
6855 let mut part = self.alloc_uninit::<f32>(nb)?;
6856 let f1 = self.func("prob_of_token_partial_f32");
6857 let cfg1 = LaunchConfig {
6858 grid_dim: (nb as u32, 1, 1),
6859 block_dim: (256, 1, 1),
6860 shared_mem_bytes: 0,
6861 };
6862 let nv = n_vocab as i32;
6863 let __s_b1 = self.gpu.stream();
6864 let mut b1 = __s_b1.launch_builder(&f1);
6865 b1.arg(logits).arg(&tok_v).arg(&mut part).arg(&nv);
6866 unsafe {
6867 b1.launch(cfg1)?;
6868 }
6869 let f2 = self.func("prob_of_token_final_f32");
6870 let cfg2 = LaunchConfig {
6871 grid_dim: (1, 1, 1),
6872 block_dim: (256, 1, 1),
6873 shared_mem_bytes: 0,
6874 };
6875 let nbi = nb as i32;
6876 let __s_b2 = self.gpu.stream();
6877 let mut b2 = __s_b2.launch_builder(&f2);
6878 b2.arg(&part).arg(&mut p_v).arg(&nbi);
6879 unsafe {
6880 b2.launch(cfg2)?;
6881 }
6882 Ok(())
6883 }
6884
6885 pub fn prob_of_token_device_into(
6886 &self,
6887 logits: &CudaSlice<f32>,
6888 tok: &CudaSlice<u32>,
6889 p_out: &mut CudaSlice<f32>,
6890 n_vocab: usize,
6891 ) -> Result<(), Box<dyn std::error::Error>> {
6892 let nb = ARGMAX_NB;
6893 let mut part = self.alloc_uninit::<f32>(nb)?;
6894 let f1 = self.func("prob_of_token_partial_f32");
6895 let cfg1 = LaunchConfig {
6896 grid_dim: (nb as u32, 1, 1),
6897 block_dim: (256, 1, 1),
6898 shared_mem_bytes: 0,
6899 };
6900 let nv = n_vocab as i32;
6901 let __s_b1 = self.gpu.stream();
6902 let mut b1 = __s_b1.launch_builder(&f1);
6903 b1.arg(logits).arg(tok).arg(&mut part).arg(&nv);
6904 unsafe {
6905 b1.launch(cfg1)?;
6906 }
6907 let f2 = self.func("prob_of_token_final_f32");
6908 let cfg2 = LaunchConfig {
6909 grid_dim: (1, 1, 1),
6910 block_dim: (256, 1, 1),
6911 shared_mem_bytes: 0,
6912 };
6913 let nbi = nb as i32;
6914 let __s_b2 = self.gpu.stream();
6915 let mut b2 = __s_b2.launch_builder(&f2);
6916 b2.arg(&part).arg(p_out).arg(&nbi);
6917 unsafe {
6918 b2.launch(cfg2)?;
6919 }
6920 Ok(())
6921 }
6922
6923 pub fn argmax_token_device(
6924 &self,
6925 logits: &CudaSlice<f32>,
6926 n_vocab: usize,
6927 ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
6928 let mut tok = unsafe { self.gpu.stream().alloc::<u32>(1)? };
6929 self.argmax_token_device_into(logits, &mut tok, n_vocab)?;
6930 Ok(tok)
6931 }
6932 pub fn argmax_token_device_into(
6939 &self,
6940 logits: &CudaSlice<f32>,
6941 tok: &mut CudaSlice<u32>,
6942 n_vocab: usize,
6943 ) -> Result<(), Box<dyn std::error::Error>> {
6944 let nb = ARGMAX_NB;
6945 let f1 = self.func("argmax_partial_f32");
6946 let f2 = self.func("argmax_final_f32");
6947 let mut guard = self.argmax_partials.lock().unwrap();
6948 if guard.is_none() {
6949 let pv = self.gpu.stream().alloc_zeros::<f32>(nb)?;
6952 let pi = self.gpu.stream().alloc_zeros::<i32>(nb)?;
6953 *guard = Some((pv, pi));
6954 }
6955 let (part_v, part_i) = guard.as_mut().unwrap();
6956 let nv = n_vocab as i32;
6957 let nbi = nb as i32;
6958 let cfg1 = LaunchConfig {
6960 grid_dim: (nb as u32, 1, 1),
6961 block_dim: (256, 1, 1),
6962 shared_mem_bytes: 0,
6963 };
6964 let __s_b1 = self.gpu.stream();
6965 let mut b1 = __s_b1.launch_builder(&f1);
6966 b1.arg(logits).arg(&mut *part_v).arg(&mut *part_i).arg(&nv);
6967 unsafe {
6968 b1.launch(cfg1)?;
6969 }
6970 let cfg2 = LaunchConfig {
6972 grid_dim: (1, 1, 1),
6973 block_dim: (256, 1, 1),
6974 shared_mem_bytes: 0,
6975 };
6976 let __s_b2 = self.gpu.stream();
6977 let mut b2 = __s_b2.launch_builder(&f2);
6978 b2.arg(&*part_v).arg(&*part_i).arg(tok).arg(&nbi);
6979 unsafe {
6980 b2.launch(cfg2)?;
6981 }
6982 Ok(())
6983 }
6984 pub fn argmax_token_device_col(
6990 &self,
6991 logits: &CudaSlice<f32>,
6992 col: usize,
6993 n_vocab: usize,
6994 toks: &mut CudaSlice<u32>,
6995 out_idx: usize,
6996 ) -> Result<(), Box<dyn std::error::Error>> {
6997 let nb = ARGMAX_NB;
6998 let f1 = self.func("argmax_partial_f32");
6999 let f2 = self.func("argmax_final_f32");
7000 let mut guard = self.argmax_partials.lock().unwrap();
7001 if guard.is_none() {
7002 let pv = self.gpu.stream().alloc_zeros::<f32>(nb)?;
7003 let pi = self.gpu.stream().alloc_zeros::<i32>(nb)?;
7004 *guard = Some((pv, pi));
7005 }
7006 let (part_v, part_i) = guard.as_mut().unwrap();
7007 let col_view = logits.slice(col * n_vocab..(col + 1) * n_vocab);
7008 let nv = n_vocab as i32;
7009 let nbi = nb as i32;
7010 let cfg1 = LaunchConfig {
7011 grid_dim: (nb as u32, 1, 1),
7012 block_dim: (256, 1, 1),
7013 shared_mem_bytes: 0,
7014 };
7015 let __s_b1 = self.gpu.stream();
7016 let mut b1 = __s_b1.launch_builder(&f1);
7017 b1.arg(&col_view)
7018 .arg(&mut *part_v)
7019 .arg(&mut *part_i)
7020 .arg(&nv);
7021 unsafe {
7022 b1.launch(cfg1)?;
7023 }
7024 let mut tok_view = toks.slice_mut(out_idx..out_idx + 1);
7025 let cfg2 = LaunchConfig {
7026 grid_dim: (1, 1, 1),
7027 block_dim: (256, 1, 1),
7028 shared_mem_bytes: 0,
7029 };
7030 let __s_b2 = self.gpu.stream();
7031 let mut b2 = __s_b2.launch_builder(&f2);
7032 b2.arg(&*part_v).arg(&*part_i).arg(&mut tok_view).arg(&nbi);
7033 unsafe {
7034 b2.launch(cfg2)?;
7035 }
7036 Ok(())
7037 }
7038 pub fn htod_u32_v(&self, v: &[u32]) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
7040 Ok(self.gpu.stream().clone_htod(v)?)
7041 }
7042 pub fn dtoh_u32(&self, d: &CudaSlice<u32>) -> Result<Vec<u32>, Box<dyn std::error::Error>> {
7043 let v = self.gpu.stream().clone_dtoh(d)?;
7044 self.gpu.stream().synchronize()?;
7045 Ok(v)
7046 }
7047 pub fn htod_u32_into(
7051 &self,
7052 dst: &mut CudaSlice<u32>,
7053 src: &[u32],
7054 ) -> Result<(), Box<dyn std::error::Error>> {
7055 let mut view = dst.slice_mut(0..src.len());
7056 self.gpu.stream().memcpy_htod(src, &mut view)?;
7057 Ok(())
7058 }
7059
7060 pub fn htod_i32_into(
7063 &self,
7064 dst: &mut CudaSlice<i32>,
7065 src: &[i32],
7066 ) -> Result<(), Box<dyn std::error::Error>> {
7067 let mut view = dst.slice_mut(0..src.len());
7068 self.gpu.stream().memcpy_htod(src, &mut view)?;
7069 Ok(())
7070 }
7071
7072 pub fn alloc_u32_zeroed(&self, n: usize) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
7073 let s = self.gpu.stream().alloc_zeros::<u32>(n)?;
7074 self.keep_if_capturing(&s);
7075 Ok(s)
7076 }
7077 pub fn embed_gather_device_into(
7080 &self,
7081 embd: &CudaSlice<u8>,
7082 token_d: &CudaSlice<u32>,
7083 x_out: &mut CudaSlice<f32>,
7084 n_embd: usize,
7085 qtype: i32,
7086 row_bytes: usize,
7087 ) -> Result<(), Box<dyn std::error::Error>> {
7088 let f = self.func("embed_gather_u32");
7089 let cfg = LaunchConfig {
7090 grid_dim: (((n_embd as u32 + 255) / 256).max(1), 1, 1),
7091 block_dim: (256, 1, 1),
7092 shared_mem_bytes: 0,
7093 };
7094 let (ne, qt, rb) = (n_embd as i32, qtype, row_bytes as i64);
7095 let __s_b = self.gpu.stream();
7096 let mut b = __s_b.launch_builder(&f);
7097 b.arg(embd)
7098 .arg(token_d)
7099 .arg(x_out)
7100 .arg(&ne)
7101 .arg(&qt)
7102 .arg(&rb);
7103 unsafe {
7104 b.launch(cfg)?;
7105 }
7106 Ok(())
7107 }
7108 pub fn dtoh_i32_one(&self, d: &CudaSlice<i32>) -> Result<i32, Box<dyn std::error::Error>> {
7110 let v = self.gpu.stream().clone_dtoh(d)?;
7111 self.gpu.stream().synchronize()?;
7112 Ok(v[0])
7113 }
7114 pub fn i32_set_k(
7121 &self,
7122 dst: &mut CudaSlice<i32>,
7123 v: i32,
7124 ) -> Result<(), Box<dyn std::error::Error>> {
7125 let f = self.func("i32_set_k");
7126 let cfg = LaunchConfig {
7127 grid_dim: (1, 1, 1),
7128 block_dim: (1, 1, 1),
7129 shared_mem_bytes: 0,
7130 };
7131 let idx = 0i32;
7132 let __s_b = self.gpu.stream();
7133 let mut b = __s_b.launch_builder(&f);
7134 b.arg(dst).arg(&v).arg(&idx);
7135 unsafe {
7136 b.launch(cfg)?;
7137 }
7138 Ok(())
7139 }
7140
7141 pub fn set_i32_one(
7142 &self,
7143 d: &mut CudaSlice<i32>,
7144 v: i32,
7145 ) -> Result<(), Box<dyn std::error::Error>> {
7146 self.gpu.stream().memcpy_htod(&[v], d)?;
7147 Ok(())
7148 }
7149 pub fn set_u32_one(
7152 &self,
7153 d: &mut CudaSlice<u32>,
7154 v: u32,
7155 ) -> Result<(), Box<dyn std::error::Error>> {
7156 self.gpu.stream().memcpy_htod(&[v], d)?;
7157 Ok(())
7158 }
7159 pub fn dtoh_u32_one(&self, d: &CudaSlice<u32>) -> Result<u32, Box<dyn std::error::Error>> {
7161 let v = self.gpu.stream().clone_dtoh(d)?;
7162 self.gpu.stream().synchronize()?;
7163 Ok(v[0])
7164 }
7165 pub fn upload_u8(&self, bytes: &[u8]) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
7167 Ok(self.gpu.stream().clone_htod(bytes)?)
7168 }
7169 pub fn embed_gather_device(
7173 &self,
7174 embd: &CudaSlice<u8>,
7175 token_d: &CudaSlice<u32>,
7176 n_embd: usize,
7177 qtype: i32,
7178 row_bytes: usize,
7179 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7180 let f = self.func("embed_gather_u32");
7181 let mut x = self.alloc_uninit::<f32>(n_embd)?;
7182 let cfg = LaunchConfig {
7183 grid_dim: (((n_embd as u32 + 255) / 256).max(1), 1, 1),
7184 block_dim: (256, 1, 1),
7185 shared_mem_bytes: 0,
7186 };
7187 let (ne, qt, rb) = (n_embd as i32, qtype, row_bytes as i64);
7188 let __s_b = self.gpu.stream();
7189 let mut b = __s_b.launch_builder(&f);
7190 b.arg(embd)
7191 .arg(token_d)
7192 .arg(&mut x)
7193 .arg(&ne)
7194 .arg(&qt)
7195 .arg(&rb);
7196 unsafe {
7197 b.launch(cfg)?;
7198 }
7199 Ok(x)
7200 }
7201
7202 pub fn embed_gather_device_t(
7206 &self,
7207 embd: &CudaSlice<u8>,
7208 tokens: &[u32],
7209 n_embd: usize,
7210 qtype: i32,
7211 row_bytes: usize,
7212 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7213 let t = tokens.len();
7214 let tok_d = self.gpu.stream().clone_htod(tokens)?;
7215 let f = self.func("embed_gather_u32_t");
7216 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7217 let cfg = LaunchConfig {
7218 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7219 block_dim: (256, 1, 1),
7220 shared_mem_bytes: 0,
7221 };
7222 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7223 let __s_b = self.gpu.stream();
7224 let mut b = __s_b.launch_builder(&f);
7225 b.arg(embd)
7226 .arg(&tok_d)
7227 .arg(&mut x)
7228 .arg(&ne)
7229 .arg(&qt)
7230 .arg(&rb)
7231 .arg(&ti);
7232 unsafe {
7233 b.launch(cfg)?;
7234 }
7235 Ok(x)
7236 }
7237
7238 pub fn embed_gather_device_tv(
7243 &self,
7244 embd: &CudaSlice<u8>,
7245 tok_v: &cudarc::driver::CudaView<u32>,
7246 t: usize,
7247 n_embd: usize,
7248 qtype: i32,
7249 row_bytes: usize,
7250 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7251 let f = self.func("embed_gather_u32_t");
7252 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7253 let cfg = LaunchConfig {
7254 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7255 block_dim: (256, 1, 1),
7256 shared_mem_bytes: 0,
7257 };
7258 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7259 let __s_b = self.gpu.stream();
7260 let mut b = __s_b.launch_builder(&f);
7261 b.arg(embd)
7262 .arg(tok_v)
7263 .arg(&mut x)
7264 .arg(&ne)
7265 .arg(&qt)
7266 .arg(&rb)
7267 .arg(&ti);
7268 unsafe {
7269 b.launch(cfg)?;
7270 }
7271 Ok(x)
7272 }
7273
7274 pub fn embed_gather_device_td(
7275 &self,
7276 embd: &CudaSlice<u8>,
7277 tok_d: &CudaSlice<u32>,
7278 t: usize,
7279 n_embd: usize,
7280 qtype: i32,
7281 row_bytes: usize,
7282 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7283 let f = self.func("embed_gather_u32_t");
7284 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7285 let cfg = LaunchConfig {
7286 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7287 block_dim: (256, 1, 1),
7288 shared_mem_bytes: 0,
7289 };
7290 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7291 let __s_b = self.gpu.stream();
7292 let mut b = __s_b.launch_builder(&f);
7293 b.arg(embd)
7294 .arg(tok_d)
7295 .arg(&mut x)
7296 .arg(&ne)
7297 .arg(&qt)
7298 .arg(&rb)
7299 .arg(&ti);
7300 unsafe {
7301 b.launch(cfg)?;
7302 }
7303 Ok(x)
7304 }
7305
7306 #[inline]
7312 fn keep_if_capturing<T: cudarc::driver::DeviceRepr + Send + 'static>(&self, s: &CudaSlice<T>) {
7314 if self
7315 .capture_keep_on
7316 .load(std::sync::atomic::Ordering::Relaxed)
7317 {
7318 self.capture_keep.lock().unwrap().push(Box::new(s.clone()));
7319 }
7320 }
7321
7322 fn alloc_uninit<T: cudarc::driver::DeviceRepr + Send + 'static>(
7323 &self,
7324 n: usize,
7325 ) -> Result<CudaSlice<T>, Box<dyn std::error::Error>> {
7326 let mut s = unsafe { self.gpu.stream().alloc::<T>(n)? };
7327 {
7331 static Z: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7332 if *Z.get_or_init(|| std::env::var("MEMRA_DEBUG_ZERO_ALLOCS").as_deref() == Ok("1")) {
7333 use cudarc::driver::DevicePtrMut;
7335 let n_bytes = s.len() * std::mem::size_of::<T>();
7336 let stream = self.gpu.stream();
7337 let (p_, _g) = s.device_ptr_mut(&stream);
7338 unsafe {
7339 cudarc::driver::sys::cuMemsetD8Async(p_, 0, n_bytes, stream.cu_stream())
7340 .result()?;
7341 }
7342 }
7343 }
7344 self.keep_if_capturing(&s);
7345 Ok(s)
7346 }
7347
7348 pub fn uninit_q8_pair(
7353 &self,
7354 n: usize,
7355 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7356 Ok((
7357 self.alloc_uninit::<i8>(n)?,
7358 self.alloc_uninit::<f32>(n / 32)?,
7359 ))
7360 }
7361
7362 pub fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7363 self.alloc_uninit::<f32>(n)
7364 }
7365
7366 pub fn alloc_i8_uninit(&self, n: usize) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
7368 self.alloc_uninit::<i8>(n)
7369 }
7370
7371 #[allow(clippy::too_many_arguments)]
7375 pub fn rms_norm3(
7376 &self,
7377 x: &CudaSlice<f32>,
7378 w0: &CudaSlice<f32>,
7379 w1: &CudaSlice<f32>,
7380 w2: &CudaSlice<f32>,
7381 d0: &mut CudaSlice<f32>,
7382 d1: &mut CudaSlice<f32>,
7383 d2: &mut CudaSlice<f32>,
7384 ncols: usize,
7385 nrows: usize,
7386 eps: f32,
7387 ) -> Result<(), Box<dyn std::error::Error>> {
7388 let f = self.func("rms_norm3_f32");
7389 let cfg = LaunchConfig {
7390 grid_dim: (nrows as u32, 1, 1),
7391 block_dim: (rms_block(), 1, 1),
7392 shared_mem_bytes: 0,
7393 };
7394 let (nc, e) = (ncols as i32, eps);
7395 let __s_b = self.gpu.stream();
7396 let mut b = __s_b.launch_builder(&f);
7397 b.arg(x)
7398 .arg(w0)
7399 .arg(w1)
7400 .arg(w2)
7401 .arg(d0)
7402 .arg(d1)
7403 .arg(d2)
7404 .arg(&nc)
7405 .arg(&e);
7406 unsafe {
7407 b.launch(cfg)?;
7408 }
7409 Ok(())
7410 }
7411
7412 #[allow(clippy::too_many_arguments)]
7414 pub fn qkvnorm_w_on_prefill(rows: usize, ncols: usize) -> bool {
7417 static WARP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7418 *WARP_ON.get_or_init(|| {
7419 std::env::var("MEMRA_QKVNORM_W")
7420 .map(|v| v != "0")
7421 .unwrap_or(true)
7422 }) && ncols % 4 == 0
7423 && rows >= 64
7424 }
7425
7426 #[allow(clippy::too_many_arguments)]
7429 pub fn rms_norm_qkv_w4b(
7430 &self,
7431 q: &CudaSlice<f32>,
7432 k: &CudaSlice<f32>,
7433 v: &CudaSlice<f32>,
7434 wq: &CudaSlice<f32>,
7435 wk: &CudaSlice<f32>,
7436 wv: &CudaSlice<f32>,
7437 dq: &mut CudaSlice<f32>,
7438 dk: &mut CudaSlice<f32>,
7439 dv: &mut CudaSlice<f32>,
7440 dvb: &mut CudaSlice<u8>,
7441 ncols: usize,
7442 rq: usize,
7443 rk: usize,
7444 eps: f32,
7445 vf16: bool,
7446 ) -> Result<(), Box<dyn std::error::Error>> {
7447 assert!(ncols % 4 == 0 && rq + 2 * rk >= 64);
7448 let f = self.func("rms_norm_qkv_w4b_f32");
7449 let rows = (rq + 2 * rk) as u32;
7450 let cfg = LaunchConfig {
7451 grid_dim: (rows.div_ceil(8), 1, 1),
7452 block_dim: (256, 1, 1),
7453 shared_mem_bytes: 0,
7454 };
7455 let (nc, rqi, rki, rvi, e) = (ncols as i32, rq as i32, rk as i32, rk as i32, eps);
7456 let vf = vf16 as i32;
7457 let __s_b = self.gpu.stream();
7458 let mut b = __s_b.launch_builder(&f);
7459 b.arg(q)
7460 .arg(k)
7461 .arg(v)
7462 .arg(wq)
7463 .arg(wk)
7464 .arg(wv)
7465 .arg(dq)
7466 .arg(dk)
7467 .arg(dv)
7468 .arg(&mut *dvb)
7469 .arg(&nc)
7470 .arg(&rqi)
7471 .arg(&rki)
7472 .arg(&rvi)
7473 .arg(&e)
7474 .arg(&vf);
7475 unsafe {
7476 b.launch(cfg)?;
7477 }
7478 Ok(())
7479 }
7480
7481 pub fn rms_norm_qkv(
7482 &self,
7483 q: &CudaSlice<f32>,
7484 k: &CudaSlice<f32>,
7485 v: &CudaSlice<f32>,
7486 wq: &CudaSlice<f32>,
7487 wk: &CudaSlice<f32>,
7488 wv: &CudaSlice<f32>,
7489 dq: &mut CudaSlice<f32>,
7490 dk: &mut CudaSlice<f32>,
7491 dv: &mut CudaSlice<f32>,
7492 ncols: usize,
7493 rq: usize,
7494 rk: usize,
7495 eps: f32,
7496 ) -> Result<(), Box<dyn std::error::Error>> {
7497 static WARP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7501 let warp_on = *WARP_ON.get_or_init(|| {
7502 std::env::var("MEMRA_QKVNORM_W")
7503 .map(|v| v != "0")
7504 .unwrap_or(true)
7505 });
7506 if warp_on && ncols % 4 == 0 && rq + 2 * rk >= 64 {
7509 let f = self.func("rms_norm_qkv_w4_f32");
7510 let rows = (rq + 2 * rk) as u32;
7511 let cfg = LaunchConfig {
7512 grid_dim: (rows.div_ceil(8), 1, 1),
7513 block_dim: (256, 1, 1),
7514 shared_mem_bytes: 0,
7515 };
7516 let (nc, rqi, rki, rvi, e) = (ncols as i32, rq as i32, rk as i32, rk as i32, eps);
7517 let __s_b = self.gpu.stream();
7518 let mut b = __s_b.launch_builder(&f);
7519 b.arg(q)
7520 .arg(k)
7521 .arg(v)
7522 .arg(wq)
7523 .arg(wk)
7524 .arg(wv)
7525 .arg(dq)
7526 .arg(dk)
7527 .arg(dv)
7528 .arg(&nc)
7529 .arg(&rqi)
7530 .arg(&rki)
7531 .arg(&rvi)
7532 .arg(&e);
7533 unsafe {
7534 b.launch(cfg)?;
7535 }
7536 return Ok(());
7537 }
7538 let f = self.func("rms_norm_qkv_f32");
7539 let grid = (rq + 2 * rk) as u32;
7540 let cfg = LaunchConfig {
7541 grid_dim: (grid, 1, 1),
7542 block_dim: (rms_block(), 1, 1),
7543 shared_mem_bytes: 0,
7544 };
7545 let (nc, rqi, rki, e) = (ncols as i32, rq as i32, rk as i32, eps);
7546 let __s_b = self.gpu.stream();
7547 let mut b = __s_b.launch_builder(&f);
7548 b.arg(q)
7549 .arg(k)
7550 .arg(v)
7551 .arg(wq)
7552 .arg(wk)
7553 .arg(wv)
7554 .arg(dq)
7555 .arg(dk)
7556 .arg(dv)
7557 .arg(&nc)
7558 .arg(&rqi)
7559 .arg(&rki)
7560 .arg(&e);
7561 unsafe {
7562 b.launch(cfg)?;
7563 }
7564 Ok(())
7565 }
7566
7567 #[allow(clippy::too_many_arguments)]
7569 pub fn rms_norm2x(
7570 &self,
7571 a: &CudaSlice<f32>,
7572 bb: &CudaSlice<f32>,
7573 wa: &CudaSlice<f32>,
7574 wb: &CudaSlice<f32>,
7575 da: &mut CudaSlice<f32>,
7576 db: &mut CudaSlice<f32>,
7577 ncols: usize,
7578 nrows: usize,
7579 eps: f32,
7580 ) -> Result<(), Box<dyn std::error::Error>> {
7581 let f = self.func("rms_norm2x_f32");
7582 let cfg = LaunchConfig {
7583 grid_dim: (2 * nrows as u32, 1, 1),
7584 block_dim: (rms_block(), 1, 1),
7585 shared_mem_bytes: 0,
7586 };
7587 let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
7588 let __s_b = self.gpu.stream();
7589 let mut b = __s_b.launch_builder(&f);
7590 b.arg(a)
7591 .arg(bb)
7592 .arg(wa)
7593 .arg(wb)
7594 .arg(da)
7595 .arg(db)
7596 .arg(&nc)
7597 .arg(&nr)
7598 .arg(&e);
7599 unsafe {
7600 b.launch(cfg)?;
7601 }
7602 Ok(())
7603 }
7604
7605 pub fn softcap(
7607 &self,
7608 y: &mut CudaSlice<f32>,
7609 cap: f32,
7610 n: usize,
7611 ) -> Result<(), Box<dyn std::error::Error>> {
7612 let f = self.func("softcap_f32");
7613 let cfg = LaunchConfig::for_num_elems(n as u32);
7614 let ni = n as i32;
7615 let __s_b = self.gpu.stream();
7616 let mut b = __s_b.launch_builder(&f);
7617 b.arg(y).arg(&cap).arg(&ni);
7618 unsafe {
7619 b.launch(cfg)?;
7620 }
7621 Ok(())
7622 }
7623
7624 pub fn mask_ids_rows(
7627 &self,
7628 y: &mut CudaSlice<f32>,
7629 ids: &CudaSlice<i32>,
7630 n_ids: usize,
7631 n_vocab: usize,
7632 t: usize,
7633 ) -> Result<(), Box<dyn std::error::Error>> {
7634 let f = self.func("mask_ids_rows_f32");
7635 let cfg = LaunchConfig::for_num_elems((n_ids * t) as u32);
7636 let (ni, nv, ti) = (n_ids as i32, n_vocab as i32, t as i32);
7637 let __s_b = self.gpu.stream();
7638 let mut b = __s_b.launch_builder(&f);
7639 b.arg(y).arg(ids).arg(&ni).arg(&nv).arg(&ti);
7640 unsafe {
7641 b.launch(cfg)?;
7642 }
7643 Ok(())
7644 }
7645
7646 #[allow(clippy::too_many_arguments)]
7648 pub fn add_scale_rms_norm(
7649 &self,
7650 a: &CudaSlice<f32>,
7651 b_in: &CudaSlice<f32>,
7652 c: f32,
7653 w: &CudaSlice<f32>,
7654 res: &mut CudaSlice<f32>,
7655 dst: &mut CudaSlice<f32>,
7656 ncols: usize,
7657 nrows: usize,
7658 eps: f32,
7659 ) -> Result<(), Box<dyn std::error::Error>> {
7660 let f = self.func("add_scale_rms_norm_f32");
7661 let cfg = LaunchConfig {
7662 grid_dim: (nrows as u32, 1, 1),
7663 block_dim: (rms_block(), 1, 1),
7664 shared_mem_bytes: 0,
7665 };
7666 let (nc, e2) = (ncols as i32, eps);
7667 let __s_b = self.gpu.stream();
7668 let mut b = __s_b.launch_builder(&f);
7669 b.arg(a)
7670 .arg(b_in)
7671 .arg(&c)
7672 .arg(w)
7673 .arg(res)
7674 .arg(dst)
7675 .arg(&nc)
7676 .arg(&e2);
7677 unsafe {
7678 b.launch(cfg)?;
7679 }
7680 Ok(())
7681 }
7682
7683 #[allow(clippy::too_many_arguments)]
7686 pub fn add_scale_rms_norm_q8_1(
7687 &self,
7688 a: &CudaSlice<f32>,
7689 b_in: &CudaSlice<f32>,
7690 c: f32,
7691 w: &CudaSlice<f32>,
7692 res: &mut CudaSlice<f32>,
7693 ncols: usize,
7694 nrows: usize,
7695 eps: f32,
7696 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7697 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7698 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7699 let (nc, e2) = (ncols as i32, eps);
7700 if Self::pdl_on() && Self::pdl_wb_on() {
7701 {
7702 use cudarc::driver::{DevicePtr, DevicePtrMut};
7703 let s = &self.gpu.stream();
7704 let (pa, _g0) = a.device_ptr(s);
7705 let (pb, _g1) = b_in.device_ptr(s);
7706 let (pw, _g2) = w.device_ptr(s);
7707 let (pr, _g3) = res.device_ptr_mut(s);
7708 let (pq, _g4) = out_q.device_ptr_mut(s);
7709 let (pd, _g5) = out_d.device_ptr_mut(s);
7710 let mut ps = [
7711 &pa as *const _ as *mut std::ffi::c_void,
7712 &pb as *const _ as *mut _,
7713 &c as *const _ as *mut _,
7714 &pw as *const _ as *mut _,
7715 &pr as *const _ as *mut _,
7716 &pq as *const _ as *mut _,
7717 &pd as *const _ as *mut _,
7718 &nc as *const _ as *mut _,
7719 &e2 as *const _ as *mut _,
7720 ];
7721 unsafe {
7722 self.launch_pdl(
7723 "add_scale_rms_norm_q8_1",
7724 (nrows as u32, 1, 1),
7725 (rms_block(), 1, 1),
7726 &mut ps,
7727 )?;
7728 }
7729 }
7730 return Ok((out_q, out_d));
7731 }
7732 let f = self.func("add_scale_rms_norm_q8_1");
7733 let cfg = LaunchConfig {
7734 grid_dim: (nrows as u32, 1, 1),
7735 block_dim: (rms_block(), 1, 1),
7736 shared_mem_bytes: 0,
7737 };
7738 let __s_b = self.gpu.stream();
7739 let mut b = __s_b.launch_builder(&f);
7740 b.arg(a)
7741 .arg(b_in)
7742 .arg(&c)
7743 .arg(w)
7744 .arg(res)
7745 .arg(&mut out_q)
7746 .arg(&mut out_d)
7747 .arg(&nc)
7748 .arg(&e2);
7749 unsafe {
7750 b.launch(cfg)?;
7751 }
7752 Ok((out_q, out_d))
7753 }
7754
7755 #[allow(clippy::too_many_arguments)]
7757 pub fn add_scale_rms_norm_q8_1_into(
7758 &self,
7759 a: &CudaSlice<f32>,
7760 b_in: &CudaSlice<f32>,
7761 c: f32,
7762 w: &CudaSlice<f32>,
7763 res: &mut CudaSlice<f32>,
7764 ncols: usize,
7765 nrows: usize,
7766 eps: f32,
7767 out_q: &mut CudaSlice<i8>,
7768 out_d: &mut CudaSlice<f32>,
7769 ) -> Result<(), Box<dyn std::error::Error>> {
7770 debug_assert!(out_q.len() >= nrows * ncols && out_d.len() >= nrows * (ncols / 32));
7771 let (nc, e2) = (ncols as i32, eps);
7772 if Self::pdl_on() && Self::pdl_wb_on() {
7773 use cudarc::driver::{DevicePtr, DevicePtrMut};
7774 let s = &self.gpu.stream();
7775 let (pa, _g0) = a.device_ptr(s);
7776 let (pb, _g1) = b_in.device_ptr(s);
7777 let (pw, _g2) = w.device_ptr(s);
7778 let (pr, _g3) = res.device_ptr_mut(s);
7779 let (pq, _g4) = out_q.device_ptr_mut(s);
7780 let (pd, _g5) = out_d.device_ptr_mut(s);
7781 let mut ps = [
7782 &pa as *const _ as *mut std::ffi::c_void,
7783 &pb as *const _ as *mut _,
7784 &c as *const _ as *mut _,
7785 &pw as *const _ as *mut _,
7786 &pr as *const _ as *mut _,
7787 &pq as *const _ as *mut _,
7788 &pd as *const _ as *mut _,
7789 &nc as *const _ as *mut _,
7790 &e2 as *const _ as *mut _,
7791 ];
7792 unsafe {
7793 self.launch_pdl(
7794 "add_scale_rms_norm_q8_1",
7795 (nrows as u32, 1, 1),
7796 (rms_block(), 1, 1),
7797 &mut ps,
7798 )?;
7799 }
7800 return Ok(());
7801 }
7802 let f = self.func("add_scale_rms_norm_q8_1");
7803 let cfg = LaunchConfig {
7804 grid_dim: (nrows as u32, 1, 1),
7805 block_dim: (rms_block(), 1, 1),
7806 shared_mem_bytes: 0,
7807 };
7808 let __s_b = self.gpu.stream();
7809 let mut b = __s_b.launch_builder(&f);
7810 b.arg(a)
7811 .arg(b_in)
7812 .arg(&c)
7813 .arg(w)
7814 .arg(res)
7815 .arg(&mut *out_q)
7816 .arg(&mut *out_d)
7817 .arg(&nc)
7818 .arg(&e2);
7819 unsafe {
7820 b.launch(cfg)?;
7821 }
7822 Ok(())
7823 }
7824
7825 #[allow(clippy::too_many_arguments)]
7828 pub fn rms_pre_add_scale_rms_norm_q8_1(
7829 &self,
7830 a: &CudaSlice<f32>,
7831 wa: &CudaSlice<f32>,
7832 b_in: &CudaSlice<f32>,
7833 c: f32,
7834 w: &CudaSlice<f32>,
7835 res: &mut CudaSlice<f32>,
7836 ncols: usize,
7837 nrows: usize,
7838 eps: f32,
7839 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7840 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7841 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7842 let (nc, e2) = (ncols as i32, eps);
7843 if Self::pdl_on() {
7844 {
7845 use cudarc::driver::{DevicePtr, DevicePtrMut};
7846 let s = &self.gpu.stream();
7847 let (pa, _g0) = a.device_ptr(s);
7848 let (pwa, _g1) = wa.device_ptr(s);
7849 let (pb, _g2) = b_in.device_ptr(s);
7850 let (pw, _g3) = w.device_ptr(s);
7851 let (pr, _g4) = res.device_ptr_mut(s);
7852 let (pq, _g5) = out_q.device_ptr_mut(s);
7853 let (pd, _g6) = out_d.device_ptr_mut(s);
7854 let mut ps = [
7855 &pa as *const _ as *mut std::ffi::c_void,
7856 &pwa as *const _ as *mut _,
7857 &pb as *const _ as *mut _,
7858 &c as *const _ as *mut _,
7859 &pw as *const _ as *mut _,
7860 &pr as *const _ as *mut _,
7861 &pq as *const _ as *mut _,
7862 &pd as *const _ as *mut _,
7863 &nc as *const _ as *mut _,
7864 &e2 as *const _ as *mut _,
7865 ];
7866 unsafe {
7867 self.launch_pdl(
7868 "rms_pre_add_scale_rms_norm_q8_1",
7869 (nrows as u32, 1, 1),
7870 (rms_block(), 1, 1),
7871 &mut ps,
7872 )?;
7873 }
7874 }
7875 return Ok((out_q, out_d));
7876 }
7877 let f = self.func("rms_pre_add_scale_rms_norm_q8_1");
7878 let cfg = LaunchConfig {
7879 grid_dim: (nrows as u32, 1, 1),
7880 block_dim: (rms_block(), 1, 1),
7881 shared_mem_bytes: 0,
7882 };
7883 let __s_b = self.gpu.stream();
7884 let mut b = __s_b.launch_builder(&f);
7885 b.arg(a)
7886 .arg(wa)
7887 .arg(b_in)
7888 .arg(&c)
7889 .arg(w)
7890 .arg(res)
7891 .arg(&mut out_q)
7892 .arg(&mut out_d)
7893 .arg(&nc)
7894 .arg(&e2);
7895 unsafe {
7896 b.launch(cfg)?;
7897 }
7898 Ok((out_q, out_d))
7899 }
7900
7901 pub fn gelu_tanh_mul_q8_1(
7904 &self,
7905 gate: &CudaSlice<f32>,
7906 up: &cudarc::driver::CudaView<f32>,
7907 act: &mut CudaSlice<f32>,
7908 ncols: usize,
7909 nrows: usize,
7910 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7911 debug_assert!(ncols % 128 == 0);
7912 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7913 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7914 let nc = ncols as i32;
7915 if Self::pdl_on() {
7916 {
7917 use cudarc::driver::{DevicePtr, DevicePtrMut};
7918 let s = &self.gpu.stream();
7919 let (pg, _g0) = gate.device_ptr(s);
7920 let (pu, _g1) = up.device_ptr(s);
7921 let (pact, _g2) = act.device_ptr_mut(s);
7922 let (pq, _g3) = out_q.device_ptr_mut(s);
7923 let (pd, _g4) = out_d.device_ptr_mut(s);
7924 let mut ps = [
7925 &pg as *const _ as *mut std::ffi::c_void,
7926 &pu as *const _ as *mut _,
7927 &pact as *const _ as *mut _,
7928 &pq as *const _ as *mut _,
7929 &pd as *const _ as *mut _,
7930 &nc as *const _ as *mut _,
7931 ];
7932 unsafe {
7933 self.launch_pdl(
7934 "gelu_tanh_mul_q8_1",
7935 (nrows as u32, 1, 1),
7936 (rms_block(), 1, 1),
7937 &mut ps,
7938 )?;
7939 }
7940 }
7941 return Ok((out_q, out_d));
7942 }
7943 let f = self.func("gelu_tanh_mul_q8_1");
7944 let cfg = LaunchConfig {
7945 grid_dim: (nrows as u32, 1, 1),
7946 block_dim: (rms_block(), 1, 1),
7947 shared_mem_bytes: 0,
7948 };
7949 let __s_b = self.gpu.stream();
7950 let mut b = __s_b.launch_builder(&f);
7951 b.arg(gate)
7952 .arg(up)
7953 .arg(act)
7954 .arg(&mut out_q)
7955 .arg(&mut out_d)
7956 .arg(&nc);
7957 unsafe {
7958 b.launch(cfg)?;
7959 }
7960 Ok((out_q, out_d))
7961 }
7962
7963 #[allow(clippy::too_many_arguments)]
7965 pub fn gelu_tanh_mul_q8_1_into(
7966 &self,
7967 gate: &CudaSlice<f32>,
7968 up: &cudarc::driver::CudaView<f32>,
7969 act: &mut CudaSlice<f32>,
7970 ncols: usize,
7971 nrows: usize,
7972 out_q: &mut CudaSlice<i8>,
7973 out_d: &mut CudaSlice<f32>,
7974 ) -> Result<(), Box<dyn std::error::Error>> {
7975 debug_assert!(ncols % 128 == 0);
7976 debug_assert!(out_q.len() >= nrows * ncols && out_d.len() >= nrows * (ncols / 32));
7977 let nc = ncols as i32;
7978 if Self::pdl_on() {
7979 use cudarc::driver::{DevicePtr, DevicePtrMut};
7980 let s = &self.gpu.stream();
7981 let (pg, _g0) = gate.device_ptr(s);
7982 let (pu, _g1) = up.device_ptr(s);
7983 let (pact, _g2) = act.device_ptr_mut(s);
7984 let (pq, _g3) = out_q.device_ptr_mut(s);
7985 let (pd, _g4) = out_d.device_ptr_mut(s);
7986 let mut ps = [
7987 &pg as *const _ as *mut std::ffi::c_void,
7988 &pu as *const _ as *mut _,
7989 &pact as *const _ as *mut _,
7990 &pq as *const _ as *mut _,
7991 &pd as *const _ as *mut _,
7992 &nc as *const _ as *mut _,
7993 ];
7994 unsafe {
7995 self.launch_pdl(
7996 "gelu_tanh_mul_q8_1",
7997 (nrows as u32, 1, 1),
7998 (rms_block(), 1, 1),
7999 &mut ps,
8000 )?;
8001 }
8002 return Ok(());
8003 }
8004 let f = self.func("gelu_tanh_mul_q8_1");
8005 let cfg = LaunchConfig {
8006 grid_dim: (nrows as u32, 1, 1),
8007 block_dim: (rms_block(), 1, 1),
8008 shared_mem_bytes: 0,
8009 };
8010 let __s_b = self.gpu.stream();
8011 let mut b = __s_b.launch_builder(&f);
8012 b.arg(gate)
8013 .arg(up)
8014 .arg(&mut *act)
8015 .arg(&mut *out_q)
8016 .arg(&mut *out_d)
8017 .arg(&nc);
8018 unsafe {
8019 b.launch(cfg)?;
8020 }
8021 Ok(())
8022 }
8023
8024 #[allow(clippy::too_many_arguments)]
8026 pub fn add_rms_norm3_q8z(
8027 &self,
8028 a: &CudaSlice<f32>,
8029 b_in: &CudaSlice<f32>,
8030 w0: &CudaSlice<f32>,
8031 w1: &CudaSlice<f32>,
8032 w2: &CudaSlice<f32>,
8033 res: &mut CudaSlice<f32>,
8034 out1: &mut CudaSlice<f32>,
8035 ncols: usize,
8036 nrows: usize,
8037 eps: f32,
8038 ) -> Result<
8039 (
8040 (CudaSlice<i8>, CudaSlice<f32>),
8041 (CudaSlice<i8>, CudaSlice<f32>),
8042 ),
8043 Box<dyn std::error::Error>,
8044 > {
8045 let mut q0 = self.alloc_uninit::<i8>(nrows * ncols)?;
8046 let mut d0 = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
8047 let mut q2 = self.alloc_uninit::<i8>(nrows * ncols)?;
8048 let mut d2 = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
8049 let f = self.func("add_rms_norm3_q8z_f32");
8050 let cfg = LaunchConfig {
8051 grid_dim: (nrows as u32, 1, 1),
8052 block_dim: (rms_block(), 1, 1),
8053 shared_mem_bytes: 0,
8054 };
8055 let (nc, e2) = (ncols as i32, eps);
8056 let __s_b = self.gpu.stream();
8057 let mut b = __s_b.launch_builder(&f);
8058 b.arg(a)
8059 .arg(b_in)
8060 .arg(w0)
8061 .arg(w1)
8062 .arg(w2)
8063 .arg(res)
8064 .arg(&mut q0)
8065 .arg(&mut d0)
8066 .arg(out1)
8067 .arg(&mut q2)
8068 .arg(&mut d2)
8069 .arg(&nc)
8070 .arg(&e2);
8071 unsafe {
8072 b.launch(cfg)?;
8073 }
8074 Ok(((q0, d0), (q2, d2)))
8075 }
8076
8077 #[allow(clippy::too_many_arguments)]
8079 pub fn add_rms_norm3(
8080 &self,
8081 a: &CudaSlice<f32>,
8082 b_in: &CudaSlice<f32>,
8083 w0: &CudaSlice<f32>,
8084 w1: &CudaSlice<f32>,
8085 w2: &CudaSlice<f32>,
8086 res: &mut CudaSlice<f32>,
8087 d0: &mut CudaSlice<f32>,
8088 d1: &mut CudaSlice<f32>,
8089 d2: &mut CudaSlice<f32>,
8090 ncols: usize,
8091 nrows: usize,
8092 eps: f32,
8093 ) -> Result<(), Box<dyn std::error::Error>> {
8094 let f = self.func("add_rms_norm3_f32");
8095 let cfg = LaunchConfig {
8096 grid_dim: (nrows as u32, 1, 1),
8097 block_dim: (rms_block(), 1, 1),
8098 shared_mem_bytes: 0,
8099 };
8100 let (nc, e2) = (ncols as i32, eps);
8101 let __s_b = self.gpu.stream();
8102 let mut b = __s_b.launch_builder(&f);
8103 b.arg(a)
8104 .arg(b_in)
8105 .arg(w0)
8106 .arg(w1)
8107 .arg(w2)
8108 .arg(res)
8109 .arg(d0)
8110 .arg(d1)
8111 .arg(d2)
8112 .arg(&nc)
8113 .arg(&e2);
8114 unsafe {
8115 b.launch(cfg)?;
8116 }
8117 Ok(())
8118 }
8119
8120 pub fn add_scale(
8122 &self,
8123 a: &CudaSlice<f32>,
8124 b_in: &CudaSlice<f32>,
8125 c: f32,
8126 dst: &mut CudaSlice<f32>,
8127 n: usize,
8128 ) -> Result<(), Box<dyn std::error::Error>> {
8129 let f = self.func("add_scale_f32");
8130 let cfg = LaunchConfig::for_num_elems(n as u32);
8131 let ni = n as i32;
8132 let __s_b = self.gpu.stream();
8133 let mut b = __s_b.launch_builder(&f);
8134 b.arg(a).arg(b_in).arg(&c).arg(dst).arg(&ni);
8135 unsafe {
8136 b.launch(cfg)?;
8137 }
8138 Ok(())
8139 }
8140
8141 pub fn layer_norm_bias(
8143 &self,
8144 x: &CudaSlice<f32>,
8145 w: &CudaSlice<f32>,
8146 b: &CudaSlice<f32>,
8147 dst: &mut CudaSlice<f32>,
8148 ncols: usize,
8149 nrows: usize,
8150 eps: f32,
8151 ) -> Result<(), Box<dyn std::error::Error>> {
8152 let f = self.func("layer_norm_bias_f32");
8153 let (nc, e) = (ncols as i32, eps);
8154 let cfg = LaunchConfig {
8155 grid_dim: (nrows as u32, 1, 1),
8156 block_dim: (256, 1, 1),
8157 shared_mem_bytes: 0,
8158 };
8159 let __s_b = self.gpu.stream();
8160 let mut lb = __s_b.launch_builder(&f);
8161 lb.arg(x).arg(w).arg(b).arg(&mut *dst).arg(&nc).arg(&e);
8162 unsafe {
8163 lb.launch(cfg)?;
8164 }
8165 Ok(())
8166 }
8167
8168 pub fn gelu_tanh(
8170 &self,
8171 x: &CudaSlice<f32>,
8172 dst: &mut CudaSlice<f32>,
8173 n: usize,
8174 ) -> Result<(), Box<dyn std::error::Error>> {
8175 let f = self.func("gelu_tanh_f32");
8176 let ni = n as i64;
8177 let cfg = LaunchConfig {
8178 grid_dim: (n.div_ceil(256) as u32, 1, 1),
8179 block_dim: (256, 1, 1),
8180 shared_mem_bytes: 0,
8181 };
8182 let __s_b = self.gpu.stream();
8183 let mut lb = __s_b.launch_builder(&f);
8184 lb.arg(x).arg(&mut *dst).arg(&ni);
8185 unsafe {
8186 lb.launch(cfg)?;
8187 }
8188 Ok(())
8189 }
8190
8191 pub fn row_softmax(
8193 &self,
8194 x: &mut CudaSlice<f32>,
8195 ncols: usize,
8196 nrows: usize,
8197 ) -> Result<(), Box<dyn std::error::Error>> {
8198 let f = self.func("row_softmax_f32");
8199 let nc = ncols as i32;
8200 let cfg = LaunchConfig {
8201 grid_dim: (nrows as u32, 1, 1),
8202 block_dim: (256, 1, 1),
8203 shared_mem_bytes: 0,
8204 };
8205 let __s_b = self.gpu.stream();
8206 let mut lb = __s_b.launch_builder(&f);
8207 lb.arg(&mut *x).arg(&nc);
8208 unsafe {
8209 lb.launch(cfg)?;
8210 }
8211 Ok(())
8212 }
8213
8214 pub fn rms_norm(
8215 &self,
8216 x: &CudaSlice<f32>,
8217 w: &CudaSlice<f32>,
8218 dst: &mut CudaSlice<f32>,
8219 ncols: usize,
8220 nrows: usize,
8221 eps: f32,
8222 ) -> Result<(), Box<dyn std::error::Error>> {
8223 let (nc, e) = (ncols as i32, eps);
8224 if Self::pdl_on() && Self::pdl_wb_on() {
8225 use cudarc::driver::{DevicePtr, DevicePtrMut};
8226 let s = &self.gpu.stream();
8227 let (px, _g0) = x.device_ptr(s);
8228 let (pw, _g1) = w.device_ptr(s);
8229 let (pd, _g2) = dst.device_ptr_mut(s);
8230 let mut ps = [
8231 &px as *const _ as *mut std::ffi::c_void,
8232 &pw as *const _ as *mut _,
8233 &pd as *const _ as *mut _,
8234 &nc as *const _ as *mut _,
8235 &e as *const _ as *mut _,
8236 ];
8237 unsafe {
8238 self.launch_pdl(
8239 "rms_norm_f32",
8240 (nrows as u32, 1, 1),
8241 (rms_block(), 1, 1),
8242 &mut ps,
8243 )?;
8244 }
8245 return Ok(());
8246 }
8247 let f = self.func("rms_norm_f32");
8248 let cfg = LaunchConfig {
8249 grid_dim: (nrows as u32, 1, 1),
8250 block_dim: (rms_block(), 1, 1),
8251 shared_mem_bytes: 0,
8252 };
8253 let __s_b = self.gpu.stream();
8254 let mut b = __s_b.launch_builder(&f);
8255 b.arg(x).arg(w).arg(dst).arg(&nc).arg(&e);
8256 unsafe {
8257 b.launch(cfg)?;
8258 }
8259 Ok(())
8260 }
8261
8262 pub fn rms_norm_decode(
8270 &self,
8271 x: &CudaSlice<f32>,
8272 w: &CudaSlice<f32>,
8273 dst: &mut CudaSlice<f32>,
8274 ncols: usize,
8275 nrows: usize,
8276 eps: f32,
8277 ) -> Result<(), Box<dyn std::error::Error>> {
8278 let f = self.func("rms_norm_f32");
8279 let cfg = LaunchConfig {
8280 grid_dim: (nrows as u32, 1, 1),
8281 block_dim: (1024, 1, 1),
8282 shared_mem_bytes: 0,
8283 };
8284 let (nc, e) = (ncols as i32, eps);
8285 let __s_b = self.gpu.stream();
8286 let mut b = __s_b.launch_builder(&f);
8287 b.arg(x).arg(w).arg(dst).arg(&nc).arg(&e);
8288 unsafe {
8289 b.launch(cfg)?;
8290 }
8291 Ok(())
8292 }
8293
8294 pub fn rms_norm_q8_1(
8298 &self,
8299 x: &CudaSlice<f32>,
8300 w: &CudaSlice<f32>,
8301 ncols: usize,
8302 nrows: usize,
8303 eps: f32,
8304 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8305 let nblk = ncols / 32;
8306 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
8307 let mut d = self.alloc_uninit::<f32>(nrows * nblk)?;
8308 let (nc, e) = (ncols as i32, eps);
8309 if Self::pdl_on() {
8310 {
8311 use cudarc::driver::{DevicePtr, DevicePtrMut};
8312 let s = &self.gpu.stream();
8313 let (px, _g0) = x.device_ptr(s);
8314 let (pw, _g1) = w.device_ptr(s);
8315 let (pq, _g2) = q.device_ptr_mut(s);
8316 let (pd, _g3) = d.device_ptr_mut(s);
8317 let mut ps = [
8318 &px as *const _ as *mut std::ffi::c_void,
8319 &pw as *const _ as *mut _,
8320 &pq as *const _ as *mut _,
8321 &pd as *const _ as *mut _,
8322 &nc as *const _ as *mut _,
8323 &e as *const _ as *mut _,
8324 ];
8325 unsafe {
8326 self.launch_pdl("rms_norm_q8_1", (nrows as u32, 1, 1), (1024, 1, 1), &mut ps)?;
8327 }
8328 }
8329 return Ok((q, d));
8330 }
8331 let f = self.func("rms_norm_q8_1");
8332 let cfg = LaunchConfig {
8335 grid_dim: (nrows as u32, 1, 1),
8336 block_dim: (1024, 1, 1),
8337 shared_mem_bytes: 0,
8338 };
8339 let __s_b = self.gpu.stream();
8340 let mut b = __s_b.launch_builder(&f);
8341 b.arg(x).arg(w).arg(&mut q).arg(&mut d).arg(&nc).arg(&e);
8342 unsafe {
8343 b.launch(cfg)?;
8344 }
8345 Ok((q, d))
8346 }
8347
8348 pub fn rms_norm_q8_1_into(
8351 &self,
8352 x: &CudaSlice<f32>,
8353 w: &CudaSlice<f32>,
8354 ncols: usize,
8355 nrows: usize,
8356 eps: f32,
8357 q: &mut CudaSlice<i8>,
8358 d: &mut CudaSlice<f32>,
8359 ) -> Result<(), Box<dyn std::error::Error>> {
8360 let nblk = ncols / 32;
8361 debug_assert!(q.len() >= nrows * ncols && d.len() >= nrows * nblk);
8362 let (nc, e) = (ncols as i32, eps);
8363 if Self::pdl_on() {
8364 use cudarc::driver::{DevicePtr, DevicePtrMut};
8365 let s = &self.gpu.stream();
8366 let (px, _g0) = x.device_ptr(s);
8367 let (pw, _g1) = w.device_ptr(s);
8368 let (pq, _g2) = q.device_ptr_mut(s);
8369 let (pd, _g3) = d.device_ptr_mut(s);
8370 let mut ps = [
8371 &px as *const _ as *mut std::ffi::c_void,
8372 &pw as *const _ as *mut _,
8373 &pq as *const _ as *mut _,
8374 &pd as *const _ as *mut _,
8375 &nc as *const _ as *mut _,
8376 &e as *const _ as *mut _,
8377 ];
8378 unsafe {
8379 self.launch_pdl("rms_norm_q8_1", (nrows as u32, 1, 1), (1024, 1, 1), &mut ps)?;
8380 }
8381 return Ok(());
8382 }
8383 let f = self.func("rms_norm_q8_1");
8384 let cfg = LaunchConfig {
8385 grid_dim: (nrows as u32, 1, 1),
8386 block_dim: (1024, 1, 1),
8387 shared_mem_bytes: 0,
8388 };
8389 let __s_b = self.gpu.stream();
8390 let mut b = __s_b.launch_builder(&f);
8391 b.arg(x).arg(w).arg(&mut *q).arg(&mut *d).arg(&nc).arg(&e);
8392 unsafe {
8393 b.launch(cfg)?;
8394 }
8395 Ok(())
8396 }
8397
8398 pub fn quantize_q8_1_into(
8400 &self,
8401 x: &CudaSlice<f32>,
8402 m: usize,
8403 in_f: usize,
8404 q: &mut CudaSlice<i8>,
8405 d: &mut CudaSlice<f32>,
8406 ) -> Result<(), Box<dyn std::error::Error>> {
8407 let nblk = in_f / 32;
8408 debug_assert!(q.len() >= m * in_f && d.len() >= m * nblk);
8409 let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
8410 let (inf, mi) = (in_f as i32, m as i32);
8411 if Self::pdl_on() && Self::pdl_wb_on() {
8412 use cudarc::driver::{DevicePtr, DevicePtrMut};
8413 let s = &self.gpu.stream();
8414 let (px, _g0) = x.device_ptr(s);
8415 let (pq, _g1) = q.device_ptr_mut(s);
8416 let (pd, _g2) = d.device_ptr_mut(s);
8417 let mut ps = [
8418 &px as *const _ as *mut std::ffi::c_void,
8419 &pq as *const _ as *mut _,
8420 &pd as *const _ as *mut _,
8421 &inf as *const _ as *mut _,
8422 &mi as *const _ as *mut _,
8423 ];
8424 unsafe {
8425 self.launch_pdl("quantize_q8_1", cfg.grid_dim, cfg.block_dim, &mut ps)?;
8426 }
8427 return Ok(());
8428 }
8429 let f = self.func("quantize_q8_1");
8430 let __s_b = self.gpu.stream();
8431 let mut b = __s_b.launch_builder(&f);
8432 b.arg(x).arg(&mut *q).arg(&mut *d).arg(&inf).arg(&mi);
8433 unsafe {
8434 b.launch(cfg)?;
8435 }
8436 Ok(())
8437 }
8438
8439 pub fn add_rms_norm_q8_1(
8443 &self,
8444 a: &CudaSlice<f32>,
8445 b_in: &CudaSlice<f32>,
8446 w: &CudaSlice<f32>,
8447 res: &mut CudaSlice<f32>,
8448 ncols: usize,
8449 nrows: usize,
8450 eps: f32,
8451 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8452 let nblk = ncols / 32;
8453 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
8454 let mut d = self.alloc_uninit::<f32>(nrows * nblk)?;
8455 let f = self.func("add_rms_norm_q8_1");
8456 let cfg = LaunchConfig {
8458 grid_dim: (nrows as u32, 1, 1),
8459 block_dim: (1024, 1, 1),
8460 shared_mem_bytes: 0,
8461 };
8462 let (nc, e) = (ncols as i32, eps);
8463 let __s_bld = self.gpu.stream();
8464 let mut bld = __s_bld.launch_builder(&f);
8465 bld.arg(a)
8466 .arg(b_in)
8467 .arg(w)
8468 .arg(res)
8469 .arg(&mut q)
8470 .arg(&mut d)
8471 .arg(&nc)
8472 .arg(&e);
8473 unsafe {
8474 bld.launch(cfg)?;
8475 }
8476 Ok((q, d))
8477 }
8478
8479 pub fn add_rms_norm(
8483 &self,
8484 a: &CudaSlice<f32>,
8485 b: &CudaSlice<f32>,
8486 w: &CudaSlice<f32>,
8487 res: &mut CudaSlice<f32>,
8488 dst: &mut CudaSlice<f32>,
8489 ncols: usize,
8490 nrows: usize,
8491 eps: f32,
8492 ) -> Result<(), Box<dyn std::error::Error>> {
8493 let (nc, e) = (ncols as i32, eps);
8494 if Self::pdl_on() && Self::pdl_wb_on() {
8495 use cudarc::driver::{DevicePtr, DevicePtrMut};
8496 let s = &self.gpu.stream();
8497 let (pa, _g0) = a.device_ptr(s);
8498 let (pb, _g1) = b.device_ptr(s);
8499 let (pw, _g2) = w.device_ptr(s);
8500 let (pr, _g3) = res.device_ptr_mut(s);
8501 let (pd, _g4) = dst.device_ptr_mut(s);
8502 let mut ps = [
8503 &pa as *const _ as *mut std::ffi::c_void,
8504 &pb as *const _ as *mut _,
8505 &pw as *const _ as *mut _,
8506 &pr as *const _ as *mut _,
8507 &pd as *const _ as *mut _,
8508 &nc as *const _ as *mut _,
8509 &e as *const _ as *mut _,
8510 ];
8511 unsafe {
8512 self.launch_pdl(
8513 "add_rms_norm_f32",
8514 (nrows as u32, 1, 1),
8515 (rms_block(), 1, 1),
8516 &mut ps,
8517 )?;
8518 }
8519 return Ok(());
8520 }
8521 let f = self.func("add_rms_norm_f32");
8522 let cfg = LaunchConfig {
8523 grid_dim: (nrows as u32, 1, 1),
8524 block_dim: (rms_block(), 1, 1),
8525 shared_mem_bytes: 0,
8526 };
8527 let __s_b2 = self.gpu.stream();
8528 let mut b2 = __s_b2.launch_builder(&f);
8529 b2.arg(a)
8530 .arg(b)
8531 .arg(w)
8532 .arg(&mut *res)
8533 .arg(&mut *dst)
8534 .arg(&nc)
8535 .arg(&e);
8536 unsafe {
8537 b2.launch(cfg)?;
8538 }
8539 Ok(())
8540 }
8541
8542 #[allow(clippy::too_many_arguments)]
8545 pub fn rms_pre_add_rms_norm(
8546 &self,
8547 a: &CudaSlice<f32>,
8548 wa: &CudaSlice<f32>,
8549 b: &CudaSlice<f32>,
8550 w: &CudaSlice<f32>,
8551 res: &mut CudaSlice<f32>,
8552 dst: &mut CudaSlice<f32>,
8553 ncols: usize,
8554 nrows: usize,
8555 eps: f32,
8556 ) -> Result<(), Box<dyn std::error::Error>> {
8557 let f = self.func("rms_pre_add_rms_norm_f32");
8558 let cfg = LaunchConfig {
8559 grid_dim: (nrows as u32, 1, 1),
8560 block_dim: (rms_block(), 1, 1),
8561 shared_mem_bytes: 0,
8562 };
8563 let (nc, e) = (ncols as i32, eps);
8564 let __s_b2 = self.gpu.stream();
8565 let mut b2 = __s_b2.launch_builder(&f);
8566 b2.arg(a)
8567 .arg(wa)
8568 .arg(b)
8569 .arg(w)
8570 .arg(&mut *res)
8571 .arg(&mut *dst)
8572 .arg(&nc)
8573 .arg(&e);
8574 unsafe {
8575 b2.launch(cfg)?;
8576 }
8577 Ok(())
8578 }
8579
8580 #[allow(clippy::too_many_arguments)]
8582 pub fn rms_pre_add_rms_norm_q8z(
8583 &self,
8584 a: &CudaSlice<f32>,
8585 wa: &CudaSlice<f32>,
8586 b: &CudaSlice<f32>,
8587 w: &CudaSlice<f32>,
8588 res: &mut CudaSlice<f32>,
8589 dst: &mut CudaSlice<f32>,
8590 ncols: usize,
8591 nrows: usize,
8592 eps: f32,
8593 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8594 debug_assert!(ncols % 128 == 0);
8595 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
8596 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
8597 let (nc, e) = (ncols as i32, eps);
8598 if Self::pdl_on() {
8599 {
8600 use cudarc::driver::{DevicePtr, DevicePtrMut};
8601 let s = &self.gpu.stream();
8602 let (pa, _g0) = a.device_ptr(s);
8603 let (pwa, _g1) = wa.device_ptr(s);
8604 let (pb, _g2) = b.device_ptr(s);
8605 let (pw, _g3) = w.device_ptr(s);
8606 let (pr, _g4) = res.device_ptr_mut(s);
8607 let (pdst, _g5) = dst.device_ptr_mut(s);
8608 let (pq, _g6) = out_q.device_ptr_mut(s);
8609 let (pd, _g7) = out_d.device_ptr_mut(s);
8610 let mut ps = [
8611 &pa as *const _ as *mut std::ffi::c_void,
8612 &pwa as *const _ as *mut _,
8613 &pb as *const _ as *mut _,
8614 &pw as *const _ as *mut _,
8615 &pr as *const _ as *mut _,
8616 &pdst as *const _ as *mut _,
8617 &pq as *const _ as *mut _,
8618 &pd as *const _ as *mut _,
8619 &nc as *const _ as *mut _,
8620 &e as *const _ as *mut _,
8621 ];
8622 unsafe {
8623 self.launch_pdl(
8624 "rms_pre_add_rms_norm_q8z_f32",
8625 (nrows as u32, 1, 1),
8626 (rms_block(), 1, 1),
8627 &mut ps,
8628 )?;
8629 }
8630 }
8631 return Ok((out_q, out_d));
8632 }
8633 let f = self.func("rms_pre_add_rms_norm_q8z_f32");
8634 let cfg = LaunchConfig {
8635 grid_dim: (nrows as u32, 1, 1),
8636 block_dim: (rms_block(), 1, 1),
8637 shared_mem_bytes: 0,
8638 };
8639 let __s_b2 = self.gpu.stream();
8640 let mut b2 = __s_b2.launch_builder(&f);
8641 b2.arg(a)
8642 .arg(wa)
8643 .arg(b)
8644 .arg(w)
8645 .arg(&mut *res)
8646 .arg(&mut *dst)
8647 .arg(&mut out_q)
8648 .arg(&mut out_d)
8649 .arg(&nc)
8650 .arg(&e);
8651 unsafe {
8652 b2.launch(cfg)?;
8653 }
8654 Ok((out_q, out_d))
8655 }
8656
8657 #[allow(clippy::too_many_arguments)]
8661 pub fn rms_pre_add_rms_norm_q8z_into(
8662 &self,
8663 a: &CudaSlice<f32>,
8664 wa: &CudaSlice<f32>,
8665 b: &CudaSlice<f32>,
8666 w: &CudaSlice<f32>,
8667 res: &mut CudaSlice<f32>,
8668 dst: &mut CudaSlice<f32>,
8669 ncols: usize,
8670 nrows: usize,
8671 eps: f32,
8672 out_q: &mut CudaSlice<i8>,
8673 out_d: &mut CudaSlice<f32>,
8674 ) -> Result<(), Box<dyn std::error::Error>> {
8675 debug_assert!(ncols % 128 == 0);
8676 let (nc, e) = (ncols as i32, eps);
8677 let f = self.func("rms_pre_add_rms_norm_q8z_f32");
8678 let cfg = LaunchConfig {
8679 grid_dim: (nrows as u32, 1, 1),
8680 block_dim: (rms_block(), 1, 1),
8681 shared_mem_bytes: 0,
8682 };
8683 let __s_b = self.gpu.stream();
8684 let mut b2 = __s_b.launch_builder(&f);
8685 b2.arg(a)
8686 .arg(wa)
8687 .arg(b)
8688 .arg(w)
8689 .arg(&mut *res)
8690 .arg(&mut *dst)
8691 .arg(&mut *out_q)
8692 .arg(&mut *out_d)
8693 .arg(&nc)
8694 .arg(&e);
8695 unsafe {
8696 b2.launch(cfg)?;
8697 }
8698 Ok(())
8699 }
8700
8701 #[allow(clippy::too_many_arguments)]
8704 pub fn rms_pre_add_scale_rms_norm_q8_1_into(
8705 &self,
8706 a: &CudaSlice<f32>,
8707 wa: &CudaSlice<f32>,
8708 b_in: &CudaSlice<f32>,
8709 c: f32,
8710 w: &CudaSlice<f32>,
8711 res: &mut CudaSlice<f32>,
8712 ncols: usize,
8713 nrows: usize,
8714 eps: f32,
8715 out_q: &mut CudaSlice<i8>,
8716 out_d: &mut CudaSlice<f32>,
8717 ) -> Result<(), Box<dyn std::error::Error>> {
8718 debug_assert!(ncols % 128 == 0);
8719 let (nc, e2) = (ncols as i32, eps);
8720 let f = self.func("rms_pre_add_scale_rms_norm_q8_1");
8721 let cfg = LaunchConfig {
8722 grid_dim: (nrows as u32, 1, 1),
8723 block_dim: (rms_block(), 1, 1),
8724 shared_mem_bytes: 0,
8725 };
8726 let __s_b = self.gpu.stream();
8727 let mut b2 = __s_b.launch_builder(&f);
8728 b2.arg(a)
8729 .arg(wa)
8730 .arg(b_in)
8731 .arg(&c)
8732 .arg(w)
8733 .arg(&mut *res)
8734 .arg(&mut *out_q)
8735 .arg(&mut *out_d)
8736 .arg(&nc)
8737 .arg(&e2);
8738 unsafe {
8739 b2.launch(cfg)?;
8740 }
8741 Ok(())
8742 }
8743
8744 pub fn g4_pnfold_on() -> bool {
8752 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8753 *ON.get_or_init(|| {
8754 std::env::var("MEMRA_G4_PNFOLD")
8755 .map(|v| v != "0")
8756 .unwrap_or(true)
8757 })
8758 }
8759
8760 pub fn build_q4_out_concat3(
8764 &self,
8765 w0: &crate::model::GpuTensor,
8766 w1: &crate::model::GpuTensor,
8767 w2: &crate::model::GpuTensor,
8768 ) -> Result<Option<crate::model::GpuTensor>, Box<dyn std::error::Error>> {
8769 use crate::model::GpuTensor;
8770 let part = |w: &GpuTensor| -> Option<(usize, usize)> {
8771 match w {
8772 GpuTensor::Quant {
8773 qtype,
8774 row_bytes,
8775 rp,
8776 ..
8777 } if *qtype == QT_Q4_0 && !*rp => Some((*row_bytes, w.out_features())),
8778 _ => None,
8779 }
8780 };
8781 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (part(w0), part(w1), part(w2))
8782 else {
8783 return Ok(None);
8784 };
8785 if rb0 != rb1
8786 || rb0 != rb2
8787 || w0.in_features() != w1.in_features()
8788 || w0.in_features() != w2.in_features()
8789 {
8790 return Ok(None);
8791 }
8792 fn bytes_of(w: &crate::model::GpuTensor) -> &CudaSlice<u8> {
8793 match w {
8794 crate::model::GpuTensor::Quant { bytes, .. } => bytes,
8795 _ => unreachable!(),
8796 }
8797 }
8798 let (b0, b1, b2) = (bytes_of(w0), bytes_of(w1), bytes_of(w2));
8799 let total = rb0 * (o0 + o1 + o2);
8800 let mut cat = self.alloc_u8(total)?;
8801 self.copy_u8_into(&mut cat, 0, b0, rb0 * o0)?;
8802 self.copy_u8_into(&mut cat, rb0 * o0, b1, rb1 * o1)?;
8803 self.copy_u8_into(&mut cat, rb0 * (o0 + o1), b2, rb2 * o2)?;
8804 Ok(Some(GpuTensor::Quant {
8805 bytes: cat,
8806 qtype: QT_Q4_0,
8807 row_bytes: rb0,
8808 ne: vec![w0.in_features() as u64, (o0 + o1 + o2) as u64],
8809 scale: 1.0,
8810 rp: false,
8811 #[cfg(memra_cutlass)]
8812 cutlass: None,
8813 fp8: None,
8814 blk: None,
8815 rp4: None,
8816 f16: None,
8817 }))
8818 }
8819
8820 fn full_width_rope_only(
8838 kernel: &str,
8839 n_rot: usize,
8840 head_dim: usize,
8841 ) -> Result<(), Box<dyn std::error::Error>> {
8842 if n_rot == head_dim {
8843 return Ok(());
8844 }
8845 Err(format!(
8846 "{kernel}: PARTIAL ROTARY REFUSED — n_rot {n_rot} != head_dim {head_dim}. This fused \
8847 rms_norm+qkv+rope kernel carries no n_dims parameter and rotates the full head \
8848 width (half = ncols/2), so it would rotate dims {n_rot}..{head_dim} that must pass \
8849 through unrotated. Use the split path (rms_norm_qkv + rope_neox/rope_neox2 with \
8850 n_dims={n_rot}), or add an n_dims early-return to the kernel and widen this guard."
8851 )
8852 .into())
8853 }
8854
8855 #[allow(clippy::too_many_arguments)]
8859 pub fn rms_norm_qkv_rope_cat(
8860 &self,
8861 qkv: &CudaSlice<f32>,
8862 wq: &CudaSlice<f32>,
8863 wk: &CudaSlice<f32>,
8864 wv: &CudaSlice<f32>,
8865 q: &mut CudaSlice<f32>,
8866 k: &mut CudaSlice<f32>,
8867 v: &mut CudaSlice<f32>,
8868 head_dim: usize,
8869 n_rot: usize,
8870 rq: usize,
8871 rk: usize,
8872 pos: &CudaSlice<i32>,
8873 nh_q: usize,
8874 nh_k: usize,
8875 base: f32,
8876 freq_scale: f32,
8877 ff: Option<&CudaSlice<f32>>,
8878 eps: f32,
8879 ) -> Result<(), Box<dyn std::error::Error>> {
8880 Self::full_width_rope_only("rms_norm_qkv_rope_cat", n_rot, head_dim)?;
8881 let rows = rq + rk + rk;
8882 let theta_scale = base.powf(-2.0 / head_dim as f32);
8883 let (nc, rqi, rki, nhq, nhk) = (
8884 head_dim as i32,
8885 rq as i32,
8886 rk as i32,
8887 nh_q as i32,
8888 nh_k as i32,
8889 );
8890 if Self::pdl_on() {
8891 use cudarc::driver::{DevicePtr, DevicePtrMut};
8892 let s = &self.gpu.stream();
8893 let (pqkv, _g0) = qkv.device_ptr(s);
8894 let (pwq, _g1) = wq.device_ptr(s);
8895 let (pwk, _g2) = wk.device_ptr(s);
8896 let (pwv, _g3) = wv.device_ptr(s);
8897 let (pq, _g4) = q.device_ptr_mut(s);
8898 let (pk, _g5) = k.device_ptr_mut(s);
8899 let (pv, _g6) = v.device_ptr_mut(s);
8900 let (ppos, _g7) = pos.device_ptr(s);
8901 let (pff, _g8) = match ff {
8902 Some(t) => {
8903 let (p, g) = t.device_ptr(s);
8904 (p, Some(g))
8905 }
8906 None => (0, None),
8907 };
8908 let mut ps = [
8909 &pqkv as *const _ as *mut std::ffi::c_void,
8910 &pwq as *const _ as *mut _,
8911 &pwk as *const _ as *mut _,
8912 &pwv as *const _ as *mut _,
8913 &pq as *const _ as *mut _,
8914 &pk as *const _ as *mut _,
8915 &pv as *const _ as *mut _,
8916 &nc as *const _ as *mut _,
8917 &rqi as *const _ as *mut _,
8918 &rki as *const _ as *mut _,
8919 &ppos as *const _ as *mut _,
8920 &nhq as *const _ as *mut _,
8921 &nhk as *const _ as *mut _,
8922 &theta_scale as *const _ as *mut _,
8923 &freq_scale as *const _ as *mut _,
8924 &pff as *const _ as *mut _,
8925 &eps as *const _ as *mut _,
8926 ];
8927 unsafe {
8928 self.launch_pdl(
8929 "rms_norm_qkv_rope_cat_f32",
8930 (rows as u32, 1, 1),
8931 (rms_block(), 1, 1),
8932 &mut ps,
8933 )?;
8934 }
8935 return Ok(());
8936 }
8937 let f = self.func("rms_norm_qkv_rope_cat_f32");
8938 let cfg = LaunchConfig {
8939 grid_dim: (rows as u32, 1, 1),
8940 block_dim: (rms_block(), 1, 1),
8941 shared_mem_bytes: 0,
8942 };
8943 let __s_b = self.gpu.stream();
8944 let mut b = __s_b.launch_builder(&f);
8945 match ff {
8946 Some(t) => {
8947 b.arg(qkv)
8948 .arg(wq)
8949 .arg(wk)
8950 .arg(wv)
8951 .arg(&mut *q)
8952 .arg(&mut *k)
8953 .arg(&mut *v)
8954 .arg(&nc)
8955 .arg(&rqi)
8956 .arg(&rki)
8957 .arg(pos)
8958 .arg(&nhq)
8959 .arg(&nhk)
8960 .arg(&theta_scale)
8961 .arg(&freq_scale)
8962 .arg(t)
8963 .arg(&eps);
8964 unsafe {
8965 b.launch(cfg)?;
8966 }
8967 }
8968 None => {
8969 let null: u64 = 0;
8970 b.arg(qkv)
8971 .arg(wq)
8972 .arg(wk)
8973 .arg(wv)
8974 .arg(&mut *q)
8975 .arg(&mut *k)
8976 .arg(&mut *v)
8977 .arg(&nc)
8978 .arg(&rqi)
8979 .arg(&rki)
8980 .arg(pos)
8981 .arg(&nhq)
8982 .arg(&nhk)
8983 .arg(&theta_scale)
8984 .arg(&freq_scale)
8985 .arg(&null)
8986 .arg(&eps);
8987 unsafe {
8988 b.launch(cfg)?;
8989 }
8990 }
8991 }
8992 Ok(())
8993 }
8994
8995 #[allow(clippy::too_many_arguments)]
8999 pub fn rms_norm_qkv_rope(
9000 &self,
9001 q0: &CudaSlice<f32>,
9002 k0: &CudaSlice<f32>,
9003 v0: &CudaSlice<f32>,
9004 wq: &CudaSlice<f32>,
9005 wk: &CudaSlice<f32>,
9006 wv: &CudaSlice<f32>,
9007 q: &mut CudaSlice<f32>,
9008 k: &mut CudaSlice<f32>,
9009 v: &mut CudaSlice<f32>,
9010 head_dim: usize,
9011 n_rot: usize,
9012 rq: usize,
9013 rk: usize,
9014 pos: &CudaSlice<i32>,
9015 nh_q: usize,
9016 nh_k: usize,
9017 base: f32,
9018 freq_scale: f32,
9019 ff: Option<&CudaSlice<f32>>,
9020 eps: f32,
9021 ) -> Result<(), Box<dyn std::error::Error>> {
9022 Self::full_width_rope_only("rms_norm_qkv_rope", n_rot, head_dim)?;
9023 let f = self.func("rms_norm_qkv_rope_f32");
9024 let rows = rq + rk + rk; let cfg = LaunchConfig {
9026 grid_dim: (rows as u32, 1, 1),
9027 block_dim: (rms_block(), 1, 1),
9028 shared_mem_bytes: 0,
9029 };
9030 let theta_scale = base.powf(-2.0 / head_dim as f32);
9031 let (nc, rqi, rki, nhq, nhk) = (
9032 head_dim as i32,
9033 rq as i32,
9034 rk as i32,
9035 nh_q as i32,
9036 nh_k as i32,
9037 );
9038 let __s_b = self.gpu.stream();
9039 let mut b = __s_b.launch_builder(&f);
9040 match ff {
9041 Some(t) => {
9042 b.arg(q0)
9043 .arg(k0)
9044 .arg(v0)
9045 .arg(wq)
9046 .arg(wk)
9047 .arg(wv)
9048 .arg(&mut *q)
9049 .arg(&mut *k)
9050 .arg(&mut *v)
9051 .arg(&nc)
9052 .arg(&rqi)
9053 .arg(&rki)
9054 .arg(pos)
9055 .arg(&nhq)
9056 .arg(&nhk)
9057 .arg(&theta_scale)
9058 .arg(&freq_scale)
9059 .arg(t)
9060 .arg(&eps);
9061 unsafe {
9062 b.launch(cfg)?;
9063 }
9064 }
9065 None => {
9066 let null: u64 = 0;
9067 b.arg(q0)
9068 .arg(k0)
9069 .arg(v0)
9070 .arg(wq)
9071 .arg(wk)
9072 .arg(wv)
9073 .arg(&mut *q)
9074 .arg(&mut *k)
9075 .arg(&mut *v)
9076 .arg(&nc)
9077 .arg(&rqi)
9078 .arg(&rki)
9079 .arg(pos)
9080 .arg(&nhq)
9081 .arg(&nhk)
9082 .arg(&theta_scale)
9083 .arg(&freq_scale)
9084 .arg(&null)
9085 .arg(&eps);
9086 unsafe {
9087 b.launch(cfg)?;
9088 }
9089 }
9090 }
9091 Ok(())
9092 }
9093
9094 #[allow(clippy::too_many_arguments)]
9100 pub fn rms_norm_qkv_rope_append_dc(
9101 &self,
9102 q0: &CudaSlice<f32>,
9103 k0: &CudaSlice<f32>,
9104 v0: &CudaSlice<f32>,
9105 wq: &CudaSlice<f32>,
9106 wk: &CudaSlice<f32>,
9107 wv: &CudaSlice<f32>,
9108 q: &mut CudaSlice<f32>,
9109 k: &mut CudaSlice<f32>,
9110 v: &mut CudaSlice<f32>,
9111 head_dim: usize,
9112 n_rot: usize,
9113 rq: usize,
9114 rk: usize,
9115 pos: &CudaSlice<i32>,
9116 nh_q: usize,
9117 nh_k: usize,
9118 base: f32,
9119 freq_scale: f32,
9120 ff: Option<&CudaSlice<f32>>,
9121 eps: f32,
9122 kc: &mut CudaSlice<u8>,
9123 vc: &mut CudaSlice<u8>,
9124 t_dev: &CudaSlice<i32>,
9125 k_tok_bytes: usize,
9126 v_tok_bytes: usize,
9127 g: bool,
9128 ) -> Result<(), Box<dyn std::error::Error>> {
9129 Self::full_width_rope_only("rms_norm_qkv_rope_append_dc", n_rot, head_dim)?;
9130 let rows = rq + rk + rk;
9131 let theta_scale = base.powf(-2.0 / head_dim as f32);
9132 let (nc, rqi, rki, nhq, nhk) = (
9133 head_dim as i32,
9134 rq as i32,
9135 rk as i32,
9136 nh_q as i32,
9137 nh_k as i32,
9138 );
9139 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
9140 if Self::pdl_on() && Self::pdl_wb_on() {
9141 use cudarc::driver::{DevicePtr, DevicePtrMut};
9142 let s = &self.gpu.stream();
9143 let (p0, _a0) = q0.device_ptr(s);
9144 let (p1, _a1) = k0.device_ptr(s);
9145 let (p2, _a2) = v0.device_ptr(s);
9146 let (pwq, _a3) = wq.device_ptr(s);
9147 let (pwk, _a4) = wk.device_ptr(s);
9148 let (pwv, _a5) = wv.device_ptr(s);
9149 let (pq, _a6) = q.device_ptr_mut(s);
9150 let (pk, _a7) = k.device_ptr_mut(s);
9151 let (pv, _a8) = v.device_ptr_mut(s);
9152 let (pp, _a9) = pos.device_ptr(s);
9153 let pff: u64 = match ff {
9154 Some(t) => {
9155 let (p, _gg) = t.device_ptr(s);
9156 p as u64
9157 }
9158 None => 0,
9159 };
9160 let (pkc, _a10) = kc.device_ptr_mut(s);
9161 let (pvc, _a11) = vc.device_ptr_mut(s);
9162 let (pt, _a12) = t_dev.device_ptr(s);
9163 let mut ps = [
9164 &p0 as *const _ as *mut std::ffi::c_void,
9165 &p1 as *const _ as *mut _,
9166 &p2 as *const _ as *mut _,
9167 &pwq as *const _ as *mut _,
9168 &pwk as *const _ as *mut _,
9169 &pwv as *const _ as *mut _,
9170 &pq as *const _ as *mut _,
9171 &pk as *const _ as *mut _,
9172 &pv as *const _ as *mut _,
9173 &nc as *const _ as *mut _,
9174 &rqi as *const _ as *mut _,
9175 &rki as *const _ as *mut _,
9176 &pp as *const _ as *mut _,
9177 &nhq as *const _ as *mut _,
9178 &nhk as *const _ as *mut _,
9179 &theta_scale as *const _ as *mut _,
9180 &freq_scale as *const _ as *mut _,
9181 &pff as *const _ as *mut _,
9182 &eps as *const _ as *mut _,
9183 &pkc as *const _ as *mut _,
9184 &pvc as *const _ as *mut _,
9185 &pt as *const _ as *mut _,
9186 &ktb as *const _ as *mut _,
9187 &vtb as *const _ as *mut _,
9188 ];
9189 unsafe {
9190 self.launch_pdl_flash(
9191 g,
9192 "rms_norm_qkv_rope_append_dc_f32",
9193 (rows as u32, 1, 1),
9194 (rms_block(), 1, 1),
9195 0,
9196 &mut ps,
9197 )?;
9198 }
9199 return Ok(());
9200 }
9201 let f = if g {
9202 self.func_g("rms_norm_qkv_rope_append_dc_f32")
9203 } else {
9204 self.func("rms_norm_qkv_rope_append_dc_f32")
9205 };
9206 let cfg = LaunchConfig {
9207 grid_dim: (rows as u32, 1, 1),
9208 block_dim: (rms_block(), 1, 1),
9209 shared_mem_bytes: 0,
9210 };
9211 let __s_b = self.gpu.stream();
9212 let mut b = __s_b.launch_builder(&f);
9213 match ff {
9214 Some(t) => {
9215 b.arg(q0)
9216 .arg(k0)
9217 .arg(v0)
9218 .arg(wq)
9219 .arg(wk)
9220 .arg(wv)
9221 .arg(&mut *q)
9222 .arg(&mut *k)
9223 .arg(&mut *v)
9224 .arg(&nc)
9225 .arg(&rqi)
9226 .arg(&rki)
9227 .arg(pos)
9228 .arg(&nhq)
9229 .arg(&nhk)
9230 .arg(&theta_scale)
9231 .arg(&freq_scale)
9232 .arg(t)
9233 .arg(&eps)
9234 .arg(&mut *kc)
9235 .arg(&mut *vc)
9236 .arg(t_dev)
9237 .arg(&ktb)
9238 .arg(&vtb);
9239 unsafe {
9240 b.launch(cfg)?;
9241 }
9242 }
9243 None => {
9244 let null: u64 = 0;
9245 b.arg(q0)
9246 .arg(k0)
9247 .arg(v0)
9248 .arg(wq)
9249 .arg(wk)
9250 .arg(wv)
9251 .arg(&mut *q)
9252 .arg(&mut *k)
9253 .arg(&mut *v)
9254 .arg(&nc)
9255 .arg(&rqi)
9256 .arg(&rki)
9257 .arg(pos)
9258 .arg(&nhq)
9259 .arg(&nhk)
9260 .arg(&theta_scale)
9261 .arg(&freq_scale)
9262 .arg(&null)
9263 .arg(&eps)
9264 .arg(&mut *kc)
9265 .arg(&mut *vc)
9266 .arg(t_dev)
9267 .arg(&ktb)
9268 .arg(&vtb);
9269 unsafe {
9270 b.launch(cfg)?;
9271 }
9272 }
9273 }
9274 Ok(())
9275 }
9276
9277 #[allow(clippy::too_many_arguments)]
9285 pub fn rms_norm_qkv_rope_append(
9286 &self,
9287 q0: &CudaSlice<f32>,
9288 k0: &CudaSlice<f32>,
9289 v0: &CudaSlice<f32>,
9290 wq: &CudaSlice<f32>,
9291 wk: &CudaSlice<f32>,
9292 wv: &CudaSlice<f32>,
9293 q: &mut CudaSlice<f32>,
9294 k: &mut CudaSlice<f32>,
9295 v: &mut CudaSlice<f32>,
9296 head_dim: usize,
9297 n_rot: usize,
9298 rq: usize,
9299 rk: usize,
9300 pos: &CudaSlice<i32>,
9301 nh_q: usize,
9302 nh_k: usize,
9303 base: f32,
9304 freq_scale: f32,
9305 ff: Option<&CudaSlice<f32>>,
9306 eps: f32,
9307 kc: &mut CudaSlice<u8>,
9308 vc: &mut CudaSlice<u8>,
9309 t: usize,
9310 k_tok_bytes: usize,
9311 v_tok_bytes: usize,
9312 g: bool,
9313 ) -> Result<(), Box<dyn std::error::Error>> {
9314 Self::full_width_rope_only("rms_norm_qkv_rope_append", n_rot, head_dim)?;
9315 let rows = rq + rk + rk;
9316 let theta_scale = base.powf(-2.0 / head_dim as f32);
9317 let (nc, rqi, rki, nhq, nhk) = (
9318 head_dim as i32,
9319 rq as i32,
9320 rk as i32,
9321 nh_q as i32,
9322 nh_k as i32,
9323 );
9324 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
9325 let ti = t as i32;
9326 if Self::pdl_on() && Self::pdl_wb_on() {
9327 use cudarc::driver::{DevicePtr, DevicePtrMut};
9328 let s = &self.gpu.stream();
9329 let (p0, _a0) = q0.device_ptr(s);
9330 let (p1, _a1) = k0.device_ptr(s);
9331 let (p2, _a2) = v0.device_ptr(s);
9332 let (pwq, _a3) = wq.device_ptr(s);
9333 let (pwk, _a4) = wk.device_ptr(s);
9334 let (pwv, _a5) = wv.device_ptr(s);
9335 let (pq, _a6) = q.device_ptr_mut(s);
9336 let (pk, _a7) = k.device_ptr_mut(s);
9337 let (pv, _a8) = v.device_ptr_mut(s);
9338 let (pp, _a9) = pos.device_ptr(s);
9339 let pff: u64 = match ff {
9340 Some(t) => {
9341 let (p, _gg) = t.device_ptr(s);
9342 p as u64
9343 }
9344 None => 0,
9345 };
9346 let (pkc, _a10) = kc.device_ptr_mut(s);
9347 let (pvc, _a11) = vc.device_ptr_mut(s);
9348 let mut ps = [
9349 &p0 as *const _ as *mut std::ffi::c_void,
9350 &p1 as *const _ as *mut _,
9351 &p2 as *const _ as *mut _,
9352 &pwq as *const _ as *mut _,
9353 &pwk as *const _ as *mut _,
9354 &pwv as *const _ as *mut _,
9355 &pq as *const _ as *mut _,
9356 &pk as *const _ as *mut _,
9357 &pv as *const _ as *mut _,
9358 &nc as *const _ as *mut _,
9359 &rqi as *const _ as *mut _,
9360 &rki as *const _ as *mut _,
9361 &pp as *const _ as *mut _,
9362 &nhq as *const _ as *mut _,
9363 &nhk as *const _ as *mut _,
9364 &theta_scale as *const _ as *mut _,
9365 &freq_scale as *const _ as *mut _,
9366 &pff as *const _ as *mut _,
9367 &eps as *const _ as *mut _,
9368 &pkc as *const _ as *mut _,
9369 &pvc as *const _ as *mut _,
9370 &ti as *const _ as *mut _,
9371 &ktb as *const _ as *mut _,
9372 &vtb as *const _ as *mut _,
9373 ];
9374 unsafe {
9375 self.launch_pdl_flash(
9376 g,
9377 "rms_norm_qkv_rope_append_f32",
9378 (rows as u32, 1, 1),
9379 (rms_block(), 1, 1),
9380 0,
9381 &mut ps,
9382 )?;
9383 }
9384 return Ok(());
9385 }
9386 let f = if g {
9387 self.func_g("rms_norm_qkv_rope_append_f32")
9388 } else {
9389 self.func("rms_norm_qkv_rope_append_f32")
9390 };
9391 let cfg = LaunchConfig {
9392 grid_dim: (rows as u32, 1, 1),
9393 block_dim: (rms_block(), 1, 1),
9394 shared_mem_bytes: 0,
9395 };
9396 let __s_b = self.gpu.stream();
9397 let mut b = __s_b.launch_builder(&f);
9398 let null: u64 = 0;
9399 b.arg(q0)
9400 .arg(k0)
9401 .arg(v0)
9402 .arg(wq)
9403 .arg(wk)
9404 .arg(wv)
9405 .arg(&mut *q)
9406 .arg(&mut *k)
9407 .arg(&mut *v)
9408 .arg(&nc)
9409 .arg(&rqi)
9410 .arg(&rki)
9411 .arg(pos)
9412 .arg(&nhq)
9413 .arg(&nhk)
9414 .arg(&theta_scale)
9415 .arg(&freq_scale);
9416 match ff {
9417 Some(t) => {
9418 b.arg(t);
9419 }
9420 None => {
9421 b.arg(&null);
9422 }
9423 }
9424 b.arg(&eps)
9425 .arg(&mut *kc)
9426 .arg(&mut *vc)
9427 .arg(&ti)
9428 .arg(&ktb)
9429 .arg(&vtb);
9430 unsafe {
9431 b.launch(cfg)?;
9432 }
9433 Ok(())
9434 }
9435
9436 pub fn add_q8_1(
9437 &self,
9438 a: &CudaSlice<f32>,
9439 b: &CudaSlice<f32>,
9440 res: &mut CudaSlice<f32>,
9441 ncols: usize,
9442 nrows: usize,
9443 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9444 debug_assert!(ncols % 128 == 0);
9445 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
9446 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9447 let f = self.func("add_q8_1_f32");
9448 let cfg = LaunchConfig {
9449 grid_dim: (nrows as u32, 1, 1),
9450 block_dim: (rms_block(), 1, 1),
9451 shared_mem_bytes: 0,
9452 };
9453 let nc = ncols as i32;
9454 let __s_b2 = self.gpu.stream();
9455 let mut b2 = __s_b2.launch_builder(&f);
9456 b2.arg(a)
9457 .arg(b)
9458 .arg(&mut *res)
9459 .arg(&mut out_q)
9460 .arg(&mut out_d)
9461 .arg(&nc);
9462 unsafe {
9463 b2.launch(cfg)?;
9464 }
9465 Ok((out_q, out_d))
9466 }
9467
9468 pub fn rms_pre_add_q8_1(
9472 &self,
9473 a: &CudaSlice<f32>,
9474 wa: &CudaSlice<f32>,
9475 b: &CudaSlice<f32>,
9476 res: &mut CudaSlice<f32>,
9477 ncols: usize,
9478 nrows: usize,
9479 eps: f32,
9480 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9481 debug_assert!(ncols % 128 == 0);
9482 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
9483 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9484 let f = self.func("rms_pre_add_q8_1_f32");
9485 let cfg = LaunchConfig {
9486 grid_dim: (nrows as u32, 1, 1),
9487 block_dim: (rms_block(), 1, 1),
9488 shared_mem_bytes: 0,
9489 };
9490 let (nc, ep) = (ncols as i32, eps);
9491 let __s_b2 = self.gpu.stream();
9492 let mut b2 = __s_b2.launch_builder(&f);
9493 b2.arg(a)
9494 .arg(wa)
9495 .arg(b)
9496 .arg(&mut *res)
9497 .arg(&mut out_q)
9498 .arg(&mut out_d)
9499 .arg(&nc)
9500 .arg(&ep);
9501 unsafe {
9502 b2.launch(cfg)?;
9503 }
9504 Ok((out_q, out_d))
9505 }
9506
9507 pub fn l2_v2_on(ncols: usize) -> bool {
9511 ncols == 128 && std::env::var("MEMRA_L2_V2").as_deref() != Ok("0")
9512 }
9513
9514 pub fn l2_norm_pp(
9515 &self,
9516 x: &CudaSlice<f32>,
9517 dst: &mut CudaSlice<f32>,
9518 dst16: Option<&mut CudaSlice<u8>>,
9519 ncols: usize,
9520 nrows: usize,
9521 eps: f32,
9522 ) -> Result<(), Box<dyn std::error::Error>> {
9523 if Self::l2_v2_on(ncols) {
9524 let f = self.func("l2_norm_pp_v2_f32");
9525 let rows_per_block = 8u32; let cfg = LaunchConfig {
9527 grid_dim: ((nrows as u32).div_ceil(rows_per_block), 1, 1),
9528 block_dim: (256, 1, 1),
9529 shared_mem_bytes: 0,
9530 };
9531 let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
9532 let d16: u64 = match dst16 {
9534 Some(d) => self.addr_u8(d),
9535 None => 0,
9536 };
9537 let __s_b = self.gpu.stream();
9538 let mut b = __s_b.launch_builder(&f);
9539 b.arg(x).arg(dst).arg(&d16).arg(&nc).arg(&nr).arg(&e);
9540 unsafe {
9541 b.launch(cfg)?;
9542 }
9543 return Ok(());
9544 }
9545 self.l2_norm(x, dst, ncols, nrows, eps)
9546 }
9547
9548 pub fn l2_norm(
9549 &self,
9550 x: &CudaSlice<f32>,
9551 dst: &mut CudaSlice<f32>,
9552 ncols: usize,
9553 nrows: usize,
9554 eps: f32,
9555 ) -> Result<(), Box<dyn std::error::Error>> {
9556 let f = self.func("l2_norm_f32");
9557 let cfg = LaunchConfig {
9558 grid_dim: (nrows as u32, 1, 1),
9559 block_dim: (256, 1, 1),
9560 shared_mem_bytes: 0,
9561 };
9562 let (nc, e) = (ncols as i32, eps);
9563 let __s_b = self.gpu.stream();
9564 let mut b = __s_b.launch_builder(&f);
9565 b.arg(x).arg(dst).arg(&nc).arg(&e);
9566 unsafe {
9567 b.launch(cfg)?;
9568 }
9569 Ok(())
9570 }
9571
9572 pub fn l2_norm_decode(
9578 &self,
9579 x: &CudaSlice<f32>,
9580 dst: &mut CudaSlice<f32>,
9581 ncols: usize,
9582 nrows: usize,
9583 eps: f32,
9584 ) -> Result<(), Box<dyn std::error::Error>> {
9585 let f = self.func("l2_norm_f32");
9586 let cfg = LaunchConfig {
9587 grid_dim: (nrows as u32, 1, 1),
9588 block_dim: (32, 1, 1),
9589 shared_mem_bytes: 0,
9590 };
9591 let (nc, e) = (ncols as i32, eps);
9592 let __s_b = self.gpu.stream();
9593 let mut b = __s_b.launch_builder(&f);
9594 b.arg(x).arg(dst).arg(&nc).arg(&e);
9595 unsafe {
9596 b.launch(cfg)?;
9597 }
9598 Ok(())
9599 }
9600
9601 pub fn rope_neox(
9603 &self,
9604 x: &mut CudaSlice<f32>,
9605 pos: &CudaSlice<i32>,
9606 head_dim: usize,
9607 n_dims: usize,
9608 n_heads: usize,
9609 n_tokens: usize,
9610 freq_base: f32,
9611 freq_scale: f32,
9612 ) -> Result<(), Box<dyn std::error::Error>> {
9613 let f = self.func("rope_neox_f32");
9614 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9615 let grid = (n_heads * n_tokens) as u32;
9616 let cfg = LaunchConfig {
9617 grid_dim: (grid, 1, 1),
9618 block_dim: ((head_dim / 2) as u32, 1, 1),
9619 shared_mem_bytes: 0,
9620 };
9621 let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
9622 let __s_b = self.gpu.stream();
9623 let mut b = __s_b.launch_builder(&f);
9624 b.arg(x)
9625 .arg(pos)
9626 .arg(&hd)
9627 .arg(&nd)
9628 .arg(&nh)
9629 .arg(&theta_scale)
9630 .arg(&freq_scale);
9631 unsafe {
9632 b.launch(cfg)?;
9633 }
9634 Ok(())
9635 }
9636
9637 pub fn rope_neox_ff(
9639 &self,
9640 x: &mut CudaSlice<f32>,
9641 pos: &CudaSlice<i32>,
9642 head_dim: usize,
9643 n_dims: usize,
9644 n_heads: usize,
9645 n_tokens: usize,
9646 freq_base: f32,
9647 freq_scale: f32,
9648 ff: &CudaSlice<f32>,
9649 ) -> Result<(), Box<dyn std::error::Error>> {
9650 let f = self.func("rope_neox_ff_f32");
9651 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9652 let grid = (n_heads * n_tokens) as u32;
9653 let cfg = LaunchConfig {
9654 grid_dim: (grid, 1, 1),
9655 block_dim: ((head_dim / 2) as u32, 1, 1),
9656 shared_mem_bytes: 0,
9657 };
9658 let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
9659 let __s_b = self.gpu.stream();
9660 let mut b = __s_b.launch_builder(&f);
9661 b.arg(x)
9662 .arg(pos)
9663 .arg(&hd)
9664 .arg(&nd)
9665 .arg(&nh)
9666 .arg(&theta_scale)
9667 .arg(&freq_scale)
9668 .arg(ff);
9669 unsafe {
9670 b.launch(cfg)?;
9671 }
9672 Ok(())
9673 }
9674
9675 #[allow(clippy::too_many_arguments)]
9677 pub fn rope_neox2(
9678 &self,
9679 q: &mut CudaSlice<f32>,
9680 k: &mut CudaSlice<f32>,
9681 pos: &CudaSlice<i32>,
9682 head_dim: usize,
9683 n_dims: usize,
9684 nh_q: usize,
9685 nh_k: usize,
9686 n_tokens: usize,
9687 freq_base: f32,
9688 freq_scale: f32,
9689 ff: Option<&CudaSlice<f32>>,
9690 ) -> Result<(), Box<dyn std::error::Error>> {
9691 let f = self.func("rope_neox2_f32");
9692 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9693 let grid = ((nh_q + nh_k) * n_tokens) as u32;
9694 let cfg = LaunchConfig {
9695 grid_dim: (grid, 1, 1),
9696 block_dim: ((head_dim / 2) as u32, 1, 1),
9697 shared_mem_bytes: 0,
9698 };
9699 let (hd, nd, nq, nk, nt) = (
9700 head_dim as i32,
9701 n_dims as i32,
9702 nh_q as i32,
9703 nh_k as i32,
9704 n_tokens as i32,
9705 );
9706 let __s_b = self.gpu.stream();
9707 let mut b = __s_b.launch_builder(&f);
9708 b.arg(q)
9709 .arg(k)
9710 .arg(pos)
9711 .arg(&hd)
9712 .arg(&nd)
9713 .arg(&nq)
9714 .arg(&nk)
9715 .arg(&nt)
9716 .arg(&theta_scale)
9717 .arg(&freq_scale);
9718 match ff {
9719 Some(ffv) => {
9720 b.arg(ffv);
9721 unsafe {
9722 b.launch(cfg)?;
9723 }
9724 }
9725 None => {
9726 let null: u64 = 0;
9727 b.arg(&null);
9728 unsafe {
9729 b.launch(cfg)?;
9730 }
9731 }
9732 }
9733 Ok(())
9734 }
9735
9736 pub fn gelu_tanh_mul(
9738 &self,
9739 gate: &CudaSlice<f32>,
9740 up: &CudaSlice<f32>,
9741 dst: &mut CudaSlice<f32>,
9742 n: usize,
9743 ) -> Result<(), Box<dyn std::error::Error>> {
9744 let f = self.func("gelu_tanh_mul_f32");
9745 let cfg = LaunchConfig::for_num_elems(n as u32);
9746 let ni = n as i32;
9747 let __s_b = self.gpu.stream();
9748 let mut b = __s_b.launch_builder(&f);
9749 b.arg(gate).arg(up).arg(dst).arg(&ni);
9750 unsafe {
9751 b.launch(cfg)?;
9752 }
9753 Ok(())
9754 }
9755
9756 pub fn silu_mul(
9757 &self,
9758 gate: &CudaSlice<f32>,
9759 up: &CudaSlice<f32>,
9760 dst: &mut CudaSlice<f32>,
9761 n: usize,
9762 ) -> Result<(), Box<dyn std::error::Error>> {
9763 let f = self.func("silu_mul_f32");
9764 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9766 let ni = n as i32;
9767 let __s_b = self.gpu.stream();
9768 let mut b = __s_b.launch_builder(&f);
9769 b.arg(gate).arg(up).arg(dst).arg(&ni);
9770 unsafe {
9771 b.launch(cfg)?;
9772 }
9773 Ok(())
9774 }
9775
9776 pub fn silu_mul_f16out(
9779 &self,
9780 gate: &CudaSlice<f32>,
9781 up: &CudaSlice<f32>,
9782 dst: &mut CudaSlice<f32>,
9783 dst16: &mut CudaSlice<u8>,
9784 n: usize,
9785 ) -> Result<(), Box<dyn std::error::Error>> {
9786 let f = self.func("silu_mul_f16out_f32");
9787 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9788 let ni = n as i32;
9789 let __s_b = self.gpu.stream();
9790 let mut b = __s_b.launch_builder(&f);
9791 b.arg(gate).arg(up).arg(dst).arg(dst16).arg(&ni);
9792 unsafe {
9793 b.launch(cfg)?;
9794 }
9795 Ok(())
9796 }
9797
9798 pub fn silu_mul_scaled(
9805 &self,
9806 gate: &CudaSlice<f32>,
9807 up: &CudaSlice<f32>,
9808 gs: f32,
9809 us: f32,
9810 dst: &mut CudaSlice<f32>,
9811 n: usize,
9812 ) -> Result<(), Box<dyn std::error::Error>> {
9813 let f = self.func("silu_mul_scaled_f32");
9814 let cfg = LaunchConfig::for_num_elems(n as u32);
9815 let ni = n as i32;
9816 let (gsf, usf) = (gs, us);
9817 let __s_b = self.gpu.stream();
9818 let mut b = __s_b.launch_builder(&f);
9819 b.arg(gate).arg(up).arg(&gsf).arg(&usf).arg(dst).arg(&ni);
9820 unsafe {
9821 b.launch(cfg)?;
9822 }
9823 Ok(())
9824 }
9825
9826 #[allow(clippy::too_many_arguments)]
9830 pub fn swigluoai_mul_scaled(
9831 &self,
9832 gate: &CudaSlice<f32>,
9833 up: &CudaSlice<f32>,
9834 gs: f32,
9835 us: f32,
9836 alpha: f32,
9837 limit: f32,
9838 dst: &mut CudaSlice<f32>,
9839 n: usize,
9840 ) -> Result<(), Box<dyn std::error::Error>> {
9841 let f = self.func("swigluoai_mul_scaled_f32");
9842 let cfg = LaunchConfig::for_num_elems(n as u32);
9843 let ni = n as i32;
9844 let __s_b = self.gpu.stream();
9845 let mut b = __s_b.launch_builder(&f);
9846 b.arg(gate)
9847 .arg(up)
9848 .arg(&gs)
9849 .arg(&us)
9850 .arg(&alpha)
9851 .arg(&limit)
9852 .arg(dst)
9853 .arg(&ni);
9854 unsafe {
9855 b.launch(cfg)?;
9856 }
9857 Ok(())
9858 }
9859
9860 pub fn silu_mul_scaled_q8_1(
9868 &self,
9869 gate: &CudaSlice<f32>,
9870 up: &CudaSlice<f32>,
9871 gs: f32,
9872 us: f32,
9873 n: usize,
9874 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9875 let f = self.func("silu_mul_scaled_q8_1");
9876 let nblk = n / 32;
9877 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);
9881 let (gsf, usf, ni) = (gs, us, n as i32);
9882 let __s_b = self.gpu.stream();
9883 let mut b = __s_b.launch_builder(&f);
9884 b.arg(gate)
9885 .arg(up)
9886 .arg(&gsf)
9887 .arg(&usf)
9888 .arg(&mut aq)
9889 .arg(&mut ad)
9890 .arg(&ni);
9891 unsafe {
9892 b.launch(cfg)?;
9893 }
9894 Ok((aq, ad))
9895 }
9896
9897 pub fn add(
9898 &self,
9899 a: &CudaSlice<f32>,
9900 b_in: &CudaSlice<f32>,
9901 dst: &mut CudaSlice<f32>,
9902 n: usize,
9903 ) -> Result<(), Box<dyn std::error::Error>> {
9904 let f = self.func("add_f32");
9905 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9907 let ni = n as i32;
9908 let __s_bld = self.gpu.stream();
9909 let mut bld = __s_bld.launch_builder(&f);
9910 bld.arg(a).arg(b_in).arg(dst).arg(&ni);
9911 unsafe {
9912 bld.launch(cfg)?;
9913 }
9914 Ok(())
9915 }
9916
9917 pub fn mul(
9918 &self,
9919 a: &CudaSlice<f32>,
9920 b_in: &CudaSlice<f32>,
9921 dst: &mut CudaSlice<f32>,
9922 n: usize,
9923 ) -> Result<(), Box<dyn std::error::Error>> {
9924 let f = self.func("mul_f32");
9925 let cfg = LaunchConfig::for_num_elems(n as u32);
9926 let ni = n as i32;
9927 let __s_bld = self.gpu.stream();
9928 let mut bld = __s_bld.launch_builder(&f);
9929 bld.arg(a).arg(b_in).arg(dst).arg(&ni);
9930 unsafe {
9931 bld.launch(cfg)?;
9932 }
9933 Ok(())
9934 }
9935
9936 pub fn matmul(
9939 &self,
9940 w: &crate::model::GpuTensor,
9941 x: &CudaSlice<f32>,
9942 m: usize,
9943 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9944 use crate::model::GpuTensor;
9945 let in_f = w.in_features();
9946 let out_f = w.out_features();
9947 #[allow(non_snake_case)]
9955 let GEMM_M_THRESHOLD = if self.verify_exact_on() {
9958 usize::MAX
9959 } else {
9960 16usize
9961 };
9962
9963 const GEMM_MIN_OUT_F: usize = 128; if m >= GEMM_M_THRESHOLD {
9988 if let Some(y) = self.try_fp8_gemm(w, x, m)? {
9989 return Ok(y);
9990 }
9991 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
9998 return Ok(y);
9999 }
10000 if let Some(y) = self.try_f16_gemm(w, x, m)? {
10003 return Ok(y);
10004 }
10005 }
10006 if let GpuTensor::Quant { qtype, .. } = w {
10021 if *qtype == QT_F8_E4M3_BLK {
10022 if m >= GEMM_M_THRESHOLD {
10023 if let Some(y) = self.try_e4m3_blk_prefill(w, x, m)? {
10024 return Ok(y);
10025 }
10026 }
10027 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10028 if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
10029 return Ok(y);
10030 }
10031 }
10032 }
10033 if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.mmq_supports(w) {
10034 return self.qmatvec_mmq(w, x, m);
10035 }
10036 if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.gemm_supports(w) {
10037 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10038 return self.qmatvec_gemm(w, &aq, &ad, m);
10039 }
10040 if m >= GEMM_M_THRESHOLD {
10043 if let Some(y) = self.try_fp4_gemm(w, x, m, in_f, out_f)? {
10044 return Ok(y);
10045 }
10046 }
10047 let fast = std::env::var("MEMRA_FAST").as_deref() != Ok("0");
10051 if m == 1 && fast {
10056 if let GpuTensor::Quant {
10057 bytes,
10058 qtype,
10059 row_bytes,
10060 rp,
10061 rp4,
10062 scale,
10063 ..
10064 } = w
10065 {
10066 if self.mmvq_supports(*qtype) {
10067 let (bytes, rp) = match rp4 {
10071 Some(m4) => (m4, true),
10072 None => (bytes, *rp),
10073 };
10074 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10075 return self.qmatvec_mmvq(
10076 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, rp,
10077 );
10078 }
10079 }
10080 }
10081 if (2..=16).contains(&m)
10097 && fast
10098 && std::env::var("MEMRA_NO_BATCHED").is_err()
10099 && (m <= 4 || Self::b8_enabled())
10100 {
10101 let m_ok = m <= 8
10111 || matches!(w, GpuTensor::Quant { qtype, .. }
10112 if *qtype == QT_Q4_0 || *qtype == QT_Q6_K || *qtype == QT_F8_E4M3
10113 || *qtype == QT_NVFP4 || *qtype == QT_Q4_K || *qtype == QT_Q5_K || *qtype == QT_Q8_0);
10114 if m_ok {
10115 if let GpuTensor::Quant {
10116 bytes,
10117 qtype,
10118 row_bytes,
10119 rp,
10120 rp4,
10121 ..
10122 } = w
10123 {
10124 if self.batched_supports(*qtype) && self.mmvq_supports(*qtype) {
10125 let (bytes, rp) = match rp4 {
10126 Some(m4) => (m4, true),
10127 None => (bytes, *rp),
10128 };
10129 let mcols = Self::batched_mcols(m);
10130 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10131 let mut y = self.qmatvec_mmvq_batched(
10132 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, mcols, 1.0, rp,
10133 )?;
10134 if let GpuTensor::Quant { scale, .. } = w {
10135 if *scale != 1.0 {
10136 self.scale_inplace(&mut y, *scale, m * out_f)?;
10137 }
10138 }
10139 return Ok(y);
10140 }
10141 }
10142 }
10143 }
10144 if fast {
10150 if let GpuTensor::Quant {
10151 bytes,
10152 qtype,
10153 row_bytes,
10154 scale,
10155 ..
10156 } = w
10157 {
10158 if *qtype == QT_F8_E4M3 {
10159 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10160 return self.qmatvec_mmvq(
10161 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, false,
10162 );
10163 }
10164 }
10165 }
10166 let mut y = match w {
10167 GpuTensor::Quant {
10168 bytes,
10169 qtype,
10170 row_bytes,
10171 ..
10172 } if fast && *qtype == QT_Q8_0 => {
10173 self.qmatvec_q8_0_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10174 }
10175 GpuTensor::Quant {
10176 bytes,
10177 qtype,
10178 row_bytes,
10179 ..
10180 } if fast && *qtype == QT_Q4_K => {
10181 self.qmatvec_q4_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10182 }
10183 GpuTensor::Quant {
10184 bytes,
10185 qtype,
10186 row_bytes,
10187 ..
10188 } if fast && *qtype == QT_Q6_K => {
10189 self.qmatvec_q6_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10190 }
10191 GpuTensor::Quant {
10192 bytes,
10193 qtype,
10194 row_bytes,
10195 ..
10196 } if fast && *qtype == QT_Q5_K => {
10197 self.qmatvec_q5_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10198 }
10199 GpuTensor::Quant {
10200 bytes,
10201 qtype,
10202 row_bytes,
10203 ..
10204 } if fast && *qtype == QT_Q3_K => {
10205 self.qmatvec_q3_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10206 }
10207 GpuTensor::Quant {
10208 bytes,
10209 qtype,
10210 row_bytes,
10211 rp,
10212 ..
10213 } if fast && *qtype == QT_NVFP4 => self.qmatvec_dp4a_named(
10214 if *rp {
10215 "qmatvec_nvfp4_dp4a_rp"
10216 } else {
10217 "qmatvec_nvfp4_dp4a"
10218 },
10219 bytes,
10220 x,
10221 m,
10222 in_f,
10223 out_f,
10224 *row_bytes,
10225 )?,
10226 GpuTensor::Quant {
10230 bytes,
10231 qtype,
10232 row_bytes,
10233 ..
10234 } if fast && *qtype == QT_IQ4_XS && Self::iq_fast_enabled() => {
10235 self.qmatvec_iq4_XS_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10236 }
10237 GpuTensor::Quant {
10242 bytes,
10243 qtype,
10244 row_bytes,
10245 rp,
10246 ..
10247 } =>
10248 {
10251 self.qmatvec(
10252 bytes,
10253 x,
10254 m,
10255 in_f,
10256 out_f,
10257 if *rp && *qtype == QT_NVFP4 {
10258 QT_NVFP4_RP
10259 } else {
10260 *qtype
10261 },
10262 *row_bytes,
10263 )?
10264 }
10265 GpuTensor::Float { data, .. } => self.linear(x, data, m, in_f, out_f)?,
10266 GpuTensor::FloatBf16 { data, .. } => {
10269 self.linear_bf16_chunked(x, data, m, in_f, out_f, false)?
10270 }
10271 };
10272 if let GpuTensor::Quant { scale, .. } = w {
10274 if *scale != 1.0 {
10275 self.scale_inplace(&mut y, *scale, m * out_f)?;
10276 }
10277 }
10278 Ok(y)
10279 }
10280
10281 pub fn stage_a_raw_needed() -> bool {
10291 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10292 *ON.get_or_init(|| std::env::var("MEMRA_FAST").as_deref() == Ok("0"))
10293 }
10294
10295 pub fn uses_q8_1_fast(&self, w: &crate::model::GpuTensor) -> bool {
10298 use crate::model::GpuTensor;
10299 if std::env::var("MEMRA_FAST").as_deref() == Ok("0") {
10300 return false;
10301 }
10302 match w {
10303 GpuTensor::Quant { qtype, .. } => {
10310 matches!(
10311 *qtype,
10312 QT_Q8_0
10313 | QT_Q4_K
10314 | QT_Q6_K
10315 | QT_Q5_K
10316 | QT_Q3_K
10317 | QT_NVFP4
10318 | QT_F8_E4M3
10319 | QT_F8_E4M3_BLK
10320 | QT_Q4_0
10321 ) || (*qtype == QT_IQ4_XS && Self::iq_fast_enabled())
10322 }
10323 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
10324 }
10325 }
10326
10327 pub fn matmul_pre(
10332 &self,
10333 w: &crate::model::GpuTensor,
10334 aq: &CudaSlice<i8>,
10335 ad: &CudaSlice<f32>,
10336 x_fallback: &CudaSlice<f32>,
10337 m: usize,
10338 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10339 use crate::model::GpuTensor;
10340 let x_raw_ok = x_fallback.len() >= m * w.in_features();
10346 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
10349 if let Some(y) = self.try_fp8_gemm(w, x_fallback, m)? {
10350 return Ok(y);
10351 }
10352 if let Some(y) = self.try_fp8_blk_mmq(w, x_fallback, m)? {
10355 return Ok(y);
10356 }
10357 if let Some(y) = self.try_f16_gemm(w, x_fallback, m)? {
10359 return Ok(y);
10360 }
10361 }
10362 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
10368 if let Some(y) = self.try_e4m3_blk_prefill(w, x_fallback, m)? {
10369 return Ok(y);
10370 }
10371 }
10372 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
10373 return Ok(y);
10374 }
10375 if m >= 16
10380 && w.out_features() >= 128
10381 && self.mmq_supports(w)
10382 && !self.verify_exact_on()
10383 && x_raw_ok
10384 {
10385 return self.qmatvec_mmq(w, x_fallback, m);
10386 }
10387 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
10390 if let Some(y) =
10391 self.try_fp4_gemm(w, x_fallback, m, w.in_features(), w.out_features())?
10392 {
10393 return Ok(y);
10394 }
10395 }
10396 if m >= 16 && self.gemm_supports(w) && !self.verify_exact_on() {
10399 return self.qmatvec_gemm(w, aq, ad, m);
10400 }
10401 if !self.uses_q8_1_fast(w) {
10420 if !x_raw_ok {
10421 return Err(format!(
10422 "matmul_pre: q8_1-fast is off for this weight but x_fallback holds {} f32 \
10423 (need m*in_f = {}*{} = {}). This call site pre-quantized its activation and \
10424 dropped the f32, so there is nothing to fall back to — pass the real f32 \
10425 activation (see Engine::rms_norm_decode, which is bit-identical to \
10426 rms_norm_q8_1's reduction) or keep the weight on the q8_1 path.",
10427 x_fallback.len(),
10428 m,
10429 w.in_features(),
10430 m * w.in_features()
10431 )
10432 .into());
10433 }
10434 return self.matmul(w, x_fallback, m);
10435 }
10436 let in_f = w.in_features();
10437 let out_f = w.out_features();
10438 let (bytes, qtype, row_bytes, scale, rp) = match w {
10439 GpuTensor::Quant {
10440 bytes,
10441 qtype,
10442 row_bytes,
10443 scale,
10444 rp,
10445 ..
10446 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10447 _ => unreachable!("uses_q8_1_fast guaranteed Quant"),
10448 };
10449 let (mbytes, mrp) = match w {
10452 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10453 _ => (bytes, rp),
10454 };
10455 if m == 1 && self.mmvq_supports(qtype) {
10459 return self.qmatvec_mmvq(mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, mrp);
10460 }
10461 if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
10474 && std::env::var("MEMRA_NO_BATCHED").is_err()
10475 && (m <= 4 || Self::b8_enabled())
10476 && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_NVFP4
10480 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_F8_E4M3 || qtype == QT_Q8_0)
10481 {
10482 let mcols = Self::batched_mcols(m);
10483 return self.qmatvec_mmvq_batched(
10484 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, mrp,
10485 );
10486 }
10487 if qtype == QT_F8_E4M3 || qtype == QT_Q4_0 {
10493 let (b2, r2) = if qtype == QT_Q4_0 {
10494 (mbytes, mrp)
10495 } else {
10496 (bytes, rp)
10497 };
10498 return self.qmatvec_mmvq(b2, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, r2);
10499 }
10500 let name = match qtype {
10501 QT_Q8_0 => "qmatvec_q8_0_dp4a",
10502 QT_Q4_K => "qmatvec_q4_K_dp4a",
10503 QT_Q6_K => "qmatvec_q6_K_dp4a",
10504 QT_Q5_K => "qmatvec_q5_K_dp4a",
10505 QT_Q3_K => "qmatvec_q3_K_dp4a",
10506 QT_NVFP4 => {
10507 if rp {
10508 "qmatvec_nvfp4_dp4a_rp"
10509 } else {
10510 "qmatvec_nvfp4_dp4a"
10511 }
10512 }
10513 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
10514 _ => unreachable!(),
10515 };
10516 let f = self.func(name);
10517 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
10519 grid_dim: (out_f as u32, m as u32, 1),
10520 block_dim: (128, 1, 1),
10521 shared_mem_bytes: 0,
10522 };
10523 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
10524 let __s_b = self.gpu.stream();
10525 let mut b = __s_b.launch_builder(&f);
10526 b.arg(bytes)
10527 .arg(aq)
10528 .arg(ad)
10529 .arg(&mut y)
10530 .arg(&inf)
10531 .arg(&outf)
10532 .arg(&mi)
10533 .arg(&rb);
10534 unsafe {
10535 b.launch(cfg)?;
10536 }
10537 if scale != 1.0 {
10538 self.scale_inplace(&mut y, scale, m * out_f)?;
10539 }
10540 Ok(y)
10541 }
10542
10543 pub fn matmul_decode_exact(
10551 &self,
10552 w: &crate::model::GpuTensor,
10553 x: &CudaSlice<f32>,
10554 m: usize,
10555 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10556 use crate::model::GpuTensor;
10557 if let GpuTensor::Float { data, .. } = w {
10565 return self.linear_decode_exact(x, data, m, w.in_features(), w.out_features());
10566 }
10567 if let GpuTensor::FloatBf16 { data, .. } = w {
10570 let (in_f, out_f) = (w.in_features(), w.out_features());
10571 return self.linear_bf16_chunked(x, data, m, in_f, out_f, true);
10572 }
10573 if !self.uses_q8_1_fast(w) {
10574 return self.matmul(w, x, m);
10575 }
10576 let in_f = w.in_features();
10577 let out_f = w.out_features();
10578 let (bytes, qtype, row_bytes, scale, rp) = match w {
10579 GpuTensor::Quant {
10580 bytes,
10581 qtype,
10582 row_bytes,
10583 scale,
10584 rp,
10585 ..
10586 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10587 _ => return self.matmul(w, x, m),
10588 };
10589 let (bytes, rp) = match w {
10592 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10593 _ => (bytes, rp),
10594 };
10595 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10596 if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
10600 return Ok(y);
10601 }
10602 if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
10611 && std::env::var("MEMRA_NO_BATCHED").is_err()
10612 && (m <= 4 || Self::b8_enabled())
10613 && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_F8_E4M3
10616 || qtype == QT_NVFP4 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_Q8_0)
10617 {
10618 let mcols = Self::batched_mcols(m);
10619 return self.qmatvec_mmvq_batched(
10620 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
10621 );
10622 }
10623 if self.mmvq_supports(qtype) {
10624 return self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
10627 }
10628 self.matmul_pre(w, &aq, &ad, x, m)
10631 }
10632
10633 pub fn matmul_decode_exact_pre(
10643 &self,
10644 w: &crate::model::GpuTensor,
10645 aq: &CudaSlice<i8>,
10646 ad: &CudaSlice<f32>,
10647 m: usize,
10648 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10649 use crate::model::GpuTensor;
10650 debug_assert!(
10651 self.uses_q8_1_fast(w),
10652 "matmul_decode_exact_pre: caller must guarantee q8_1-fast"
10653 );
10654 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
10656 return Ok(y);
10657 }
10658 let in_f = w.in_features();
10659 let out_f = w.out_features();
10660 let (bytes, qtype, row_bytes, scale, rp) = match w {
10661 GpuTensor::Quant {
10662 bytes,
10663 qtype,
10664 row_bytes,
10665 scale,
10666 rp,
10667 ..
10668 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10669 _ => {
10670 return Err(
10671 "matmul_decode_exact_pre: Quant tensor required (q8_1-fast contract)".into(),
10672 );
10673 }
10674 };
10675 let (bytes, rp) = match w {
10677 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10678 _ => (bytes, rp),
10679 };
10680 if (2..=16).contains(&m)
10682 && self.batched_supports(qtype)
10683 && self.mmvq_supports(qtype)
10684 && std::env::var("MEMRA_NO_BATCHED").is_err()
10685 && (m <= 4 || Self::b8_enabled())
10686 && (m <= 8
10687 || qtype == QT_Q4_0
10688 || qtype == QT_Q6_K
10689 || qtype == QT_F8_E4M3
10690 || qtype == QT_NVFP4
10691 || qtype == QT_Q4_K
10692 || qtype == QT_Q5_K
10693 || qtype == QT_Q8_0)
10694 {
10695 let mcols = Self::batched_mcols(m);
10696 return self.qmatvec_mmvq_batched(
10697 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
10698 );
10699 }
10700 if self.mmvq_supports(qtype) {
10701 return self.qmatvec_mmvq(bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
10702 }
10703 let x0 = self.zeros(0)?;
10706 self.matmul_pre(w, aq, ad, &x0, m)
10707 }
10708
10709 pub fn matmul_decode_exact_dual_pre(
10718 &self,
10719 w0: &crate::model::GpuTensor,
10720 w1: &crate::model::GpuTensor,
10721 aq: &CudaSlice<i8>,
10722 ad: &CudaSlice<f32>,
10723 m: usize,
10724 ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
10725 {
10726 use crate::model::GpuTensor;
10727 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10728 let on = *ON.get_or_init(|| {
10729 std::env::var("MEMRA_SPEC_DUAL_T")
10730 .map(|v| v != "0")
10731 .unwrap_or(true)
10732 });
10733 if !on
10734 || !(2..=7).contains(&m)
10735 || std::env::var("MEMRA_NO_BATCHED").is_ok()
10736 || !self.uses_q8_1_fast(w0)
10737 || !self.uses_q8_1_fast(w1)
10738 {
10739 return Ok(None);
10740 }
10741 if !self.mmvq_supports(QT_NVFP4) {
10746 return Ok(None);
10747 }
10748 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10749 if w1.in_features() != in_f || w1.out_features() != out_f {
10750 return Ok(None);
10751 }
10752 let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
10753 (
10754 GpuTensor::Quant {
10755 bytes: b0,
10756 qtype: q0,
10757 row_bytes: rb0,
10758 scale: s0,
10759 rp: rp0,
10760 rp4: None,
10761 ..
10762 },
10763 GpuTensor::Quant {
10764 bytes: b1,
10765 qtype: q1,
10766 row_bytes: rb1,
10767 scale: s1,
10768 rp: rp1,
10769 rp4: None,
10770 ..
10771 },
10772 ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
10773 (b0, b1, *rb0, *s0, *s1, *rp0)
10774 }
10775 _ => return Ok(None),
10776 };
10777 if m > 4 && !(rp && Self::b8_enabled() && std::env::var("MEMRA_B567").as_deref() != Ok("0"))
10780 {
10781 return Ok(None);
10782 }
10783 let (y0, y1) =
10784 self.qmatvec_batched_dual_raw(b0, b1, aq, ad, m, in_f, out_f, row_bytes, rp)?;
10785 Ok(Some(((y0, s0), (y1, s1))))
10786 }
10787
10788 pub fn matmul_decode_exact_dual(
10804 &self,
10805 w0: &crate::model::GpuTensor,
10806 w1: &crate::model::GpuTensor,
10807 x: &CudaSlice<f32>,
10808 m: usize,
10809 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10810 use crate::model::GpuTensor;
10811 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10812 let on = *ON.get_or_init(|| {
10813 std::env::var("MEMRA_SPEC_DUAL_T")
10814 .map(|v| v != "0")
10815 .unwrap_or(true)
10816 });
10817 if !on
10818 || !(2..=4).contains(&m)
10819 || std::env::var("MEMRA_NO_BATCHED").is_ok()
10820 || !self.uses_q8_1_fast(w0)
10821 || !self.uses_q8_1_fast(w1)
10822 {
10823 return Ok(None);
10824 }
10825 if !self.mmvq_supports(QT_NVFP4) {
10830 return Ok(None);
10831 }
10832 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10833 if w1.in_features() != in_f || w1.out_features() != out_f {
10834 return Ok(None);
10835 }
10836 let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
10837 (
10838 GpuTensor::Quant {
10839 bytes: b0,
10840 qtype: q0,
10841 row_bytes: rb0,
10842 scale: s0,
10843 rp: rp0,
10844 rp4: None,
10845 ..
10846 },
10847 GpuTensor::Quant {
10848 bytes: b1,
10849 qtype: q1,
10850 row_bytes: rb1,
10851 scale: s1,
10852 rp: rp1,
10853 rp4: None,
10854 ..
10855 },
10856 ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
10857 (b0, b1, *rb0, *s0, *s1, *rp0)
10858 }
10859 _ => return Ok(None),
10860 };
10861 if std::env::var("MEMRA_DEBUG").is_ok() {
10864 static ONCE: std::sync::Once = std::sync::Once::new();
10865 ONCE.call_once(|| eprintln!("[memra] dual gate+up batched ENGAGED (m={m} rp={rp})"));
10866 }
10867 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10868 let (y0, y1) =
10869 self.qmatvec_batched_dual_raw(b0, b1, &aq, &ad, m, in_f, out_f, row_bytes, rp)?;
10870 let mut y0 = y0;
10871 let mut y1 = y1;
10872 if s0 != 1.0 {
10873 self.scale_inplace(&mut y0, s0, m * out_f)?;
10874 }
10875 if s1 != 1.0 {
10876 self.scale_inplace(&mut y1, s1, m * out_f)?;
10877 }
10878 Ok(Some((y0, y1)))
10879 }
10880
10881 #[allow(clippy::too_many_arguments)]
10886 pub fn qmatvec_batched_dual_raw(
10887 &self,
10888 b0: &CudaSlice<u8>,
10889 b1: &CudaSlice<u8>,
10890 aq: &CudaSlice<i8>,
10891 ad: &CudaSlice<f32>,
10892 m: usize,
10893 in_f: usize,
10894 out_f: usize,
10895 row_bytes: usize,
10896 rp: bool,
10897 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10898 const ROWS_PER_BLOCK: u32 = 4;
10899 let mcols = Self::batched_mcols(m);
10900 let tiny_rp1 = rp
10903 && mcols == 4
10904 && out_f <= 128
10905 && std::env::var("MEMRA_NVFP4_AUX_DUAL").as_deref() != Ok("0");
10906 let (name, rows_per_block) = if tiny_rp1 {
10907 ("qmatvec_nvfp4_mmvq_dual_b4_rp", ROWS_PER_BLOCK)
10908 } else {
10909 match (mcols, rp, m) {
10910 (2, false, _) => ("qmatvec_nvfp4_mmvq_dual_b2", ROWS_PER_BLOCK),
10911 (4, false, _) => ("qmatvec_nvfp4_mmvq_dual_b4_r2", ROWS_PER_BLOCK * 2),
10912 (2, true, _) => ("qmatvec_nvfp4_mmvq_dual_b2_rp", ROWS_PER_BLOCK),
10913 (4, true, _) => ("qmatvec_nvfp4_mmvq_dual_b4_rpr2", ROWS_PER_BLOCK * 2),
10914 (8, true, 5) => ("qmatvec_nvfp4_mmvq_dual_b5_rpr2", ROWS_PER_BLOCK * 2),
10915 (8, true, 6) => ("qmatvec_nvfp4_mmvq_dual_b6_rpr2", ROWS_PER_BLOCK * 2),
10916 (8, true, 7) => ("qmatvec_nvfp4_mmvq_dual_b7_rpr2", ROWS_PER_BLOCK * 2),
10917 _ => {
10918 return Err(
10919 format!("qmatvec_batched_dual_raw: no dual kernel for m {m}").into(),
10920 );
10921 }
10922 }
10923 };
10924 let f = self.func(name);
10925 let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
10926 let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
10927 let cfg = LaunchConfig {
10928 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
10929 block_dim: (32, ROWS_PER_BLOCK, 1),
10930 shared_mem_bytes: 0,
10931 };
10932 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
10933 let __s_b = self.gpu.stream();
10934 let mut b = __s_b.launch_builder(&f);
10935 b.arg(b0)
10936 .arg(b1)
10937 .arg(aq)
10938 .arg(ad)
10939 .arg(&mut y0)
10940 .arg(&mut y1)
10941 .arg(&inf)
10942 .arg(&outf)
10943 .arg(&mi)
10944 .arg(&rb);
10945 unsafe {
10946 b.launch(cfg)?;
10947 }
10948 Ok((y0, y1))
10949 }
10950
10951 pub fn matmul_pre_dual_noscale(
10963 &self,
10964 w0: &crate::model::GpuTensor,
10965 w1: &crate::model::GpuTensor,
10966 aq: &CudaSlice<i8>,
10967 ad: &CudaSlice<f32>,
10968 m: usize,
10969 ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
10970 {
10971 use crate::model::GpuTensor;
10972 if m != 1 || !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
10973 return Ok(None);
10974 }
10975 if !self.mmvq_supports(QT_NVFP4) {
10985 return Ok(None);
10986 }
10987 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10988 if w1.in_features() != in_f || w1.out_features() != out_f {
10989 return Ok(None);
10990 }
10991 let no_mirror =
11004 |w: &crate::model::GpuTensor| !matches!(w, GpuTensor::Quant { rp4: Some(_), .. });
11005 if self.q8_ffn_fuse2_on()
11006 && no_mirror(w0)
11007 && no_mirror(w1)
11008 && let Some([p0, p1]) = self.q8_fused_params(&[w0, w1])
11009 {
11010 let (y0, y1) = self.q8_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2)?;
11011 return Ok(Some(((y0, 1.0), (y1, 1.0))));
11012 }
11013 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
11023 let (y0, y1) =
11024 self.e4m3_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2, 1.0, 1.0)?;
11025 return Ok(Some(((y0, p0.3), (y1, p1.3))));
11026 }
11027 let (b0, q0, rb0, s0, rp0) = match w0 {
11028 GpuTensor::Quant {
11029 bytes,
11030 qtype,
11031 row_bytes,
11032 scale,
11033 rp,
11034 ..
11035 } => (bytes, *qtype, *row_bytes, *scale, *rp),
11036 _ => return Ok(None),
11037 };
11038 let (b1, q1, rb1, s1, rp1) = match w1 {
11039 GpuTensor::Quant {
11040 bytes,
11041 qtype,
11042 row_bytes,
11043 scale,
11044 rp,
11045 ..
11046 } => (bytes, *qtype, *row_bytes, *scale, *rp),
11047 _ => return Ok(None),
11048 };
11049 if q0 != QT_NVFP4 || q1 != QT_NVFP4 || rb0 != rb1 || rp0 != rp1 {
11050 return Ok(None);
11051 }
11052 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11054 let rows_per_block = ROWS_PER_BLOCK * RPW;
11055 let f = self.func(if rp0 {
11056 "qmatvec_nvfp4_mmvq_dual_mr2_rp"
11057 } else {
11058 "qmatvec_nvfp4_mmvq_dual_mr2"
11059 });
11060 let mut y0 = self.alloc_uninit::<f32>(out_f)?;
11061 let mut y1 = self.alloc_uninit::<f32>(out_f)?;
11062 let cfg = LaunchConfig {
11063 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
11064 block_dim: (32, ROWS_PER_BLOCK, 1),
11065 shared_mem_bytes: 0,
11066 };
11067 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, rb0 as i64);
11068 let one = 1.0f32;
11071 let __s_b = self.gpu.stream();
11072 let mut b = __s_b.launch_builder(&f);
11073 b.arg(b0)
11074 .arg(b1)
11075 .arg(aq)
11076 .arg(ad)
11077 .arg(&mut y0)
11078 .arg(&mut y1)
11079 .arg(&inf)
11080 .arg(&outf)
11081 .arg(&mi)
11082 .arg(&rb)
11083 .arg(&one)
11084 .arg(&one);
11085 unsafe {
11086 b.launch(cfg)?;
11087 }
11088 Ok(Some(((y0, s0), (y1, s1))))
11089 }
11090
11091 #[allow(clippy::too_many_arguments)]
11099 pub fn matmul_nvfp4_fused3(
11100 &self,
11101 w0: &crate::model::GpuTensor,
11102 w1: &crate::model::GpuTensor,
11103 w2: &crate::model::GpuTensor,
11104 aq: &CudaSlice<i8>,
11105 ad: &CudaSlice<f32>,
11106 m: usize,
11107 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11108 {
11109 use crate::model::GpuTensor;
11110 if m != 1
11114 || !self.mmvq_supports(QT_NVFP4)
11115 || !self.uses_q8_1_fast(w0)
11116 || !self.uses_q8_1_fast(w1)
11117 || !self.uses_q8_1_fast(w2)
11118 {
11119 return Ok(None);
11120 }
11121 let unpack = |w: &crate::model::GpuTensor| match w {
11122 GpuTensor::Quant {
11123 bytes,
11124 qtype,
11125 scale,
11126 rp,
11127 ..
11128 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
11129 _ => None,
11130 };
11131 let (Some(p0), Some(p1), Some(p2)) = (unpack(w0), unpack(w1), unpack(w2)) else {
11132 return Ok(None);
11133 };
11134 let in_f = w0.in_features();
11135 if w1.in_features() != in_f || w2.in_features() != in_f {
11136 return Ok(None);
11137 }
11138 let (o0, o1, o2) = (w0.out_features(), w1.out_features(), w2.out_features());
11139 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11141 let rows_pb = ROWS_PER_BLOCK * RPW;
11142 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
11143 let f = self.func("qmatvec_nvfp4_mmvq_fused3_rp");
11144 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11145 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11146 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
11147 let cfg = LaunchConfig {
11148 grid_dim: (nb(o0) + nb(o1) + nb(o2), m as u32, 1),
11149 block_dim: (32, ROWS_PER_BLOCK, 1),
11150 shared_mem_bytes: 0,
11151 };
11152 let (inf, oi0, oi1, oi2, mi) = (in_f as i32, o0 as i32, o1 as i32, o2 as i32, m as i32);
11153 let (b0, b1, b2) = unsafe { (&*p0.0, &*p1.0, &*p2.0) };
11156 let __s_b = self.gpu.stream();
11157 let mut b = __s_b.launch_builder(&f);
11158 b.arg(b0)
11159 .arg(b1)
11160 .arg(b2)
11161 .arg(aq)
11162 .arg(ad)
11163 .arg(&mut y0)
11164 .arg(&mut y1)
11165 .arg(&mut y2)
11166 .arg(&inf)
11167 .arg(&oi0)
11168 .arg(&oi1)
11169 .arg(&oi2)
11170 .arg(&mi)
11171 .arg(&p0.1)
11172 .arg(&p1.1)
11173 .arg(&p2.1);
11174 unsafe {
11175 b.launch(cfg)?;
11176 }
11177 Ok(Some((y0, y1, y2)))
11178 }
11179
11180 pub fn matmul_nvfp4_fused2(
11189 &self,
11190 w0: &crate::model::GpuTensor,
11191 w1: &crate::model::GpuTensor,
11192 aq: &CudaSlice<i8>,
11193 ad: &CudaSlice<f32>,
11194 m: usize,
11195 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11196 use crate::model::GpuTensor;
11197 static FUSED2_OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11198 let off =
11199 *FUSED2_OFF.get_or_init(|| std::env::var("MEMRA_NVFP4_FUSED2").as_deref() == Ok("0"));
11200 if off
11203 || m != 1
11204 || !self.mmvq_supports(QT_NVFP4)
11205 || !self.uses_q8_1_fast(w0)
11206 || !self.uses_q8_1_fast(w1)
11207 {
11208 return Ok(None);
11209 }
11210 let unpack = |w: &crate::model::GpuTensor| match w {
11211 GpuTensor::Quant {
11212 bytes,
11213 qtype,
11214 scale,
11215 rp,
11216 ..
11217 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
11218 _ => None,
11219 };
11220 let (Some(p0), Some(p1)) = (unpack(w0), unpack(w1)) else {
11221 return Ok(None);
11222 };
11223 let in_f = w0.in_features();
11224 if w1.in_features() != in_f {
11225 return Ok(None);
11226 }
11227 let (o0, o1) = (w0.out_features(), w1.out_features());
11228 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11230 let rows_pb = ROWS_PER_BLOCK * RPW;
11231 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
11232 let f = self.func("qmatvec_nvfp4_mmvq_fused2_rp");
11233 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11234 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11235 let cfg = LaunchConfig {
11236 grid_dim: (nb(o0) + nb(o1), m as u32, 1),
11237 block_dim: (32, ROWS_PER_BLOCK, 1),
11238 shared_mem_bytes: 0,
11239 };
11240 let (inf, oi0, oi1, mi) = (in_f as i32, o0 as i32, o1 as i32, m as i32);
11241 let (b0, b1) = unsafe { (&*p0.0, &*p1.0) };
11244 if Self::pdl_on() && Self::pdl_mmvq_on() && Self::pdl_nvfp4q8_on() {
11247 {
11248 use cudarc::driver::{DevicePtr, DevicePtrMut};
11249 let s = &self.gpu.stream();
11250 let (pw0, _g0) = b0.device_ptr(s);
11251 let (pw1, _g1) = b1.device_ptr(s);
11252 let (paq, _g2) = aq.device_ptr(s);
11253 let (pad, _g3) = ad.device_ptr(s);
11254 let (py0, _g4) = y0.device_ptr_mut(s);
11255 let (py1, _g5) = y1.device_ptr_mut(s);
11256 let (s0, s1) = (p0.1, p1.1);
11257 let mut ps = [
11258 &pw0 as *const _ as *mut std::ffi::c_void,
11259 &pw1 as *const _ as *mut _,
11260 &paq as *const _ as *mut _,
11261 &pad as *const _ as *mut _,
11262 &py0 as *const _ as *mut _,
11263 &py1 as *const _ as *mut _,
11264 &inf as *const _ as *mut _,
11265 &oi0 as *const _ as *mut _,
11266 &oi1 as *const _ as *mut _,
11267 &mi as *const _ as *mut _,
11268 &s0 as *const _ as *mut _,
11269 &s1 as *const _ as *mut _,
11270 ];
11271 unsafe {
11272 self.launch_pdl(
11273 "qmatvec_nvfp4_mmvq_fused2_rp",
11274 cfg.grid_dim,
11275 cfg.block_dim,
11276 &mut ps,
11277 )?;
11278 }
11279 }
11280 return Ok(Some((y0, y1)));
11281 }
11282 let __s_b = self.gpu.stream();
11283 let mut b = __s_b.launch_builder(&f);
11284 b.arg(b0)
11285 .arg(b1)
11286 .arg(aq)
11287 .arg(ad)
11288 .arg(&mut y0)
11289 .arg(&mut y1)
11290 .arg(&inf)
11291 .arg(&oi0)
11292 .arg(&oi1)
11293 .arg(&mi)
11294 .arg(&p0.1)
11295 .arg(&p1.1);
11296 unsafe {
11297 b.launch(cfg)?;
11298 }
11299 Ok(Some((y0, y1)))
11300 }
11301
11302 pub fn matmul_nvfp4_fused2_into(
11307 &self,
11308 w0: &crate::model::GpuTensor,
11309 w1: &crate::model::GpuTensor,
11310 aq: &CudaSlice<i8>,
11311 ad: &CudaSlice<f32>,
11312 y0: &mut CudaSlice<f32>,
11313 y1: &mut CudaSlice<f32>,
11314 ) -> Result<bool, Box<dyn std::error::Error>> {
11315 use crate::model::GpuTensor;
11316 static FUSED2_OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11317 let off =
11318 *FUSED2_OFF.get_or_init(|| std::env::var("MEMRA_NVFP4_FUSED2").as_deref() == Ok("0"));
11319 if off
11320 || !self.mmvq_supports(QT_NVFP4)
11321 || !self.uses_q8_1_fast(w0)
11322 || !self.uses_q8_1_fast(w1)
11323 {
11324 return Ok(false);
11325 }
11326 let unpack = |w: &crate::model::GpuTensor| match w {
11327 GpuTensor::Quant {
11328 bytes,
11329 qtype,
11330 scale,
11331 rp,
11332 ..
11333 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
11334 _ => None,
11335 };
11336 let (Some(p0), Some(p1)) = (unpack(w0), unpack(w1)) else {
11337 return Ok(false);
11338 };
11339 let in_f = w0.in_features();
11340 if w1.in_features() != in_f {
11341 return Ok(false);
11342 }
11343 let (o0, o1) = (w0.out_features(), w1.out_features());
11344 if y0.len() < o0 || y1.len() < o1 {
11345 return Ok(false);
11346 }
11347 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11349 let rows_pb = ROWS_PER_BLOCK * RPW;
11350 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
11351 let f = self.func("qmatvec_nvfp4_mmvq_fused2_rp");
11352 let cfg = LaunchConfig {
11353 grid_dim: (nb(o0) + nb(o1), 1, 1),
11354 block_dim: (32, ROWS_PER_BLOCK, 1),
11355 shared_mem_bytes: 0,
11356 };
11357 let (inf, oi0, oi1, mi) = (in_f as i32, o0 as i32, o1 as i32, 1i32);
11358 let (b0, b1) = unsafe { (&*p0.0, &*p1.0) };
11361 let __s_b = self.gpu.stream();
11362 let mut b = __s_b.launch_builder(&f);
11363 b.arg(b0)
11364 .arg(b1)
11365 .arg(aq)
11366 .arg(ad)
11367 .arg(&mut *y0)
11368 .arg(&mut *y1)
11369 .arg(&inf)
11370 .arg(&oi0)
11371 .arg(&oi1)
11372 .arg(&mi)
11373 .arg(&p0.1)
11374 .arg(&p1.1);
11375 unsafe {
11376 b.launch(cfg)?;
11377 }
11378 Ok(true)
11379 }
11380
11381 #[allow(clippy::type_complexity)]
11386 pub fn matmul_nvfp4_fused4(
11387 &self,
11388 w0: &crate::model::GpuTensor,
11389 w1: &crate::model::GpuTensor,
11390 w2: &crate::model::GpuTensor,
11391 w3: &crate::model::GpuTensor,
11392 aq: &CudaSlice<i8>,
11393 ad: &CudaSlice<f32>,
11394 m: usize,
11395 ) -> Result<
11396 Option<(
11397 CudaSlice<f32>,
11398 CudaSlice<f32>,
11399 CudaSlice<f32>,
11400 CudaSlice<f32>,
11401 )>,
11402 Box<dyn std::error::Error>,
11403 > {
11404 use crate::model::GpuTensor;
11405 if m != 1
11407 || std::env::var("MEMRA_NVFP4_FUSED4").as_deref() == Ok("0")
11408 || !self.mmvq_supports(QT_NVFP4)
11409 || !self.uses_q8_1_fast(w0)
11410 || !self.uses_q8_1_fast(w1)
11411 || !self.uses_q8_1_fast(w2)
11412 || !self.uses_q8_1_fast(w3)
11413 {
11414 return Ok(None);
11415 }
11416 let unpack = |w: &crate::model::GpuTensor| match w {
11417 GpuTensor::Quant {
11418 bytes,
11419 qtype,
11420 scale,
11421 rp,
11422 ..
11423 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
11424 _ => None,
11425 };
11426 let (Some(p0), Some(p1), Some(p2), Some(p3)) =
11427 (unpack(w0), unpack(w1), unpack(w2), unpack(w3))
11428 else {
11429 return Ok(None);
11430 };
11431 let in_f = w0.in_features();
11432 if w1.in_features() != in_f || w2.in_features() != in_f || w3.in_features() != in_f {
11433 return Ok(None);
11434 }
11435 let (o0, o1, o2, o3) = (
11436 w0.out_features(),
11437 w1.out_features(),
11438 w2.out_features(),
11439 w3.out_features(),
11440 );
11441 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11443 let rows_pb = ROWS_PER_BLOCK * RPW;
11444 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
11445 let f = self.func("qmatvec_nvfp4_mmvq_fused4_rp");
11446 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11447 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11448 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
11449 let mut y3 = self.alloc_uninit::<f32>(m * o3)?;
11450 let cfg = LaunchConfig {
11451 grid_dim: (nb(o0) + nb(o1) + nb(o2) + nb(o3), m as u32, 1),
11452 block_dim: (32, ROWS_PER_BLOCK, 1),
11453 shared_mem_bytes: 0,
11454 };
11455 let (inf, oi0, oi1, oi2, oi3, mi) = (
11456 in_f as i32,
11457 o0 as i32,
11458 o1 as i32,
11459 o2 as i32,
11460 o3 as i32,
11461 m as i32,
11462 );
11463 let (b0, b1, b2, b3) = unsafe { (&*p0.0, &*p1.0, &*p2.0, &*p3.0) };
11466 let __s_b = self.gpu.stream();
11467 let mut b = __s_b.launch_builder(&f);
11468 b.arg(b0)
11469 .arg(b1)
11470 .arg(b2)
11471 .arg(b3)
11472 .arg(aq)
11473 .arg(ad)
11474 .arg(&mut y0)
11475 .arg(&mut y1)
11476 .arg(&mut y2)
11477 .arg(&mut y3)
11478 .arg(&inf)
11479 .arg(&oi0)
11480 .arg(&oi1)
11481 .arg(&oi2)
11482 .arg(&oi3)
11483 .arg(&mi)
11484 .arg(&p0.1)
11485 .arg(&p1.1)
11486 .arg(&p2.1)
11487 .arg(&p3.1);
11488 unsafe {
11489 b.launch(cfg)?;
11490 }
11491 Ok(Some((y0, y1, y2, y3)))
11492 }
11493
11494 pub fn matmul_q8_fused2(
11502 &self,
11503 w0: &crate::model::GpuTensor,
11504 w1: &crate::model::GpuTensor,
11505 aq: &CudaSlice<i8>,
11506 ad: &CudaSlice<f32>,
11507 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11508 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
11514 return Ok(Some(self.e4m3_fused2_core(
11515 p0.0,
11516 p1.0,
11517 aq,
11518 ad,
11519 w0.in_features(),
11520 p0.1,
11521 p1.1,
11522 p0.2,
11523 p0.3,
11524 p1.3,
11525 )?));
11526 }
11527 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
11528 return Ok(None);
11529 };
11530 Ok(Some(self.q8_fused2_core(
11531 p0.0,
11532 p1.0,
11533 aq,
11534 ad,
11535 w0.in_features(),
11536 p0.1,
11537 p1.1,
11538 p0.2,
11539 )?))
11540 }
11541
11542 #[allow(clippy::too_many_arguments)]
11543 fn q8_fused2_core(
11544 &self,
11545 b0: &CudaSlice<u8>,
11546 b1: &CudaSlice<u8>,
11547 aq: &CudaSlice<i8>,
11548 ad: &CudaSlice<f32>,
11549 in_f: usize,
11550 out0: usize,
11551 out1: usize,
11552 row_bytes: usize,
11553 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11554 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11556 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11557 let f = self.func("qmatvec_q8_0_mmvq_fused2");
11558 let mut y0 = self.alloc_uninit::<f32>(out0)?;
11559 let mut y1 = self.alloc_uninit::<f32>(out1)?;
11560 let cfg = LaunchConfig {
11561 grid_dim: (nb0 + nb1, 1, 1),
11562 block_dim: (32, ROWS_PER_BLOCK, 1),
11563 shared_mem_bytes: 0,
11564 };
11565 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
11566 let __s_b = self.gpu.stream();
11567 let mut b = __s_b.launch_builder(&f);
11568 b.arg(b0)
11569 .arg(b1)
11570 .arg(aq)
11571 .arg(ad)
11572 .arg(&mut y0)
11573 .arg(&mut y1)
11574 .arg(&inf)
11575 .arg(&o0)
11576 .arg(&o1)
11577 .arg(&rbl);
11578 unsafe {
11579 b.launch(cfg)?;
11580 }
11581 Ok((y0, y1))
11582 }
11583
11584 pub fn matmul_q8_fused2_x(
11590 &self,
11591 w0: &crate::model::GpuTensor,
11592 w1: &crate::model::GpuTensor,
11593 x: &CudaSlice<f32>,
11594 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11595 if !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
11596 return Ok(None);
11597 }
11598 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
11599 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
11600 return Ok(Some(self.e4m3_fused2_core(
11601 p0.0,
11602 p1.0,
11603 &aq,
11604 &ad,
11605 w0.in_features(),
11606 p0.1,
11607 p1.1,
11608 p0.2,
11609 p0.3,
11610 p1.3,
11611 )?));
11612 }
11613 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
11614 return Ok(None);
11615 };
11616 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
11617 Ok(Some(self.q8_fused2_core(
11618 p0.0,
11619 p1.0,
11620 &aq,
11621 &ad,
11622 w0.in_features(),
11623 p0.1,
11624 p1.1,
11625 p0.2,
11626 )?))
11627 }
11628
11629 #[allow(clippy::too_many_arguments)]
11632 pub fn qmatvec_q8_fused2_raw(
11633 &self,
11634 b0: &CudaSlice<u8>,
11635 b1: &CudaSlice<u8>,
11636 x: &CudaSlice<f32>,
11637 in_f: usize,
11638 out0: usize,
11639 out1: usize,
11640 row_bytes: usize,
11641 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11642 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
11643 self.q8_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes)
11644 }
11645
11646 pub fn matmul_q4_fused3(
11652 &self,
11653 w0: &crate::model::GpuTensor,
11654 w1: &crate::model::GpuTensor,
11655 w2: &crate::model::GpuTensor,
11656 aq: &CudaSlice<i8>,
11657 ad: &CudaSlice<f32>,
11658 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11659 {
11660 use crate::model::GpuTensor;
11661 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11662 match w {
11663 GpuTensor::Quant {
11664 qtype, row_bytes, ..
11665 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11666 _ => None,
11667 }
11668 };
11669 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
11670 return Ok(None);
11671 };
11672 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
11673 return Ok(None);
11674 }
11675 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11679 match w {
11680 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11681 Some(m) => (m, true),
11682 None => (bytes, *rp),
11683 },
11684 _ => unreachable!(),
11685 }
11686 }
11687 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
11688 if rp0 != rp1 || rp1 != rp2 {
11689 return Ok(None);
11690 }
11691 let rp = rp0;
11692 let rpb: u32 = 4;
11693 let mr1 = rp && Self::q40_mr1_on();
11697 let nb = |o: usize| {
11698 if mr1 {
11699 (o as u32).div_ceil(rpb)
11700 } else {
11701 (o as u32).div_ceil(2).div_ceil(rpb)
11702 }
11703 };
11704 let grid = nb(o0) + nb(o1) + nb(o2);
11705 let mut y0 = self.alloc_uninit::<f32>(o0)?;
11706 let mut y1 = self.alloc_uninit::<f32>(o1)?;
11707 let mut y2 = self.alloc_uninit::<f32>(o2)?;
11708 let f = self.func(if mr1 {
11709 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
11710 } else if rp {
11711 "qmatvec_q4_0_mmvq_fused3_rp"
11712 } else {
11713 "qmatvec_q4_0_mmvq_fused3"
11714 });
11715 let cfg = LaunchConfig {
11716 grid_dim: (grid, 1, 1),
11717 block_dim: (32, rpb, 1),
11718 shared_mem_bytes: 0,
11719 };
11720 let inf = w0.in_features() as i32;
11721 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
11722 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
11723 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11726 {
11727 use cudarc::driver::{DevicePtr, DevicePtrMut};
11728 let s = &self.gpu.stream();
11729 let (p0, _g0) = b0.device_ptr(s);
11730 let (p1, _g1) = b1.device_ptr(s);
11731 let (p2, _g2) = b2.device_ptr(s);
11732 let (paq, _g3) = aq.device_ptr(s);
11733 let (pad, _g4) = ad.device_ptr(s);
11734 let (py0, _g5) = y0.device_ptr_mut(s);
11735 let (py1, _g6) = y1.device_ptr_mut(s);
11736 let (py2, _g7) = y2.device_ptr_mut(s);
11737 let mut ps = [
11738 &p0 as *const _ as *mut std::ffi::c_void,
11739 &p1 as *const _ as *mut _,
11740 &p2 as *const _ as *mut _,
11741 &paq as *const _ as *mut _,
11742 &pad as *const _ as *mut _,
11743 &py0 as *const _ as *mut _,
11744 &py1 as *const _ as *mut _,
11745 &py2 as *const _ as *mut _,
11746 &inf as *const _ as *mut _,
11747 &oo0 as *const _ as *mut _,
11748 &oo1 as *const _ as *mut _,
11749 &oo2 as *const _ as *mut _,
11750 &r0 as *const _ as *mut _,
11751 &r1 as *const _ as *mut _,
11752 &r2 as *const _ as *mut _,
11753 ];
11754 unsafe {
11755 self.launch_pdl(
11756 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
11757 (grid, 1, 1),
11758 (32, rpb, 1),
11759 &mut ps,
11760 )?;
11761 }
11762 }
11763 return Ok(Some((y0, y1, y2)));
11764 }
11765 let __s_b = self.gpu.stream();
11766 let mut b = __s_b.launch_builder(&f);
11767 b.arg(b0)
11768 .arg(b1)
11769 .arg(b2)
11770 .arg(aq)
11771 .arg(ad)
11772 .arg(&mut y0)
11773 .arg(&mut y1)
11774 .arg(&mut y2)
11775 .arg(&inf)
11776 .arg(&oo0)
11777 .arg(&oo1)
11778 .arg(&oo2)
11779 .arg(&r0)
11780 .arg(&r1)
11781 .arg(&r2);
11782 unsafe {
11783 b.launch(cfg)?;
11784 }
11785 Ok(Some((y0, y1, y2)))
11786 }
11787
11788 #[allow(clippy::too_many_arguments)]
11791 pub fn matmul_q4_fused3_into(
11792 &self,
11793 w0: &crate::model::GpuTensor,
11794 w1: &crate::model::GpuTensor,
11795 w2: &crate::model::GpuTensor,
11796 aq: &CudaSlice<i8>,
11797 ad: &CudaSlice<f32>,
11798 y0: &mut CudaSlice<f32>,
11799 y1: &mut CudaSlice<f32>,
11800 y2: &mut CudaSlice<f32>,
11801 ) -> Result<bool, Box<dyn std::error::Error>> {
11802 use crate::model::GpuTensor;
11803 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11804 match w {
11805 GpuTensor::Quant {
11806 qtype, row_bytes, ..
11807 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11808 _ => None,
11809 }
11810 };
11811 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
11812 return Ok(false);
11813 };
11814 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
11815 return Ok(false);
11816 }
11817 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11818 match w {
11819 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11820 Some(m) => (m, true),
11821 None => (bytes, *rp),
11822 },
11823 _ => unreachable!(),
11824 }
11825 }
11826 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
11827 if rp0 != rp1 || rp1 != rp2 {
11828 return Ok(false);
11829 }
11830 let rp = rp0;
11831 let rpb: u32 = 4;
11832 let mr1 = rp && Self::q40_mr1_on();
11833 let nb = |o: usize| {
11834 if mr1 {
11835 (o as u32).div_ceil(rpb)
11836 } else {
11837 (o as u32).div_ceil(2).div_ceil(rpb)
11838 }
11839 };
11840 let grid = nb(o0) + nb(o1) + nb(o2);
11841 debug_assert!(y0.len() >= o0 && y1.len() >= o1 && y2.len() >= o2);
11842 let f = self.func(if mr1 {
11843 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
11844 } else if rp {
11845 "qmatvec_q4_0_mmvq_fused3_rp"
11846 } else {
11847 "qmatvec_q4_0_mmvq_fused3"
11848 });
11849 let cfg = LaunchConfig {
11850 grid_dim: (grid, 1, 1),
11851 block_dim: (32, rpb, 1),
11852 shared_mem_bytes: 0,
11853 };
11854 let inf = w0.in_features() as i32;
11855 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
11856 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
11857 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11859 use cudarc::driver::{DevicePtr, DevicePtrMut};
11860 let s = &self.gpu.stream();
11861 let (p0, _g0) = b0.device_ptr(s);
11862 let (p1, _g1) = b1.device_ptr(s);
11863 let (p2, _g2) = b2.device_ptr(s);
11864 let (paq, _g3) = aq.device_ptr(s);
11865 let (pad, _g4) = ad.device_ptr(s);
11866 let (py0, _g5) = y0.device_ptr_mut(s);
11867 let (py1, _g6) = y1.device_ptr_mut(s);
11868 let (py2, _g7) = y2.device_ptr_mut(s);
11869 let mut ps = [
11870 &p0 as *const _ as *mut std::ffi::c_void,
11871 &p1 as *const _ as *mut _,
11872 &p2 as *const _ as *mut _,
11873 &paq as *const _ as *mut _,
11874 &pad as *const _ as *mut _,
11875 &py0 as *const _ as *mut _,
11876 &py1 as *const _ as *mut _,
11877 &py2 as *const _ as *mut _,
11878 &inf as *const _ as *mut _,
11879 &oo0 as *const _ as *mut _,
11880 &oo1 as *const _ as *mut _,
11881 &oo2 as *const _ as *mut _,
11882 &r0 as *const _ as *mut _,
11883 &r1 as *const _ as *mut _,
11884 &r2 as *const _ as *mut _,
11885 ];
11886 unsafe {
11887 self.launch_pdl(
11888 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
11889 (grid, 1, 1),
11890 (32, rpb, 1),
11891 &mut ps,
11892 )?;
11893 }
11894 return Ok(true);
11895 }
11896 let __s_b = self.gpu.stream();
11897 let mut b = __s_b.launch_builder(&f);
11898 b.arg(b0)
11899 .arg(b1)
11900 .arg(b2)
11901 .arg(aq)
11902 .arg(ad)
11903 .arg(&mut *y0)
11904 .arg(&mut *y1)
11905 .arg(&mut *y2)
11906 .arg(&inf)
11907 .arg(&oo0)
11908 .arg(&oo1)
11909 .arg(&oo2)
11910 .arg(&r0)
11911 .arg(&r1)
11912 .arg(&r2);
11913 unsafe {
11914 b.launch(cfg)?;
11915 }
11916 Ok(true)
11917 }
11918
11919 pub fn matmul_q4_fused2(
11921 &self,
11922 w0: &crate::model::GpuTensor,
11923 w1: &crate::model::GpuTensor,
11924 aq: &CudaSlice<i8>,
11925 ad: &CudaSlice<f32>,
11926 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11927 use crate::model::GpuTensor;
11928 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11929 match w {
11930 GpuTensor::Quant {
11931 qtype, row_bytes, ..
11932 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11933 _ => None,
11934 }
11935 };
11936 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
11937 return Ok(None);
11938 };
11939 if w0.in_features() != w1.in_features() {
11940 return Ok(None);
11941 }
11942 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11944 match w {
11945 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11946 Some(m) => (m, true),
11947 None => (bytes, *rp),
11948 },
11949 _ => unreachable!(),
11950 }
11951 }
11952 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11953 if rp0 != rp1 {
11954 return Ok(None);
11955 }
11956 let rp = rp0;
11957 let rpb: u32 = 4;
11958 let mr1 = rp && Self::q40_mr1_on();
11960 let nb = |o: usize| {
11961 if mr1 {
11962 (o as u32).div_ceil(rpb)
11963 } else {
11964 (o as u32).div_ceil(2).div_ceil(rpb)
11965 }
11966 };
11967 let grid = nb(o0) + nb(o1);
11968 let mut y0 = self.alloc_uninit::<f32>(o0)?;
11969 let mut y1 = self.alloc_uninit::<f32>(o1)?;
11970 let f = self.func(if mr1 {
11971 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
11972 } else if rp {
11973 "qmatvec_q4_0_mmvq_fused2_rp"
11974 } else {
11975 "qmatvec_q4_0_mmvq_fused2"
11976 });
11977 let cfg = LaunchConfig {
11978 grid_dim: (grid, 1, 1),
11979 block_dim: (32, rpb, 1),
11980 shared_mem_bytes: 0,
11981 };
11982 let inf = w0.in_features() as i32;
11983 let (oo0, oo1) = (o0 as i32, o1 as i32);
11984 let (r0, r1) = (rb0 as i64, rb1 as i64);
11985 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11987 {
11988 use cudarc::driver::{DevicePtr, DevicePtrMut};
11989 let s = &self.gpu.stream();
11990 let (p0, _g0) = b0.device_ptr(s);
11991 let (p1, _g1) = b1.device_ptr(s);
11992 let (paq, _g2) = aq.device_ptr(s);
11993 let (pad, _g3) = ad.device_ptr(s);
11994 let (py0, _g4) = y0.device_ptr_mut(s);
11995 let (py1, _g5) = y1.device_ptr_mut(s);
11996 let mut ps = [
11997 &p0 as *const _ as *mut std::ffi::c_void,
11998 &p1 as *const _ as *mut _,
11999 &paq as *const _ as *mut _,
12000 &pad as *const _ as *mut _,
12001 &py0 as *const _ as *mut _,
12002 &py1 as *const _ as *mut _,
12003 &inf as *const _ as *mut _,
12004 &oo0 as *const _ as *mut _,
12005 &oo1 as *const _ as *mut _,
12006 &r0 as *const _ as *mut _,
12007 &r1 as *const _ as *mut _,
12008 ];
12009 unsafe {
12010 self.launch_pdl(
12011 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
12012 (grid, 1, 1),
12013 (32, rpb, 1),
12014 &mut ps,
12015 )?;
12016 }
12017 }
12018 return Ok(Some((y0, y1)));
12019 }
12020 let __s_b = self.gpu.stream();
12021 let mut b = __s_b.launch_builder(&f);
12022 b.arg(b0)
12023 .arg(b1)
12024 .arg(aq)
12025 .arg(ad)
12026 .arg(&mut y0)
12027 .arg(&mut y1)
12028 .arg(&inf)
12029 .arg(&oo0)
12030 .arg(&oo1)
12031 .arg(&r0)
12032 .arg(&r1);
12033 unsafe {
12034 b.launch(cfg)?;
12035 }
12036 Ok(Some((y0, y1)))
12037 }
12038
12039 pub fn matmul_q4_fused2_into(
12041 &self,
12042 w0: &crate::model::GpuTensor,
12043 w1: &crate::model::GpuTensor,
12044 aq: &CudaSlice<i8>,
12045 ad: &CudaSlice<f32>,
12046 y0: &mut CudaSlice<f32>,
12047 y1: &mut CudaSlice<f32>,
12048 ) -> Result<bool, Box<dyn std::error::Error>> {
12049 use crate::model::GpuTensor;
12050 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
12051 match w {
12052 GpuTensor::Quant {
12053 qtype, row_bytes, ..
12054 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
12055 _ => None,
12056 }
12057 };
12058 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
12059 return Ok(false);
12060 };
12061 if w0.in_features() != w1.in_features() {
12062 return Ok(false);
12063 }
12064 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
12065 match w {
12066 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
12067 Some(m) => (m, true),
12068 None => (bytes, *rp),
12069 },
12070 _ => unreachable!(),
12071 }
12072 }
12073 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
12074 if rp0 != rp1 {
12075 return Ok(false);
12076 }
12077 let rp = rp0;
12078 let rpb: u32 = 4;
12079 let mr1 = rp && Self::q40_mr1_on();
12080 let nb = |o: usize| {
12081 if mr1 {
12082 (o as u32).div_ceil(rpb)
12083 } else {
12084 (o as u32).div_ceil(2).div_ceil(rpb)
12085 }
12086 };
12087 let grid = nb(o0) + nb(o1);
12088 debug_assert!(y0.len() >= o0 && y1.len() >= o1);
12089 let f = self.func(if mr1 {
12090 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
12091 } else if rp {
12092 "qmatvec_q4_0_mmvq_fused2_rp"
12093 } else {
12094 "qmatvec_q4_0_mmvq_fused2"
12095 });
12096 let cfg = LaunchConfig {
12097 grid_dim: (grid, 1, 1),
12098 block_dim: (32, rpb, 1),
12099 shared_mem_bytes: 0,
12100 };
12101 let inf = w0.in_features() as i32;
12102 let (oo0, oo1) = (o0 as i32, o1 as i32);
12103 let (r0, r1) = (rb0 as i64, rb1 as i64);
12104 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
12106 use cudarc::driver::{DevicePtr, DevicePtrMut};
12107 let s = &self.gpu.stream();
12108 let (p0, _g0) = b0.device_ptr(s);
12109 let (p1, _g1) = b1.device_ptr(s);
12110 let (paq, _g2) = aq.device_ptr(s);
12111 let (pad, _g3) = ad.device_ptr(s);
12112 let (py0, _g4) = y0.device_ptr_mut(s);
12113 let (py1, _g5) = y1.device_ptr_mut(s);
12114 let mut ps = [
12115 &p0 as *const _ as *mut std::ffi::c_void,
12116 &p1 as *const _ as *mut _,
12117 &paq as *const _ as *mut _,
12118 &pad as *const _ as *mut _,
12119 &py0 as *const _ as *mut _,
12120 &py1 as *const _ as *mut _,
12121 &inf as *const _ as *mut _,
12122 &oo0 as *const _ as *mut _,
12123 &oo1 as *const _ as *mut _,
12124 &r0 as *const _ as *mut _,
12125 &r1 as *const _ as *mut _,
12126 ];
12127 unsafe {
12128 self.launch_pdl(
12129 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
12130 (grid, 1, 1),
12131 (32, rpb, 1),
12132 &mut ps,
12133 )?;
12134 }
12135 return Ok(true);
12136 }
12137 let __s_b = self.gpu.stream();
12138 let mut b = __s_b.launch_builder(&f);
12139 b.arg(b0)
12140 .arg(b1)
12141 .arg(aq)
12142 .arg(ad)
12143 .arg(&mut *y0)
12144 .arg(&mut *y1)
12145 .arg(&inf)
12146 .arg(&oo0)
12147 .arg(&oo1)
12148 .arg(&r0)
12149 .arg(&r1);
12150 unsafe {
12151 b.launch(cfg)?;
12152 }
12153 Ok(true)
12154 }
12155
12156 pub fn matmul_q4_fused2_batched(
12161 &self,
12162 w0: &crate::model::GpuTensor,
12163 w1: &crate::model::GpuTensor,
12164 aq: &CudaSlice<i8>,
12165 ad: &CudaSlice<f32>,
12166 m: usize,
12167 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
12168 use crate::model::GpuTensor;
12169 if m < 2 || m > 8 {
12170 return Ok(None);
12171 }
12172 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
12173 match w {
12174 GpuTensor::Quant {
12175 qtype, row_bytes, ..
12176 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
12177 _ => None,
12178 }
12179 };
12180 let (Some((rb0, o0)), Some((_rb1, o1))) = (q4(w0), q4(w1)) else {
12181 return Ok(None);
12182 };
12183 if w0.in_features() != w1.in_features() {
12184 return Ok(None);
12185 }
12186 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
12187 match w {
12188 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
12189 Some(mr) => (mr, true),
12190 None => (bytes, *rp),
12191 },
12192 _ => unreachable!(),
12193 }
12194 }
12195 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
12196 if !rp0 || !rp1 {
12197 return Ok(None);
12198 }
12199 let mcols = Self::batched_mcols(m);
12200 let rpb: u32 = 4;
12201 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
12202 let grid = nb(o0) + nb(o1);
12203 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
12204 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
12205 let f = self.func(match mcols {
12206 2 => "qmatvec_q4_0_mmvq_b2_f2_rp",
12207 4 => "qmatvec_q4_0_mmvq_b4_f2_rp",
12208 _ => "qmatvec_q4_0_mmvq_b8_f2_rp",
12209 });
12210 let cfg = LaunchConfig {
12211 grid_dim: (grid, 1, 1),
12212 block_dim: (32, rpb, 1),
12213 shared_mem_bytes: 0,
12214 };
12215 let inf = w0.in_features() as i32;
12216 let (oo0, oo1, mi) = (o0 as i32, o1 as i32, m as i32);
12217 let rb = rb0 as i64;
12218 let __s_b = self.gpu.stream();
12219 let mut b = __s_b.launch_builder(&f);
12220 b.arg(b0)
12221 .arg(b1)
12222 .arg(aq)
12223 .arg(ad)
12224 .arg(&mut y0)
12225 .arg(&mut y1)
12226 .arg(&inf)
12227 .arg(&oo0)
12228 .arg(&oo1)
12229 .arg(&mi)
12230 .arg(&rb);
12231 unsafe {
12232 b.launch(cfg)?;
12233 }
12234 Ok(Some((y0, y1)))
12235 }
12236
12237 #[allow(clippy::too_many_arguments)]
12240 pub fn matmul_q4_fused3_batched(
12241 &self,
12242 w0: &crate::model::GpuTensor,
12243 w1: &crate::model::GpuTensor,
12244 w2: &crate::model::GpuTensor,
12245 aq: &CudaSlice<i8>,
12246 ad: &CudaSlice<f32>,
12247 m: usize,
12248 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
12249 {
12250 use crate::model::GpuTensor;
12251 if m < 2 || m > 8 {
12252 return Ok(None);
12253 }
12254 let q4 = |w: &GpuTensor| -> Option<usize> {
12255 match w {
12256 GpuTensor::Quant { qtype, .. } if *qtype == QT_Q4_0 => Some(w.out_features()),
12257 _ => None,
12258 }
12259 };
12260 let (Some(o0), Some(o1), Some(o2)) = (q4(w0), q4(w1), q4(w2)) else {
12261 return Ok(None);
12262 };
12263 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
12264 return Ok(None);
12265 }
12266 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
12267 match w {
12268 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
12269 Some(mr) => (mr, true),
12270 None => (bytes, *rp),
12271 },
12272 _ => unreachable!(),
12273 }
12274 }
12275 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
12276 if !rp0 || !rp1 || !rp2 {
12277 return Ok(None);
12278 }
12279 let mcols = Self::batched_mcols(m);
12280 let rpb: u32 = 4;
12281 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
12282 let grid = nb(o0) + nb(o1) + nb(o2);
12283 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
12284 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
12285 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
12286 let f = self.func(match mcols {
12287 2 => "qmatvec_q4_0_mmvq_b2_f3_rp",
12288 4 => "qmatvec_q4_0_mmvq_b4_f3_rp",
12289 _ => "qmatvec_q4_0_mmvq_b8_f3_rp",
12290 });
12291 let cfg = LaunchConfig {
12292 grid_dim: (grid, 1, 1),
12293 block_dim: (32, rpb, 1),
12294 shared_mem_bytes: 0,
12295 };
12296 let inf = w0.in_features() as i32;
12297 let (oo0, oo1, oo2, mi) = (o0 as i32, o1 as i32, o2 as i32, m as i32);
12298 let rb = 0i64;
12299 let __s_b = self.gpu.stream();
12300 let mut b = __s_b.launch_builder(&f);
12301 b.arg(b0)
12302 .arg(b1)
12303 .arg(b2)
12304 .arg(aq)
12305 .arg(ad)
12306 .arg(&mut y0)
12307 .arg(&mut y1)
12308 .arg(&mut y2)
12309 .arg(&inf)
12310 .arg(&oo0)
12311 .arg(&oo1)
12312 .arg(&oo2)
12313 .arg(&mi)
12314 .arg(&rb);
12315 unsafe {
12316 b.launch(cfg)?;
12317 }
12318 Ok(Some((y0, y1, y2)))
12319 }
12320
12321 pub fn matmul_q8_fused3(
12322 &self,
12323 w0: &crate::model::GpuTensor,
12324 w1: &crate::model::GpuTensor,
12325 w2: &crate::model::GpuTensor,
12326 aq: &CudaSlice<i8>,
12327 ad: &CudaSlice<f32>,
12328 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
12329 {
12330 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
12333 return Ok(Some(self.e4m3_fused3_core(
12334 p0.0,
12335 p1.0,
12336 p2.0,
12337 aq,
12338 ad,
12339 w0.in_features(),
12340 p0.1,
12341 p1.1,
12342 p2.1,
12343 p0.2,
12344 p0.3,
12345 p1.3,
12346 p2.3,
12347 )?));
12348 }
12349 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
12350 return Ok(None);
12351 };
12352 Ok(Some(self.q8_fused3_core(
12353 p0.0,
12354 p1.0,
12355 p2.0,
12356 aq,
12357 ad,
12358 w0.in_features(),
12359 p0.1,
12360 p1.1,
12361 p2.1,
12362 p0.2,
12363 )?))
12364 }
12365
12366 #[allow(clippy::too_many_arguments)]
12367 fn q8_fused3_core(
12368 &self,
12369 b0: &CudaSlice<u8>,
12370 b1: &CudaSlice<u8>,
12371 b2: &CudaSlice<u8>,
12372 aq: &CudaSlice<i8>,
12373 ad: &CudaSlice<f32>,
12374 in_f: usize,
12375 out0: usize,
12376 out1: usize,
12377 out2: usize,
12378 row_bytes: usize,
12379 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12380 const ROWS_PER_BLOCK: u32 = 4;
12381 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12382 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12383 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12384 let f = self.func("qmatvec_q8_0_mmvq_fused3");
12385 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12386 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12387 let mut y2 = self.alloc_uninit::<f32>(out2)?;
12388 let cfg = LaunchConfig {
12389 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12390 block_dim: (32, ROWS_PER_BLOCK, 1),
12391 shared_mem_bytes: 0,
12392 };
12393 let (inf, o0, o1, o2, rbl) = (
12394 in_f as i32,
12395 out0 as i32,
12396 out1 as i32,
12397 out2 as i32,
12398 row_bytes as i64,
12399 );
12400 let __s_b = self.gpu.stream();
12401 let mut b = __s_b.launch_builder(&f);
12402 b.arg(b0)
12403 .arg(b1)
12404 .arg(b2)
12405 .arg(aq)
12406 .arg(ad)
12407 .arg(&mut y0)
12408 .arg(&mut y1)
12409 .arg(&mut y2)
12410 .arg(&inf)
12411 .arg(&o0)
12412 .arg(&o1)
12413 .arg(&o2)
12414 .arg(&rbl);
12415 unsafe {
12416 b.launch(cfg)?;
12417 }
12418 Ok((y0, y1, y2))
12419 }
12420
12421 #[allow(clippy::too_many_arguments)]
12423 pub fn qmatvec_q8_fused3_raw(
12424 &self,
12425 b0: &CudaSlice<u8>,
12426 b1: &CudaSlice<u8>,
12427 b2: &CudaSlice<u8>,
12428 x: &CudaSlice<f32>,
12429 in_f: usize,
12430 out0: usize,
12431 out1: usize,
12432 out2: usize,
12433 row_bytes: usize,
12434 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12435 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
12436 self.q8_fused3_core(b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes)
12437 }
12438
12439 pub fn matmul_q8_fused2_t(
12450 &self,
12451 w0: &crate::model::GpuTensor,
12452 w1: &crate::model::GpuTensor,
12453 aq: &CudaSlice<i8>,
12454 ad: &CudaSlice<f32>,
12455 m: usize,
12456 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
12457 if !(2..=8).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
12461 return Ok(None);
12462 }
12463 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
12466 if m > 4 && !Self::b8_enabled() {
12467 return Ok(None);
12468 }
12469 return Ok(Some(self.e4m3_fused2_t_core(
12470 p0.0,
12471 p1.0,
12472 aq,
12473 ad,
12474 m,
12475 w0.in_features(),
12476 p0.1,
12477 p1.1,
12478 p0.2,
12479 p0.3,
12480 p1.3,
12481 )?));
12482 }
12483 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
12484 return Ok(None);
12485 };
12486 Ok(Some(self.q8_fused2_t_core(
12487 p0.0,
12488 p1.0,
12489 aq,
12490 ad,
12491 m,
12492 w0.in_features(),
12493 p0.1,
12494 p1.1,
12495 p0.2,
12496 )?))
12497 }
12498
12499 #[allow(clippy::too_many_arguments)]
12500 fn q8_fused2_t_core(
12501 &self,
12502 b0: &CudaSlice<u8>,
12503 b1: &CudaSlice<u8>,
12504 aq: &CudaSlice<i8>,
12505 ad: &CudaSlice<f32>,
12506 m: usize,
12507 in_f: usize,
12508 out0: usize,
12509 out1: usize,
12510 row_bytes: usize,
12511 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12512 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12514 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12515 let f = self.func(match Self::batched_mcols(m) {
12516 2 => "qmatvec_q8_0_mmvq_fused2_b2",
12517 4 => "qmatvec_q8_0_mmvq_fused2_b4",
12518 _ => "qmatvec_q8_0_mmvq_fused2_b8",
12520 });
12521 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12522 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12523 let cfg = LaunchConfig {
12524 grid_dim: (nb0 + nb1, 1, 1),
12525 block_dim: (32, ROWS_PER_BLOCK, 1),
12526 shared_mem_bytes: 0,
12527 };
12528 let (inf, o0, o1, mi, rbl) = (
12529 in_f as i32,
12530 out0 as i32,
12531 out1 as i32,
12532 m as i32,
12533 row_bytes as i64,
12534 );
12535 let __s_b = self.gpu.stream();
12536 let mut b = __s_b.launch_builder(&f);
12537 b.arg(b0)
12538 .arg(b1)
12539 .arg(aq)
12540 .arg(ad)
12541 .arg(&mut y0)
12542 .arg(&mut y1)
12543 .arg(&inf)
12544 .arg(&o0)
12545 .arg(&o1)
12546 .arg(&mi)
12547 .arg(&rbl);
12548 unsafe {
12549 b.launch(cfg)?;
12550 }
12551 Ok((y0, y1))
12552 }
12553
12554 #[allow(clippy::too_many_arguments)]
12557 pub fn qmatvec_q8_fused2_t_raw(
12558 &self,
12559 b0: &CudaSlice<u8>,
12560 b1: &CudaSlice<u8>,
12561 x: &CudaSlice<f32>,
12562 m: usize,
12563 in_f: usize,
12564 out0: usize,
12565 out1: usize,
12566 row_bytes: usize,
12567 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12568 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12569 self.q8_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes)
12570 }
12571
12572 #[allow(clippy::too_many_arguments)]
12575 pub fn matmul_q8_fused3_t(
12576 &self,
12577 w0: &crate::model::GpuTensor,
12578 w1: &crate::model::GpuTensor,
12579 w2: &crate::model::GpuTensor,
12580 aq: &CudaSlice<i8>,
12581 ad: &CudaSlice<f32>,
12582 m: usize,
12583 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
12584 {
12585 if !(2..=4).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
12586 return Ok(None);
12587 }
12588 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
12589 return Ok(Some(self.e4m3_fused3_t_core(
12590 p0.0,
12591 p1.0,
12592 p2.0,
12593 aq,
12594 ad,
12595 m,
12596 w0.in_features(),
12597 p0.1,
12598 p1.1,
12599 p2.1,
12600 p0.2,
12601 p0.3,
12602 p1.3,
12603 p2.3,
12604 )?));
12605 }
12606 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
12607 return Ok(None);
12608 };
12609 Ok(Some(self.q8_fused3_t_core(
12610 p0.0,
12611 p1.0,
12612 p2.0,
12613 aq,
12614 ad,
12615 m,
12616 w0.in_features(),
12617 p0.1,
12618 p1.1,
12619 p2.1,
12620 p0.2,
12621 )?))
12622 }
12623
12624 #[allow(clippy::too_many_arguments)]
12625 fn q8_fused3_t_core(
12626 &self,
12627 b0: &CudaSlice<u8>,
12628 b1: &CudaSlice<u8>,
12629 b2: &CudaSlice<u8>,
12630 aq: &CudaSlice<i8>,
12631 ad: &CudaSlice<f32>,
12632 m: usize,
12633 in_f: usize,
12634 out0: usize,
12635 out1: usize,
12636 out2: usize,
12637 row_bytes: usize,
12638 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12639 const ROWS_PER_BLOCK: u32 = 4;
12640 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12641 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12642 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12643 let f = self.func(if Self::batched_mcols(m) == 2 {
12644 "qmatvec_q8_0_mmvq_fused3_b2"
12645 } else {
12646 "qmatvec_q8_0_mmvq_fused3_b4"
12647 });
12648 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12649 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12650 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
12651 let cfg = LaunchConfig {
12652 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12653 block_dim: (32, ROWS_PER_BLOCK, 1),
12654 shared_mem_bytes: 0,
12655 };
12656 let (inf, o0, o1, o2, mi, rbl) = (
12657 in_f as i32,
12658 out0 as i32,
12659 out1 as i32,
12660 out2 as i32,
12661 m as i32,
12662 row_bytes as i64,
12663 );
12664 let __s_b = self.gpu.stream();
12665 let mut b = __s_b.launch_builder(&f);
12666 b.arg(b0)
12667 .arg(b1)
12668 .arg(b2)
12669 .arg(aq)
12670 .arg(ad)
12671 .arg(&mut y0)
12672 .arg(&mut y1)
12673 .arg(&mut y2)
12674 .arg(&inf)
12675 .arg(&o0)
12676 .arg(&o1)
12677 .arg(&o2)
12678 .arg(&mi)
12679 .arg(&rbl);
12680 unsafe {
12681 b.launch(cfg)?;
12682 }
12683 Ok((y0, y1, y2))
12684 }
12685
12686 #[allow(clippy::too_many_arguments)]
12688 pub fn qmatvec_q8_fused3_t_raw(
12689 &self,
12690 b0: &CudaSlice<u8>,
12691 b1: &CudaSlice<u8>,
12692 b2: &CudaSlice<u8>,
12693 x: &CudaSlice<f32>,
12694 m: usize,
12695 in_f: usize,
12696 out0: usize,
12697 out1: usize,
12698 out2: usize,
12699 row_bytes: usize,
12700 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12701 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12702 self.q8_fused3_t_core(b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes)
12703 }
12704
12705 pub fn q8_ffn_fuse2_on(&self) -> bool {
12709 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12710 *ON.get_or_init(|| std::env::var("MEMRA_Q8_FFN_FUSE2").as_deref() != Ok("0"))
12711 }
12712
12713 #[allow(clippy::type_complexity)]
12719 fn q8_fused_params<'w, const N: usize>(
12720 &self,
12721 ws: &[&'w crate::model::GpuTensor; N],
12722 ) -> Option<[(&'w CudaSlice<u8>, usize, usize); N]> {
12723 use crate::model::GpuTensor;
12724 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
12725 return None;
12726 }
12727 if std::env::var("MEMRA_Q8_DUAL").is_ok_and(|v| v == "0") {
12728 return None;
12729 }
12730 let in_f = ws[0].in_features();
12731 let mut out: [Option<(&CudaSlice<u8>, usize, usize)>; N] = [None; N];
12732 for (i, w) in ws.iter().enumerate() {
12733 match w {
12734 GpuTensor::Quant {
12735 bytes,
12736 qtype,
12737 row_bytes,
12738 scale,
12739 ..
12740 } if *qtype == QT_Q8_0 && *scale == 1.0 && w.in_features() == in_f => {
12741 out[i] = Some((bytes, w.out_features(), *row_bytes))
12742 }
12743 _ => return None,
12744 }
12745 }
12746 Some(out.map(|o| o.unwrap()))
12747 }
12748
12749 pub fn e4m3_dual_on(&self) -> bool {
12752 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12753 *ON.get_or_init(|| std::env::var("MEMRA_E4M3_DUAL").as_deref() != Ok("0"))
12754 }
12755
12756 #[allow(clippy::type_complexity)]
12768 fn e4m3_fused_params<'w, const N: usize>(
12769 &self,
12770 ws: &[&'w crate::model::GpuTensor; N],
12771 ) -> Option<[(&'w CudaSlice<u8>, usize, usize, f32); N]> {
12772 use crate::model::GpuTensor;
12773 if !self.e4m3_dual_on() {
12774 return None;
12775 }
12776 let in_f = ws[0].in_features();
12777 let mut out: [Option<(&CudaSlice<u8>, usize, usize, f32)>; N] = [None; N];
12778 for (i, w) in ws.iter().enumerate() {
12779 match w {
12780 GpuTensor::Quant {
12781 bytes,
12782 qtype,
12783 row_bytes,
12784 scale,
12785 rp,
12786 rp4,
12787 ..
12788 } if *qtype == QT_F8_E4M3
12789 && w.in_features() == in_f
12790 && *row_bytes == in_f
12791 && !*rp
12792 && rp4.is_none() =>
12793 {
12794 out[i] = Some((bytes, w.out_features(), *row_bytes, *scale))
12795 }
12796 _ => return None,
12797 }
12798 }
12799 Some(out.map(|o| o.unwrap()))
12800 }
12801
12802 #[allow(clippy::too_many_arguments)]
12806 fn e4m3_fused2_core(
12807 &self,
12808 b0: &CudaSlice<u8>,
12809 b1: &CudaSlice<u8>,
12810 aq: &CudaSlice<i8>,
12811 ad: &CudaSlice<f32>,
12812 in_f: usize,
12813 out0: usize,
12814 out1: usize,
12815 row_bytes: usize,
12816 ws0: f32,
12817 ws1: f32,
12818 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12819 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12821 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12822 let f = self.func("qmatvec_e4m3_mmvq_fused2");
12823 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12824 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12825 let cfg = LaunchConfig {
12826 grid_dim: (nb0 + nb1, 1, 1),
12827 block_dim: (32, ROWS_PER_BLOCK, 1),
12828 shared_mem_bytes: 0,
12829 };
12830 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
12831 let __s_b = self.gpu.stream();
12832 let mut b = __s_b.launch_builder(&f);
12833 b.arg(b0)
12834 .arg(b1)
12835 .arg(aq)
12836 .arg(ad)
12837 .arg(&mut y0)
12838 .arg(&mut y1)
12839 .arg(&inf)
12840 .arg(&o0)
12841 .arg(&o1)
12842 .arg(&rbl)
12843 .arg(&ws0)
12844 .arg(&ws1);
12845 unsafe {
12846 b.launch(cfg)?;
12847 }
12848 Ok((y0, y1))
12849 }
12850
12851 #[allow(clippy::too_many_arguments)]
12853 fn e4m3_fused3_core(
12854 &self,
12855 b0: &CudaSlice<u8>,
12856 b1: &CudaSlice<u8>,
12857 b2: &CudaSlice<u8>,
12858 aq: &CudaSlice<i8>,
12859 ad: &CudaSlice<f32>,
12860 in_f: usize,
12861 out0: usize,
12862 out1: usize,
12863 out2: usize,
12864 row_bytes: usize,
12865 ws0: f32,
12866 ws1: f32,
12867 ws2: f32,
12868 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12869 const ROWS_PER_BLOCK: u32 = 4;
12870 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12871 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12872 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12873 let f = self.func("qmatvec_e4m3_mmvq_fused3");
12874 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12875 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12876 let mut y2 = self.alloc_uninit::<f32>(out2)?;
12877 let cfg = LaunchConfig {
12878 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12879 block_dim: (32, ROWS_PER_BLOCK, 1),
12880 shared_mem_bytes: 0,
12881 };
12882 let (inf, o0, o1, o2, rbl) = (
12883 in_f as i32,
12884 out0 as i32,
12885 out1 as i32,
12886 out2 as i32,
12887 row_bytes as i64,
12888 );
12889 let __s_b = self.gpu.stream();
12890 let mut b = __s_b.launch_builder(&f);
12891 b.arg(b0)
12892 .arg(b1)
12893 .arg(b2)
12894 .arg(aq)
12895 .arg(ad)
12896 .arg(&mut y0)
12897 .arg(&mut y1)
12898 .arg(&mut y2)
12899 .arg(&inf)
12900 .arg(&o0)
12901 .arg(&o1)
12902 .arg(&o2)
12903 .arg(&rbl)
12904 .arg(&ws0)
12905 .arg(&ws1)
12906 .arg(&ws2);
12907 unsafe {
12908 b.launch(cfg)?;
12909 }
12910 Ok((y0, y1, y2))
12911 }
12912
12913 #[allow(clippy::too_many_arguments)]
12917 fn e4m3_fused2_t_core(
12918 &self,
12919 b0: &CudaSlice<u8>,
12920 b1: &CudaSlice<u8>,
12921 aq: &CudaSlice<i8>,
12922 ad: &CudaSlice<f32>,
12923 m: usize,
12924 in_f: usize,
12925 out0: usize,
12926 out1: usize,
12927 row_bytes: usize,
12928 ws0: f32,
12929 ws1: f32,
12930 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12931 const ROWS_PER_BLOCK: u32 = 4;
12932 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12933 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12934 let f = self.func(match Self::batched_mcols(m) {
12935 2 => "qmatvec_e4m3_mmvq_fused2_b2",
12936 4 => "qmatvec_e4m3_mmvq_fused2_b4",
12937 _ => "qmatvec_e4m3_mmvq_fused2_b8",
12938 });
12939 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12940 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12941 let cfg = LaunchConfig {
12942 grid_dim: (nb0 + nb1, 1, 1),
12943 block_dim: (32, ROWS_PER_BLOCK, 1),
12944 shared_mem_bytes: 0,
12945 };
12946 let (inf, o0, o1, mi, rbl) = (
12947 in_f as i32,
12948 out0 as i32,
12949 out1 as i32,
12950 m as i32,
12951 row_bytes as i64,
12952 );
12953 let __s_b = self.gpu.stream();
12954 let mut b = __s_b.launch_builder(&f);
12955 b.arg(b0)
12956 .arg(b1)
12957 .arg(aq)
12958 .arg(ad)
12959 .arg(&mut y0)
12960 .arg(&mut y1)
12961 .arg(&inf)
12962 .arg(&o0)
12963 .arg(&o1)
12964 .arg(&mi)
12965 .arg(&rbl);
12966 unsafe {
12967 b.launch(cfg)?;
12968 }
12969 if ws0 != 1.0 {
12970 self.scale_inplace(&mut y0, ws0, m * out0)?;
12971 }
12972 if ws1 != 1.0 {
12973 self.scale_inplace(&mut y1, ws1, m * out1)?;
12974 }
12975 Ok((y0, y1))
12976 }
12977
12978 #[allow(clippy::too_many_arguments)]
12980 fn e4m3_fused3_t_core(
12981 &self,
12982 b0: &CudaSlice<u8>,
12983 b1: &CudaSlice<u8>,
12984 b2: &CudaSlice<u8>,
12985 aq: &CudaSlice<i8>,
12986 ad: &CudaSlice<f32>,
12987 m: usize,
12988 in_f: usize,
12989 out0: usize,
12990 out1: usize,
12991 out2: usize,
12992 row_bytes: usize,
12993 ws0: f32,
12994 ws1: f32,
12995 ws2: f32,
12996 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12997 const ROWS_PER_BLOCK: u32 = 4;
12998 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12999 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
13000 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
13001 let f = self.func(if Self::batched_mcols(m) == 2 {
13002 "qmatvec_e4m3_mmvq_fused3_b2"
13003 } else {
13004 "qmatvec_e4m3_mmvq_fused3_b4"
13005 });
13006 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
13007 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
13008 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
13009 let cfg = LaunchConfig {
13010 grid_dim: (nb0 + nb1 + nb2, 1, 1),
13011 block_dim: (32, ROWS_PER_BLOCK, 1),
13012 shared_mem_bytes: 0,
13013 };
13014 let (inf, o0, o1, o2, mi, rbl) = (
13015 in_f as i32,
13016 out0 as i32,
13017 out1 as i32,
13018 out2 as i32,
13019 m as i32,
13020 row_bytes as i64,
13021 );
13022 let __s_b = self.gpu.stream();
13023 let mut b = __s_b.launch_builder(&f);
13024 b.arg(b0)
13025 .arg(b1)
13026 .arg(b2)
13027 .arg(aq)
13028 .arg(ad)
13029 .arg(&mut y0)
13030 .arg(&mut y1)
13031 .arg(&mut y2)
13032 .arg(&inf)
13033 .arg(&o0)
13034 .arg(&o1)
13035 .arg(&o2)
13036 .arg(&mi)
13037 .arg(&rbl);
13038 unsafe {
13039 b.launch(cfg)?;
13040 }
13041 if ws0 != 1.0 {
13042 self.scale_inplace(&mut y0, ws0, m * out0)?;
13043 }
13044 if ws1 != 1.0 {
13045 self.scale_inplace(&mut y1, ws1, m * out1)?;
13046 }
13047 if ws2 != 1.0 {
13048 self.scale_inplace(&mut y2, ws2, m * out2)?;
13049 }
13050 Ok((y0, y1, y2))
13051 }
13052
13053 pub fn qmatvec_e4m3_blk_mmvq(
13063 &self,
13064 bytes: &CudaSlice<u8>,
13065 aq: &CudaSlice<i8>,
13066 ad: &CudaSlice<f32>,
13067 scales: &CudaSlice<f32>,
13068 m: usize,
13069 in_f: usize,
13070 out_f: usize,
13071 row_bytes: usize,
13072 scale_cols: usize,
13073 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13074 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_e4m3_blk_mmvq_into(
13076 bytes, aq, ad, scales, m, in_f, out_f, row_bytes, scale_cols, &mut y,
13077 )?;
13078 Ok(y)
13079 }
13080
13081 #[allow(clippy::too_many_arguments)]
13083 pub fn qmatvec_e4m3_blk_mmvq_into(
13084 &self,
13085 bytes: &CudaSlice<u8>,
13086 aq: &CudaSlice<i8>,
13087 ad: &CudaSlice<f32>,
13088 scales: &CudaSlice<f32>,
13089 m: usize,
13090 in_f: usize,
13091 out_f: usize,
13092 row_bytes: usize,
13093 scale_cols: usize,
13094 y: &mut CudaSlice<f32>,
13095 ) -> Result<(), Box<dyn std::error::Error>> {
13096 const ROWS_PER_BLOCK: u32 = 4; let f = self.func("qmatvec_e4m3_blk_mmvq");
13098 let cfg = LaunchConfig {
13099 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), m as u32, 1),
13100 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
13103 let (inf, outf, mi, rb, sc) = (
13104 in_f as i32,
13105 out_f as i32,
13106 m as i32,
13107 row_bytes as i64,
13108 scale_cols as i32,
13109 );
13110 let __s_b = self.gpu.stream();
13111 let mut b = __s_b.launch_builder(&f);
13112 b.arg(bytes)
13113 .arg(aq)
13114 .arg(ad)
13115 .arg(scales)
13116 .arg(&mut *y)
13117 .arg(&inf)
13118 .arg(&outf)
13119 .arg(&mi)
13120 .arg(&rb)
13121 .arg(&sc);
13122 unsafe {
13123 b.launch(cfg)?;
13124 }
13125 Ok(())
13126 }
13127
13128 #[allow(clippy::too_many_arguments)]
13134 pub fn qmatvec_e4m3_blk_mmvq_batched(
13135 &self,
13136 bytes: &CudaSlice<u8>,
13137 aq: &CudaSlice<i8>,
13138 ad: &CudaSlice<f32>,
13139 scales: &CudaSlice<f32>,
13140 m: usize,
13141 in_f: usize,
13142 out_f: usize,
13143 row_bytes: usize,
13144 scale_cols: usize,
13145 mcols: usize,
13146 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13147 const ROWS_PER_BLOCK: u32 = 4; debug_assert!(mcols >= m, "blk batched: mcols {mcols} < m {m}");
13149 let name = match mcols {
13150 2 => "qmatvec_e4m3_blk_mmvq_b2",
13151 4 => "qmatvec_e4m3_blk_mmvq_b4",
13152 8 => "qmatvec_e4m3_blk_mmvq_b8",
13153 16 => "qmatvec_e4m3_blk_mmvq_b16",
13154 _ => {
13155 return Err(
13156 format!("qmatvec_e4m3_blk_mmvq_batched: no kernel for mcols {mcols}").into(),
13157 );
13158 }
13159 };
13160 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13161 let f = self.func(name);
13162 let cfg = LaunchConfig {
13163 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), 1, 1),
13164 block_dim: (32, ROWS_PER_BLOCK, 1),
13165 shared_mem_bytes: 0,
13166 };
13167 let (inf, outf, mi, rb, sc) = (
13168 in_f as i32,
13169 out_f as i32,
13170 m as i32,
13171 row_bytes as i64,
13172 scale_cols as i32,
13173 );
13174 let __s_b = self.gpu.stream();
13175 let mut b = __s_b.launch_builder(&f);
13176 b.arg(bytes)
13177 .arg(aq)
13178 .arg(ad)
13179 .arg(scales)
13180 .arg(&mut y)
13181 .arg(&inf)
13182 .arg(&outf)
13183 .arg(&mi)
13184 .arg(&rb)
13185 .arg(&sc);
13186 unsafe {
13187 b.launch(cfg)?;
13188 }
13189 Ok(y)
13190 }
13191
13192 #[allow(clippy::too_many_arguments)]
13195 pub fn qmatvec_e4m3_blk_batched_raw(
13196 &self,
13197 bytes: &CudaSlice<u8>,
13198 x: &CudaSlice<f32>,
13199 scales: &CudaSlice<f32>,
13200 m: usize,
13201 in_f: usize,
13202 out_f: usize,
13203 row_bytes: usize,
13204 scale_cols: usize,
13205 mcols: usize,
13206 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13207 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13208 self.qmatvec_e4m3_blk_mmvq_batched(
13209 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols, mcols,
13210 )
13211 }
13212
13213 #[allow(clippy::too_many_arguments)]
13216 pub fn qmatvec_e4m3_blk_mmvq_raw(
13217 &self,
13218 bytes: &CudaSlice<u8>,
13219 x: &CudaSlice<f32>,
13220 scales: &CudaSlice<f32>,
13221 m: usize,
13222 in_f: usize,
13223 out_f: usize,
13224 row_bytes: usize,
13225 scale_cols: usize,
13226 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13227 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13228 self.qmatvec_e4m3_blk_mmvq(
13229 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols,
13230 )
13231 }
13232
13233 #[allow(clippy::too_many_arguments)]
13236 pub fn qmatvec_e4m3_fused2_raw(
13237 &self,
13238 b0: &CudaSlice<u8>,
13239 b1: &CudaSlice<u8>,
13240 x: &CudaSlice<f32>,
13241 in_f: usize,
13242 out0: usize,
13243 out1: usize,
13244 row_bytes: usize,
13245 ws0: f32,
13246 ws1: f32,
13247 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13248 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
13249 self.e4m3_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes, ws0, ws1)
13250 }
13251
13252 #[allow(clippy::too_many_arguments)]
13253 pub fn qmatvec_e4m3_fused3_raw(
13254 &self,
13255 b0: &CudaSlice<u8>,
13256 b1: &CudaSlice<u8>,
13257 b2: &CudaSlice<u8>,
13258 x: &CudaSlice<f32>,
13259 in_f: usize,
13260 out0: usize,
13261 out1: usize,
13262 out2: usize,
13263 row_bytes: usize,
13264 ws0: f32,
13265 ws1: f32,
13266 ws2: f32,
13267 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13268 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
13269 self.e4m3_fused3_core(
13270 b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
13271 )
13272 }
13273
13274 #[allow(clippy::too_many_arguments)]
13275 pub fn qmatvec_e4m3_fused2_t_raw(
13276 &self,
13277 b0: &CudaSlice<u8>,
13278 b1: &CudaSlice<u8>,
13279 x: &CudaSlice<f32>,
13280 m: usize,
13281 in_f: usize,
13282 out0: usize,
13283 out1: usize,
13284 row_bytes: usize,
13285 ws0: f32,
13286 ws1: f32,
13287 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13288 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13289 self.e4m3_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes, ws0, ws1)
13290 }
13291
13292 #[allow(clippy::too_many_arguments)]
13293 pub fn qmatvec_e4m3_fused3_t_raw(
13294 &self,
13295 b0: &CudaSlice<u8>,
13296 b1: &CudaSlice<u8>,
13297 b2: &CudaSlice<u8>,
13298 x: &CudaSlice<f32>,
13299 m: usize,
13300 in_f: usize,
13301 out0: usize,
13302 out1: usize,
13303 out2: usize,
13304 row_bytes: usize,
13305 ws0: f32,
13306 ws1: f32,
13307 ws2: f32,
13308 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13309 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13310 self.e4m3_fused3_t_core(
13311 b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
13312 )
13313 }
13314
13315 fn try_e4m3_blk_pre(
13326 &self,
13327 w: &crate::model::GpuTensor,
13328 aq: &CudaSlice<i8>,
13329 ad: &CudaSlice<f32>,
13330 m: usize,
13331 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13332 use crate::model::GpuTensor;
13333 if let GpuTensor::Quant {
13334 bytes,
13335 qtype,
13336 row_bytes,
13337 blk: Some(g),
13338 ..
13339 } = w
13340 {
13341 if *qtype == QT_F8_E4M3_BLK {
13342 if (2..=16).contains(&m)
13348 && std::env::var("MEMRA_NO_BATCHED").is_err()
13349 && (m <= 4 || Self::b8_enabled())
13350 {
13351 let mcols = Self::batched_mcols(m);
13352 return Ok(Some(self.qmatvec_e4m3_blk_mmvq_batched(
13353 bytes,
13354 aq,
13355 ad,
13356 &g.scales,
13357 m,
13358 w.in_features(),
13359 w.out_features(),
13360 *row_bytes,
13361 g.cols,
13362 mcols,
13363 )?));
13364 }
13365 return Ok(Some(self.qmatvec_e4m3_blk_mmvq(
13366 bytes,
13367 aq,
13368 ad,
13369 &g.scales,
13370 m,
13371 w.in_features(),
13372 w.out_features(),
13373 *row_bytes,
13374 g.cols,
13375 )?));
13376 }
13377 }
13378 Ok(None)
13379 }
13380
13381 fn try_e4m3_blk_prefill(
13428 &self,
13429 w: &crate::model::GpuTensor,
13430 x: &CudaSlice<f32>,
13431 m: usize,
13432 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13433 use crate::model::GpuTensor;
13434 let GpuTensor::Quant {
13435 bytes,
13436 qtype,
13437 blk: Some(g),
13438 ..
13439 } = w
13440 else {
13441 return Ok(None);
13442 };
13443 if *qtype != QT_F8_E4M3_BLK {
13444 return Ok(None);
13445 }
13446 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
13451 return Ok(Some(y));
13452 }
13453 let (in_f, out_f) = (w.in_features(), w.out_features());
13454 let slab = self.fp8_blk_dequant_q8_0_dev(bytes, &g.scales, out_f, in_f)?;
13455 let tmp = GpuTensor::Quant {
13456 bytes: slab,
13457 qtype: QT_Q8_0,
13458 row_bytes: in_f / 32 * 34,
13459 ne: vec![in_f as u64, out_f as u64],
13460 scale: 1.0,
13461 rp: false,
13462 #[cfg(memra_cutlass)]
13463 cutlass: None,
13464 fp8: None,
13465 blk: None,
13466 f16: None,
13467 rp4: None,
13468 };
13469 Ok(Some(self.matmul(&tmp, x, m)?))
13471 }
13472
13473 pub fn matmul_pre_noscale(
13474 &self,
13475 w: &crate::model::GpuTensor,
13476 aq: &CudaSlice<i8>,
13477 ad: &CudaSlice<f32>,
13478 m: usize,
13479 ) -> Result<Option<(CudaSlice<f32>, f32)>, Box<dyn std::error::Error>> {
13480 use crate::model::GpuTensor;
13481 if m == 1 {
13485 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
13486 return Ok(Some((y, 1.0)));
13487 }
13488 }
13489 if m != 1 || !self.uses_q8_1_fast(w) {
13491 return Ok(None);
13492 }
13493 let in_f = w.in_features();
13494 let out_f = w.out_features();
13495 let (bytes, qtype, row_bytes, scale, rp) = match w {
13496 GpuTensor::Quant {
13497 bytes,
13498 qtype,
13499 row_bytes,
13500 scale,
13501 rp,
13502 ..
13503 } => (bytes, *qtype, *row_bytes, *scale, *rp),
13504 _ => return Ok(None),
13505 };
13506 if self.mmvq_supports(qtype) {
13508 let (mbytes, mrp) = match w {
13510 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
13511 _ => (bytes, rp),
13512 };
13513 let y = self.qmatvec_mmvq(
13514 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, 1.0, mrp,
13515 )?;
13516 return Ok(Some((y, scale)));
13517 }
13518 let name = match qtype {
13520 QT_Q8_0 => "qmatvec_q8_0_dp4a",
13521 QT_Q4_K => "qmatvec_q4_K_dp4a",
13522 QT_Q6_K => "qmatvec_q6_K_dp4a",
13523 QT_Q5_K => "qmatvec_q5_K_dp4a",
13524 QT_Q3_K => "qmatvec_q3_K_dp4a",
13525 QT_NVFP4 => {
13526 if rp {
13527 "qmatvec_nvfp4_dp4a_rp"
13528 } else {
13529 "qmatvec_nvfp4_dp4a"
13530 }
13531 }
13532 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
13533 _ => return Ok(None),
13534 };
13535 let f = self.func(name);
13536 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13537 let cfg = LaunchConfig {
13538 grid_dim: (out_f as u32, m as u32, 1),
13539 block_dim: (128, 1, 1),
13540 shared_mem_bytes: 0,
13541 };
13542 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13543 let __s_b = self.gpu.stream();
13544 let mut b = __s_b.launch_builder(&f);
13545 b.arg(bytes)
13546 .arg(aq)
13547 .arg(ad)
13548 .arg(&mut y)
13549 .arg(&inf)
13550 .arg(&outf)
13551 .arg(&mi)
13552 .arg(&rb);
13553 unsafe {
13554 b.launch(cfg)?;
13555 }
13556 Ok(Some((y, scale)))
13557 }
13558
13559 pub fn mmvq_supports(&self, qtype: i32) -> bool {
13562 if qtype == QT_F8_E4M3 {
13567 return true;
13568 }
13569 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
13570 return false;
13571 }
13572 matches!(
13573 qtype,
13574 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_Q4_0
13575 )
13576 }
13577
13578 pub fn qmatvec_mmvq(
13583 &self,
13584 bytes: &CudaSlice<u8>,
13585 aq: &CudaSlice<i8>,
13586 ad: &CudaSlice<f32>,
13587 m: usize,
13588 in_f: usize,
13589 out_f: usize,
13590 qtype: i32,
13591 row_bytes: usize,
13592 scale: f32,
13593 rp: bool,
13594 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13595 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_mmvq_into(
13597 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp, &mut y,
13598 )?;
13599 Ok(y)
13600 }
13601
13602 #[allow(clippy::too_many_arguments)]
13604 pub fn qmatvec_mmvq_into(
13605 &self,
13606 bytes: &CudaSlice<u8>,
13607 aq: &CudaSlice<i8>,
13608 ad: &CudaSlice<f32>,
13609 m: usize,
13610 in_f: usize,
13611 out_f: usize,
13612 qtype: i32,
13613 row_bytes: usize,
13614 scale: f32,
13615 rp: bool,
13616 y: &mut CudaSlice<f32>,
13617 ) -> Result<(), Box<dyn std::error::Error>> {
13618 debug_assert!(y.len() >= m * out_f);
13619 const ROWS_PER_BLOCK: u32 = 4; if qtype == QT_Q8_0
13625 && rp
13626 && m == 1
13627 && out_f >= 64
13628 && (out_f as u32).div_ceil(ROWS_PER_BLOCK) < 4 * self.sm_count() as u32
13629 && {
13630 static G2: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13631 *G2.get_or_init(|| std::env::var("MEMRA_Q80_G2").as_deref() != Ok("0"))
13632 }
13633 {
13634 let f = self.func("qmatvec_q8_0_mmvq_rp_g2");
13635 let cfg = LaunchConfig {
13636 grid_dim: ((out_f as u32).div_ceil(2), 1, 1),
13637 block_dim: (32, 2, 1),
13638 shared_mem_bytes: 0,
13639 };
13640 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, row_bytes as i64);
13641 let __s_b = self.gpu.stream();
13642 let mut b = __s_b.launch_builder(&f);
13643 b.arg(bytes)
13644 .arg(aq)
13645 .arg(ad)
13646 .arg(&mut *y)
13647 .arg(&inf)
13648 .arg(&outf)
13649 .arg(&mi)
13650 .arg(&rb);
13651 unsafe {
13652 b.launch(cfg)?;
13653 }
13654 if scale != 1.0 {
13655 self.scale_inplace(y, scale, out_f)?;
13656 }
13657 return Ok(());
13658 }
13659 let mut mr: u32 = if m == 1 && (qtype == QT_NVFP4 || qtype == QT_Q5_K) {
13668 2
13669 } else {
13670 1
13671 };
13672 if m == 1 && qtype == QT_Q4_0 {
13677 static Q40MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
13678 mr = *Q40MR.get_or_init(|| {
13681 std::env::var("MEMRA_Q40_MR")
13682 .ok()
13683 .and_then(|v| v.parse().ok())
13684 .unwrap_or(1)
13685 });
13686 }
13687 let q5_mode = std::env::var("MEMRA_Q5K_ISSUE").ok();
13698 let q5_force = q5_mode.as_deref() == Some("2");
13699 let q5_il = qtype == QT_Q5_K
13702 && m == 1
13703 && (q5_force || q5_mode.as_deref().map(|v| v != "0").unwrap_or(true));
13704 if q5_il && !q5_force && out_f > 65536 {
13705 mr = 1;
13706 }
13707 if qtype == QT_Q4_0 && rp && mr != 1 {
13710 mr = 2;
13711 }
13712 if qtype == QT_Q8_0 && rp {
13716 static Q80MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
13717 mr = *Q80MR.get_or_init(|| {
13718 std::env::var("MEMRA_Q80_MR")
13719 .ok()
13720 .and_then(|v| v.parse().ok())
13721 .unwrap_or(1)
13722 });
13723 }
13724 let name = match (qtype, mr, rp) {
13725 (QT_NVFP4, 2, false) => "qmatvec_nvfp4_mmvq_mr2",
13726 (QT_NVFP4, 2, true) => "qmatvec_nvfp4_mmvq_mr2_rp",
13727 (QT_NVFP4, _, true) => "qmatvec_nvfp4_mmvq_rp",
13728 (QT_Q4_0, 1, true) => "qmatvec_q4_0_mmvq_rp",
13729 (QT_Q4_0, _, true) => "qmatvec_q4_0_mmvq_mr2_rp",
13730 (QT_Q5_K, 2, _) => {
13731 if q5_il {
13732 "qmatvec_q5_K_mmvq_mr2_il"
13733 } else {
13734 "qmatvec_q5_K_mmvq_mr2"
13735 }
13736 }
13737 (QT_Q8_0, 2, true) => "qmatvec_q8_0_mmvq_mr2_rp",
13738 (QT_Q8_0, _, true)
13743 if in_f % 1024 == 0 && {
13744 static CA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13745 *CA.get_or_init(|| std::env::var("MEMRA_Q80_CA").as_deref() == Ok("1"))
13746 } =>
13747 {
13748 "qmatvec_q8_0_mmvq_rpca"
13749 }
13750 (QT_Q8_0, _, true) => "qmatvec_q8_0_mmvq_rp",
13751 (QT_Q8_0, _, _) => "qmatvec_q8_0_mmvq",
13752 (QT_Q4_K, _, true) => "qmatvec_q4_K_mmvq_rp",
13756 (QT_Q6_K, _, true) => "qmatvec_q6_K_mmvq_rp",
13757 (QT_Q4_K, _, _) => "qmatvec_q4_K_mmvq",
13758 (QT_Q4_0, 2, false) => "qmatvec_q4_0_mmvq_mr2",
13759 (QT_Q4_0, _, false) => "qmatvec_q4_0_mmvq",
13760 (QT_Q5_K, _, _) => {
13761 if q5_il {
13762 "qmatvec_q5_K_mmvq_il"
13763 } else {
13764 "qmatvec_q5_K_mmvq"
13765 }
13766 }
13767 (QT_Q6_K, _, _) => "qmatvec_q6_K_mmvq",
13768 (QT_NVFP4, _, false) => "qmatvec_nvfp4_mmvq",
13769 (QT_F8_E4M3, _, _) => "qmatvec_e4m3_mmvq",
13770 _ => panic!("qmatvec_mmvq: qtype {qtype} has no MMVQ kernel"),
13771 };
13772 let f = self.func(name);
13773 let rows_per_block = ROWS_PER_BLOCK * mr;
13775 let cfg = LaunchConfig {
13776 grid_dim: (
13777 (out_f as u32 + rows_per_block - 1) / rows_per_block,
13778 m as u32,
13779 1,
13780 ),
13781 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
13784 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13785 let __s_b = self.gpu.stream();
13786 let mut b = __s_b.launch_builder(&f);
13787 if qtype == QT_NVFP4 || qtype == QT_F8_E4M3 {
13792 if Self::pdl_on()
13795 && Self::pdl_mmvq_on()
13796 && Self::pdl_nvfp4q8_on()
13797 && name == "qmatvec_nvfp4_mmvq_mr2_rp"
13798 {
13799 use cudarc::driver::{DevicePtr, DevicePtrMut};
13800 let s = &self.gpu.stream();
13801 let (pw, _g0) = bytes.device_ptr(s);
13802 let (paq, _g1) = aq.device_ptr(s);
13803 let (pad, _g2) = ad.device_ptr(s);
13804 let (py, _g3) = y.device_ptr_mut(s);
13805 let mut ps = [
13806 &pw as *const _ as *mut std::ffi::c_void,
13807 &paq as *const _ as *mut _,
13808 &pad as *const _ as *mut _,
13809 &py as *const _ as *mut _,
13810 &inf as *const _ as *mut _,
13811 &outf as *const _ as *mut _,
13812 &mi as *const _ as *mut _,
13813 &rb as *const _ as *mut _,
13814 &scale as *const _ as *mut _,
13815 ];
13816 unsafe {
13817 self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
13818 }
13819 return Ok(());
13820 }
13821 b.arg(bytes)
13822 .arg(aq)
13823 .arg(ad)
13824 .arg(&mut *y)
13825 .arg(&inf)
13826 .arg(&outf)
13827 .arg(&mi)
13828 .arg(&rb)
13829 .arg(&scale);
13830 unsafe {
13831 b.launch(cfg)?;
13832 }
13833 } else if Self::pdl_on()
13834 && Self::pdl_mmvq_on()
13835 && (matches!(
13836 name,
13837 "qmatvec_q4_0_mmvq_rp" | "qmatvec_q6_K_mmvq" | "qmatvec_q6_K_mmvq_rp"
13838 ) || (Self::pdl_nvfp4q8_on()
13839 && matches!(name, "qmatvec_q8_0_mmvq_rp" | "qmatvec_q8_0_mmvq_mr2_rp")))
13840 {
13841 {
13845 use cudarc::driver::{DevicePtr, DevicePtrMut};
13846 let s = &self.gpu.stream();
13847 let (pw, _g0) = bytes.device_ptr(s);
13848 let (paq, _g1) = aq.device_ptr(s);
13849 let (pad, _g2) = ad.device_ptr(s);
13850 let (py, _g3) = y.device_ptr_mut(s);
13851 let mut ps = [
13852 &pw as *const _ as *mut std::ffi::c_void,
13853 &paq as *const _ as *mut _,
13854 &pad as *const _ as *mut _,
13855 &py as *const _ as *mut _,
13856 &inf as *const _ as *mut _,
13857 &outf as *const _ as *mut _,
13858 &mi as *const _ as *mut _,
13859 &rb as *const _ as *mut _,
13860 ];
13861 unsafe {
13862 self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
13863 }
13864 }
13865 if scale != 1.0 {
13866 self.scale_inplace(y, scale, m * out_f)?;
13867 }
13868 } else {
13869 b.arg(bytes)
13870 .arg(aq)
13871 .arg(ad)
13872 .arg(&mut *y)
13873 .arg(&inf)
13874 .arg(&outf)
13875 .arg(&mi)
13876 .arg(&rb);
13877 unsafe {
13878 b.launch(cfg)?;
13879 }
13880 if scale != 1.0 {
13881 self.scale_inplace(y, scale, m * out_f)?;
13882 }
13883 }
13884 Ok(())
13885 }
13886
13887 pub fn qmatvec_mmvq_raw(
13891 &self,
13892 bytes: &CudaSlice<u8>,
13893 x: &CudaSlice<f32>,
13894 m: usize,
13895 in_f: usize,
13896 out_f: usize,
13897 qtype: i32,
13898 row_bytes: usize,
13899 rp: bool,
13900 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13901 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13902 self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, 1.0, rp)
13903 }
13904
13905 pub fn batched_supports(&self, qtype: i32) -> bool {
13909 matches!(
13910 qtype,
13911 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_F8_E4M3 | QT_Q4_0
13912 )
13913 }
13914
13915 pub fn iq_fast_enabled() -> bool {
13923 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13924 *ON.get_or_init(|| {
13925 std::env::var("MEMRA_IQ_FAST")
13926 .map(|v| v != "0")
13927 .unwrap_or(true)
13928 })
13929 }
13930
13931 pub fn b8_enabled() -> bool {
13934 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13935 *ON.get_or_init(|| std::env::var("MEMRA_B8").map(|v| v != "0").unwrap_or(true))
13936 }
13937
13938 pub fn batched_mcols(m: usize) -> usize {
13940 if m == 2 {
13941 2
13942 } else if m <= 4 {
13943 4
13944 } else if m <= 8 {
13945 8
13946 } else {
13947 16
13948 }
13949 }
13950
13951 fn batched_kernel_name(qtype: i32, mcols: usize) -> Option<&'static str> {
13956 Some(match (qtype, mcols) {
13957 (QT_Q8_0, 2) => "qmatvec_q8_0_mmvq_b2",
13958 (QT_Q8_0, 4) => "qmatvec_q8_0_mmvq_b4",
13959 (QT_Q8_0, 8) => "qmatvec_q8_0_mmvq_b8",
13960 (QT_Q8_0, 16) => "qmatvec_q8_0_mmvq_b16",
13966 (QT_Q4_K, 2) => "qmatvec_q4_K_mmvq_b2",
13967 (QT_Q4_K, 4) => "qmatvec_q4_K_mmvq_b4",
13968 (QT_Q4_K, 8) => "qmatvec_q4_K_mmvq_b8",
13969 (QT_Q4_K, 16) => "qmatvec_q4_K_mmvq_b16",
13972 (QT_Q5_K, 2) => "qmatvec_q5_K_mmvq_b2",
13973 (QT_Q5_K, 4) => "qmatvec_q5_K_mmvq_b4",
13974 (QT_Q5_K, 8) => "qmatvec_q5_K_mmvq_b8",
13975 (QT_Q5_K, 16) => "qmatvec_q5_K_mmvq_b16",
13978 (QT_Q6_K, 2) => "qmatvec_q6_K_mmvq_b2",
13979 (QT_Q6_K, 4) => "qmatvec_q6_K_mmvq_b4",
13980 (QT_Q6_K, 8) => "qmatvec_q6_K_mmvq_b8",
13981 (QT_Q6_K, 16) => "qmatvec_q6_K_mmvq_b16",
13982 (QT_NVFP4, 2) => "qmatvec_nvfp4_mmvq_b2",
13983 (QT_NVFP4, 4) => "qmatvec_nvfp4_mmvq_b4",
13984 (QT_NVFP4, 8) => "qmatvec_nvfp4_mmvq_b8",
13985 (QT_NVFP4, 16) => "qmatvec_nvfp4_mmvq_b16",
13989 (QT_F8_E4M3, 2) => "qmatvec_e4m3_mmvq_b2",
13990 (QT_F8_E4M3, 4) => "qmatvec_e4m3_mmvq_b4",
13991 (QT_F8_E4M3, 8) => "qmatvec_e4m3_mmvq_b8",
13992 (QT_F8_E4M3, 16) => "qmatvec_e4m3_mmvq_b16",
13996 (QT_Q4_0, 2) => "qmatvec_q4_0_mmvq_b2",
13997 (QT_Q4_0, 4) => "qmatvec_q4_0_mmvq_b4",
13998 (QT_Q4_0, 8) => "qmatvec_q4_0_mmvq_b8",
13999 (QT_Q4_0, 16) => "qmatvec_q4_0_mmvq_b16",
14000 _ => return None,
14001 })
14002 }
14003
14004 pub fn sm_count(&self) -> i32 {
14039 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
14040 *SMS.get_or_init(|| {
14041 use cudarc::driver::sys::CUdevice_attribute_enum as A;
14042 self.gpu
14043 .ctx
14044 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
14045 .unwrap_or(82)
14046 })
14047 }
14048
14049 pub fn batched_variant(
14050 &self,
14051 _m: usize,
14052 in_f: usize,
14053 out_f: usize,
14054 qtype: i32,
14055 row_bytes: usize,
14056 mcols: usize,
14057 rp: bool,
14058 ) -> &'static str {
14059 if qtype == QT_Q8_0 {
14064 return if rp { "rp" } else { "base" };
14065 }
14066 static BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
14067 let bv = *BV.get_or_init(|| match std::env::var("MEMRA_MMVQ_BV").as_deref() {
14068 Ok("base") => "base",
14069 Ok("pf") => "pf",
14070 Ok("r2") => "r2",
14071 Ok("r2w8") => "r2w8",
14072 Ok("pfr2") => "pfr2",
14073 Ok("ca") => "ca",
14074 Ok("car2") => "car2",
14075 Ok("rp") => "rp",
14078 Ok("rpr2") => "rpr2",
14079 Ok("rpr2w8") => "rpr2w8",
14080 Ok("rpca") => "rpca",
14083 Ok("rpcar2") => "rpcar2",
14084 Ok("rpsc") => "rpsc",
14091 Ok("rpms") => "rpms",
14092 Ok("rpmsc") => "rpmsc",
14093 Ok("rpks") => "rpks",
14094 Ok("rpksc") => "rpksc",
14095 _ => "auto",
14096 });
14097 let ca_ok = qtype == QT_NVFP4 && (row_bytes % 16 == 0) && (in_f % 1024 == 0);
14101 static KS_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14106 let ks_on = *KS_ON.get_or_init(|| std::env::var("MEMRA_KS").as_deref() != Ok("0"));
14107 let sc_ok = ks_on && qtype == QT_NVFP4 && (in_f % 256 == 0) && (in_f / 64 <= 272);
14108 let ks_ok = ks_on && qtype == QT_NVFP4 && (in_f % 512 == 0) && (in_f / 64 <= 272);
14109 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
14110 let sms = *SMS.get_or_init(|| {
14111 use cudarc::driver::sys::CUdevice_attribute_enum as A;
14112 self.gpu
14113 .ctx
14114 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
14115 .unwrap_or(82)
14116 });
14117 let kq_r2 = matches!(qtype, QT_Q4_K | QT_Q5_K | QT_Q6_K);
14137 static KQBV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
14140 let kq_bv = *KQBV.get_or_init(|| match std::env::var("MEMRA_KQ_BV").as_deref() {
14141 Ok("base") => "base",
14142 Ok("r2") => "r2",
14143 Ok("r2w8") => "r2w8",
14144 _ => "auto",
14145 });
14146 let variant: &'static str = if qtype == QT_Q4_0 {
14147 static Q40BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
14151 let q40 = *Q40BV.get_or_init(|| match std::env::var("MEMRA_Q40_BV").as_deref() {
14152 Ok("base") => "base",
14158 Ok("r2") => "r2",
14159 Ok("ms") => "ms",
14160 Ok("sm") => "sm",
14161 Ok("la") => "la",
14162 _ => "auto",
14163 });
14164 let v = if q40 != "auto" {
14165 q40
14166 } else if (out_f as u32).div_ceil(8) >= 4 * sms as u32 {
14167 "r2"
14168 } else {
14169 "base"
14170 };
14171 if rp {
14176 match v {
14177 "ms" => "r2ms_rp",
14178 "sm" => "r2sm_rp",
14179 "la" => "r2la_rp",
14180 "r2" => "r2_rp",
14181 _ => "rp",
14182 }
14183 } else if matches!(v, "ms" | "sm" | "la") {
14184 "r2"
14185 } else {
14186 v
14187 }
14188 } else if qtype != QT_NVFP4 && !kq_r2 {
14189 "base"
14190 } else if kq_r2 && rp {
14191 "rp"
14195 } else if kq_r2 {
14196 if kq_bv != "auto" {
14199 if kq_bv == "r2w8" && mcols != 4 {
14200 "r2"
14201 } else {
14202 kq_bv
14203 }
14204 } else if bv != "auto" {
14205 match bv {
14206 "r2" | "pfr2" | "rpr2" | "car2" => "r2",
14207 "r2w8" | "rpr2w8" => {
14208 if mcols != 4 {
14209 "r2"
14210 } else {
14211 "r2w8"
14212 }
14213 }
14214 _ => "base", }
14216 } else {
14217 let blocks = (out_f + 7) / 8;
14218 let waves = blocks as f64 / (7 * sms as usize) as f64;
14219 let filled = blocks >= 4 * sms as usize;
14220 let use_r2 = if qtype == QT_Q4_K {
14221 filled
14222 } else {
14223 waves >= 2.0
14224 };
14225 if use_r2 { "r2" } else { "base" }
14226 }
14227 } else if bv != "auto" {
14228 let v = if bv == "r2w8" && mcols == 2 {
14233 "r2"
14234 } else if bv == "ca" && (!ca_ok || mcols == 8) {
14235 "pf"
14236 } else if bv == "car2" && (!ca_ok || mcols == 8) {
14237 "r2"
14238 } else if bv == "pfr2" && mcols == 8 {
14239 "r2"
14240 } else if (bv == "rpr2w8" || bv == "rpr2") && mcols == 2 {
14241 "rpr2"
14242 }
14243 else if (bv == "rpca" || bv == "rpcar2") && (!ca_ok || mcols == 8) {
14245 if mcols == 8 { "rpr2w8" } else { "rpr2" }
14246 } else if bv == "rpcar2" && mcols == 2 {
14247 "rpca"
14248 }
14249 else if (bv == "rpsc" || bv == "rpmsc") && !sc_ok {
14252 "rpr2"
14253 } else if (bv == "rpks" || bv == "rpksc") && !ks_ok {
14254 "rpr2"
14255 } else {
14256 bv
14257 };
14258 if rp {
14259 match v {
14260 "base" | "pf" | "ca" | "rp" => "rp",
14261 "r2" | "pfr2" | "car2" | "rpr2" => "rpr2",
14262 "r2w8" | "rpr2w8" => {
14263 if mcols == 2 {
14264 "rpr2"
14265 } else {
14266 "rpr2w8"
14267 }
14268 }
14269 other => other, }
14271 } else {
14272 v
14273 }
14274 } else if mcols == 8 {
14275 if rp {
14286 if sc_ok { "rpsc" } else { "rpr2w8" }
14287 } else {
14288 "r2w8"
14289 }
14290 } else if mcols >= 4 {
14291 let blocks = (out_f + 7) / 8;
14295 let r7 = 7 * sms as usize;
14296 let r8 = 8 * sms as usize;
14297 let waves = blocks as f64 / r7 as f64;
14298 let filled = blocks >= 4 * sms as usize;
14299 if filled && blocks.div_ceil(r8) < blocks.div_ceil(r7) {
14303 if rp { "rpr2w8" } else { "r2w8" }
14307 } else if waves >= 2.0 || (waves <= 1.0 && filled) {
14308 if rp { "rpr2" } else { "r2" }
14311 } else {
14312 if rp { "rp" } else { "pf" }
14316 }
14317 } else if in_f >= 6144 {
14318 if rp { "rpr2" } else { "r2" }
14322 } else if rp {
14323 let waves = ((out_f + 7) / 8) as f64 / (7 * sms as usize) as f64;
14328 if sc_ok && waves >= 0.9 && waves <= 1.1 {
14329 "rpsc"
14330 } else {
14331 "rp"
14332 }
14333 } else {
14334 "base"
14335 };
14336 variant
14337 }
14338
14339 pub fn qmatvec_mmvq_batched(
14340 &self,
14341 bytes: &CudaSlice<u8>,
14342 aq: &CudaSlice<i8>,
14343 ad: &CudaSlice<f32>,
14344 m: usize,
14345 in_f: usize,
14346 out_f: usize,
14347 qtype: i32,
14348 row_bytes: usize,
14349 mcols: usize,
14350 scale: f32,
14351 rp: bool,
14352 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14353 const ROWS_PER_BLOCK: u32 = 4;
14354 let forced: Option<&'static str> = {
14359 static V: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
14360 V.get_or_init(|| std::env::var("MEMRA_BVAR").ok())
14361 .as_deref()
14362 .map(|s| Box::leak(s.to_string().into_boxed_str()) as &'static str)
14363 };
14364 let variant = match forced {
14365 Some(v) if !rp || v.contains("rp") => v,
14366 _ => self.batched_variant(m, in_f, out_f, qtype, row_bytes, mcols, rp),
14367 };
14368 let base_name = Self::batched_kernel_name(qtype, mcols).ok_or_else(|| {
14369 format!("qmatvec_mmvq_batched: no kernel for qtype {qtype} mcols {mcols}")
14370 })?;
14371 let variant = if mcols == 16 {
14375 if rp { "rp" } else { "base" }
14376 } else {
14377 variant
14378 };
14379 static B567: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14386 let b567 = *B567.get_or_init(|| std::env::var("MEMRA_B567").as_deref() != Ok("0"));
14387 if b567
14388 && qtype == QT_NVFP4
14389 && rp
14390 && mcols == 8
14391 && (5..=7).contains(&m)
14392 && matches!(variant, "rpsc" | "rpr2w8")
14393 {
14394 let f = self.func(&format!("qmatvec_nvfp4_mmvq_b{m}_{variant}"));
14395 let rows_per_block = ROWS_PER_BLOCK * 2; let mut y = self.alloc_uninit::<f32>(m * out_f)?;
14397 let cfg = LaunchConfig {
14398 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
14399 block_dim: (32, ROWS_PER_BLOCK, 1),
14400 shared_mem_bytes: 0,
14401 };
14402 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14403 let __s_b = self.gpu.stream();
14404 let mut b = __s_b.launch_builder(&f);
14405 b.arg(bytes)
14406 .arg(aq)
14407 .arg(ad)
14408 .arg(&mut y)
14409 .arg(&inf)
14410 .arg(&outf)
14411 .arg(&mi)
14412 .arg(&rb);
14413 unsafe {
14414 b.launch(cfg)?;
14415 }
14416 if scale != 1.0 {
14417 self.scale_inplace(&mut y, scale, m * out_f)?;
14418 }
14419 return Ok(y);
14420 }
14421 let (name, rows_per_block): (std::borrow::Cow<'static, str>, u32) = match variant {
14422 "base" => (base_name.into(), ROWS_PER_BLOCK),
14423 "pf" => (format!("{base_name}_pf").into(), ROWS_PER_BLOCK),
14424 "ca" => (format!("{base_name}_ca").into(), ROWS_PER_BLOCK),
14425 "rp" => (format!("{base_name}_rp").into(), ROWS_PER_BLOCK),
14426 "rpca" => (format!("{base_name}_rpca").into(), ROWS_PER_BLOCK), "rpks" => (format!("{base_name}_rpks").into(), ROWS_PER_BLOCK),
14430 "rpksc" => (format!("{base_name}_rpksc").into(), ROWS_PER_BLOCK),
14431 "rpms" => (format!("{base_name}_rpms").into(), ROWS_PER_BLOCK),
14432 "rpmsc" => (format!("{base_name}_rpmsc").into(), ROWS_PER_BLOCK),
14433 "r2ms_rp" => (format!("{base_name}_r2ms_rp").into(), ROWS_PER_BLOCK),
14434 "r2sm_rp" => (format!("{base_name}_r2sm_rp").into(), ROWS_PER_BLOCK * 2),
14435 "r2la_rp" => (format!("{base_name}_r2la_rp").into(), ROWS_PER_BLOCK * 2),
14436 v => (format!("{base_name}_{v}").into(), ROWS_PER_BLOCK * 2), };
14438 debug_assert!(
14439 !rp || name.contains("_rp"),
14440 "rp weight dispatched to a GGUF-layout kernel"
14441 );
14442 let f = self.func(&name);
14443 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
14444 let smem = if name.contains("_r2sm_rp") {
14446 (mcols * 32 * 9 * 4 + mcols * 32 * 4) as u32
14447 } else {
14448 0
14449 };
14450 let cfg = LaunchConfig {
14451 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
14452 block_dim: (32, ROWS_PER_BLOCK, 1),
14453 shared_mem_bytes: smem,
14454 };
14455 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14456 let __s_b = self.gpu.stream();
14457 let mut b = __s_b.launch_builder(&f);
14458 b.arg(bytes)
14459 .arg(aq)
14460 .arg(ad)
14461 .arg(&mut y)
14462 .arg(&inf)
14463 .arg(&outf)
14464 .arg(&mi)
14465 .arg(&rb);
14466 unsafe {
14467 b.launch(cfg)?;
14468 }
14469 if scale != 1.0 {
14470 self.scale_inplace(&mut y, scale, m * out_f)?;
14471 }
14472 Ok(y)
14473 }
14474
14475 pub fn qmatvec_batched_raw(
14479 &self,
14480 bytes: &CudaSlice<u8>,
14481 x: &CudaSlice<f32>,
14482 m: usize,
14483 in_f: usize,
14484 out_f: usize,
14485 qtype: i32,
14486 row_bytes: usize,
14487 mcols: usize,
14488 rp: bool,
14489 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14490 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
14491 self.qmatvec_mmvq_batched(
14492 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, 1.0, rp,
14493 )
14494 }
14495
14496 pub fn qmatvec_nvfp4_batched_raw(
14498 &self,
14499 bytes: &CudaSlice<u8>,
14500 x: &CudaSlice<f32>,
14501 m: usize,
14502 in_f: usize,
14503 out_f: usize,
14504 row_bytes: usize,
14505 mcols: usize,
14506 rp: bool,
14507 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14508 self.qmatvec_batched_raw(bytes, x, m, in_f, out_f, QT_NVFP4, row_bytes, mcols, rp)
14509 }
14510
14511 fn try_fp4_gemm(
14515 &self,
14516 w: &crate::model::GpuTensor,
14517 x: &CudaSlice<f32>,
14518 m: usize,
14519 in_f: usize,
14520 out_f: usize,
14521 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
14522 use crate::model::GpuTensor;
14523 if cfg!(memra_portable_cuda) {
14524 return Ok(None);
14525 }
14526 if std::env::var("MEMRA_FP4").is_err() {
14527 return Ok(None);
14528 }
14529 #[cfg(memra_cutlass)]
14538 if m >= 128 && std::env::var("MEMRA_FP4_CUTLASS").is_ok() {
14539 if let GpuTensor::Quant {
14540 bytes,
14541 qtype,
14542 scale,
14543 row_bytes,
14544 cutlass,
14545 ..
14546 } = w
14547 {
14548 if *qtype == QT_NVFP4 && in_f % 64 == 0 {
14549 if let Some(cw) = cutlass {
14550 let y = self.cutlass_fp4_gemm(
14552 &cw.b_packed,
14553 &cw.sfb_swizzled,
14554 x,
14555 *scale,
14556 m,
14557 out_f,
14558 in_f,
14559 )?;
14560 return Ok(Some(y));
14561 } else if std::env::var("MEMRA_FP4_CUTLASS_OTF").is_ok() {
14562 let (b_packed, sfb_sw) =
14567 self.build_cutlass_weight(bytes, out_f, in_f, *row_bytes)?;
14568 let y =
14569 self.cutlass_fp4_gemm(&b_packed, &sfb_sw, x, *scale, m, out_f, in_f)?;
14570 return Ok(Some(y));
14571 }
14572 }
14573 }
14574 }
14575 if let GpuTensor::Quant {
14576 bytes,
14577 qtype,
14578 row_bytes,
14579 scale,
14580 rp,
14581 ..
14582 } = w
14583 {
14584 if *qtype == QT_NVFP4 && in_f % 64 == 0 && !*rp {
14587 let y =
14588 self.qmatvec_gemm_nvfp4_fp4(bytes, x, m, in_f, out_f, *row_bytes, *scale)?;
14589 return Ok(Some(y));
14590 }
14591 }
14592 Ok(None)
14593 }
14594
14595 pub fn rms_norm_f16out(
14599 &self,
14600 x: &CudaSlice<f32>,
14601 w: &CudaSlice<f32>,
14602 dst: &mut CudaSlice<f32>,
14603 dst16: &mut CudaSlice<u8>,
14604 ncols: usize,
14605 nrows: usize,
14606 eps: f32,
14607 ) -> Result<(), Box<dyn std::error::Error>> {
14608 let f = self.func("rms_norm_f16out_f32");
14609 let cfg = LaunchConfig {
14610 grid_dim: (nrows as u32, 1, 1),
14611 block_dim: (rms_block(), 1, 1),
14612 shared_mem_bytes: 0,
14613 };
14614 let (nc, e) = (ncols as i32, eps);
14615 let __s_b = self.gpu.stream();
14616 let mut b = __s_b.launch_builder(&f);
14617 b.arg(x).arg(w).arg(dst).arg(dst16).arg(&nc).arg(&e);
14618 unsafe {
14619 b.launch(cfg)?;
14620 }
14621 Ok(())
14622 }
14623
14624 #[allow(clippy::too_many_arguments)]
14627 pub fn add_rms_norm_f16out(
14628 &self,
14629 a: &CudaSlice<f32>,
14630 b: &CudaSlice<f32>,
14631 w: &CudaSlice<f32>,
14632 res: &mut CudaSlice<f32>,
14633 dst: &mut CudaSlice<f32>,
14634 dst16: &mut CudaSlice<u8>,
14635 ncols: usize,
14636 nrows: usize,
14637 eps: f32,
14638 ) -> Result<(), Box<dyn std::error::Error>> {
14639 let f = self.func("add_rms_norm_f16out_f32");
14640 let cfg = LaunchConfig {
14641 grid_dim: (nrows as u32, 1, 1),
14642 block_dim: (rms_block(), 1, 1),
14643 shared_mem_bytes: 0,
14644 };
14645 let (nc, e) = (ncols as i32, eps);
14646 let __s_lb = self.gpu.stream();
14647 let mut lb = __s_lb.launch_builder(&f);
14648 lb.arg(a)
14649 .arg(b)
14650 .arg(w)
14651 .arg(res)
14652 .arg(dst)
14653 .arg(dst16)
14654 .arg(&nc)
14655 .arg(&e);
14656 unsafe {
14657 lb.launch(cfg)?;
14658 }
14659 Ok(())
14660 }
14661
14662 pub fn matmul_group_xh(
14665 &self,
14666 ws: &[&crate::model::GpuTensor],
14667 x: &CudaSlice<f32>,
14668 xh: &CudaSlice<u8>,
14669 m: usize,
14670 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
14671 let mut out = Vec::with_capacity(ws.len());
14672 let in_f = ws[0].in_features();
14673 for w in ws {
14674 if w.in_features() == in_f && m >= 16 && !self.verify_exact_on() {
14675 if let Some(y) = self.try_f16_gemm_pre(w, xh, m)? {
14676 out.push(y);
14677 continue;
14678 }
14679 }
14680 out.push(self.matmul(w, x, m)?);
14681 }
14682 Ok(out)
14683 }
14684
14685 pub fn gdn_pad_mask(
14688 &self,
14689 beta: &mut CudaSlice<f32>,
14690 g_log: &mut CudaSlice<f32>,
14691 len_d: &CudaSlice<i32>,
14692 h: usize,
14693 t: usize,
14694 ) -> Result<(), Box<dyn std::error::Error>> {
14695 let f = self.func("gdn_pad_mask_f32");
14696 let cfg = LaunchConfig::for_num_elems((t * h) as u32);
14697 let (hi, ti) = (h as i32, t as i32);
14698 let __s_b = self.gpu.stream();
14699 let mut b = __s_b.launch_builder(&f);
14700 b.arg(beta).arg(g_log).arg(len_d).arg(&hi).arg(&ti);
14701 unsafe {
14702 b.launch(cfg)?;
14703 }
14704 Ok(())
14705 }
14706
14707 pub fn row_gather_dev(
14710 &self,
14711 src: &CudaSlice<f32>,
14712 dst: &mut CudaSlice<f32>,
14713 len_d: &CudaSlice<i32>,
14714 ncols: usize,
14715 ) -> Result<(), Box<dyn std::error::Error>> {
14716 let f = self.func("row_gather_dev_f32");
14717 let cfg = LaunchConfig::for_num_elems(ncols as u32);
14718 let nc = ncols as i32;
14719 let __s_b = self.gpu.stream();
14720 let mut b = __s_b.launch_builder(&f);
14721 b.arg(src).arg(dst).arg(len_d).arg(&nc);
14722 unsafe {
14723 b.launch(cfg)?;
14724 }
14725 Ok(())
14726 }
14727
14728 pub fn matmul_group(
14735 &self,
14736 ws: &[&crate::model::GpuTensor],
14737 x: &CudaSlice<f32>,
14738 m: usize,
14739 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
14740 use crate::model::GpuTensor;
14741 let mut out = Vec::with_capacity(ws.len());
14742 let any_mirror = ws
14743 .iter()
14744 .any(|w| matches!(w, GpuTensor::Quant { f16: Some(_), .. }));
14745 if m >= 16 && any_mirror && !self.verify_exact_on() {
14746 let in_f = ws[0].in_features();
14747 let xh = self.f16_act(x, m * in_f, in_f)?;
14748 for w in ws {
14749 if w.in_features() == in_f {
14750 if let Some(y) = self.try_f16_gemm_pre(w, &xh, m)? {
14751 out.push(y);
14752 continue;
14753 }
14754 }
14755 out.push(self.matmul(w, x, m)?);
14756 }
14757 return Ok(out);
14758 }
14759 for w in ws {
14760 out.push(self.matmul(w, x, m)?);
14761 }
14762 Ok(out)
14763 }
14764
14765 pub fn matmul_group_multi(
14772 &self,
14773 ws: &[&crate::model::GpuTensor],
14774 xs: &[&CudaSlice<f32>],
14775 ms: &[usize],
14776 ) -> Result<Vec<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
14777 assert_eq!(xs.len(), ms.len());
14778 let in_f = ws[0].in_features();
14779 let total: usize = ms.iter().sum();
14780 let mut xcat = self.uninit(total * in_f)?;
14781 let mut off = 0usize;
14782 for (x, &m) in xs.iter().zip(ms) {
14783 self.copy_into(&mut xcat, off * in_f, x, m * in_f)?;
14784 off += m;
14785 }
14786 let ys = self.matmul_group(ws, &xcat, total)?;
14787 let mut out: Vec<Vec<CudaSlice<f32>>> = (0..xs.len()).map(|_| Vec::new()).collect();
14788 for (w, y) in ws.iter().zip(ys) {
14789 let out_f = w.out_features();
14790 let mut off = 0usize;
14791 for (s, &m) in ms.iter().enumerate() {
14792 let mut ys_s = self.uninit(m * out_f)?;
14793 let src = y.slice(off * out_f..(off + m) * out_f);
14794 self.gpu.stream().memcpy_dtod(&src, &mut ys_s)?;
14795 out[s].push(ys_s);
14796 off += m;
14797 }
14798 }
14799 Ok(out)
14800 }
14801
14802 pub fn gemm_supports(&self, w: &crate::model::GpuTensor) -> bool {
14812 use crate::model::GpuTensor;
14813 if !legacy_quant_gemm_allowed(
14814 cfg!(memra_portable_cuda),
14815 cfg!(memra_hopper_mma),
14816 std::env::var_os("MEMRA_NO_GEMM").is_some(),
14817 ) {
14818 return false;
14819 }
14820 match w {
14821 GpuTensor::Quant { qtype, .. } => {
14822 matches!(*qtype, QT_Q8_0 | QT_Q4_K | QT_Q6_K | QT_Q5_K | QT_Q4_0)
14823 || (*qtype == QT_NVFP4 && w.in_features() % 64 == 0)
14824 }
14825 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
14826 }
14827 }
14828
14829 pub fn qmatvec_gemm(
14836 &self,
14837 w: &crate::model::GpuTensor,
14838 aq: &CudaSlice<i8>,
14839 ad: &CudaSlice<f32>,
14840 m: usize,
14841 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14842 use crate::model::GpuTensor;
14843 let in_f = w.in_features();
14844 let out_f = w.out_features();
14845 let (bytes, qtype, row_bytes, scale, rp) = match w {
14846 GpuTensor::Quant {
14847 bytes,
14848 qtype,
14849 row_bytes,
14850 scale,
14851 rp,
14852 ..
14853 } => (bytes, *qtype, *row_bytes, *scale, *rp),
14854 _ => unreachable!("gemm_supports guaranteed Quant"),
14855 };
14856 if cfg!(memra_hopper_mma) && qtype == QT_Q8_0 && out_f % 64 == 0 && wgmma_gemm_enabled() {
14862 if let GpuTensor::Quant { rp4: Some(m4), .. } = w {
14863 let mut y = self.qmatvec_gemm_q8_0_wgmma_raw(m4, aq, ad, m, in_f, out_f)?;
14864 if scale != 1.0 {
14865 self.scale_inplace(&mut y, scale, m * out_f)?;
14866 }
14867 return Ok(y);
14868 }
14869 }
14870 let name = match qtype {
14871 QT_Q8_0 => "qmatvec_gemm_q8_0",
14872 QT_Q4_K => "qmatvec_gemm_q4_K",
14873 QT_Q4_0 => {
14874 if rp {
14875 "qmatvec_gemm_q4_0_rp"
14876 } else {
14877 "qmatvec_gemm_q4_0"
14878 }
14879 }
14880 QT_Q5_K => "qmatvec_gemm_q5_K",
14881 QT_Q6_K => "qmatvec_gemm_q6_K",
14882 QT_NVFP4 => {
14883 if rp {
14884 "qmatvec_gemm_nvfp4_rp"
14885 } else {
14886 "qmatvec_gemm_nvfp4"
14887 }
14888 }
14889 _ => unreachable!(),
14890 };
14891 let f = self.func(name);
14892 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);
14897 let k1_tile = if is_k1 {
14899 k1_launch_override().unwrap_or((128, 128, 8))
14900 } else {
14901 (128, 128, 8)
14902 };
14903 let (bm, bn): (u32, u32) = if is_k1 {
14904 (k1_tile.0, k1_tile.1)
14905 } else {
14906 (64, 256)
14907 };
14908 let warps: u32 = if is_k1 {
14909 k1_tile.2
14910 } else {
14911 match qtype {
14912 QT_NVFP4 => 8,
14913 _ => 4,
14914 }
14915 };
14916 let cfg = LaunchConfig {
14917 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
14918 block_dim: (32, warps, 1),
14919 shared_mem_bytes: 0,
14920 };
14921 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14922 let __s_b = self.gpu.stream();
14923 let mut b = __s_b.launch_builder(&f);
14924 b.arg(bytes)
14925 .arg(aq)
14926 .arg(ad)
14927 .arg(&mut y)
14928 .arg(&inf)
14929 .arg(&outf)
14930 .arg(&mi)
14931 .arg(&rb);
14932 unsafe {
14933 b.launch(cfg)?;
14934 }
14935 if scale != 1.0 {
14936 self.scale_inplace(&mut y, scale, m * out_f)?;
14937 }
14938 Ok(y)
14939 }
14940
14941 pub fn qmatvec_gemm_raw(
14946 &self,
14947 bytes: &CudaSlice<u8>,
14948 x: &CudaSlice<f32>,
14949 m: usize,
14950 in_f: usize,
14951 out_f: usize,
14952 qtype: i32,
14953 row_bytes: usize,
14954 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14955 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
14956 let name = match qtype {
14957 QT_Q8_0 => "qmatvec_gemm_q8_0",
14958 QT_Q4_K => "qmatvec_gemm_q4_K",
14959 QT_Q4_0 => "qmatvec_gemm_q4_0",
14960 QT_Q5_K => "qmatvec_gemm_q5_K",
14961 QT_Q6_K => "qmatvec_gemm_q6_K",
14962 QT_NVFP4 => "qmatvec_gemm_nvfp4",
14963 QT_NVFP4_RP => "qmatvec_gemm_nvfp4_rp",
14964 _ => panic!("qmatvec_gemm_raw: qtype {qtype} has no GEMM kernel"),
14965 };
14966 let f = self.func(name);
14967 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);
14971 let k1_tile = if is_k1 {
14973 k1_launch_override().unwrap_or((128, 128, 8))
14974 } else {
14975 (128, 128, 8)
14976 };
14977 let (bm, bn): (u32, u32) = if is_k1 {
14978 (k1_tile.0, k1_tile.1)
14979 } else {
14980 (64, 256)
14981 };
14982 let warps: u32 = if is_k1 {
14983 k1_tile.2
14984 } else {
14985 match qtype {
14986 QT_NVFP4 | QT_NVFP4_RP => 8,
14987 _ => 4,
14988 }
14989 };
14990 let cfg = LaunchConfig {
14991 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
14992 block_dim: (32, warps, 1),
14993 shared_mem_bytes: 0,
14994 };
14995 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14996 let __s_b = self.gpu.stream();
14997 let mut b = __s_b.launch_builder(&f);
14998 b.arg(bytes)
14999 .arg(&aq)
15000 .arg(&ad)
15001 .arg(&mut y)
15002 .arg(&inf)
15003 .arg(&outf)
15004 .arg(&mi)
15005 .arg(&rb);
15006 unsafe {
15007 b.launch(cfg)?;
15008 }
15009 Ok(y)
15010 }
15011
15012 pub fn qmatvec_gemm_q8_0_wgmma_raw(
15019 &self,
15020 rp4: &CudaSlice<u8>,
15021 aq: &CudaSlice<i8>,
15022 ad: &CudaSlice<f32>,
15023 m: usize,
15024 in_f: usize,
15025 out_f: usize,
15026 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15027 assert!(
15028 out_f % 64 == 0 && in_f % 32 == 0,
15029 "wgmma GEMM needs out_f%64==0, in_f%32==0"
15030 );
15031 let f = self.func("qmatvec_gemm_q8_0_wgmma");
15032 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
15034 grid_dim: ((out_f / 64) as u32, (m as u32).div_ceil(64), 1),
15035 block_dim: (128, 1, 1),
15036 shared_mem_bytes: 0,
15037 };
15038 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
15039 let __s_b = self.gpu.stream();
15040 let mut b = __s_b.launch_builder(&f);
15041 b.arg(rp4)
15042 .arg(aq)
15043 .arg(ad)
15044 .arg(&mut y)
15045 .arg(&inf)
15046 .arg(&outf)
15047 .arg(&mi);
15048 unsafe {
15049 b.launch(cfg)?;
15050 }
15051 Ok(y)
15052 }
15053
15054 pub fn scale_inplace(
15056 &self,
15057 y: &mut CudaSlice<f32>,
15058 s: f32,
15059 n: usize,
15060 ) -> Result<(), Box<dyn std::error::Error>> {
15061 let f = self.func("scale_f32");
15062 let cfg = LaunchConfig::for_num_elems(n as u32);
15063 let (sf, ni) = (s, n as i32);
15064 let __s_b = self.gpu.stream();
15065 let mut b = __s_b.launch_builder(&f);
15066 b.arg(y).arg(&sf).arg(&ni);
15067 unsafe {
15068 b.launch(cfg)?;
15069 }
15070 Ok(())
15071 }
15072
15073 pub fn bf16_to_f32(
15078 &self,
15079 data: &cudarc::driver::CudaView<'_, u8>,
15080 n: usize,
15081 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15082 let mut out = self.alloc_uninit::<f32>(n)?;
15083 let f = self.func("bf16_to_f32");
15084 let cfg = LaunchConfig::for_num_elems(n as u32);
15085 let ni = n as i32;
15086 let __s_b = self.gpu.stream();
15087 let mut b = __s_b.launch_builder(&f);
15088 b.arg(data).arg(&mut out).arg(&ni);
15089 unsafe {
15090 b.launch(cfg)?;
15091 }
15092 Ok(out)
15093 }
15094
15095 fn linear_bf16_chunked(
15102 &self,
15103 x: &CudaSlice<f32>,
15104 data: &CudaSlice<u8>,
15105 m: usize,
15106 in_f: usize,
15107 out_f: usize,
15108 exact: bool,
15109 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15110 const CHUNK_BYTES: usize = 256 << 20;
15111 let chunk_rows = (CHUNK_BYTES / (in_f * 4)).max(1).min(out_f);
15112 if chunk_rows >= out_f {
15113 let wf32 = self.bf16_to_f32(&data.slice(0..in_f * out_f * 2), in_f * out_f)?;
15114 return if exact {
15115 self.linear_decode_exact(x, &wf32, m, in_f, out_f)
15116 } else {
15117 self.linear(x, &wf32, m, in_f, out_f)
15118 };
15119 }
15120 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
15121 let mut r0 = 0usize;
15122 while r0 < out_f {
15123 let rows = chunk_rows.min(out_f - r0);
15124 let wslice = data.slice(r0 * in_f * 2..(r0 + rows) * in_f * 2);
15125 let wf32 = self.bf16_to_f32(&wslice, in_f * rows)?;
15126 let yc = if exact {
15127 self.linear_decode_exact(x, &wf32, m, in_f, rows)?
15128 } else {
15129 self.linear(x, &wf32, m, in_f, rows)?
15130 };
15131 for mi in 0..m {
15133 let src = yc.slice(mi * rows..(mi + 1) * rows);
15134 let mut dst = y.slice_mut(mi * out_f + r0..mi * out_f + r0 + rows);
15135 self.gpu.stream().memcpy_dtod(&src, &mut dst)?;
15136 }
15137 r0 += rows;
15138 }
15139 Ok(y)
15140 }
15141
15142 pub fn linear_decode_exact(
15149 &self,
15150 x: &CudaSlice<f32>,
15151 w: &CudaSlice<f32>,
15152 m_tokens: usize,
15153 in_f: usize,
15154 out_f: usize,
15155 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15156 if m_tokens == 1 {
15157 return self.linear(x, w, 1, in_f, out_f);
15158 }
15159 let xv = self.view(x, m_tokens * in_f);
15160 let mut y = self.alloc_uninit::<f32>(m_tokens * out_f)?;
15161 for t in 0..m_tokens {
15162 let row = xv.slice(t * in_f..(t + 1) * in_f);
15163 let mut xr = self.alloc_uninit::<f32>(in_f)?;
15164 self.copy_view_into(&mut xr, 0, &row, in_f)?;
15165 let yr = self.linear(&xr, w, 1, in_f, out_f)?;
15166 self.copy_into(&mut y, t * out_f, &yr, out_f)?;
15167 }
15168 Ok(y)
15169 }
15170
15171 pub fn linear(
15172 &self,
15173 x: &CudaSlice<f32>,
15174 w: &CudaSlice<f32>,
15175 m_tokens: usize,
15176 in_f: usize,
15177 out_f: usize,
15178 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15179 use cudarc::cublaslt::{Matmul, MatmulConfig};
15180 let mut c = self.alloc_uninit::<f32>(m_tokens * out_f)?; let cfg = MatmulConfig {
15182 transa: true,
15183 transb: false,
15184 transc: false,
15185 m: out_f as u64,
15186 n: m_tokens as u64,
15187 k: in_f as u64,
15188 alpha: 1.0,
15189 lda: in_f as i64,
15190 ldb: in_f as i64,
15191 beta: 0.0,
15192 ldc: out_f as i64,
15193 stride_a: None,
15194 stride_b: None,
15195 stride_c: None,
15196 stride_bias: None,
15197 batch_size: None,
15198 };
15199 unsafe {
15200 self.gpu.blas.matmul(cfg, w, x, &mut c, None, None)?;
15201 }
15202 Ok(c)
15203 }
15204
15205 pub fn sdpa_naive(
15207 &self,
15208 q: &CudaSlice<f32>,
15209 k: &CudaSlice<f32>,
15210 v: &CudaSlice<f32>,
15211 o: &mut CudaSlice<f32>,
15212 head_dim: usize,
15213 n_head: usize,
15214 n_head_kv: usize,
15215 t: usize,
15216 t_kv: usize,
15217 scale: f32,
15218 causal: bool,
15219 ) -> Result<(), Box<dyn std::error::Error>> {
15220 let f = self.func("sdpa_naive_f32");
15221 let cfg = LaunchConfig {
15222 grid_dim: (n_head as u32, t as u32, 1),
15223 block_dim: (128, 1, 1),
15224 shared_mem_bytes: (t_kv * 4) as u32,
15225 };
15226 let (hd, nh, nhkv, ti, tkvi, cz) = (
15227 head_dim as i32,
15228 n_head as i32,
15229 n_head_kv as i32,
15230 t as i32,
15231 t_kv as i32,
15232 causal as i32,
15233 );
15234 let __s_b = self.gpu.stream();
15235 let mut b = __s_b.launch_builder(&f);
15236 b.arg(q)
15237 .arg(k)
15238 .arg(v)
15239 .arg(o)
15240 .arg(&hd)
15241 .arg(&nh)
15242 .arg(&nhkv)
15243 .arg(&ti)
15244 .arg(&tkvi)
15245 .arg(&scale)
15246 .arg(&cz);
15247 unsafe {
15248 b.launch(cfg)?;
15249 }
15250 Ok(())
15251 }
15252
15253 #[allow(clippy::too_many_arguments)]
15258 pub fn sdpa_naive_island(
15259 &self,
15260 q: &CudaSlice<f32>,
15261 k: &CudaSlice<f32>,
15262 v: &CudaSlice<f32>,
15263 o: &mut CudaSlice<f32>,
15264 span_id: &CudaSlice<i32>,
15265 head_dim: usize,
15266 n_head: usize,
15267 n_head_kv: usize,
15268 t: usize,
15269 t_kv: usize,
15270 scale: f32,
15271 window: usize,
15272 ) -> Result<(), Box<dyn std::error::Error>> {
15273 let f = self.func("sdpa_naive_island_f32");
15274 let cfg = LaunchConfig {
15275 grid_dim: (n_head as u32, t as u32, 1),
15276 block_dim: (128, 1, 1),
15277 shared_mem_bytes: (t_kv * 4) as u32,
15278 };
15279 let (hd, nh, nhkv, ti, tkvi, wi) = (
15280 head_dim as i32,
15281 n_head as i32,
15282 n_head_kv as i32,
15283 t as i32,
15284 t_kv as i32,
15285 window as i32,
15286 );
15287 let __s_b = self.gpu.stream();
15288 let mut b = __s_b.launch_builder(&f);
15289 b.arg(q)
15290 .arg(k)
15291 .arg(v)
15292 .arg(o)
15293 .arg(span_id)
15294 .arg(&hd)
15295 .arg(&nh)
15296 .arg(&nhkv)
15297 .arg(&ti)
15298 .arg(&tkvi)
15299 .arg(&scale)
15300 .arg(&wi);
15301 unsafe {
15302 b.launch(cfg)?;
15303 }
15304 Ok(())
15305 }
15306
15307 #[allow(clippy::too_many_arguments)]
15309 pub fn sdpa_naive_w(
15310 &self,
15311 q: &CudaSlice<f32>,
15312 k: &CudaSlice<f32>,
15313 v: &CudaSlice<f32>,
15314 o: &mut CudaSlice<f32>,
15315 head_dim: usize,
15316 n_head: usize,
15317 n_head_kv: usize,
15318 t: usize,
15319 t_kv: usize,
15320 scale: f32,
15321 causal: bool,
15322 window: usize,
15323 ) -> Result<(), Box<dyn std::error::Error>> {
15324 let f = self.func("sdpa_naive_w_f32");
15325 let cfg = LaunchConfig {
15326 grid_dim: (n_head as u32, t as u32, 1),
15327 block_dim: (128, 1, 1),
15328 shared_mem_bytes: (t_kv * 4) as u32,
15329 };
15330 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15331 head_dim as i32,
15332 n_head as i32,
15333 n_head_kv as i32,
15334 t as i32,
15335 t_kv as i32,
15336 causal as i32,
15337 window as i32,
15338 );
15339 let __s_b = self.gpu.stream();
15340 let mut b = __s_b.launch_builder(&f);
15341 b.arg(q)
15342 .arg(k)
15343 .arg(v)
15344 .arg(o)
15345 .arg(&hd)
15346 .arg(&nh)
15347 .arg(&nhkv)
15348 .arg(&ti)
15349 .arg(&tkvi)
15350 .arg(&scale)
15351 .arg(&cz)
15352 .arg(&wi);
15353 unsafe {
15354 b.launch(cfg)?;
15355 }
15356 Ok(())
15357 }
15358
15359 pub fn sdpa_naive_view(
15361 &self,
15362 q: &CudaSlice<f32>,
15363 k: &cudarc::driver::CudaView<f32>,
15364 v: &cudarc::driver::CudaView<f32>,
15365 o: &mut CudaSlice<f32>,
15366 head_dim: usize,
15367 n_head: usize,
15368 n_head_kv: usize,
15369 t: usize,
15370 t_kv: usize,
15371 scale: f32,
15372 causal: bool,
15373 ) -> Result<(), Box<dyn std::error::Error>> {
15374 let f = self.func("sdpa_naive_f32");
15375 let cfg = LaunchConfig {
15376 grid_dim: (n_head as u32, t as u32, 1),
15377 block_dim: (128, 1, 1),
15378 shared_mem_bytes: (t_kv * 4) as u32,
15379 };
15380 let (hd, nh, nhkv, ti, tkvi, cz) = (
15381 head_dim as i32,
15382 n_head as i32,
15383 n_head_kv as i32,
15384 t as i32,
15385 t_kv as i32,
15386 causal as i32,
15387 );
15388 let __s_b = self.gpu.stream();
15389 let mut b = __s_b.launch_builder(&f);
15390 b.arg(q)
15391 .arg(k)
15392 .arg(v)
15393 .arg(o)
15394 .arg(&hd)
15395 .arg(&nh)
15396 .arg(&nhkv)
15397 .arg(&ti)
15398 .arg(&tkvi)
15399 .arg(&scale)
15400 .arg(&cz);
15401 unsafe {
15402 b.launch(cfg)?;
15403 }
15404 Ok(())
15405 }
15406
15407 #[allow(clippy::too_many_arguments)]
15415 pub fn fa_dequant_kv_view_f32(
15416 &self,
15417 k: &cudarc::driver::CudaView<u8>,
15418 v: &cudarc::driver::CudaView<u8>,
15419 kf: &mut CudaSlice<f32>,
15420 vf: &mut CudaSlice<f32>,
15421 kv_dim_k: usize,
15422 kv_dim_v: usize,
15423 t_kv: usize,
15424 k_tok_bytes: usize,
15425 v_tok_bytes: usize,
15426 g: bool,
15427 ) -> Result<(), Box<dyn std::error::Error>> {
15428 let f = if g {
15429 self.func_g("fa_dequant_kv_ws_f32")
15430 } else {
15431 self.func("fa_dequant_kv_ws_f32")
15432 };
15433 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
15434 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
15435 let cfg = LaunchConfig {
15436 grid_dim: (nblk.max(1), 1, 1),
15437 block_dim: (256, 1, 1),
15438 shared_mem_bytes: 0,
15439 };
15440 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
15441 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
15442 let __s_b = self.gpu.stream();
15443 let mut b = __s_b.launch_builder(&f);
15444 b.arg(k)
15445 .arg(v)
15446 .arg(&mut *kf)
15447 .arg(&mut *vf)
15448 .arg(&kdk)
15449 .arg(&kdv)
15450 .arg(&tkvi)
15451 .arg(&ktb)
15452 .arg(&vtb);
15453 unsafe {
15454 b.launch(cfg)?;
15455 }
15456 Ok(())
15457 }
15458
15459 #[allow(clippy::too_many_arguments)]
15460 pub fn sdpa_naive_quantized_view(
15461 &self,
15462 q: &CudaSlice<f32>,
15463 k: &cudarc::driver::CudaView<u8>,
15464 v: &cudarc::driver::CudaView<u8>,
15465 o: &mut CudaSlice<f32>,
15466 head_dim: usize,
15467 n_head: usize,
15468 n_head_kv: usize,
15469 t: usize,
15470 t_kv: usize,
15471 scale: f32,
15472 causal: bool,
15473 k_tok_bytes: usize,
15474 v_tok_bytes: usize,
15475 ) -> Result<(), Box<dyn std::error::Error>> {
15476 let kv_dim = n_head_kv * head_dim;
15477 let mut kf = self.uninit(t_kv * kv_dim)?;
15478 let mut vf = self.uninit(t_kv * kv_dim)?;
15479 let f = self.func("fa_dequant_kv_ws_f32");
15480 let total = (2 * t_kv * kv_dim) as u64;
15481 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
15482 let cfg = LaunchConfig {
15483 grid_dim: (nblk.max(1), 1, 1),
15484 block_dim: (256, 1, 1),
15485 shared_mem_bytes: 0,
15486 };
15487 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
15488 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
15489 let __s_b = self.gpu.stream();
15490 let mut b = __s_b.launch_builder(&f);
15491 b.arg(k)
15492 .arg(v)
15493 .arg(&mut kf)
15494 .arg(&mut vf)
15495 .arg(&kv_dim_i)
15496 .arg(&kv_dim_i)
15497 .arg(&t_kv_i)
15498 .arg(&k_tok_bytes_i)
15499 .arg(&v_tok_bytes_i);
15500 unsafe { b.launch(cfg)? };
15501 self.sdpa_naive(
15502 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
15503 )
15504 }
15505
15506 #[allow(clippy::too_many_arguments)]
15518 pub fn sdpa_naive_w_quantized_view(
15519 &self,
15520 q: &CudaSlice<f32>,
15521 k: &cudarc::driver::CudaView<u8>,
15522 v: &cudarc::driver::CudaView<u8>,
15523 o: &mut CudaSlice<f32>,
15524 head_dim: usize,
15525 n_head: usize,
15526 n_head_kv: usize,
15527 t: usize,
15528 t_kv: usize,
15529 scale: f32,
15530 causal: bool,
15531 window: usize,
15532 k_tok_bytes: usize,
15533 v_tok_bytes: usize,
15534 ) -> Result<(), Box<dyn std::error::Error>> {
15535 let kv_dim = n_head_kv * head_dim;
15536 let mut kf = self.uninit(t_kv * kv_dim)?;
15537 let mut vf = self.uninit(t_kv * kv_dim)?;
15538 let f = self.func("fa_dequant_kv_ws_f32");
15539 let total = (2 * t_kv * kv_dim) as u64;
15540 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
15541 let cfg = LaunchConfig {
15542 grid_dim: (nblk.max(1), 1, 1),
15543 block_dim: (256, 1, 1),
15544 shared_mem_bytes: 0,
15545 };
15546 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
15547 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
15548 let __s_b = self.gpu.stream();
15549 let mut b = __s_b.launch_builder(&f);
15550 b.arg(k)
15551 .arg(v)
15552 .arg(&mut kf)
15553 .arg(&mut vf)
15554 .arg(&kv_dim_i)
15555 .arg(&kv_dim_i)
15556 .arg(&t_kv_i)
15557 .arg(&k_tok_bytes_i)
15558 .arg(&v_tok_bytes_i);
15559 unsafe { b.launch(cfg)? };
15560 self.sdpa_naive_w(
15561 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
15562 )
15563 }
15564
15565 pub fn fa_prefill(
15569 &self,
15570 q: &CudaSlice<f32>,
15571 k: &CudaSlice<f32>,
15572 v: &CudaSlice<f32>,
15573 o: &mut CudaSlice<f32>,
15574 head_dim: usize,
15575 n_head: usize,
15576 n_head_kv: usize,
15577 t: usize,
15578 t_kv: usize,
15579 scale: f32,
15580 causal: bool,
15581 ) -> Result<(), Box<dyn std::error::Error>> {
15582 if portable_mma_gated() {
15583 return self.sdpa_naive(
15584 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
15585 );
15586 }
15587 let fa3_on = head_dim == 256
15595 && causal
15596 && t == t_kv
15597 && match std::env::var("MEMRA_FA3").as_deref() {
15598 Ok("0") => false,
15599 Ok("1") => true,
15600 _ => cfg!(memra_hopper_mma),
15601 };
15602 if fa3_on {
15603 let n = t * n_head * head_dim;
15604 let nkv = t * n_head_kv * head_dim;
15605 let mut q16 = self.alloc_u8_uninit(n * 2)?;
15606 let mut k16 = self.alloc_u8_uninit(nkv * 2)?;
15607 let mut v16 = self.alloc_u8_uninit(nkv * 2)?;
15608 self.f32_to_bf16_into(q, &mut q16, n)?;
15609 self.f32_to_bf16_into(k, &mut k16, nkv)?;
15610 self.f32_to_bf16_into(v, &mut v16, nkv)?;
15611 let rc = {
15612 use cudarc::driver::{DevicePtr, DevicePtrMut};
15613 let stream = self.gpu.stream();
15614 let (qp, _g1) = q16.device_ptr(&stream);
15615 let (kp, _g2) = k16.device_ptr(&stream);
15616 let (vp, _g3) = v16.device_ptr(&stream);
15617 let (op, _g4) = o.device_ptr_mut(&stream);
15618 unsafe {
15619 memra_fa3_prefill(
15620 qp as *const core::ffi::c_void,
15621 kp as *const core::ffi::c_void,
15622 vp as *const core::ffi::c_void,
15623 op as *mut f32,
15624 t as i32,
15625 n_head as i32,
15626 n_head_kv as i32,
15627 head_dim as i32,
15628 scale,
15629 stream.cu_stream() as *mut core::ffi::c_void,
15630 )
15631 }
15632 };
15633 if rc != 0 {
15634 return Err(format!("memra_fa3_prefill rc={rc}").into());
15635 }
15636 return Ok(());
15637 }
15638 static FA_P1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15643 let fa_p1 = *FA_P1.get_or_init(|| std::env::var("MEMRA_FA_P1").as_deref() == Ok("1"));
15644 if fa_p1 && head_dim == 256 && !std::env::var("MEMRA_FA_FLOOR").is_ok() {
15645 const BLOCK_Q: usize = 64;
15646 const BKX: usize = 32;
15647 let f = self.func("fa_prefill_bf16_p1");
15648 let shmem = (2 * (2 * BKX * head_dim + BLOCK_Q * BKX)
15649 + 4 * (BLOCK_Q * BKX + 2 * BLOCK_Q)) as u32;
15650 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15651 f.set_attribute(
15652 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15653 shmem as i32,
15654 )?;
15655 let cfg = LaunchConfig {
15656 grid_dim: (
15657 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15658 n_head as u32,
15659 1,
15660 ),
15661 block_dim: (32, 4, 1),
15662 shared_mem_bytes: shmem,
15663 };
15664 let (hd, nh, nhkv, ti, tkvi, cz) = (
15665 head_dim as i32,
15666 n_head as i32,
15667 n_head_kv as i32,
15668 t as i32,
15669 t_kv as i32,
15670 causal as i32,
15671 );
15672 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15673 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15674 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15675 let __s_b = self.gpu.stream();
15676 let mut b = __s_b.launch_builder(&f);
15677 b.arg(&qb)
15678 .arg(&kb)
15679 .arg(&vb)
15680 .arg(o)
15681 .arg(&hd)
15682 .arg(&nh)
15683 .arg(&nhkv)
15684 .arg(&ti)
15685 .arg(&tkvi)
15686 .arg(&scale)
15687 .arg(&cz);
15688 unsafe {
15689 b.launch(cfg)?;
15690 }
15691 return Ok(());
15692 }
15693 const BK: usize = 32;
15699 let w2 = std::env::var("MEMRA_FA_PP_W2").as_deref() == Ok("1");
15702 let (block_q, warps, w2_sfx): (usize, u32, &str) =
15703 if w2 { (32, 2, "_w2") } else { (64, 4, "") };
15704 let hd_sfx = fa_hd_suffix(head_dim)?;
15708 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
15709 let bf16kv = !floor && !w2 && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0");
15714 let (kb16, vb16) = if bf16kv {
15715 let n = t_kv * n_head_kv * head_dim;
15716 let mut kb = self.alloc_u8_uninit(n * 2)?;
15717 let mut vb = self.alloc_u8_uninit(n * 2)?;
15718 let fcv = self.func("f32_to_bf16_bulk");
15719 let ni = n as i64;
15720 let cfgc = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
15721 let __s_b = self.gpu.stream();
15722 let mut b = __s_b.launch_builder(&fcv);
15723 b.arg(k).arg(&mut kb).arg(&ni);
15724 unsafe {
15725 b.launch(cfgc)?;
15726 }
15727 let __s_b = self.gpu.stream();
15728 let mut b = __s_b.launch_builder(&fcv);
15729 b.arg(v).arg(&mut vb).arg(&ni);
15730 unsafe {
15731 b.launch(cfgc)?;
15732 }
15733 (Some(kb), Some(vb))
15734 } else {
15735 (None, None)
15736 };
15737 let f = self.func(&if bf16kv {
15738 format!("fa_prefill_bf16kv_pp{hd_sfx}")
15739 } else {
15740 format!(
15741 "fa_prefill_f32{}{}{hd_sfx}",
15742 if floor { "" } else { "_pp" },
15743 if floor { "" } else { w2_sfx }
15744 )
15745 });
15746 let kv_stages = if bf16kv { 2 } else { 1 };
15749 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
15750 + 4 * (block_q * BK + 2 * block_q)) as u32;
15751 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15752 f.set_attribute(
15753 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15754 shmem as i32,
15755 )?;
15756 let cfg = LaunchConfig {
15757 grid_dim: (
15758 (t as u32 + block_q as u32 - 1) / block_q as u32,
15759 n_head as u32,
15760 1,
15761 ),
15762 block_dim: (32, warps, 1),
15763 shared_mem_bytes: shmem,
15764 };
15765 let (hd, nh, nhkv, ti, tkvi, cz) = (
15766 head_dim as i32,
15767 n_head as i32,
15768 n_head_kv as i32,
15769 t as i32,
15770 t_kv as i32,
15771 causal as i32,
15772 );
15773 let __s_b = self.gpu.stream();
15774 let mut b = __s_b.launch_builder(&f);
15775 b.arg(q);
15776 match (&kb16, &vb16) {
15777 (Some(kb), Some(vb)) => {
15778 b.arg(kb).arg(vb);
15779 }
15780 _ => {
15781 b.arg(k).arg(v);
15782 }
15783 }
15784 b.arg(o)
15785 .arg(&hd)
15786 .arg(&nh)
15787 .arg(&nhkv)
15788 .arg(&ti)
15789 .arg(&tkvi)
15790 .arg(&scale)
15791 .arg(&cz);
15792 unsafe {
15793 b.launch(cfg)?;
15794 }
15795 Ok(())
15796 }
15797
15798 #[allow(clippy::too_many_arguments)]
15802 pub fn fa_prefill_w(
15803 &self,
15804 q: &CudaSlice<f32>,
15805 k: &CudaSlice<f32>,
15806 v: &CudaSlice<f32>,
15807 o: &mut CudaSlice<f32>,
15808 head_dim: usize,
15809 n_head: usize,
15810 n_head_kv: usize,
15811 t: usize,
15812 t_kv: usize,
15813 scale: f32,
15814 causal: bool,
15815 window: usize,
15816 ) -> Result<(), Box<dyn std::error::Error>> {
15817 if portable_mma_gated() {
15820 return self.sdpa_naive_w(
15821 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
15822 );
15823 }
15824 static FAW_F32: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15828 let faw_f32 =
15829 *FAW_F32.get_or_init(|| std::env::var("MEMRA_FAW_STAGE").as_deref() == Ok("f32"));
15830 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
15831 self.fa_prefill_w_arm(
15832 q,
15833 k,
15834 v,
15835 o,
15836 head_dim,
15837 n_head,
15838 n_head_kv,
15839 t,
15840 t_kv,
15841 scale,
15842 causal,
15843 window,
15844 floor || faw_f32,
15845 floor,
15846 )
15847 }
15848
15849 #[allow(clippy::too_many_arguments)]
15852 pub fn fa_prefill_w_pre(
15853 &self,
15854 qb: &CudaSlice<u8>,
15855 kb: &CudaSlice<u8>,
15856 vb: &CudaSlice<u8>,
15857 o: &mut CudaSlice<f32>,
15858 head_dim: usize,
15859 n_head: usize,
15860 n_head_kv: usize,
15861 t: usize,
15862 t_kv: usize,
15863 scale: f32,
15864 causal: bool,
15865 window: usize,
15866 v_f16: bool,
15867 ) -> Result<(), Box<dyn std::error::Error>> {
15868 const BLOCK_Q: usize = 64;
15869 const BK: usize = 32;
15870 debug_assert_eq!(head_dim, 256);
15871 let hp = fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15872 debug_assert!(!v_f16 || hp, "f16 V emitted but the SWA hp arm is off");
15873 if hp {
15874 const BLOCK_QH: usize = 32;
15875 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
15878 let vh: &CudaSlice<u8> = if v_f16 {
15879 vb
15880 } else {
15881 let n = t_kv * n_head_kv * head_dim;
15882 if vguard.as_ref().map(|b| b.len() < n * 2).unwrap_or(true) {
15883 *vguard = Some(self.alloc_uninit::<u8>(n * 2)?);
15884 }
15885 self.bf16_to_f16_into(vb, n, vguard.as_mut().unwrap())?;
15886 vguard.as_ref().unwrap()
15887 };
15888 let f = self.func("fa_prefill_w_bf16_p1h2");
15889 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
15890 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15891 f.set_attribute(
15892 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15893 shmem as i32,
15894 )?;
15895 let cfg = LaunchConfig {
15896 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
15897 block_dim: (32, 4, 1),
15898 shared_mem_bytes: shmem,
15899 };
15900 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15901 head_dim as i32,
15902 n_head as i32,
15903 n_head_kv as i32,
15904 t as i32,
15905 t_kv as i32,
15906 causal as i32,
15907 window as i32,
15908 );
15909 let __s_b = self.gpu.stream();
15910 let mut b = __s_b.launch_builder(&f);
15911 b.arg(qb)
15912 .arg(kb)
15913 .arg(vh)
15914 .arg(o)
15915 .arg(&hd)
15916 .arg(&nh)
15917 .arg(&nhkv)
15918 .arg(&ti)
15919 .arg(&tkvi)
15920 .arg(&scale)
15921 .arg(&cz)
15922 .arg(&wi);
15923 unsafe {
15924 b.launch(cfg)?;
15925 }
15926 return Ok(());
15927 }
15928 let f = self.func("fa_prefill_w_bf16_p1");
15929 let shmem =
15930 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15931 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15932 f.set_attribute(
15933 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15934 shmem as i32,
15935 )?;
15936 let cfg = LaunchConfig {
15937 grid_dim: (
15938 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15939 n_head as u32,
15940 1,
15941 ),
15942 block_dim: (32, 4, 1),
15943 shared_mem_bytes: shmem,
15944 };
15945 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15946 head_dim as i32,
15947 n_head as i32,
15948 n_head_kv as i32,
15949 t as i32,
15950 t_kv as i32,
15951 causal as i32,
15952 window as i32,
15953 );
15954 let __s_b = self.gpu.stream();
15955 let mut b = __s_b.launch_builder(&f);
15956 b.arg(qb)
15957 .arg(kb)
15958 .arg(vb)
15959 .arg(o)
15960 .arg(&hd)
15961 .arg(&nh)
15962 .arg(&nhkv)
15963 .arg(&ti)
15964 .arg(&tkvi)
15965 .arg(&scale)
15966 .arg(&cz)
15967 .arg(&wi);
15968 unsafe {
15969 b.launch(cfg)?;
15970 }
15971 Ok(())
15972 }
15973
15974 #[allow(clippy::too_many_arguments)]
15976 pub fn fa_prefill_w_arm(
15977 &self,
15978 q: &CudaSlice<f32>,
15979 k: &CudaSlice<f32>,
15980 v: &CudaSlice<f32>,
15981 o: &mut CudaSlice<f32>,
15982 head_dim: usize,
15983 n_head: usize,
15984 n_head_kv: usize,
15985 t: usize,
15986 t_kv: usize,
15987 scale: f32,
15988 causal: bool,
15989 window: usize,
15990 f32_stage: bool,
15991 floor: bool,
15992 ) -> Result<(), Box<dyn std::error::Error>> {
15993 const BLOCK_Q: usize = 64;
15994 const BK: usize = 32;
15995 debug_assert_eq!(head_dim, 256, "fa_prefill_w is stamped hd256 only");
15996 static P1_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16000 let p1 = !floor
16001 && !f32_stage
16002 && *P1_ON.get_or_init(|| {
16003 std::env::var("MEMRA_FAW_P1")
16004 .map(|v| v != "0")
16005 .unwrap_or(true)
16006 });
16007 let hp =
16008 p1 && fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
16009 if hp {
16010 const BLOCK_QH: usize = 32;
16011 let f = self.func("fa_prefill_w_bf16_p1h2");
16012 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
16013 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16014 f.set_attribute(
16015 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16016 shmem as i32,
16017 )?;
16018 let cfg = LaunchConfig {
16019 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
16020 block_dim: (32, 4, 1),
16021 shared_mem_bytes: shmem,
16022 };
16023 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
16024 head_dim as i32,
16025 n_head as i32,
16026 n_head_kv as i32,
16027 t as i32,
16028 t_kv as i32,
16029 causal as i32,
16030 window as i32,
16031 );
16032 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16033 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16034 let vh = self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?;
16035 let __s_b = self.gpu.stream();
16036 let mut b = __s_b.launch_builder(&f);
16037 b.arg(&qb)
16038 .arg(&kb)
16039 .arg(&vh)
16040 .arg(o)
16041 .arg(&hd)
16042 .arg(&nh)
16043 .arg(&nhkv)
16044 .arg(&ti)
16045 .arg(&tkvi)
16046 .arg(&scale)
16047 .arg(&cz)
16048 .arg(&wi);
16049 unsafe {
16050 b.launch(cfg)?;
16051 }
16052 return Ok(());
16053 }
16054 if p1 {
16055 let f = self.func("fa_prefill_w_bf16_p1");
16056 let shmem =
16057 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
16058 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16059 f.set_attribute(
16060 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16061 shmem as i32,
16062 )?;
16063 let cfg = LaunchConfig {
16064 grid_dim: (
16065 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16066 n_head as u32,
16067 1,
16068 ),
16069 block_dim: (32, 4, 1),
16070 shared_mem_bytes: shmem,
16071 };
16072 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
16073 head_dim as i32,
16074 n_head as i32,
16075 n_head_kv as i32,
16076 t as i32,
16077 t_kv as i32,
16078 causal as i32,
16079 window as i32,
16080 );
16081 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16082 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16083 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
16084 let __s_b = self.gpu.stream();
16085 let mut b = __s_b.launch_builder(&f);
16086 b.arg(&qb)
16087 .arg(&kb)
16088 .arg(&vb)
16089 .arg(o)
16090 .arg(&hd)
16091 .arg(&nh)
16092 .arg(&nhkv)
16093 .arg(&ti)
16094 .arg(&tkvi)
16095 .arg(&scale)
16096 .arg(&cz)
16097 .arg(&wi);
16098 unsafe {
16099 b.launch(cfg)?;
16100 }
16101 return Ok(());
16102 }
16103 static G4_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16106 let g4 = !floor
16107 && !f32_stage
16108 && n_head_kv == 1
16109 && n_head % 4 == 0
16110 && *G4_ON.get_or_init(|| {
16111 std::env::var("MEMRA_FAW_G4")
16112 .map(|v| v != "0")
16113 .unwrap_or(true)
16114 });
16115 if g4 {
16116 const SP_M: usize = 16;
16117 static O2_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16120 let o2 = *O2_ON.get_or_init(|| {
16121 std::env::var("MEMRA_FAW_O2")
16122 .map(|v| v != "0")
16123 .unwrap_or(true)
16124 });
16125 let f = self.func(if o2 {
16126 "fa_prefill_w_bf16_g4o2"
16127 } else {
16128 "fa_prefill_w_bf16_g4"
16129 });
16130 let shmem = if o2 {
16131 (2 * (4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M)) as u32
16132 } else {
16133 (2 * (2 * BK * head_dim + 4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M))
16134 as u32
16135 };
16136 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16137 f.set_attribute(
16138 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16139 shmem as i32,
16140 )?;
16141 let cfg = LaunchConfig {
16142 grid_dim: ((t as u32).div_ceil(SP_M as u32), (n_head / 4) as u32, 1),
16143 block_dim: (32, 4, 1),
16144 shared_mem_bytes: shmem,
16145 };
16146 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
16147 head_dim as i32,
16148 n_head as i32,
16149 n_head_kv as i32,
16150 t as i32,
16151 t_kv as i32,
16152 causal as i32,
16153 window as i32,
16154 );
16155 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16156 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16157 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
16158 let __s_b = self.gpu.stream();
16159 let mut b = __s_b.launch_builder(&f);
16160 b.arg(&qb)
16161 .arg(&kb)
16162 .arg(&vb)
16163 .arg(o)
16164 .arg(&hd)
16165 .arg(&nh)
16166 .arg(&nhkv)
16167 .arg(&ti)
16168 .arg(&tkvi)
16169 .arg(&scale)
16170 .arg(&cz)
16171 .arg(&wi);
16172 unsafe {
16173 b.launch(cfg)?;
16174 }
16175 return Ok(());
16176 }
16177 let f = self.func(if floor {
16178 "fa_prefill_w_f32"
16179 } else if f32_stage {
16180 "fa_prefill_w_f32_pp"
16181 } else {
16182 "fa_prefill_w_bf16_pp"
16183 });
16184 let shmem =
16185 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
16186 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16187 f.set_attribute(
16188 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16189 shmem as i32,
16190 )?;
16191 let cfg = LaunchConfig {
16192 grid_dim: (
16193 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16194 n_head as u32,
16195 1,
16196 ),
16197 block_dim: (32, 4, 1),
16198 shared_mem_bytes: shmem,
16199 };
16200 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
16201 head_dim as i32,
16202 n_head as i32,
16203 n_head_kv as i32,
16204 t as i32,
16205 t_kv as i32,
16206 causal as i32,
16207 window as i32,
16208 );
16209 if f32_stage {
16210 let __s_b = self.gpu.stream();
16211 let mut b = __s_b.launch_builder(&f);
16212 b.arg(q)
16213 .arg(k)
16214 .arg(v)
16215 .arg(o)
16216 .arg(&hd)
16217 .arg(&nh)
16218 .arg(&nhkv)
16219 .arg(&ti)
16220 .arg(&tkvi)
16221 .arg(&scale)
16222 .arg(&cz)
16223 .arg(&wi);
16224 unsafe {
16225 b.launch(cfg)?;
16226 }
16227 } else {
16228 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16229 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16230 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
16231 let __s_b = self.gpu.stream();
16232 let mut b = __s_b.launch_builder(&f);
16233 b.arg(&qb)
16234 .arg(&kb)
16235 .arg(&vb)
16236 .arg(o)
16237 .arg(&hd)
16238 .arg(&nh)
16239 .arg(&nhkv)
16240 .arg(&ti)
16241 .arg(&tkvi)
16242 .arg(&scale)
16243 .arg(&cz)
16244 .arg(&wi);
16245 unsafe {
16246 b.launch(cfg)?;
16247 }
16248 }
16249 Ok(())
16250 }
16251
16252 #[allow(clippy::too_many_arguments)]
16256 pub fn fa_prefill_hd512(
16257 &self,
16258 q: &CudaSlice<f32>,
16259 k: &CudaSlice<f32>,
16260 v: &CudaSlice<f32>,
16261 o: &mut CudaSlice<f32>,
16262 head_dim: usize,
16263 n_head: usize,
16264 n_head_kv: usize,
16265 t: usize,
16266 t_kv: usize,
16267 scale: f32,
16268 causal: bool,
16269 ) -> Result<(), Box<dyn std::error::Error>> {
16270 if portable_mma_gated() {
16272 return self.sdpa_naive(
16273 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
16274 );
16275 }
16276 static F32_STAGE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16282 let f32_stage =
16283 *F32_STAGE.get_or_init(|| std::env::var("MEMRA_FA512_STAGE").as_deref() == Ok("f32"));
16284 static SP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16288 let sp = !f32_stage
16289 && *SP_ON.get_or_init(|| {
16290 std::env::var("MEMRA_FA512_SP")
16291 .map(|v| v != "0")
16292 .unwrap_or(true)
16293 });
16294 self.fa_prefill_hd512_arm(
16295 q,
16296 k,
16297 v,
16298 o,
16299 head_dim,
16300 n_head,
16301 n_head_kv,
16302 t,
16303 t_kv,
16304 scale,
16305 causal,
16306 f32_stage,
16307 sp,
16308 sp && fa_f16pv_on(),
16309 )
16310 }
16311
16312 #[allow(clippy::too_many_arguments)]
16314 pub fn fa_prefill_hd512_pre(
16315 &self,
16316 qb: &CudaSlice<u8>,
16317 kb: &CudaSlice<u8>,
16318 vb: &CudaSlice<u8>,
16319 o: &mut CudaSlice<f32>,
16320 head_dim: usize,
16321 n_head: usize,
16322 n_head_kv: usize,
16323 t: usize,
16324 t_kv: usize,
16325 scale: f32,
16326 causal: bool,
16327 v_f16: bool,
16328 ) -> Result<(), Box<dyn std::error::Error>> {
16329 debug_assert_eq!(head_dim, 512);
16330 const SP_M: usize = 16;
16331 const BKS: usize = 32;
16332 let f16pv = fa_f16pv_on();
16336 let nw = if f16pv { fa512_wide_warps() } else { 2 };
16337 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
16338 debug_assert!(!v_f16 || f16pv, "f16 V emitted without the door on");
16339 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
16340 let vref: &CudaSlice<u8> = if f16pv && !v_f16 {
16341 let n = t_kv * n_head_kv * head_dim;
16343 let need = n * 2;
16344 if vguard.as_ref().map(|b| b.len() < need).unwrap_or(true) {
16345 *vguard = Some(self.alloc_uninit::<u8>(need)?);
16346 }
16347 let dst = vguard.as_mut().unwrap();
16348 self.bf16_to_f16_into(vb, n, dst)?;
16349 vguard.as_ref().unwrap()
16350 } else {
16351 vb
16352 };
16353 let f = self.func(if hp {
16354 "fa_prefill_bf16_hd512_sp16h2"
16355 } else {
16356 match (f16pv, nw) {
16357 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
16358 (true, _) => "fa_prefill_bf16_hd512_sp16",
16359 _ => "fa_prefill_bf16_hd512_sp",
16360 }
16361 });
16362 let (nwarp, npart) = if hp {
16363 (4usize, 4usize)
16364 } else if nw > 2 {
16365 (nw, nw)
16366 } else {
16367 (2, 1)
16368 };
16369 let shmem = if hp {
16371 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS) + 4 * (2 * npart * SP_M * BKS + 2 * SP_M))
16372 as u32
16373 } else {
16374 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
16375 + 4 * (npart * SP_M * BKS + SP_M)) as u32
16376 };
16377 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16378 f.set_attribute(
16379 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16380 shmem as i32,
16381 )?;
16382 let grid_y = if hp {
16383 (n_head / 2) as u32
16384 } else {
16385 n_head as u32
16386 };
16387 let cfg = LaunchConfig {
16388 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
16389 block_dim: (32, nwarp as u32, 1),
16390 shared_mem_bytes: shmem,
16391 };
16392 let (hd, nh, nhkv, ti, tkvi, cz) = (
16393 head_dim as i32,
16394 n_head as i32,
16395 n_head_kv as i32,
16396 t as i32,
16397 t_kv as i32,
16398 causal as i32,
16399 );
16400 let __s_b = self.gpu.stream();
16401 let mut b = __s_b.launch_builder(&f);
16402 b.arg(qb)
16403 .arg(kb)
16404 .arg(vref)
16405 .arg(o)
16406 .arg(&hd)
16407 .arg(&nh)
16408 .arg(&nhkv)
16409 .arg(&ti)
16410 .arg(&tkvi)
16411 .arg(&scale)
16412 .arg(&cz);
16413 unsafe {
16414 b.launch(cfg)?;
16415 }
16416 Ok(())
16417 }
16418
16419 #[allow(clippy::too_many_arguments)]
16422 pub fn fa_prefill_hd512_arm(
16423 &self,
16424 q: &CudaSlice<f32>,
16425 k: &CudaSlice<f32>,
16426 v: &CudaSlice<f32>,
16427 o: &mut CudaSlice<f32>,
16428 head_dim: usize,
16429 n_head: usize,
16430 n_head_kv: usize,
16431 t: usize,
16432 t_kv: usize,
16433 scale: f32,
16434 causal: bool,
16435 f32_stage: bool,
16436 sp: bool,
16437 f16pv: bool,
16438 ) -> Result<(), Box<dyn std::error::Error>> {
16439 debug_assert_eq!(head_dim, 512, "fa_prefill_hd512 is hd512 only");
16440 if sp && !f32_stage {
16441 const SP_M: usize = 16;
16445 const BKS: usize = 32;
16446 let nw = if f16pv { fa512_wide_warps() } else { 2 };
16447 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
16448 let f = self.func(if hp {
16449 "fa_prefill_bf16_hd512_sp16h2"
16450 } else {
16451 match (f16pv, nw) {
16452 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
16453 (true, _) => "fa_prefill_bf16_hd512_sp16",
16454 _ => "fa_prefill_bf16_hd512_sp",
16455 }
16456 });
16457 let (nwarp, npart) = if hp {
16458 (4usize, 4usize)
16459 } else if nw > 2 {
16460 (nw, nw)
16461 } else {
16462 (2, 1)
16463 };
16464 let shmem = if hp {
16465 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS)
16466 + 4 * (2 * npart * SP_M * BKS + 2 * SP_M)) as u32
16467 } else {
16468 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
16469 + 4 * (npart * SP_M * BKS + SP_M)) as u32
16470 };
16471 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16472 f.set_attribute(
16473 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16474 shmem as i32,
16475 )?;
16476 let grid_y = if hp {
16477 (n_head / 2) as u32
16478 } else {
16479 n_head as u32
16480 };
16481 let cfg = LaunchConfig {
16482 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
16483 block_dim: (32, nwarp as u32, 1),
16484 shared_mem_bytes: shmem,
16485 };
16486 let (hd, nh, nhkv, ti, tkvi, cz) = (
16487 head_dim as i32,
16488 n_head as i32,
16489 n_head_kv as i32,
16490 t as i32,
16491 t_kv as i32,
16492 causal as i32,
16493 );
16494 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16495 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16496 let vb = if f16pv {
16497 self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?
16498 } else {
16499 self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?
16500 };
16501 let __s_b = self.gpu.stream();
16502 let mut b = __s_b.launch_builder(&f);
16503 b.arg(&qb)
16504 .arg(&kb)
16505 .arg(&vb)
16506 .arg(o)
16507 .arg(&hd)
16508 .arg(&nh)
16509 .arg(&nhkv)
16510 .arg(&ti)
16511 .arg(&tkvi)
16512 .arg(&scale)
16513 .arg(&cz);
16514 unsafe {
16515 b.launch(cfg)?;
16516 }
16517 return Ok(());
16518 }
16519 const BLOCK_Q: usize = 32;
16520 const BK: usize = 32;
16521 const HALF: usize = 256;
16522 let f = self.func(if f32_stage {
16523 "fa_prefill_f32_hd512"
16524 } else {
16525 "fa_prefill_bf16_hd512"
16526 });
16527 let shmem = (2 * (BLOCK_Q * head_dim + BK * head_dim + BK * HALF + BLOCK_Q * BK)
16529 + 4 * BLOCK_Q) as u32;
16530 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16531 f.set_attribute(
16532 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16533 shmem as i32,
16534 )?;
16535 let cfg = LaunchConfig {
16536 grid_dim: (
16537 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16538 n_head as u32,
16539 2,
16540 ),
16541 block_dim: (32, 2, 1),
16542 shared_mem_bytes: shmem,
16543 };
16544 let (hd, nh, nhkv, ti, tkvi, cz) = (
16545 head_dim as i32,
16546 n_head as i32,
16547 n_head_kv as i32,
16548 t as i32,
16549 t_kv as i32,
16550 causal as i32,
16551 );
16552 if f32_stage {
16553 let __s_b = self.gpu.stream();
16554 let mut b = __s_b.launch_builder(&f);
16555 b.arg(q)
16556 .arg(k)
16557 .arg(v)
16558 .arg(o)
16559 .arg(&hd)
16560 .arg(&nh)
16561 .arg(&nhkv)
16562 .arg(&ti)
16563 .arg(&tkvi)
16564 .arg(&scale)
16565 .arg(&cz);
16566 unsafe {
16567 b.launch(cfg)?;
16568 }
16569 } else {
16570 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16571 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16572 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
16573 let __s_b = self.gpu.stream();
16574 let mut b = __s_b.launch_builder(&f);
16575 b.arg(&qb)
16576 .arg(&kb)
16577 .arg(&vb)
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 unsafe {
16587 b.launch(cfg)?;
16588 }
16589 }
16590 Ok(())
16591 }
16592
16593 #[allow(clippy::too_many_arguments)]
16597 pub fn rope_neox2_bf16e(
16598 &self,
16599 q: &mut CudaSlice<f32>,
16600 k: &mut CudaSlice<f32>,
16601 qb: &mut CudaSlice<u8>,
16602 kb: &mut CudaSlice<u8>,
16603 pos: &CudaSlice<i32>,
16604 head_dim: usize,
16605 n_dims: usize,
16606 nh_q: usize,
16607 nh_k: usize,
16608 n_tokens: usize,
16609 base: f32,
16610 freq_scale: f32,
16611 ff: Option<&CudaSlice<f32>>,
16612 ) -> Result<(), Box<dyn std::error::Error>> {
16613 let f = self.func("rope_neox2_bf16e_f32");
16614 let rows = ((nh_q + nh_k) * n_tokens) as u32;
16615 let cfg = LaunchConfig {
16616 grid_dim: (rows, 1, 1),
16617 block_dim: ((head_dim / 2) as u32, 1, 1),
16618 shared_mem_bytes: 0,
16619 };
16620 let theta_scale = base.powf(-2.0 / n_dims as f32);
16621 let (hd, nd, nhq, nhk, nt) = (
16622 head_dim as i32,
16623 n_dims as i32,
16624 nh_q as i32,
16625 nh_k as i32,
16626 n_tokens as i32,
16627 );
16628 let __s_b = self.gpu.stream();
16629 let mut b = __s_b.launch_builder(&f);
16630 match ff {
16631 Some(t) => {
16632 b.arg(&mut *q)
16633 .arg(&mut *k)
16634 .arg(&mut *qb)
16635 .arg(&mut *kb)
16636 .arg(pos)
16637 .arg(&hd)
16638 .arg(&nd)
16639 .arg(&nhq)
16640 .arg(&nhk)
16641 .arg(&nt)
16642 .arg(&theta_scale)
16643 .arg(&freq_scale)
16644 .arg(t);
16645 unsafe {
16646 b.launch(cfg)?;
16647 }
16648 }
16649 None => {
16650 let null: u64 = 0;
16651 b.arg(&mut *q)
16652 .arg(&mut *k)
16653 .arg(&mut *qb)
16654 .arg(&mut *kb)
16655 .arg(pos)
16656 .arg(&hd)
16657 .arg(&nd)
16658 .arg(&nhq)
16659 .arg(&nhk)
16660 .arg(&nt)
16661 .arg(&theta_scale)
16662 .arg(&freq_scale)
16663 .arg(&null);
16664 unsafe {
16665 b.launch(cfg)?;
16666 }
16667 }
16668 }
16669 Ok(())
16670 }
16671
16672 pub fn f32_to_bf16(
16675 &self,
16676 x: &CudaSlice<f32>,
16677 n: usize,
16678 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
16679 assert!(n % 4 == 0, "f32_to_bf16 requires n % 4 == 0, got {n}");
16680 let mut y = self.alloc_uninit::<u8>(n * 2)?;
16681 let f = self.func("f32_to_bf16_flat");
16682 let n_i = n as i64;
16683 let cfg = LaunchConfig {
16684 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
16685 block_dim: (256, 1, 1),
16686 shared_mem_bytes: 0,
16687 };
16688 let __s_b = self.gpu.stream();
16689 let mut b = __s_b.launch_builder(&f);
16690 b.arg(x).arg(&mut y).arg(&n_i);
16691 unsafe {
16692 b.launch(cfg)?;
16693 }
16694 Ok(y)
16695 }
16696
16697 pub fn f32_to_f16(
16698 &self,
16699 x: &CudaSlice<f32>,
16700 n: usize,
16701 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
16702 assert!(n % 4 == 0, "f32_to_f16 requires n % 4 == 0, got {n}");
16703 let mut y = self.alloc_uninit::<u8>(n * 2)?;
16704 let f = self.func("f32_to_f16_flat");
16705 let n_i = n as i64;
16706 let cfg = LaunchConfig {
16707 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
16708 block_dim: (256, 1, 1),
16709 shared_mem_bytes: 0,
16710 };
16711 let __s_b = self.gpu.stream();
16712 let mut b = __s_b.launch_builder(&f);
16713 b.arg(x).arg(&mut y).arg(&n_i);
16714 unsafe {
16715 b.launch(cfg)?;
16716 }
16717 Ok(y)
16718 }
16719
16720 pub fn bf16_to_f16(
16722 &self,
16723 xb: &CudaSlice<u8>,
16724 n: usize,
16725 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
16726 let mut y = self.alloc_uninit::<u8>(n * 2)?;
16727 self.bf16_to_f16_into(xb, n, &mut y)?;
16728 Ok(y)
16729 }
16730
16731 pub fn bf16_to_f16_into(
16733 &self,
16734 xb: &CudaSlice<u8>,
16735 n: usize,
16736 y: &mut CudaSlice<u8>,
16737 ) -> Result<(), Box<dyn std::error::Error>> {
16738 assert!(n % 2 == 0, "bf16_to_f16 requires n % 2 == 0, got {n}");
16739 assert!(y.len() >= n * 2);
16740 let f = self.func("bf16_to_f16_flat");
16741 let n2 = (n / 2) as i64;
16742 let cfg = LaunchConfig {
16743 grid_dim: (((n / 2) as u32).div_ceil(256), 1, 1),
16744 block_dim: (256, 1, 1),
16745 shared_mem_bytes: 0,
16746 };
16747 let __s_b = self.gpu.stream();
16748 let mut b = __s_b.launch_builder(&f);
16749 b.arg(xb).arg(y).arg(&n2);
16750 unsafe {
16751 b.launch(cfg)?;
16752 }
16753 Ok(())
16754 }
16755
16756 #[allow(clippy::too_many_arguments)]
16761 pub fn fa_prefill_vl8(
16762 &self,
16763 seqs: &[FaSeqVl],
16764 head_dim: usize,
16765 n_head: usize,
16766 n_head_kv: usize,
16767 scale: f32,
16768 ) -> Result<(), Box<dyn std::error::Error>> {
16769 const BK: usize = 32;
16770 let b = seqs.len();
16771 assert!(b >= 1 && b <= 8);
16772 let mut packed = [FaSeqVl::default(); 8];
16773 packed[..b].copy_from_slice(seqs);
16774 let v = FaVl8(packed);
16775 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
16776 let ept = (n_head_kv * head_dim) as i32;
16777 {
16778 let f = self.func("fa_mirror_vl");
16779 let max_n = (max_t as i64) * ept as i64;
16780 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
16781 for which in 0..2i32 {
16782 let cfg = LaunchConfig {
16783 grid_dim: (blocks, 1, b as u32),
16784 block_dim: (256, 1, 1),
16785 shared_mem_bytes: 0,
16786 };
16787 let __s_lb = self.gpu.stream();
16788 let mut lb = __s_lb.launch_builder(&f);
16789 lb.arg(&v).arg(&ept).arg(&which);
16790 unsafe {
16791 lb.launch(cfg)?;
16792 }
16793 }
16794 }
16795 let hd_sfx = fa_hd_suffix(head_dim)?;
16796 let f = self.func(&format!("fa_prefill_bf16kv_vl{hd_sfx}"));
16797 let block_q = 64usize;
16798 let kv_stages = 2usize;
16799 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
16800 + 4 * (block_q * BK + 2 * block_q)) as u32;
16801 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16802 f.set_attribute(
16803 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16804 shmem as i32,
16805 )?;
16806 let cfg = LaunchConfig {
16807 grid_dim: (max_t.div_ceil(block_q as u32), n_head as u32, b as u32),
16808 block_dim: (32, 4, 1),
16809 shared_mem_bytes: shmem,
16810 };
16811 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
16812 let __s_lb = self.gpu.stream();
16813 let mut lb = __s_lb.launch_builder(&f);
16814 lb.arg(&v).arg(&hd).arg(&nh).arg(&nhkv).arg(&scale);
16815 unsafe {
16816 lb.launch(cfg)?;
16817 }
16818 Ok(())
16819 }
16820
16821 #[allow(clippy::too_many_arguments)]
16825 pub fn attn_pre_vl8(
16826 &self,
16827 seqs: &[AttnPreVl],
16828 wq: &CudaSlice<f32>,
16829 wk: &CudaSlice<f32>,
16830 head_dim: usize,
16831 rope_dims: usize,
16832 n_head: usize,
16833 n_head_kv: usize,
16834 eps: f32,
16835 freq_base: f32,
16836 freq_scale: f32,
16837 kv_dim_k: usize,
16838 kv_dim_v: usize,
16839 k_tok_bytes: usize,
16840 v_tok_bytes: usize,
16841 ) -> Result<(), Box<dyn std::error::Error>> {
16842 let b = seqs.len();
16843 assert!(b >= 1 && b <= 8);
16844 let mut packed = [AttnPreVl::default(); 8];
16845 packed[..b].copy_from_slice(seqs);
16846 let v = AttnPreVl8(packed);
16847 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
16848 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
16849 {
16850 let f = self.func("q_gate_split_vl");
16851 let n = max_t * (n_head * head_dim) as u32;
16852 let cfg = LaunchConfig {
16853 grid_dim: (n.div_ceil(256), 1, b as u32),
16854 block_dim: (256, 1, 1),
16855 shared_mem_bytes: 0,
16856 };
16857 let __s_lb = self.gpu.stream();
16858 let mut lb = __s_lb.launch_builder(&f);
16859 lb.arg(&v).arg(&hd).arg(&nh);
16860 unsafe {
16861 lb.launch(cfg)?;
16862 }
16863 }
16864 {
16865 let f = self.func("attn_rms_vl");
16866 let cfg = LaunchConfig {
16867 grid_dim: (max_t * n_head as u32, 2, b as u32),
16868 block_dim: (rms_block(), 1, 1),
16869 shared_mem_bytes: 0,
16870 };
16871 let __s_lb = self.gpu.stream();
16872 let mut lb = __s_lb.launch_builder(&f);
16873 lb.arg(&v)
16874 .arg(wq)
16875 .arg(wk)
16876 .arg(&hd)
16877 .arg(&nh)
16878 .arg(&nhkv)
16879 .arg(&eps);
16880 unsafe {
16881 lb.launch(cfg)?;
16882 }
16883 }
16884 {
16885 let f = self.func("attn_rope_vl");
16886 let theta_scale = freq_base.powf(-2.0 / rope_dims as f32);
16887 let nd = rope_dims as i32;
16888 let cfg = LaunchConfig {
16889 grid_dim: (max_t * n_head as u32, 2, b as u32),
16890 block_dim: ((head_dim / 2) as u32, 1, 1),
16891 shared_mem_bytes: 0,
16892 };
16893 let __s_lb = self.gpu.stream();
16894 let mut lb = __s_lb.launch_builder(&f);
16895 lb.arg(&v)
16896 .arg(&hd)
16897 .arg(&nd)
16898 .arg(&nh)
16899 .arg(&nhkv)
16900 .arg(&theta_scale)
16901 .arg(&freq_scale);
16902 unsafe {
16903 lb.launch(cfg)?;
16904 }
16905 }
16906 {
16907 let f = self.func("append_kv_vl");
16908 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
16909 let cfg = LaunchConfig {
16910 grid_dim: (nblk, max_t, b as u32),
16911 block_dim: (32, 1, 1),
16912 shared_mem_bytes: 0,
16913 };
16914 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
16915 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16916 let __s_lb = self.gpu.stream();
16917 let mut lb = __s_lb.launch_builder(&f);
16918 lb.arg(&v).arg(&kdk).arg(&kdv).arg(&ktb).arg(&vtb);
16919 unsafe {
16920 lb.launch(cfg)?;
16921 }
16922 }
16923 Ok(())
16924 }
16925
16926 pub fn fa_prefill_view(
16931 &self,
16932 q: &CudaSlice<f32>,
16933 k: &cudarc::driver::CudaView<u8>,
16934 v: &cudarc::driver::CudaView<u8>,
16935 o: &mut CudaSlice<f32>,
16936 head_dim: usize,
16937 n_head: usize,
16938 n_head_kv: usize,
16939 t: usize,
16940 t_kv: usize,
16941 scale: f32,
16942 causal: bool,
16943 k_tok_bytes: usize,
16944 v_tok_bytes: usize,
16945 g: bool,
16946 ) -> Result<(), Box<dyn std::error::Error>> {
16947 if portable_mma_gated() {
16948 return self.sdpa_naive_quantized_view(
16949 q,
16950 k,
16951 v,
16952 o,
16953 head_dim,
16954 n_head,
16955 n_head_kv,
16956 t,
16957 t_kv,
16958 scale,
16959 causal,
16960 k_tok_bytes,
16961 v_tok_bytes,
16962 );
16963 }
16964 const BLOCK_Q: usize = 64;
16965 const BK: usize = 32;
16966 let name = format!("fa_prefill_q{}", fa_hd_suffix(head_dim)?);
16969 let f = if g {
16970 self.func_g(&name)
16971 } else {
16972 self.func(&name)
16973 };
16974 let shmem =
16975 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
16976 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16977 f.set_attribute(
16978 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16979 shmem as i32,
16980 )?;
16981 let cfg = LaunchConfig {
16982 grid_dim: (
16983 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16984 n_head as u32,
16985 1,
16986 ),
16987 block_dim: (32, 4, 1),
16988 shared_mem_bytes: shmem,
16989 };
16990 let (hd, nh, nhkv, ti, tkvi, cz) = (
16991 head_dim as i32,
16992 n_head as i32,
16993 n_head_kv as i32,
16994 t as i32,
16995 t_kv as i32,
16996 causal as i32,
16997 );
16998 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16999 let __s_b = self.gpu.stream();
17000 let mut b = __s_b.launch_builder(&f);
17001 b.arg(q)
17002 .arg(k)
17003 .arg(v)
17004 .arg(o)
17005 .arg(&hd)
17006 .arg(&nh)
17007 .arg(&nhkv)
17008 .arg(&ti)
17009 .arg(&tkvi)
17010 .arg(&scale)
17011 .arg(&cz)
17012 .arg(&ktb)
17013 .arg(&vtb);
17014 unsafe {
17015 b.launch(cfg)?;
17016 }
17017 Ok(())
17018 }
17019
17020 #[allow(clippy::too_many_arguments)]
17030 pub fn fa_prefill_view_ws(
17031 &self,
17032 q: &CudaSlice<f32>,
17033 k: &cudarc::driver::CudaView<u8>,
17034 v: &cudarc::driver::CudaView<u8>,
17035 o: &mut CudaSlice<f32>,
17036 head_dim: usize,
17037 n_head: usize,
17038 n_head_kv: usize,
17039 t: usize,
17040 t_kv: usize,
17041 scale: f32,
17042 causal: bool,
17043 k_tok_bytes: usize,
17044 v_tok_bytes: usize,
17045 g: bool,
17046 ) -> Result<(), Box<dyn std::error::Error>> {
17047 if portable_mma_gated() {
17048 return self.sdpa_naive_quantized_view(
17049 q,
17050 k,
17051 v,
17052 o,
17053 head_dim,
17054 n_head,
17055 n_head_kv,
17056 t,
17057 t_kv,
17058 scale,
17059 causal,
17060 k_tok_bytes,
17061 v_tok_bytes,
17062 );
17063 }
17064 const BLOCK_Q: usize = 64;
17065 const BK: usize = 32;
17066 let kv_dim_k = n_head_kv * head_dim;
17067 let kv_dim_v = n_head_kv * head_dim;
17068 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
17070 let mut guard = self.prime_deqw_ws.lock().unwrap();
17072 let need_grow = match guard.as_ref() {
17073 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
17074 None => true,
17075 };
17076 if need_grow {
17077 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
17078 let (ck, cv) = guard
17079 .as_ref()
17080 .map(|(a, b)| (a.len(), b.len()))
17081 .unwrap_or((0, 0));
17082 *guard = Some((
17083 self.alloc_u8(grow(ck, k_ws_bytes))?,
17084 self.alloc_u8(grow(cv, v_ws_bytes))?,
17085 ));
17086 }
17087 let (kw, vw) = guard.as_mut().unwrap();
17088 {
17090 let f = if g {
17092 self.func_g("fa_dequant_kv_ws_bf16")
17093 } else {
17094 self.func("fa_dequant_kv_ws_bf16")
17095 };
17096 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
17097 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
17098 let cfg = LaunchConfig {
17099 grid_dim: (nblk.max(1), 1, 1),
17100 block_dim: (256, 1, 1),
17101 shared_mem_bytes: 0,
17102 };
17103 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
17104 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17105 let __s_b = self.gpu.stream();
17106 let mut b = __s_b.launch_builder(&f);
17107 b.arg(k)
17108 .arg(v)
17109 .arg(&mut *kw)
17110 .arg(&mut *vw)
17111 .arg(&kdk)
17112 .arg(&kdv)
17113 .arg(&tkvi)
17114 .arg(&ktb)
17115 .arg(&vtb);
17116 unsafe {
17117 b.launch(cfg)?;
17118 }
17119 }
17120 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
17128 .map(|v| v != "0")
17129 .unwrap_or(true);
17130 {
17131 let hd_sfx = fa_hd_suffix(head_dim)?;
17132 let f = self.func(&format!(
17133 "fa_prefill_qw{}{hd_sfx}",
17134 if db { "_db" } else { "" }
17135 ));
17136 let shmem = if db {
17137 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
17139 } else {
17140 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
17141 };
17142 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17143 f.set_attribute(
17144 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17145 shmem as i32,
17146 )?;
17147 let cfg = LaunchConfig {
17148 grid_dim: (
17149 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
17150 n_head as u32,
17151 1,
17152 ),
17153 block_dim: (32, 4, 1),
17154 shared_mem_bytes: shmem,
17155 };
17156 let (hd, nh, nhkv, ti, tkvi, cz) = (
17157 head_dim as i32,
17158 n_head as i32,
17159 n_head_kv as i32,
17160 t as i32,
17161 t_kv as i32,
17162 causal as i32,
17163 );
17164 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
17165 let __s_b = self.gpu.stream();
17166 let mut b = __s_b.launch_builder(&f);
17167 b.arg(q)
17168 .arg(&*kw)
17169 .arg(&*vw)
17170 .arg(o)
17171 .arg(&hd)
17172 .arg(&nh)
17173 .arg(&nhkv)
17174 .arg(&ti)
17175 .arg(&tkvi)
17176 .arg(&scale)
17177 .arg(&cz)
17178 .arg(&kdk)
17179 .arg(&kdv);
17180 unsafe {
17181 b.launch(cfg)?;
17182 }
17183 }
17184 Ok(())
17185 }
17186
17187 #[allow(clippy::too_many_arguments)]
17203 pub fn fa_prefill_view_ws_w_hd128(
17204 &self,
17205 q: &CudaSlice<f32>,
17206 k: &cudarc::driver::CudaView<u8>,
17207 v: &cudarc::driver::CudaView<u8>,
17208 o: &mut CudaSlice<f32>,
17209 head_dim: usize,
17210 n_head: usize,
17211 n_head_kv: usize,
17212 t: usize,
17213 t_kv: usize,
17214 scale: f32,
17215 causal: bool,
17216 window: usize,
17217 k_tok_bytes: usize,
17218 v_tok_bytes: usize,
17219 ) -> Result<(), Box<dyn std::error::Error>> {
17220 assert_eq!(
17221 head_dim, 128,
17222 "fa_prefill_view_ws_w_hd128: only the hd128 twin is stamped"
17223 );
17224 if portable_mma_gated() {
17225 return self.sdpa_naive_w_quantized_view(
17226 q,
17227 k,
17228 v,
17229 o,
17230 head_dim,
17231 n_head,
17232 n_head_kv,
17233 t,
17234 t_kv,
17235 scale,
17236 causal,
17237 window,
17238 k_tok_bytes,
17239 v_tok_bytes,
17240 );
17241 }
17242 const BLOCK_Q: usize = 64;
17243 const BK: usize = 32;
17244 let kv_dim_k = n_head_kv * head_dim;
17245 let kv_dim_v = n_head_kv * head_dim;
17246 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
17248 let mut guard = self.prime_deqw_ws.lock().unwrap();
17249 let need_grow = match guard.as_ref() {
17250 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
17251 None => true,
17252 };
17253 if need_grow {
17254 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
17255 let (ck, cv) = guard
17256 .as_ref()
17257 .map(|(a, b)| (a.len(), b.len()))
17258 .unwrap_or((0, 0));
17259 *guard = Some((
17260 self.alloc_u8(grow(ck, k_ws_bytes))?,
17261 self.alloc_u8(grow(cv, v_ws_bytes))?,
17262 ));
17263 }
17264 let (kw, vw) = guard.as_mut().unwrap();
17265 {
17268 let f = self.func("fa_dequant_kv_ws_bf16");
17269 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
17270 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
17271 let cfg = LaunchConfig {
17272 grid_dim: (nblk.max(1), 1, 1),
17273 block_dim: (256, 1, 1),
17274 shared_mem_bytes: 0,
17275 };
17276 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
17277 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17278 let __s_b = self.gpu.stream();
17279 let mut b = __s_b.launch_builder(&f);
17280 b.arg(k)
17281 .arg(v)
17282 .arg(&mut *kw)
17283 .arg(&mut *vw)
17284 .arg(&kdk)
17285 .arg(&kdv)
17286 .arg(&tkvi)
17287 .arg(&ktb)
17288 .arg(&vtb);
17289 unsafe {
17290 b.launch(cfg)?;
17291 }
17292 }
17293 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
17295 .map(|v| v != "0")
17296 .unwrap_or(true);
17297 {
17298 let f = self.func(if db {
17299 "fa_prefill_qw_db_w_hd128"
17300 } else {
17301 "fa_prefill_qw_w_hd128"
17302 });
17303 let shmem = if db {
17304 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
17305 } else {
17306 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
17307 };
17308 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17309 f.set_attribute(
17310 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17311 shmem as i32,
17312 )?;
17313 let cfg = LaunchConfig {
17314 grid_dim: (
17315 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
17316 n_head as u32,
17317 1,
17318 ),
17319 block_dim: (32, 4, 1),
17320 shared_mem_bytes: shmem,
17321 };
17322 let (hd, nh, nhkv, ti, tkvi, cz) = (
17323 head_dim as i32,
17324 n_head as i32,
17325 n_head_kv as i32,
17326 t as i32,
17327 t_kv as i32,
17328 causal as i32,
17329 );
17330 let (kdk, kdv, wnd) = (kv_dim_k as i32, kv_dim_v as i32, window as i32);
17331 let __s_b = self.gpu.stream();
17332 let mut b = __s_b.launch_builder(&f);
17333 b.arg(q)
17334 .arg(&*kw)
17335 .arg(&*vw)
17336 .arg(o)
17337 .arg(&hd)
17338 .arg(&nh)
17339 .arg(&nhkv)
17340 .arg(&ti)
17341 .arg(&tkvi)
17342 .arg(&scale)
17343 .arg(&cz)
17344 .arg(&kdk)
17345 .arg(&kdv)
17346 .arg(&wnd);
17347 unsafe {
17348 b.launch(cfg)?;
17349 }
17350 }
17351 Ok(())
17352 }
17353
17354 pub fn fa_decode(
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 t_kv: usize,
17367 scale: f32,
17368 k_tok_bytes: usize,
17369 v_tok_bytes: usize,
17370 ) -> Result<(), Box<dyn std::error::Error>> {
17371 self.fa_decode_kvmod(
17372 q,
17373 k,
17374 v,
17375 o,
17376 head_dim,
17377 n_head,
17378 n_head_kv,
17379 t_kv,
17380 scale,
17381 k_tok_bytes,
17382 v_tok_bytes,
17383 false,
17384 )
17385 }
17386
17387 #[allow(clippy::too_many_arguments)]
17391 #[allow(clippy::too_many_arguments)]
17395 #[allow(clippy::too_many_arguments)]
17396 fn fa_decode_scalar_unified(
17397 &self,
17398 q: &cudarc::driver::CudaView<f32>,
17399 k: &cudarc::driver::CudaView<u8>,
17400 v: &cudarc::driver::CudaView<u8>,
17401 o: &mut cudarc::driver::CudaViewMut<f32>,
17402 head_dim: usize,
17403 n_head: usize,
17404 n_head_kv: usize,
17405 t_kv_host: usize,
17406 t_kv_dev: Option<&CudaSlice<i32>>,
17407 scale: f32,
17408 n_splits: usize,
17409 split_keys: usize,
17410 k_tok_bytes: usize,
17411 v_tok_bytes: usize,
17412 g: bool,
17413 part_o: &mut CudaSlice<f32>,
17414 part_m: &mut CudaSlice<f32>,
17415 part_l: &mut CudaSlice<f32>,
17416 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
17417 ) -> Result<(), Box<dyn std::error::Error>> {
17418 let f = if g {
17419 self.func_g("fa_decode_f32")
17420 } else {
17421 self.fa_func("fa_decode_f32", head_dim)
17422 };
17423 let cfg = LaunchConfig {
17424 grid_dim: (n_head as u32, n_splits as u32, 1),
17425 block_dim: (head_dim as u32, 1, 1),
17426 shared_mem_bytes: (4 * (head_dim + 32)) as u32,
17427 };
17428 let (hd, nh, nhkv, nsp) = (
17429 head_dim as i32,
17430 n_head as i32,
17431 n_head_kv as i32,
17432 n_splits as i32,
17433 );
17434 let (ktb, vtb, tkvi, ski) = (
17435 k_tok_bytes as i64,
17436 v_tok_bytes as i64,
17437 t_kv_host as i32,
17438 split_keys as i32,
17439 );
17440 let __s_b = self.gpu.stream();
17441 let mut b = __s_b.launch_builder(&f);
17442 match t_kv_dev {
17443 Some(d) => {
17444 b.arg(q)
17445 .arg(k)
17446 .arg(v)
17447 .arg(&mut *part_o)
17448 .arg(&mut *part_m)
17449 .arg(&mut *part_l)
17450 .arg(&hd)
17451 .arg(&nh)
17452 .arg(&nhkv)
17453 .arg(&tkvi)
17454 .arg(d)
17455 .arg(&scale)
17456 .arg(&nsp)
17457 .arg(&ski)
17458 .arg(&ktb)
17459 .arg(&vtb);
17460 unsafe {
17461 b.launch(cfg)?;
17462 }
17463 }
17464 None => {
17465 let null: u64 = 0;
17466 b.arg(q)
17467 .arg(k)
17468 .arg(v)
17469 .arg(&mut *part_o)
17470 .arg(&mut *part_m)
17471 .arg(&mut *part_l)
17472 .arg(&hd)
17473 .arg(&nh)
17474 .arg(&nhkv)
17475 .arg(&tkvi)
17476 .arg(&null)
17477 .arg(&scale)
17478 .arg(&nsp)
17479 .arg(&ski)
17480 .arg(&ktb)
17481 .arg(&vtb);
17482 unsafe {
17483 b.launch(cfg)?;
17484 }
17485 }
17486 }
17487 let cfg2 = LaunchConfig {
17488 grid_dim: (n_head as u32, 1, 1),
17489 block_dim: (head_dim as u32, 1, 1),
17490 shared_mem_bytes: 0,
17491 };
17492 if let Some((oq, od)) = q8_out {
17493 let fc = if g {
17495 self.func_g("fa_decode_combine_q8_1")
17496 } else {
17497 self.fa_func("fa_decode_combine_q8_1", head_dim)
17498 };
17499 let __s_b2 = self.gpu.stream();
17500 let mut b2 = __s_b2.launch_builder(&fc);
17501 b2.arg(&*part_o)
17502 .arg(&*part_m)
17503 .arg(&*part_l)
17504 .arg(oq)
17505 .arg(od)
17506 .arg(&hd)
17507 .arg(&nh)
17508 .arg(&nsp);
17509 unsafe {
17510 b2.launch(cfg2)?;
17511 }
17512 return Ok(());
17513 }
17514 let fc = if g {
17515 self.func_g("fa_decode_combine_f32")
17516 } else {
17517 self.fa_func("fa_decode_combine_f32", head_dim)
17518 };
17519 let __s_b2 = self.gpu.stream();
17520 let mut b2 = __s_b2.launch_builder(&fc);
17521 b2.arg(&*part_o)
17522 .arg(&*part_m)
17523 .arg(&*part_l)
17524 .arg(o)
17525 .arg(&hd)
17526 .arg(&nh)
17527 .arg(&nsp);
17528 unsafe {
17529 b2.launch(cfg2)?;
17530 }
17531 Ok(())
17532 }
17533
17534 pub fn fa_decode_kvmod(
17535 &self,
17536 q: &CudaSlice<f32>,
17537 k: &cudarc::driver::CudaView<u8>,
17538 v: &cudarc::driver::CudaView<u8>,
17539 o: &mut CudaSlice<f32>,
17540 head_dim: usize,
17541 n_head: usize,
17542 n_head_kv: usize,
17543 t_kv: usize,
17544 scale: f32,
17545 k_tok_bytes: usize,
17546 v_tok_bytes: usize,
17547 g: bool,
17548 ) -> Result<(), Box<dyn std::error::Error>> {
17549 let q_view = q.as_view();
17550 let mut o_view = o.as_view_mut();
17551 self.fa_decode_kvmod_view(
17552 &q_view,
17553 k,
17554 v,
17555 &mut o_view,
17556 head_dim,
17557 n_head,
17558 n_head_kv,
17559 t_kv,
17560 scale,
17561 k_tok_bytes,
17562 v_tok_bytes,
17563 g,
17564 )
17565 }
17566
17567 #[allow(clippy::too_many_arguments)]
17572 pub fn fa_decode_kvmod_view(
17573 &self,
17574 q: &cudarc::driver::CudaView<f32>,
17575 k: &cudarc::driver::CudaView<u8>,
17576 v: &cudarc::driver::CudaView<u8>,
17577 o: &mut cudarc::driver::CudaViewMut<f32>,
17578 head_dim: usize,
17579 n_head: usize,
17580 n_head_kv: usize,
17581 t_kv: usize,
17582 scale: f32,
17583 k_tok_bytes: usize,
17584 v_tok_bytes: usize,
17585 g: bool,
17586 ) -> Result<(), Box<dyn std::error::Error>> {
17587 let mut fa_vec = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
17608 if g && head_dim == 256 && !fa_v4_at(t_kv) {
17612 fa_vec = false;
17613 }
17614 let sp = fa_split_keys(t_kv, n_head_kv);
17615 let n_splits = if fa_vec {
17616 ((t_kv + sp - 1) / sp).max(1)
17617 } else {
17618 ((t_kv + 255) / 256).max(1)
17619 };
17620 let o_len = n_head * n_splits * head_dim;
17621 let ml_len = n_head * n_splits;
17622 let mut part_guard = self.fa_part_pool.lock().unwrap();
17623 if part_guard
17624 .as_ref()
17625 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17626 .unwrap_or(true)
17627 {
17628 let old = part_guard.take();
17639 let (co, cm) = old
17640 .as_ref()
17641 .map(|pp| (pp.0.len(), pp.1.len()))
17642 .unwrap_or((0, 0));
17643 if let Some(old) = old {
17644 self.fa_part_retired.lock().unwrap().push(old);
17645 }
17646 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17647 eprintln!(
17648 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17649 co, o_len, cm, ml_len
17650 );
17651 }
17652 *part_guard = Some((
17653 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17654 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17655 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17656 ));
17657 }
17658 let pg = part_guard.as_mut().unwrap();
17659 self.gpu
17660 .stream()
17661 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17662 self.gpu
17663 .stream()
17664 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17665 self.gpu
17666 .stream()
17667 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17668 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17669 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
17670 let (hd, nh, nhkv, tkvi, nsp) = (
17671 head_dim as i32,
17672 n_head as i32,
17673 n_head_kv as i32,
17674 t_kv as i32,
17675 n_splits as i32,
17676 );
17677 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17678 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
17682 let fa512_min = fa512_min_tkv();
17687 let deep = fa_vec
17690 && head_dim == 256
17691 && fa_v4_at(t_kv)
17692 && !g
17693 && fa_deep_at(t_kv)
17694 && !matches!(fa_v4_mode(), "noB3" | "stage");
17695 let (f, cfg) = if fa_vec && head_dim == 512 && t_kv >= fa512_min {
17696 let gqa = (n_head / n_head_kv).max(1) as u32;
17699 let fv = self.fa_func("fa_decode_vec_q_dpl16", head_dim);
17700 (
17701 fv,
17702 LaunchConfig {
17703 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17704 block_dim: (32, gqa, 1),
17705 shared_mem_bytes: 0,
17706 },
17707 )
17708 } else if fa_vec && head_dim <= 256 {
17709 let gqa = (n_head / n_head_kv).max(1) as u32;
17710 static SMEM_TKV: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17721 let smem_tkv = *SMEM_TKV.get_or_init(|| {
17722 std::env::var("MEMRA_FA_SMEM_TKV")
17723 .ok()
17724 .and_then(|v| v.parse().ok())
17725 .unwrap_or_else(|| {
17726 FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
17727 })
17728 });
17729 if fa_v4_at(t_kv) && head_dim == 256 {
17730 let v4name = match fa_v4_mode() {
17734 "noB3" => "fa_decode_vec_q_v4_noB3", "stage" => "fa_decode_vec_q_v4_stage", _ if deep => "fa_decode_vec_q_v4_deep",
17737 _ => "fa_decode_vec_q_v4",
17738 };
17739 let fv = if g {
17740 self.func_g(v4name)
17741 } else {
17742 self.func(v4name)
17743 };
17744 let shmem = (if deep { 12160 } else { 11520 }
17747 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
17748 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17749 fv.set_attribute(
17750 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17751 shmem as i32,
17752 )?;
17753 (
17754 fv,
17755 LaunchConfig {
17756 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17757 block_dim: (32, gqa, 1),
17758 shared_mem_bytes: shmem,
17759 },
17760 )
17761 } else if fa_v3_active(head_dim) {
17762 let fv = if g {
17765 self.func_g("fa_decode_vec_q_v3")
17766 } else {
17767 self.func("fa_decode_vec_q_v3")
17768 };
17769 let shmem = (32 * head_dim * 2) as u32; (
17771 fv,
17772 LaunchConfig {
17773 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17774 block_dim: (32, gqa, 1),
17775 shared_mem_bytes: shmem,
17776 },
17777 )
17778 } else if fa_v2_on() {
17779 let fv = if g {
17783 self.func_g("fa_decode_vec_q_v2")
17784 } else {
17785 self.func("fa_decode_vec_q_v2")
17786 };
17787 let shmem = (2 * 32 * head_dim * 2) as u32; (
17789 fv,
17790 LaunchConfig {
17791 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17792 block_dim: (32, gqa, 1),
17793 shared_mem_bytes: shmem,
17794 },
17795 )
17796 } else if smem_tkv > 0 && t_kv >= smem_tkv && !g && !(head_dim == 512 && Self::gkv_on())
17797 {
17798 let fv = if g {
17802 self.func_g("fa_decode_vec_q_smem")
17803 } else {
17804 self.func("fa_decode_vec_q_smem")
17805 };
17806 let shmem = (2 * 32 * head_dim * 2) as u32; use cudarc::driver::sys::CUfunction_attribute_enum as A;
17808 fv.set_attribute(
17809 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17810 shmem as i32,
17811 )?;
17812 (
17813 fv,
17814 LaunchConfig {
17815 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17816 block_dim: (32, gqa, 1),
17817 shared_mem_bytes: shmem,
17818 },
17819 )
17820 } else {
17821 let fv = if g {
17824 self.func_g("fa_decode_vec_q")
17825 } else {
17826 self.func("fa_decode_vec_q")
17827 };
17828 (
17829 fv,
17830 LaunchConfig {
17831 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17832 block_dim: (32, gqa, 1),
17833 shared_mem_bytes: 0,
17834 },
17835 )
17836 }
17837 } else {
17838 return self.fa_decode_scalar_unified(
17841 q,
17842 k,
17843 v,
17844 o,
17845 head_dim,
17846 n_head,
17847 n_head_kv,
17848 t_kv,
17849 None,
17850 scale,
17851 n_splits,
17852 if fa_vec { sp } else { 256 },
17853 k_tok_bytes,
17854 v_tok_bytes,
17855 g,
17856 part_o,
17857 part_m,
17858 part_l,
17859 None,
17860 );
17861 };
17862 let __s_b = self.gpu.stream();
17863 let mut b = __s_b.launch_builder(&f);
17864 b.arg(q)
17865 .arg(k)
17866 .arg(v)
17867 .arg(&mut *part_o)
17868 .arg(&mut *part_m)
17869 .arg(&mut *part_l)
17870 .arg(&hd)
17871 .arg(&nh)
17872 .arg(&nhkv)
17873 .arg(&tkvi)
17874 .arg(&scale)
17875 .arg(&nsp)
17876 .arg(&ktb)
17877 .arg(&vtb);
17878 unsafe {
17879 b.launch(cfg)?;
17880 }
17881 let (fc, cfg2) = (
17884 if g {
17885 self.func_g("fa_decode_combine_f32")
17886 } else {
17887 self.fa_func("fa_decode_combine_f32", head_dim)
17888 },
17889 LaunchConfig {
17890 grid_dim: (n_head as u32, 1, 1),
17891 block_dim: (head_dim as u32, 1, 1),
17892 shared_mem_bytes: 0,
17893 },
17894 );
17895 let __s_b2 = self.gpu.stream();
17896 let mut b2 = __s_b2.launch_builder(&fc);
17897 b2.arg(&*part_o)
17898 .arg(&*part_m)
17899 .arg(&*part_l)
17900 .arg(o)
17901 .arg(&hd)
17902 .arg(&nh)
17903 .arg(&nsp);
17904 unsafe {
17905 b2.launch(cfg2)?;
17906 }
17907 Ok(())
17908 }
17909
17910 #[allow(clippy::too_many_arguments)]
17921 pub fn fa_decode_batch_seqs_v4(
17922 &self,
17923 q: &CudaSlice<f32>,
17924 kv_ptrs: &cudarc::driver::CudaView<u64>,
17925 pos_seq: &CudaSlice<i32>,
17926 o: &mut CudaSlice<f32>,
17927 head_dim: usize,
17928 n_head: usize,
17929 n_head_kv: usize,
17930 b_n: usize,
17931 t_kv_max: usize,
17932 scale: f32,
17933 split_keys: usize,
17934 k_tok_bytes: usize,
17935 v_tok_bytes: usize,
17936 ) -> Result<(), Box<dyn std::error::Error>> {
17937 debug_assert!(head_dim == 256, "seqs twin is v4-stamped (hd256 only)");
17938 let n_splits_max = (t_kv_max + split_keys - 1) / split_keys;
17939 let o_len = b_n * n_head * n_splits_max * head_dim;
17940 let ml_len = b_n * n_head * n_splits_max;
17941 let mut part_guard = self.fa_part_pool.lock().unwrap();
17942 if part_guard
17943 .as_ref()
17944 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17945 .unwrap_or(true)
17946 {
17947 let old = part_guard.take();
17958 let (co, cm) = old
17959 .as_ref()
17960 .map(|pp| (pp.0.len(), pp.1.len()))
17961 .unwrap_or((0, 0));
17962 if let Some(old) = old {
17963 self.fa_part_retired.lock().unwrap().push(old);
17964 }
17965 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17966 eprintln!(
17967 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17968 co, o_len, cm, ml_len
17969 );
17970 }
17971 *part_guard = Some((
17972 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17973 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17974 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17975 ));
17976 }
17977 let pg = part_guard.as_mut().unwrap();
17978 self.gpu
17979 .stream()
17980 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17981 self.gpu
17982 .stream()
17983 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17984 self.gpu
17985 .stream()
17986 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17987 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17988 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17989 let (nspm, spk) = (n_splits_max as i32, split_keys as i32);
17990 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17991 let gqa = (n_head / n_head_kv).max(1) as u32;
17992 let f = self.func("fa_decode_vec_q_seqs_v4");
17993 let shmem = (11520 + 32 * head_dim * 2) as u32;
17995 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17996 f.set_attribute(
17997 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17998 shmem as i32,
17999 )?;
18000 let cfg = LaunchConfig {
18001 grid_dim: (n_head_kv as u32, n_splits_max as u32, b_n as u32),
18002 block_dim: (32, gqa, 1),
18003 shared_mem_bytes: shmem,
18004 };
18005 {
18006 let __s_b = self.gpu.stream();
18007 let mut b = __s_b.launch_builder(&f);
18008 b.arg(q)
18009 .arg(kv_ptrs)
18010 .arg(pos_seq)
18011 .arg(&mut *part_o)
18012 .arg(&mut *part_m)
18013 .arg(&mut *part_l)
18014 .arg(&hd)
18015 .arg(&nh)
18016 .arg(&nhkv)
18017 .arg(&scale)
18018 .arg(&nspm)
18019 .arg(&spk)
18020 .arg(&ktb)
18021 .arg(&vtb);
18022 unsafe {
18023 b.launch(cfg)?;
18024 }
18025 }
18026 let fc = self.func("fa_decode_combine_seqs");
18027 let cfg2 = LaunchConfig {
18028 grid_dim: (n_head as u32, b_n as u32, 1),
18029 block_dim: (head_dim as u32, 1, 1),
18030 shared_mem_bytes: 0,
18031 };
18032 let __s_b2 = self.gpu.stream();
18033 let mut b2 = __s_b2.launch_builder(&fc);
18034 b2.arg(&*part_o)
18035 .arg(&*part_m)
18036 .arg(&*part_l)
18037 .arg(o)
18038 .arg(&hd)
18039 .arg(&nh)
18040 .arg(pos_seq)
18041 .arg(&nspm)
18042 .arg(&spk);
18043 unsafe {
18044 b2.launch(cfg2)?;
18045 }
18046 Ok(())
18047 }
18048
18049 #[allow(clippy::too_many_arguments)]
18056 pub fn append_kv_quantized_seqs(
18057 &self,
18058 k_rows: &CudaSlice<f32>,
18059 v_rows: &CudaSlice<f32>,
18060 kv_ptrs: &cudarc::driver::CudaView<u64>,
18061 pos_seq: &CudaSlice<i32>,
18062 b_n: usize,
18063 kv_dim_k: usize,
18064 kv_dim_v: usize,
18065 k_tok_bytes: usize,
18066 v_tok_bytes: usize,
18067 ) -> Result<(), Box<dyn std::error::Error>> {
18068 let f = self.func("append_quantize_kv_q8_0_q5_1_seqs");
18069 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
18070 let cfg = LaunchConfig {
18071 grid_dim: (nblk, b_n as u32, 1),
18072 block_dim: (32, 1, 1),
18073 shared_mem_bytes: 0,
18074 };
18075 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
18076 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18077 let __s_b = self.gpu.stream();
18078 let mut b = __s_b.launch_builder(&f);
18079 b.arg(k_rows)
18080 .arg(v_rows)
18081 .arg(kv_ptrs)
18082 .arg(pos_seq)
18083 .arg(&kdk)
18084 .arg(&kdv)
18085 .arg(&ktb)
18086 .arg(&vtb);
18087 unsafe {
18088 b.launch(cfg)?;
18089 }
18090 Ok(())
18091 }
18092
18093 pub fn fa_rows_eligible(&self, base_len: usize, head_dim: usize) -> bool {
18099 std::env::var("MEMRA_NO_FA_VEC").is_err()
18100 && std::env::var("MEMRA_FA_ROWS_OFF").is_err()
18101 && base_len + 1 >= fa_vec_min_tkv()
18102 && head_dim <= 256
18103 && head_dim % 32 == 0
18104 }
18105
18106 #[allow(clippy::too_many_arguments)]
18115 pub fn fa_decode_rows(
18116 &self,
18117 q: &CudaSlice<f32>,
18118 k: &cudarc::driver::CudaView<u8>,
18119 v: &cudarc::driver::CudaView<u8>,
18120 o: &mut CudaSlice<f32>,
18121 head_dim: usize,
18122 n_head: usize,
18123 n_head_kv: usize,
18124 base_len: usize,
18125 t: usize,
18126 scale: f32,
18127 k_tok_bytes: usize,
18128 v_tok_bytes: usize,
18129 base_dev: Option<(&CudaSlice<i32>, i32)>,
18133 kv_shared: bool,
18136 g: bool,
18140 mut q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
18143 ) -> Result<(), Box<dyn std::error::Error>> {
18144 debug_assert!(base_len + 1 >= fa_vec_min_tkv() && head_dim <= 512 && head_dim % 32 == 0);
18145 let t_kv_max = base_len + t; let mut sp = fa_split_keys(t_kv_max, n_head_kv); if head_dim == 512 {
18152 static SP512: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18153 let v = *SP512.get_or_init(|| {
18156 std::env::var("MEMRA_FA_SP512")
18157 .ok()
18158 .and_then(|x| x.parse().ok())
18159 .unwrap_or(0)
18160 });
18161 sp = if v >= 8 {
18162 v
18163 } else {
18164 FA_SP512_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
18165 };
18166 }
18167 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18168 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18169 let gqa = (n_head / n_head_kv).max(1) as u32;
18170 let mut groups: Vec<(usize, usize, usize)> = Vec::new(); if head_dim == 512 || fa_split_keys(base_len + 1, n_head_kv) == sp {
18181 groups.push((0, t, sp));
18182 } else {
18183 let mut r0 = 0usize;
18184 while r0 < t {
18185 let sp_g = fa_split_keys(base_len + r0 + 1, n_head_kv);
18186 let mut r1 = r0 + 1;
18187 while r1 < t && fa_split_keys(base_len + r1 + 1, n_head_kv) == sp_g {
18188 r1 += 1;
18189 }
18190 groups.push((r0, r1 - r0, sp_g));
18191 r0 = r1;
18192 }
18193 }
18194 static SMEM_TKV_R: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18198 let smem_tkv = *SMEM_TKV_R.get_or_init(|| {
18199 std::env::var("MEMRA_FA_SMEM_TKV")
18200 .ok()
18201 .and_then(|v| v.parse().ok())
18202 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
18203 });
18204 let v4 = fa_v4_at(base_len + t) && head_dim == 256;
18205 let v3 = fa_v3_active(head_dim);
18206 let smem_rows =
18207 head_dim <= 256 && !v3 && !fa_v2_on() && smem_tkv > 0 && t_kv_max >= smem_tkv;
18208 let _ = kv_shared;
18213 let i2 = head_dim == 512 && std::env::var("MEMRA_FA_I2").as_deref() != Ok("0");
18216 static TB512: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
18230 let tb512 = head_dim == 512
18232 && sp <= 32
18233 && n_head / n_head_kv.max(1) <= 16
18234 && *TB512.get_or_init(|| std::env::var("MEMRA_FA_TB512").as_deref() != Ok("0"));
18235 let fname = if tb512 {
18236 "fa_decode_vec_q_rows_v4_512_tb"
18237 } else if i2 {
18238 "fa_decode_vec_q_rows_dpl16_i2"
18239 } else if head_dim == 512 {
18240 "fa_decode_vec_q_rows_dpl16"
18241 }
18242 else if v4 {
18244 "fa_decode_vec_q_rows_v4"
18245 } else if v3 {
18246 "fa_decode_vec_q_rows_v3"
18247 } else if fa_v2_on() {
18248 "fa_decode_vec_q_rows_v2"
18249 } else if smem_rows {
18250 "fa_decode_vec_q_rows_smem"
18251 } else {
18252 "fa_decode_vec_q_rows"
18253 };
18254 let f = if head_dim == 512 {
18255 self.fa_func(fname, head_dim)
18256 } else if g {
18257 self.func_g(if smem_rows {
18265 "fa_decode_vec_q_rows"
18266 } else {
18267 fname
18268 })
18269 } else {
18270 self.func(fname)
18271 };
18272 let shmem = if tb512 {
18273 let gk = Self::gkv_on();
18275 let sh =
18276 (8192 + 1024 + 32 * 512 + 32 * 64 + 32 * head_dim * if gk { 1 } else { 2 }) as u32;
18277 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18278 f.set_attribute(
18279 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18280 sh as i32,
18281 )?;
18282 sh
18283 } else if v4 || v3 || smem_rows || fa_v2_on() {
18284 let sh = (if v4 {
18286 11520 + 32 * head_dim * if g { 1 } else { 2 }
18287 } else if v3 {
18288 32 * head_dim * 2
18289 } else {
18290 2 * 32 * head_dim * 2
18291 }) as u32;
18292 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18293 f.set_attribute(
18294 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18295 sh as i32,
18296 )?;
18297 sh
18298 } else {
18299 0
18300 };
18301 for &(r0, t_g, sp_g) in &groups {
18305 let n_splits_g = (base_len + r0 + t_g).div_ceil(sp_g);
18306 let (nspm, spk) = (n_splits_g as i32, sp_g as i32);
18307 let base_i = (base_len + r0) as i32;
18308 let o_len = t_g * n_head * n_splits_g * head_dim;
18309 let ml_len = t_g * n_head * n_splits_g;
18310 let mut part_guard = self.fa_part_pool.lock().unwrap();
18311 if part_guard
18312 .as_ref()
18313 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18314 .unwrap_or(true)
18315 {
18316 let old = part_guard.take();
18327 let (co, cm) = old
18328 .as_ref()
18329 .map(|pp| (pp.0.len(), pp.1.len()))
18330 .unwrap_or((0, 0));
18331 if let Some(old) = old {
18332 self.fa_part_retired.lock().unwrap().push(old);
18333 }
18334 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18335 eprintln!(
18336 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18337 co, o_len, cm, ml_len
18338 );
18339 }
18340 *part_guard = Some((
18341 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18342 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18343 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18344 ));
18345 }
18346 let pg = part_guard.as_mut().unwrap();
18347 self.gpu
18348 .stream()
18349 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18350 self.gpu
18351 .stream()
18352 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18353 self.gpu
18354 .stream()
18355 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18356 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18357 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
18358 let qv = self.view(q, t * n_head * head_dim);
18359 let q_g = qv.slice(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
18360 let cfg = LaunchConfig {
18361 grid_dim: (n_head_kv as u32, n_splits_g as u32, t_g as u32),
18362 block_dim: (32, gqa, 1),
18363 shared_mem_bytes: shmem,
18364 };
18365 {
18366 let __s_b = self.gpu.stream();
18367 let mut b = __s_b.launch_builder(&f);
18368 if tb512 {
18369 let (bd, plus) =
18371 base_dev.expect("hd512 rows twin requires a device base counter");
18372 let plus_g = plus + r0 as i32;
18373 let nr = t_g as i32;
18374 if Self::pdl_on() && Self::pdl_wb_on() {
18375 use cudarc::driver::{DevicePtr, DevicePtrMut};
18377 let s = &self.gpu.stream();
18378 let (pq, _b0) = q_g.device_ptr(s);
18379 let (pk, _b1) = k.device_ptr(s);
18380 let (pv, _b2) = v.device_ptr(s);
18381 let (po, _b3) = part_o.device_ptr_mut(s);
18382 let (pm, _b4) = part_m.device_ptr_mut(s);
18383 let (pl, _b5) = part_l.device_ptr_mut(s);
18384 let (pb, _b6) = bd.device_ptr(s);
18385 let mut ps = [
18386 &pq as *const _ as *mut std::ffi::c_void,
18387 &pk as *const _ as *mut _,
18388 &pv as *const _ as *mut _,
18389 &po as *const _ as *mut _,
18390 &pm as *const _ as *mut _,
18391 &pl as *const _ as *mut _,
18392 &hd as *const _ as *mut _,
18393 &nh as *const _ as *mut _,
18394 &nhkv as *const _ as *mut _,
18395 &pb as *const _ as *mut _,
18396 &plus_g as *const _ as *mut _,
18397 &scale as *const _ as *mut _,
18398 &nspm as *const _ as *mut _,
18399 &spk as *const _ as *mut _,
18400 &ktb as *const _ as *mut _,
18401 &vtb as *const _ as *mut _,
18402 &nr as *const _ as *mut _,
18403 ];
18404 unsafe {
18405 self.launch_pdl_flash(
18406 Self::gkv_on(),
18407 "fa_decode_vec_q_rows_v4_512_tb",
18408 (n_head_kv as u32, n_splits_g as u32, 1),
18409 (32, gqa, 1),
18410 shmem,
18411 &mut ps,
18412 )?;
18413 }
18414 } else {
18415 let cfg_tb = LaunchConfig {
18416 grid_dim: (n_head_kv as u32, n_splits_g as u32, 1),
18417 block_dim: (32, gqa, 1),
18418 shared_mem_bytes: shmem,
18419 };
18420 b.arg(&q_g)
18421 .arg(k)
18422 .arg(v)
18423 .arg(&mut *part_o)
18424 .arg(&mut *part_m)
18425 .arg(&mut *part_l)
18426 .arg(&hd)
18427 .arg(&nh)
18428 .arg(&nhkv)
18429 .arg(bd)
18430 .arg(&plus_g)
18431 .arg(&scale)
18432 .arg(&nspm)
18433 .arg(&spk)
18434 .arg(&ktb)
18435 .arg(&vtb)
18436 .arg(&nr);
18437 unsafe {
18438 b.launch(cfg_tb)?;
18439 }
18440 }
18441 } else if head_dim == 512 {
18442 let (bd, plus) =
18443 base_dev.expect("hd512 rows twin requires a device base counter");
18444 let plus_g = plus + r0 as i32;
18445 b.arg(&q_g)
18446 .arg(k)
18447 .arg(v)
18448 .arg(&mut *part_o)
18449 .arg(&mut *part_m)
18450 .arg(&mut *part_l)
18451 .arg(&hd)
18452 .arg(&nh)
18453 .arg(&nhkv)
18454 .arg(bd)
18455 .arg(&plus_g)
18456 .arg(&scale)
18457 .arg(&nspm)
18458 .arg(&spk)
18459 .arg(&ktb)
18460 .arg(&vtb);
18461 unsafe {
18462 b.launch(cfg)?;
18463 }
18464 } else {
18465 b.arg(&q_g)
18466 .arg(k)
18467 .arg(v)
18468 .arg(&mut *part_o)
18469 .arg(&mut *part_m)
18470 .arg(&mut *part_l)
18471 .arg(&hd)
18472 .arg(&nh)
18473 .arg(&nhkv)
18474 .arg(&base_i)
18475 .arg(&scale)
18476 .arg(&nspm)
18477 .arg(&spk)
18478 .arg(&ktb)
18479 .arg(&vtb);
18480 unsafe {
18481 b.launch(cfg)?;
18482 }
18483 }
18484 }
18485 let cfg2 = LaunchConfig {
18486 grid_dim: (n_head as u32, t_g as u32, 1),
18487 block_dim: (head_dim as u32, 1, 1),
18488 shared_mem_bytes: 0,
18489 };
18490 let mut o_g = o.slice_mut(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
18491 if head_dim == 512 {
18492 let (bd, plus) = base_dev.unwrap();
18495 let plus_g = plus + r0 as i32;
18496 if let Some((oq, od)) = q8_out.as_mut() {
18497 debug_assert!(t == 1, "rows q8 emit is a t=1 decode arm");
18499 if Self::pdl_on() && Self::pdl_wb_on() {
18500 use cudarc::driver::{DevicePtr, DevicePtrMut};
18502 let s = &self.gpu.stream();
18503 let (po, _g0) = part_o.device_ptr(s);
18504 let (pm, _g1) = part_m.device_ptr(s);
18505 let (pl, _g2) = part_l.device_ptr(s);
18506 let (pq, _g3) = oq.device_ptr_mut(s);
18507 let (pd, _g4) = od.device_ptr_mut(s);
18508 let (pb, _g5) = bd.device_ptr(s);
18509 let mut ps = [
18510 &po as *const _ as *mut std::ffi::c_void,
18511 &pm as *const _ as *mut _,
18512 &pl as *const _ as *mut _,
18513 &pq as *const _ as *mut _,
18514 &pd as *const _ as *mut _,
18515 &hd as *const _ as *mut _,
18516 &nh as *const _ as *mut _,
18517 &pb as *const _ as *mut _,
18518 &plus_g as *const _ as *mut _,
18519 &nspm as *const _ as *mut _,
18520 &spk as *const _ as *mut _,
18521 ];
18522 unsafe {
18523 self.launch_pdl_flash(
18524 Self::gkv_on(),
18525 "fa_decode_combine_rows_dc_q8_1",
18526 cfg2.grid_dim,
18527 cfg2.block_dim,
18528 0,
18529 &mut ps,
18530 )?;
18531 }
18532 continue;
18533 }
18534 let fc = self.fa_func("fa_decode_combine_rows_dc_q8_1", head_dim);
18535 let __s_b2 = self.gpu.stream();
18536 let mut b2 = __s_b2.launch_builder(&fc);
18537 b2.arg(&*part_o)
18538 .arg(&*part_m)
18539 .arg(&*part_l)
18540 .arg(&mut **oq)
18541 .arg(&mut **od)
18542 .arg(&hd)
18543 .arg(&nh)
18544 .arg(bd)
18545 .arg(&plus_g)
18546 .arg(&nspm)
18547 .arg(&spk);
18548 unsafe {
18549 b2.launch(cfg2)?;
18550 }
18551 continue;
18552 }
18553 let fc = self.fa_func("fa_decode_combine_rows_dc", head_dim);
18554 let __s_b2 = self.gpu.stream();
18555 let mut b2 = __s_b2.launch_builder(&fc);
18556 b2.arg(&*part_o)
18557 .arg(&*part_m)
18558 .arg(&*part_l)
18559 .arg(&mut o_g)
18560 .arg(&hd)
18561 .arg(&nh)
18562 .arg(bd)
18563 .arg(&plus_g)
18564 .arg(&nspm)
18565 .arg(&spk);
18566 unsafe {
18567 b2.launch(cfg2)?;
18568 }
18569 } else {
18570 assert!(
18573 q8_out.is_none(),
18574 "rows q8 emit requires the hd512 dc combine"
18575 );
18576 let fc = self.func("fa_decode_combine_rows");
18577 let __s_b2 = self.gpu.stream();
18578 let mut b2 = __s_b2.launch_builder(&fc);
18579 b2.arg(&*part_o)
18580 .arg(&*part_m)
18581 .arg(&*part_l)
18582 .arg(&mut o_g)
18583 .arg(&hd)
18584 .arg(&nh)
18585 .arg(&base_i)
18586 .arg(&nspm)
18587 .arg(&spk);
18588 unsafe {
18589 b2.launch(cfg2)?;
18590 }
18591 }
18592 }
18593 Ok(())
18594 }
18595
18596 #[allow(clippy::too_many_arguments)]
18600 pub fn fa_decode_rows_w(
18601 &self,
18602 q: &CudaSlice<f32>,
18603 k: &cudarc::driver::CudaView<u8>,
18604 v: &cudarc::driver::CudaView<u8>,
18605 o: &mut CudaSlice<f32>,
18606 head_dim: usize,
18607 n_head: usize,
18608 n_head_kv: usize,
18609 base_dev: &CudaSlice<i32>,
18610 base_plus: i32,
18611 t: usize,
18612 scale: f32,
18613 window: usize,
18614 k_tok_bytes: usize,
18615 v_tok_bytes: usize,
18616 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
18617 ) -> Result<(), Box<dyn std::error::Error>> {
18618 debug_assert!(head_dim == 256);
18623 let sp = {
18631 static SPW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18632 let v = *SPW.get_or_init(|| {
18633 std::env::var("MEMRA_FA_SPW")
18634 .ok()
18635 .and_then(|x| x.parse().ok())
18636 .unwrap_or(0)
18637 });
18638 if v >= 8 {
18639 v
18640 } else {
18641 FA_SPW_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
18642 }
18643 };
18644 let n_splits_max = (window + sp - 1) / sp;
18645 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18646 let (nspm, spk, wini) = (n_splits_max as i32, sp as i32, window as i32);
18647 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18648 let gqa = (n_head / n_head_kv).max(1) as u32;
18649 let o_len = t * n_head * n_splits_max * head_dim;
18650 let ml_len = t * n_head * n_splits_max;
18651 let mut part_guard = self.fa_part_pool.lock().unwrap();
18652 if part_guard
18653 .as_ref()
18654 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18655 .unwrap_or(true)
18656 {
18657 let old = part_guard.take();
18668 let (co, cm) = old
18669 .as_ref()
18670 .map(|pp| (pp.0.len(), pp.1.len()))
18671 .unwrap_or((0, 0));
18672 if let Some(old) = old {
18673 self.fa_part_retired.lock().unwrap().push(old);
18674 }
18675 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18676 eprintln!(
18677 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18678 co, o_len, cm, ml_len
18679 );
18680 }
18681 *part_guard = Some((
18682 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18683 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18684 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18685 ));
18686 }
18687 let pg = part_guard.as_mut().unwrap();
18688 self.gpu
18689 .stream()
18690 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18691 self.gpu
18692 .stream()
18693 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18694 self.gpu
18695 .stream()
18696 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18697 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18698 static SMEM_TKV_W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18704 let smem_tkv = *SMEM_TKV_W.get_or_init(|| {
18705 std::env::var("MEMRA_FA_SMEM_TKV")
18706 .ok()
18707 .and_then(|v| v.parse().ok())
18708 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
18709 });
18710 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18716 let wg = Self::wkv_on();
18721 let sp2 =
18724 gqa <= 4 && fa_v4_at(window) && std::env::var("MEMRA_FA_SPW2").as_deref() != Ok("0");
18725 if sp2 {
18726 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
18727 if Self::pdl_on() && Self::pdl_wb_on() {
18728 use cudarc::driver::{DevicePtr, DevicePtrMut};
18730 let s = &self.gpu.stream();
18731 let (pq, _b0) = q.device_ptr(s);
18732 let (pk, _b1) = k.device_ptr(s);
18733 let (pv, _b2) = v.device_ptr(s);
18734 let (po, _b3) = part_o.device_ptr_mut(s);
18735 let (pm, _b4) = part_m.device_ptr_mut(s);
18736 let (pl, _b5) = part_l.device_ptr_mut(s);
18737 let (pb, _b6) = base_dev.device_ptr(s);
18738 let mut ps = [
18739 &pq as *const _ as *mut std::ffi::c_void,
18740 &pk as *const _ as *mut _,
18741 &pv as *const _ as *mut _,
18742 &po as *const _ as *mut _,
18743 &pm as *const _ as *mut _,
18744 &pl as *const _ as *mut _,
18745 &hd as *const _ as *mut _,
18746 &nh as *const _ as *mut _,
18747 &nhkv as *const _ as *mut _,
18748 &pb as *const _ as *mut _,
18749 &base_plus as *const _ as *mut _,
18750 &scale as *const _ as *mut _,
18751 &nspm as *const _ as *mut _,
18752 &spk as *const _ as *mut _,
18753 &ktb as *const _ as *mut _,
18754 &vtb as *const _ as *mut _,
18755 &wini as *const _ as *mut _,
18756 ];
18757 unsafe {
18758 self.launch_pdl_flash(
18759 wg,
18760 "fa_decode_vec_q_rows_v4_w_sp",
18761 (n_head_kv as u32, n_splits_max as u32, t as u32),
18762 (32, gqa + 1, 1),
18763 sh,
18764 &mut ps,
18765 )?;
18766 }
18767 } else {
18768 let f = if wg {
18769 self.func_g("fa_decode_vec_q_rows_v4_w_sp")
18770 } else {
18771 self.func("fa_decode_vec_q_rows_v4_w_sp")
18772 };
18773 f.set_attribute(
18774 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18775 sh as i32,
18776 )?;
18777 let cfg = LaunchConfig {
18778 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18779 block_dim: (32, gqa + 1, 1),
18780 shared_mem_bytes: sh,
18781 };
18782 let __s_b = self.gpu.stream();
18783 let mut b = __s_b.launch_builder(&f);
18784 b.arg(q)
18785 .arg(k)
18786 .arg(v)
18787 .arg(&mut *part_o)
18788 .arg(&mut *part_m)
18789 .arg(&mut *part_l)
18790 .arg(&hd)
18791 .arg(&nh)
18792 .arg(&nhkv)
18793 .arg(base_dev)
18794 .arg(&base_plus)
18795 .arg(&scale)
18796 .arg(&nspm)
18797 .arg(&spk)
18798 .arg(&ktb)
18799 .arg(&vtb)
18800 .arg(&wini);
18801 unsafe {
18802 b.launch(cfg)?;
18803 }
18804 }
18805 } else {
18806 if fa_v4_at(window) && Self::pdl_on() && Self::pdl_wb_on() {
18807 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
18809 use cudarc::driver::{DevicePtr, DevicePtrMut};
18810 let s = &self.gpu.stream();
18811 let (pq, _b0) = q.device_ptr(s);
18812 let (pk, _b1) = k.device_ptr(s);
18813 let (pv, _b2) = v.device_ptr(s);
18814 let (po, _b3) = part_o.device_ptr_mut(s);
18815 let (pm, _b4) = part_m.device_ptr_mut(s);
18816 let (pl, _b5) = part_l.device_ptr_mut(s);
18817 let (pb, _b6) = base_dev.device_ptr(s);
18818 let mut ps = [
18819 &pq as *const _ as *mut std::ffi::c_void,
18820 &pk as *const _ as *mut _,
18821 &pv as *const _ as *mut _,
18822 &po as *const _ as *mut _,
18823 &pm as *const _ as *mut _,
18824 &pl as *const _ as *mut _,
18825 &hd as *const _ as *mut _,
18826 &nh as *const _ as *mut _,
18827 &nhkv as *const _ as *mut _,
18828 &pb as *const _ as *mut _,
18829 &base_plus as *const _ as *mut _,
18830 &scale as *const _ as *mut _,
18831 &nspm as *const _ as *mut _,
18832 &spk as *const _ as *mut _,
18833 &ktb as *const _ as *mut _,
18834 &vtb as *const _ as *mut _,
18835 &wini as *const _ as *mut _,
18836 ];
18837 unsafe {
18838 self.launch_pdl_flash(
18839 wg,
18840 "fa_decode_vec_q_rows_v4_w",
18841 (n_head_kv as u32, n_splits_max as u32, t as u32),
18842 (32, gqa, 1),
18843 sh,
18844 &mut ps,
18845 )?;
18846 }
18847 } else {
18848 let pick = |name: &str| {
18849 if wg {
18850 self.func_g(name)
18851 } else {
18852 self.func(name)
18853 }
18854 };
18855 let (f, sh) = if fa_v4_at(window) {
18856 let f = pick("fa_decode_vec_q_rows_v4_w");
18857 (f, (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32)
18858 } else if smem_tkv > 0 && window >= smem_tkv {
18859 (
18862 pick("fa_decode_vec_q_rows_smem_w"),
18863 (2 * 32 * head_dim * 2) as u32,
18864 )
18865 } else {
18866 (pick("fa_decode_vec_q_rows_reg_w"), 0u32)
18867 };
18868 f.set_attribute(
18869 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18870 sh as i32,
18871 )?;
18872 let cfg = LaunchConfig {
18873 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18874 block_dim: (32, gqa, 1),
18875 shared_mem_bytes: sh,
18876 };
18877 let __s_b = self.gpu.stream();
18878 let mut b = __s_b.launch_builder(&f);
18879 b.arg(q)
18880 .arg(k)
18881 .arg(v)
18882 .arg(&mut *part_o)
18883 .arg(&mut *part_m)
18884 .arg(&mut *part_l)
18885 .arg(&hd)
18886 .arg(&nh)
18887 .arg(&nhkv)
18888 .arg(base_dev)
18889 .arg(&base_plus)
18890 .arg(&scale)
18891 .arg(&nspm)
18892 .arg(&spk)
18893 .arg(&ktb)
18894 .arg(&vtb)
18895 .arg(&wini);
18896 unsafe {
18897 b.launch(cfg)?;
18898 }
18899 }
18900 }
18901 let cfg2 = LaunchConfig {
18902 grid_dim: (n_head as u32, t as u32, 1),
18903 block_dim: (head_dim as u32, 1, 1),
18904 shared_mem_bytes: 0,
18905 };
18906 if let Some((oq, od)) = q8_out {
18907 if Self::pdl_on() && Self::pdl_wb_on() {
18910 use cudarc::driver::{DevicePtr, DevicePtrMut};
18912 let s = &self.gpu.stream();
18913 let (po, _g0) = part_o.device_ptr(s);
18914 let (pm, _g1) = part_m.device_ptr(s);
18915 let (pl, _g2) = part_l.device_ptr(s);
18916 let (pq, _g3) = oq.device_ptr_mut(s);
18917 let (pd, _g4) = od.device_ptr_mut(s);
18918 let mut ps = [
18919 &po as *const _ as *mut std::ffi::c_void,
18920 &pm as *const _ as *mut _,
18921 &pl as *const _ as *mut _,
18922 &pq as *const _ as *mut _,
18923 &pd as *const _ as *mut _,
18924 &hd as *const _ as *mut _,
18925 &nh as *const _ as *mut _,
18926 &nspm as *const _ as *mut _,
18927 &spk as *const _ as *mut _,
18928 &wini as *const _ as *mut _,
18929 ];
18930 unsafe {
18931 self.launch_pdl_flash(
18932 wg,
18933 "fa_decode_combine_rows_w_q8_1",
18934 cfg2.grid_dim,
18935 cfg2.block_dim,
18936 0,
18937 &mut ps,
18938 )?;
18939 }
18940 return Ok(());
18941 }
18942 let fc = if wg {
18943 self.func_g("fa_decode_combine_rows_w_q8_1")
18944 } else {
18945 self.func("fa_decode_combine_rows_w_q8_1")
18946 };
18947 let __s_b2 = self.gpu.stream();
18948 let mut b2 = __s_b2.launch_builder(&fc);
18949 b2.arg(&*part_o)
18950 .arg(&*part_m)
18951 .arg(&*part_l)
18952 .arg(oq)
18953 .arg(od)
18954 .arg(&hd)
18955 .arg(&nh)
18956 .arg(&nspm)
18957 .arg(&spk)
18958 .arg(&wini);
18959 unsafe {
18960 b2.launch(cfg2)?;
18961 }
18962 return Ok(());
18963 }
18964 let fc = if wg {
18965 self.func_g("fa_decode_combine_rows_w")
18966 } else {
18967 self.func("fa_decode_combine_rows_w")
18968 };
18969 let __s_b2 = self.gpu.stream();
18970 let mut b2 = __s_b2.launch_builder(&fc);
18971 b2.arg(&*part_o)
18972 .arg(&*part_m)
18973 .arg(&*part_l)
18974 .arg(o)
18975 .arg(&hd)
18976 .arg(&nh)
18977 .arg(&nspm)
18978 .arg(&spk)
18979 .arg(&wini);
18980 unsafe {
18981 b2.launch(cfg2)?;
18982 }
18983 Ok(())
18984 }
18985
18986 #[allow(clippy::too_many_arguments)]
18992 pub fn fa_decode_rows_dc(
18993 &self,
18994 q: &CudaSlice<f32>,
18995 k: &cudarc::driver::CudaView<u8>,
18996 v: &cudarc::driver::CudaView<u8>,
18997 o: &mut CudaSlice<f32>,
18998 head_dim: usize,
18999 n_head: usize,
19000 n_head_kv: usize,
19001 base_dev: &CudaSlice<i32>,
19002 t_kv_upper: usize,
19003 t: usize,
19004 scale: f32,
19005 k_tok_bytes: usize,
19006 v_tok_bytes: usize,
19007 base_plus: i32,
19008 g: bool,
19009 ) -> Result<(), Box<dyn std::error::Error>> {
19010 let v4 = head_dim == 256 && fa_v4_at(t_kv_upper);
19011 assert!(
19012 v4 || fa_v3_active(head_dim),
19013 "stream fa rows requires the v3 or v4 lane"
19014 );
19015 assert!(v4 || base_plus == 0, "v3_dc kernel takes no plus arg");
19016 if v4 {
19017 let sp = fa_split_keys(t_kv_upper, n_head_kv);
19018 let n_splits_max = (t_kv_upper + sp - 1) / sp;
19019 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
19020 let (nspm, spk) = (n_splits_max as i32, sp as i32);
19021 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
19022 let gqa = (n_head / n_head_kv).max(1) as u32;
19023 let o_len = t * n_head * n_splits_max * head_dim;
19024 let ml_len = t * n_head * n_splits_max;
19025 let mut part_guard = self.fa_part_pool.lock().unwrap();
19026 if part_guard
19027 .as_ref()
19028 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
19029 .unwrap_or(true)
19030 {
19031 let old = part_guard.take();
19042 let (co, cm) = old
19043 .as_ref()
19044 .map(|pp| (pp.0.len(), pp.1.len()))
19045 .unwrap_or((0, 0));
19046 if let Some(old) = old {
19047 self.fa_part_retired.lock().unwrap().push(old);
19048 }
19049 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
19050 eprintln!(
19051 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
19052 co, o_len, cm, ml_len
19053 );
19054 }
19055 *part_guard = Some((
19056 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
19057 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19058 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19059 ));
19060 }
19061 let pg = part_guard.as_mut().unwrap();
19062 self.gpu
19063 .stream()
19064 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
19065 self.gpu
19066 .stream()
19067 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
19068 self.gpu
19069 .stream()
19070 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
19071 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
19072 let f = if g {
19073 self.func_g("fa_decode_vec_q_rows_v4_dc")
19074 } else {
19075 self.func("fa_decode_vec_q_rows_v4_dc")
19076 };
19077 let sh = (11520 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
19078 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19079 f.set_attribute(
19080 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19081 sh as i32,
19082 )?;
19083 let cfg = LaunchConfig {
19084 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
19085 block_dim: (32, gqa, 1),
19086 shared_mem_bytes: sh,
19087 };
19088 let __s_b = self.gpu.stream();
19089 let mut b = __s_b.launch_builder(&f);
19090 b.arg(q)
19091 .arg(k)
19092 .arg(v)
19093 .arg(&mut *part_o)
19094 .arg(&mut *part_m)
19095 .arg(&mut *part_l)
19096 .arg(&hd)
19097 .arg(&nh)
19098 .arg(&nhkv)
19099 .arg(base_dev)
19100 .arg(&base_plus)
19101 .arg(&scale)
19102 .arg(&nspm)
19103 .arg(&spk)
19104 .arg(&ktb)
19105 .arg(&vtb);
19106 unsafe {
19107 b.launch(cfg)?;
19108 }
19109 let fc = self.func("fa_decode_combine_rows_dc");
19110 let cfg2 = LaunchConfig {
19111 grid_dim: (n_head as u32, t as u32, 1),
19112 block_dim: (head_dim as u32, 1, 1),
19113 shared_mem_bytes: 0,
19114 };
19115 let __s_b2 = self.gpu.stream();
19116 let mut b2 = __s_b2.launch_builder(&fc);
19117 b2.arg(&*part_o)
19118 .arg(&*part_m)
19119 .arg(&*part_l)
19120 .arg(o)
19121 .arg(&hd)
19122 .arg(&nh)
19123 .arg(base_dev)
19124 .arg(&base_plus)
19125 .arg(&nspm)
19126 .arg(&spk);
19127 unsafe {
19128 b2.launch(cfg2)?;
19129 }
19130 return Ok(());
19131 }
19132 let sp = fa_split_keys(t_kv_upper, n_head_kv);
19133 let n_splits_max = (t_kv_upper + sp - 1) / sp;
19134 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
19135 let (nspm, spk) = (n_splits_max as i32, sp as i32);
19136 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
19137 let gqa = (n_head / n_head_kv).max(1) as u32;
19138 let o_len = t * n_head * n_splits_max * head_dim;
19139 let ml_len = t * n_head * n_splits_max;
19140 let mut part_guard = self.fa_part_pool.lock().unwrap();
19141 if part_guard
19142 .as_ref()
19143 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
19144 .unwrap_or(true)
19145 {
19146 let old = part_guard.take();
19157 let (co, cm) = old
19158 .as_ref()
19159 .map(|pp| (pp.0.len(), pp.1.len()))
19160 .unwrap_or((0, 0));
19161 if let Some(old) = old {
19162 self.fa_part_retired.lock().unwrap().push(old);
19163 }
19164 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
19165 eprintln!(
19166 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
19167 co, o_len, cm, ml_len
19168 );
19169 }
19170 *part_guard = Some((
19171 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
19172 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19173 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19174 ));
19175 }
19176 let pg = part_guard.as_mut().unwrap();
19177 self.gpu
19178 .stream()
19179 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
19180 self.gpu
19181 .stream()
19182 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
19183 self.gpu
19184 .stream()
19185 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
19186 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
19187 let f = self.func("fa_decode_vec_q_rows_v3_dc");
19188 let sh = (32 * head_dim * 2) as u32;
19189 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19190 f.set_attribute(
19191 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19192 sh as i32,
19193 )?;
19194 let cfg = LaunchConfig {
19195 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
19196 block_dim: (32, gqa, 1),
19197 shared_mem_bytes: sh,
19198 };
19199 let __s_b = self.gpu.stream();
19200 let mut b = __s_b.launch_builder(&f);
19201 b.arg(q)
19202 .arg(k)
19203 .arg(v)
19204 .arg(&mut *part_o)
19205 .arg(&mut *part_m)
19206 .arg(&mut *part_l)
19207 .arg(&hd)
19208 .arg(&nh)
19209 .arg(&nhkv)
19210 .arg(base_dev)
19211 .arg(&scale)
19212 .arg(&nspm)
19213 .arg(&spk)
19214 .arg(&ktb)
19215 .arg(&vtb);
19216 unsafe {
19217 b.launch(cfg)?;
19218 }
19219 let fc = self.func("fa_decode_combine_rows_dc");
19220 let cfg2 = LaunchConfig {
19221 grid_dim: (n_head as u32, t as u32, 1),
19222 block_dim: (head_dim as u32, 1, 1),
19223 shared_mem_bytes: 0,
19224 };
19225 let plus0 = 0i32;
19226 let __s_b2 = self.gpu.stream();
19227 let mut b2 = __s_b2.launch_builder(&fc);
19228 b2.arg(&*part_o)
19229 .arg(&*part_m)
19230 .arg(&*part_l)
19231 .arg(o)
19232 .arg(&hd)
19233 .arg(&nh)
19234 .arg(base_dev)
19235 .arg(&plus0)
19236 .arg(&nspm)
19237 .arg(&spk);
19238 unsafe {
19239 b2.launch(cfg2)?;
19240 }
19241 Ok(())
19242 }
19243
19244 pub fn fa_decode_dc(
19255 &self,
19256 q: &CudaSlice<f32>,
19257 k: &cudarc::driver::CudaView<u8>,
19258 v: &cudarc::driver::CudaView<u8>,
19259 o: &mut CudaSlice<f32>,
19260 head_dim: usize,
19261 n_head: usize,
19262 n_head_kv: usize,
19263 t_kv_dev: &CudaSlice<i32>,
19264 bucket_max: usize,
19265 scale: f32,
19266 k_tok_bytes: usize,
19267 v_tok_bytes: usize,
19268 g: bool,
19269 ) -> Result<(), Box<dyn std::error::Error>> {
19270 self.fa_decode_dc_q8(
19271 q,
19272 k,
19273 v,
19274 o,
19275 head_dim,
19276 n_head,
19277 n_head_kv,
19278 t_kv_dev,
19279 bucket_max,
19280 scale,
19281 k_tok_bytes,
19282 v_tok_bytes,
19283 g,
19284 None,
19285 )
19286 }
19287
19288 #[allow(clippy::too_many_arguments)]
19291 pub fn fa_decode_dc_q8(
19292 &self,
19293 q: &CudaSlice<f32>,
19294 k: &cudarc::driver::CudaView<u8>,
19295 v: &cudarc::driver::CudaView<u8>,
19296 o: &mut CudaSlice<f32>,
19297 head_dim: usize,
19298 n_head: usize,
19299 n_head_kv: usize,
19300 t_kv_dev: &CudaSlice<i32>,
19301 bucket_max: usize,
19302 scale: f32,
19303 k_tok_bytes: usize,
19304 v_tok_bytes: usize,
19305 g: bool,
19306 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
19307 ) -> Result<(), Box<dyn std::error::Error>> {
19308 let mut fa_vec =
19316 std::env::var("MEMRA_NO_FA_VEC").is_err() && bucket_max >= fa_vec_min_tkv();
19317 if g && head_dim == 256 && !fa_v4_at(bucket_max) {
19318 fa_vec = false;
19319 } let sp = fa_split_keys(bucket_max, n_head_kv);
19321 let n_splits = if fa_vec {
19322 ((bucket_max + sp - 1) / sp).max(1)
19323 } else {
19324 ((bucket_max + 255) / 256).max(1)
19325 };
19326 let o_len = n_head * n_splits * head_dim;
19327 let ml_len = n_head * n_splits;
19328 let mut part_guard = self.fa_part_pool.lock().unwrap();
19329 if part_guard
19330 .as_ref()
19331 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
19332 .unwrap_or(true)
19333 {
19334 let old = part_guard.take();
19345 let (co, cm) = old
19346 .as_ref()
19347 .map(|pp| (pp.0.len(), pp.1.len()))
19348 .unwrap_or((0, 0));
19349 if let Some(old) = old {
19350 self.fa_part_retired.lock().unwrap().push(old);
19351 }
19352 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
19353 eprintln!(
19354 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
19355 co, o_len, cm, ml_len
19356 );
19357 }
19358 *part_guard = Some((
19359 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
19360 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19361 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19362 ));
19363 }
19364 let pg = part_guard.as_mut().unwrap();
19365 self.gpu
19366 .stream()
19367 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
19368 self.gpu
19369 .stream()
19370 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
19371 self.gpu
19372 .stream()
19373 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
19374 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
19375 let (hd, nh, nhkv, nsp) = (
19376 head_dim as i32,
19377 n_head as i32,
19378 n_head_kv as i32,
19379 n_splits as i32,
19380 );
19381 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
19382 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
19383 let deep = fa_vec
19386 && head_dim == 256
19387 && fa_v4_at(bucket_max)
19388 && !g
19389 && fa_deep_at(bucket_max)
19390 && !matches!(fa_v4_mode(), "noB3" | "stage");
19391 let (f, cfg) = if fa_vec
19392 && head_dim == 512
19393 && bucket_max >= {
19394 static FA512_MIN_DC: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
19395 *FA512_MIN_DC.get_or_init(|| {
19396 std::env::var("MEMRA_FA512_MIN")
19397 .ok()
19398 .and_then(|v| v.parse().ok())
19399 .unwrap_or(512)
19400 })
19401 } {
19402 let gqa = (n_head / n_head_kv).max(1) as u32;
19404 (
19405 self.fa_func("fa_decode_vec_q_dpl16_dc", head_dim),
19406 LaunchConfig {
19407 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19408 block_dim: (32, gqa, 1),
19409 shared_mem_bytes: 0,
19410 },
19411 )
19412 } else if fa_vec && head_dim == 512 {
19413 let q_view = q.as_view();
19416 let mut o_view = o.as_view_mut();
19417 return self.fa_decode_scalar_unified(
19418 &q_view,
19419 k,
19420 v,
19421 &mut o_view,
19422 head_dim,
19423 n_head,
19424 n_head_kv,
19425 0,
19426 Some(t_kv_dev),
19427 scale,
19428 n_splits,
19429 sp,
19430 k_tok_bytes,
19431 v_tok_bytes,
19432 g,
19433 &mut *part_o,
19434 &mut *part_m,
19435 &mut *part_l,
19436 q8_out,
19437 );
19438 } else if fa_vec && head_dim == 256 && fa_v4_at(bucket_max) {
19439 let gqa = (n_head / n_head_kv).max(1) as u32;
19442 let fv = if g {
19443 self.func_g("fa_decode_vec_q_v4_dc")
19444 } else if deep {
19445 self.func("fa_decode_vec_q_v4_deep_dc")
19446 } else {
19447 self.func("fa_decode_vec_q_v4_dc")
19448 };
19449 let shmem =
19450 (if deep { 12160 } else { 11520 } + 32 * head_dim * if g { 1 } else { 2 }) as u32;
19451 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19452 fv.set_attribute(
19453 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19454 shmem as i32,
19455 )?;
19456 (
19457 fv,
19458 LaunchConfig {
19459 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19460 block_dim: (32, gqa, 1),
19461 shared_mem_bytes: shmem,
19462 },
19463 )
19464 } else if fa_vec && fa_v3_active(head_dim) {
19465 let gqa = (n_head / n_head_kv).max(1) as u32;
19468 let fv = if g {
19469 self.func_g("fa_decode_vec_q_v3_dc")
19470 } else {
19471 self.func("fa_decode_vec_q_v3_dc")
19472 };
19473 let shmem = (32 * head_dim * 2) as u32; (
19475 fv,
19476 LaunchConfig {
19477 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19478 block_dim: (32, gqa, 1),
19479 shared_mem_bytes: shmem,
19480 },
19481 )
19482 } else if fa_vec && fa_v2_on() {
19483 let gqa = (n_head / n_head_kv).max(1) as u32;
19487 let fv = if g {
19488 self.func_g("fa_decode_vec_q_v2_dc")
19489 } else {
19490 self.func("fa_decode_vec_q_v2_dc")
19491 };
19492 let shmem = (2 * 32 * head_dim * 2) as u32; (
19494 fv,
19495 LaunchConfig {
19496 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19497 block_dim: (32, gqa, 1),
19498 shared_mem_bytes: shmem,
19499 },
19500 )
19501 } else if fa_vec {
19502 let gqa = (n_head / n_head_kv).max(1) as u32;
19503 let fv = if g {
19505 self.func_g("fa_decode_vec_q_dc")
19506 } else {
19507 self.func("fa_decode_vec_q_dc")
19508 };
19509 (
19510 fv,
19511 LaunchConfig {
19512 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19513 block_dim: (32, gqa, 1),
19514 shared_mem_bytes: 0,
19515 },
19516 )
19517 } else {
19518 let q_view = q.as_view();
19519 let mut o_view = o.as_view_mut();
19520 return self.fa_decode_scalar_unified(
19521 &q_view,
19522 k,
19523 v,
19524 &mut o_view,
19525 head_dim,
19526 n_head,
19527 n_head_kv,
19528 0,
19529 Some(t_kv_dev),
19530 scale,
19531 n_splits,
19532 if fa_vec { sp } else { 256 },
19533 k_tok_bytes,
19534 v_tok_bytes,
19535 g,
19536 &mut *part_o,
19537 &mut *part_m,
19538 &mut *part_l,
19539 q8_out,
19540 );
19541 };
19542 let ski = sp as i32; let __s_b = self.gpu.stream();
19544 let mut b = __s_b.launch_builder(&f);
19545 b.arg(q)
19546 .arg(k)
19547 .arg(v)
19548 .arg(&mut *part_o)
19549 .arg(&mut *part_m)
19550 .arg(&mut *part_l)
19551 .arg(&hd)
19552 .arg(&nh)
19553 .arg(&nhkv)
19554 .arg(t_kv_dev)
19555 .arg(&scale)
19556 .arg(&nsp)
19557 .arg(&ski)
19558 .arg(&ktb)
19559 .arg(&vtb);
19560 unsafe {
19561 b.launch(cfg)?;
19562 }
19563 let cfg2 = LaunchConfig {
19564 grid_dim: (n_head as u32, 1, 1),
19565 block_dim: (head_dim as u32, 1, 1),
19566 shared_mem_bytes: 0,
19567 };
19568 if let Some((oq, od)) = q8_out {
19569 let fc = if g {
19570 self.func_g("fa_decode_combine_q8_1")
19571 } else {
19572 self.fa_func("fa_decode_combine_q8_1", head_dim)
19573 };
19574 let __s_b2 = self.gpu.stream();
19575 let mut b2 = __s_b2.launch_builder(&fc);
19576 b2.arg(&*part_o)
19577 .arg(&*part_m)
19578 .arg(&*part_l)
19579 .arg(oq)
19580 .arg(od)
19581 .arg(&hd)
19582 .arg(&nh)
19583 .arg(&nsp);
19584 unsafe {
19585 b2.launch(cfg2)?;
19586 }
19587 return Ok(());
19588 }
19589 let fc = if g {
19590 self.func_g("fa_decode_combine_f32")
19591 } else {
19592 self.fa_func("fa_decode_combine_f32", head_dim)
19593 };
19594 let __s_b2 = self.gpu.stream();
19595 let mut b2 = __s_b2.launch_builder(&fc);
19596 b2.arg(&*part_o)
19597 .arg(&*part_m)
19598 .arg(&*part_l)
19599 .arg(o)
19600 .arg(&hd)
19601 .arg(&nh)
19602 .arg(&nsp);
19603 unsafe {
19604 b2.launch(cfg2)?;
19605 }
19606 Ok(())
19607 }
19608
19609 pub fn fa_geom_eager(
19615 &self,
19616 t_kv: usize,
19617 head_dim: usize,
19618 n_head_kv: usize,
19619 g: bool,
19620 ) -> (bool, usize) {
19621 let fa_ok = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
19625 let vec512 = fa_ok && head_dim == 512 && t_kv >= fa512_min_tkv();
19631 let mut fa_vec = vec512 || (fa_ok && head_dim <= 256 && head_dim % 32 == 0);
19632 if g && head_dim == 256 && !fa_v4_at(t_kv) {
19638 fa_vec = false;
19639 }
19640 let sp = fa_split_keys(t_kv, n_head_kv);
19641 let n_splits = if fa_vec {
19642 ((t_kv + sp - 1) / sp).max(1)
19643 } else {
19644 ((t_kv + 255) / 256).max(1)
19645 };
19646 (fa_vec, n_splits)
19647 }
19648
19649 pub fn fa_bucket_key(
19655 &self,
19656 t_kv: usize,
19657 head_dim: usize,
19658 n_head_kv: usize,
19659 g: bool,
19660 ) -> (bool, usize) {
19661 self.fa_geom_eager(t_kv, head_dim, n_head_kv, g)
19662 }
19663
19664 pub fn capture_graph_retained<F>(
19676 &self,
19677 step: F,
19678 ) -> Result<
19679 (
19680 cudarc::driver::CudaGraph,
19681 Vec<Box<dyn std::any::Any + Send>>,
19682 ),
19683 Box<dyn std::error::Error>,
19684 >
19685 where
19686 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
19687 {
19688 use cudarc::driver::sys::CUgraphInstantiate_flags;
19689 self.capture_graph_retained_flags(
19690 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
19691 step,
19692 )
19693 }
19694
19695 pub fn capture_graph_retained_flags<F>(
19700 &self,
19701 flags: cudarc::driver::sys::CUgraphInstantiate_flags,
19702 mut step: F,
19703 ) -> Result<
19704 (
19705 cudarc::driver::CudaGraph,
19706 Vec<Box<dyn std::any::Any + Send>>,
19707 ),
19708 Box<dyn std::error::Error>,
19709 >
19710 where
19711 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
19712 {
19713 use cudarc::driver::sys::CUstreamCaptureMode;
19714 self.capture_keep.lock().unwrap().clear();
19722 let was_tracking = self.gpu.ctx.is_event_tracking();
19723 if was_tracking {
19724 unsafe {
19725 self.gpu.ctx.disable_event_tracking();
19726 }
19727 }
19728 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
19729 self.capture_keep_on
19730 .store(true, std::sync::atomic::Ordering::Relaxed);
19731 let w = (|| {
19732 step(self)?;
19733 step(self)
19734 })();
19735 self.capture_keep_on
19736 .store(false, std::sync::atomic::Ordering::Relaxed);
19737 w?;
19738 self.gpu.stream().synchronize()?;
19739 self.gpu
19740 .stream()
19741 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
19742 let r = step(self);
19743 let g = self.gpu.stream().end_capture(flags);
19744 r?;
19745 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
19746 graph.upload()?;
19747 Ok(graph)
19748 };
19749 let result = run();
19750 self.capture_keep_on
19751 .store(false, std::sync::atomic::Ordering::Relaxed);
19752 if was_tracking {
19753 unsafe {
19754 self.gpu.ctx.enable_event_tracking();
19755 }
19756 }
19757 let keeper = std::mem::take(&mut *self.capture_keep.lock().unwrap());
19758 Ok((result?, keeper))
19759 }
19760
19761 pub fn capture_graph<F>(
19762 &self,
19763 mut step: F,
19764 ) -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>>
19765 where
19766 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
19767 {
19768 use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
19769 let was_tracking = self.gpu.ctx.is_event_tracking();
19777 if was_tracking {
19778 unsafe {
19779 self.gpu.ctx.disable_event_tracking();
19780 }
19781 }
19782 let iflag = {
19789 static F: std::sync::OnceLock<CUgraphInstantiate_flags> = std::sync::OnceLock::new();
19790 *F.get_or_init(|| match std::env::var("MEMRA_GRAPH_IFLAG").as_deref() {
19791 Ok("upload") => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_UPLOAD,
19794 Ok("priority") => {
19795 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY
19796 }
19797 _ => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
19798 })
19799 };
19800 let ct = {
19807 static T: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19808 *T.get_or_init(|| std::env::var("MEMRA_GRAPH_CAPTIME").as_deref() == Ok("1"))
19809 };
19810 let warmups = {
19833 static W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
19834 *W.get_or_init(|| {
19835 std::env::var("MEMRA_GRAPH_WARMUPS")
19836 .ok()
19837 .and_then(|v| v.parse().ok())
19838 .filter(|n| *n >= 1)
19839 .unwrap_or(1)
19840 })
19841 };
19842 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
19843 let t_w = std::time::Instant::now();
19844 for _ in 0..warmups {
19846 step(self)?;
19847 }
19848 self.gpu.stream().synchronize()?;
19849 let ms_warm = t_w.elapsed().as_secs_f64() * 1e3;
19850 let t_c = std::time::Instant::now();
19852 self.gpu
19853 .stream()
19854 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
19855 let r = step(self);
19858 let ms_body = t_c.elapsed().as_secs_f64() * 1e3;
19859 let t_i = std::time::Instant::now();
19860 let g = self.gpu.stream().end_capture(iflag);
19861 let ms_inst = t_i.elapsed().as_secs_f64() * 1e3;
19862 r?;
19863 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
19864 let t_u = std::time::Instant::now();
19865 graph.upload()?;
19866 if ct {
19867 println!(
19868 "[graph-captime] warmup2x {ms_warm:.2} ms capture-body {ms_body:.2} ms \
19869 instantiate {ms_inst:.2} ms upload {:.2} ms",
19870 t_u.elapsed().as_secs_f64() * 1e3
19871 );
19872 }
19873 Ok(graph)
19874 };
19875 let result = run();
19876 if was_tracking {
19877 unsafe {
19878 self.gpu.ctx.enable_event_tracking();
19879 }
19880 }
19881 result
19882 }
19883
19884 pub fn gdn_scan_s128_view(
19886 &self,
19887 q: &CudaSlice<f32>,
19888 k: &CudaSlice<f32>,
19889 v: &CudaSlice<f32>,
19890 g: &CudaSlice<f32>,
19891 beta: &CudaSlice<f32>,
19892 state_in: &cudarc::driver::CudaView<f32>,
19893 state_out: &mut cudarc::driver::CudaViewMut<f32>,
19894 o: &mut CudaSlice<f32>,
19895 n_head: usize,
19896 t: usize,
19897 scale: f32,
19898 ) -> Result<(), Box<dyn std::error::Error>> {
19899 let f = self.func("gdn_scan_s128");
19900 const S_V: u32 = 128;
19901 const WARP: u32 = 32;
19902 const COLS: u32 = 4;
19903 let cfg = LaunchConfig {
19904 grid_dim: (n_head as u32, 1, S_V / COLS),
19905 block_dim: (WARP, COLS, 1),
19906 shared_mem_bytes: 0,
19907 };
19908 let (h, ti) = (n_head as i32, t as i32);
19909 let __s_b = self.gpu.stream();
19910 let mut b = __s_b.launch_builder(&f);
19911 b.arg(q)
19912 .arg(k)
19913 .arg(v)
19914 .arg(g)
19915 .arg(beta)
19916 .arg(state_in)
19917 .arg(state_out)
19918 .arg(o)
19919 .arg(&h)
19920 .arg(&ti)
19921 .arg(&scale);
19922 unsafe {
19923 b.launch(cfg)?;
19924 }
19925 Ok(())
19926 }
19927
19928 pub fn ssm_conv1d_view(
19930 &self,
19931 x: &cudarc::driver::CudaView<f32>,
19932 w: &CudaSlice<f32>,
19933 y: &mut CudaSlice<f32>,
19934 conv_dim: usize,
19935 t: usize,
19936 d_conv: usize,
19937 silu: bool,
19938 ) -> Result<(), Box<dyn std::error::Error>> {
19939 let f = self.func("ssm_conv1d_silu_f32");
19940 let cfg = LaunchConfig {
19942 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
19943 block_dim: (256, 1, 1),
19944 shared_mem_bytes: 0,
19945 };
19946 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
19947 let __s_b = self.gpu.stream();
19948 let mut b = __s_b.launch_builder(&f);
19949 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
19950 unsafe {
19951 b.launch(cfg)?;
19952 }
19953 Ok(())
19954 }
19955
19956 pub fn ssm_conv1d_tm(
19963 &self,
19964 qkv_tm: &CudaSlice<f32>,
19965 w: &CudaSlice<f32>,
19966 y: &mut CudaSlice<f32>,
19967 conv_dim: usize,
19968 t: usize,
19969 d_conv: usize,
19970 ) -> Result<(), Box<dyn std::error::Error>> {
19971 let f = self.func("ssm_conv1d_tm_f32");
19972 let cfg = LaunchConfig {
19973 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19974 block_dim: (256, 1, 1),
19975 shared_mem_bytes: 0,
19976 };
19977 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19978 let __s_b = self.gpu.stream();
19979 let mut b = __s_b.launch_builder(&f);
19980 b.arg(qkv_tm).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc);
19981 unsafe {
19982 b.launch(cfg)?;
19983 }
19984 Ok(())
19985 }
19986
19987 pub fn ssm_conv1d_tm_state(
19995 &self,
19996 qkv_tm: &CudaSlice<f32>,
19997 conv_state: &mut CudaSlice<f32>,
19998 w: &CudaSlice<f32>,
19999 y: &mut CudaSlice<f32>,
20000 conv_dim: usize,
20001 t: usize,
20002 d_conv: usize,
20003 ) -> Result<(), Box<dyn std::error::Error>> {
20004 self.ssm_conv1d_tm_state_pad(qkv_tm, conv_state, w, y, conv_dim, t, d_conv, None)
20005 }
20006
20007 #[allow(clippy::too_many_arguments)]
20010 pub fn ssm_conv1d_tm_state_pad(
20011 &self,
20012 qkv_tm: &CudaSlice<f32>,
20013 conv_state: &mut CudaSlice<f32>,
20014 w: &CudaSlice<f32>,
20015 y: &mut CudaSlice<f32>,
20016 conv_dim: usize,
20017 t: usize,
20018 d_conv: usize,
20019 pad_len: Option<&CudaSlice<i32>>,
20020 ) -> Result<(), Box<dyn std::error::Error>> {
20021 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
20022 let ring_old = if t < d_conv - 1 {
20026 Some(self.clone_dtod(conv_state)?)
20027 } else {
20028 None
20029 };
20030 {
20031 let f = self.func("ssm_conv1d_tm_state_f32");
20032 let cfg = LaunchConfig {
20033 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
20034 block_dim: (256, 1, 1),
20035 shared_mem_bytes: 0,
20036 };
20037 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20038 let __s_b = self.gpu.stream();
20039 let mut b = __s_b.launch_builder(&f);
20040 b.arg(qkv_tm)
20041 .arg(&*conv_state)
20042 .arg(w)
20043 .arg(y)
20044 .arg(&cd)
20045 .arg(&ti)
20046 .arg(&dc);
20047 unsafe {
20048 b.launch(cfg)?;
20049 }
20050 }
20051 match (ring_old, pad_len) {
20052 (None, Some(len_d)) => {
20053 let f = self.func("ssm_conv_ring_update_dev_f32");
20054 let n = conv_dim * (d_conv - 1);
20055 let cfg = LaunchConfig::for_num_elems(n as u32);
20056 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20057 let __s_b = self.gpu.stream();
20058 let mut b = __s_b.launch_builder(&f);
20059 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
20060 unsafe {
20061 b.launch(cfg)?;
20062 }
20063 }
20064 (None, None) => {
20065 let f = self.func("ssm_conv_ring_update_f32");
20066 let n = conv_dim * (d_conv - 1);
20067 let cfg = LaunchConfig::for_num_elems(n as u32);
20068 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20069 let __s_b = self.gpu.stream();
20070 let mut b = __s_b.launch_builder(&f);
20071 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
20072 unsafe {
20073 b.launch(cfg)?;
20074 }
20075 }
20076 (Some(old), _) => {
20077 self.ssm_conv_ring_rebuild(qkv_tm, &old, conv_state, conv_dim, t, d_conv)?
20078 }
20079 }
20080 Ok(())
20081 }
20082
20083 pub fn ssm_conv1d_tm_state_pad_v(
20085 &self,
20086 qkv_tm: &cudarc::driver::CudaView<f32>,
20087 conv_state: &mut CudaSlice<f32>,
20088 w: &CudaSlice<f32>,
20089 y: &mut CudaSlice<f32>,
20090 conv_dim: usize,
20091 t: usize,
20092 d_conv: usize,
20093 pad_len: Option<&CudaSlice<i32>>,
20094 ) -> Result<(), Box<dyn std::error::Error>> {
20095 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
20096 let ring_old = if t < d_conv - 1 {
20100 Some(self.clone_dtod(conv_state)?)
20101 } else {
20102 None
20103 };
20104 {
20105 let f = self.func("ssm_conv1d_tm_state_f32");
20106 let cfg = LaunchConfig {
20107 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
20108 block_dim: (256, 1, 1),
20109 shared_mem_bytes: 0,
20110 };
20111 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20112 let __s_b = self.gpu.stream();
20113 let mut b = __s_b.launch_builder(&f);
20114 b.arg(qkv_tm)
20115 .arg(&*conv_state)
20116 .arg(w)
20117 .arg(y)
20118 .arg(&cd)
20119 .arg(&ti)
20120 .arg(&dc);
20121 unsafe {
20122 b.launch(cfg)?;
20123 }
20124 }
20125 match (ring_old, pad_len) {
20126 (None, Some(len_d)) => {
20127 let f = self.func("ssm_conv_ring_update_dev_f32");
20128 let n = conv_dim * (d_conv - 1);
20129 let cfg = LaunchConfig::for_num_elems(n as u32);
20130 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20131 let __s_b = self.gpu.stream();
20132 let mut b = __s_b.launch_builder(&f);
20133 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
20134 unsafe {
20135 b.launch(cfg)?;
20136 }
20137 }
20138 (None, None) => {
20139 let f = self.func("ssm_conv_ring_update_f32");
20140 let n = conv_dim * (d_conv - 1);
20141 let cfg = LaunchConfig::for_num_elems(n as u32);
20142 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20143 let __s_b = self.gpu.stream();
20144 let mut b = __s_b.launch_builder(&f);
20145 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
20146 unsafe {
20147 b.launch(cfg)?;
20148 }
20149 }
20150 (Some(_), _) => unreachable!(
20151 "ssm_conv1d_tm_state_pad_v: T < d_conv-1 has no view path (PRIME_MIN_T gates it)"
20152 ),
20153 }
20154 Ok(())
20155 }
20156
20157 pub fn ssm_conv_ring_rebuild(
20162 &self,
20163 qkv_tm: &CudaSlice<f32>,
20164 ring_old: &CudaSlice<f32>,
20165 conv_state: &mut CudaSlice<f32>,
20166 conv_dim: usize,
20167 tc: usize,
20168 d_conv: usize,
20169 ) -> Result<(), Box<dyn std::error::Error>> {
20170 let f = self.func("ssm_conv_ring_rebuild_f32");
20171 let n = conv_dim * (d_conv - 1);
20172 let cfg = LaunchConfig::for_num_elems(n as u32);
20173 let (cd, ti, dc) = (conv_dim as i32, tc as i32, d_conv as i32);
20174 let __s_b = self.gpu.stream();
20175 let mut b = __s_b.launch_builder(&f);
20176 b.arg(qkv_tm)
20177 .arg(ring_old)
20178 .arg(conv_state)
20179 .arg(&cd)
20180 .arg(&ti)
20181 .arg(&dc);
20182 unsafe {
20183 b.launch(cfg)?;
20184 }
20185 Ok(())
20186 }
20187
20188 #[allow(clippy::too_many_arguments)]
20193 pub fn gdn_prep_decode(
20194 &self,
20195 conv_out: &CudaSlice<f32>,
20196 beta_raw: &CudaSlice<f32>,
20197 alpha: &CudaSlice<f32>,
20198 dt_bias: &CudaSlice<f32>,
20199 a: &CudaSlice<f32>,
20200 q_l2: &mut CudaSlice<f32>,
20201 k_l2: &mut CudaSlice<f32>,
20202 v_g: &mut CudaSlice<f32>,
20203 beta: &mut CudaSlice<f32>,
20204 g_log: &mut CudaSlice<f32>,
20205 d_state: usize,
20206 num_v: usize,
20207 num_k: usize,
20208 key_dim: usize,
20209 eps: f32,
20210 ) -> Result<(), Box<dyn std::error::Error>> {
20211 let f = self.func("gdn_prep_decode_f32");
20212 let cfg = LaunchConfig {
20213 grid_dim: (num_v as u32, 1, 1),
20214 block_dim: (32, 4, 1),
20215 shared_mem_bytes: 0,
20216 };
20217 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
20218 let __s_b = self.gpu.stream();
20219 let mut b = __s_b.launch_builder(&f);
20220 b.arg(conv_out)
20221 .arg(beta_raw)
20222 .arg(alpha)
20223 .arg(dt_bias)
20224 .arg(a)
20225 .arg(q_l2)
20226 .arg(k_l2)
20227 .arg(v_g)
20228 .arg(beta)
20229 .arg(g_log)
20230 .arg(&ds)
20231 .arg(&nv)
20232 .arg(&nk)
20233 .arg(&kd)
20234 .arg(&eps);
20235 unsafe {
20236 b.launch(cfg)?;
20237 }
20238 Ok(())
20239 }
20240
20241 #[allow(clippy::too_many_arguments)]
20245 pub fn ssm_conv1d_gdn(
20246 &self,
20247 qkv_tm: &CudaSlice<f32>,
20248 w: &CudaSlice<f32>,
20249 q_g: &mut CudaSlice<f32>,
20250 k_g: &mut CudaSlice<f32>,
20251 v_g: &mut CudaSlice<f32>,
20252 conv_dim: usize,
20253 t: usize,
20254 d_conv: usize,
20255 d_state: usize,
20256 num_v: usize,
20257 num_k: usize,
20258 key_dim: usize,
20259 ) -> Result<(), Box<dyn std::error::Error>> {
20260 let f = self.func("ssm_conv1d_gdn_f32");
20261 let cfg = LaunchConfig {
20262 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
20263 block_dim: (256, 1, 1),
20264 shared_mem_bytes: 0,
20265 };
20266 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20267 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
20268 let __s_b = self.gpu.stream();
20269 let mut b = __s_b.launch_builder(&f);
20270 b.arg(qkv_tm)
20271 .arg(w)
20272 .arg(q_g)
20273 .arg(k_g)
20274 .arg(v_g)
20275 .arg(&cd)
20276 .arg(&ti)
20277 .arg(&dc)
20278 .arg(&ds)
20279 .arg(&nv)
20280 .arg(&nk)
20281 .arg(&kd);
20282 unsafe {
20283 b.launch(cfg)?;
20284 }
20285 Ok(())
20286 }
20287
20288 pub fn ssm_conv1d(
20289 &self,
20290 x: &CudaSlice<f32>,
20291 w: &CudaSlice<f32>,
20292 y: &mut CudaSlice<f32>,
20293 conv_dim: usize,
20294 t: usize,
20295 d_conv: usize,
20296 silu: bool,
20297 ) -> Result<(), Box<dyn std::error::Error>> {
20298 let f = self.func("ssm_conv1d_silu_f32");
20299 let cfg = LaunchConfig {
20300 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
20301 block_dim: (256, 1, 1),
20302 shared_mem_bytes: 0,
20303 };
20304 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
20305 let __s_b = self.gpu.stream();
20306 let mut b = __s_b.launch_builder(&f);
20307 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
20308 unsafe {
20309 b.launch(cfg)?;
20310 }
20311 Ok(())
20312 }
20313
20314 pub fn gdn_scan_s128(
20317 &self,
20318 q: &CudaSlice<f32>,
20319 k: &CudaSlice<f32>,
20320 v: &CudaSlice<f32>,
20321 g: &CudaSlice<f32>,
20322 beta: &CudaSlice<f32>,
20323 state_in: &CudaSlice<f32>,
20324 state_out: &mut CudaSlice<f32>,
20325 o: &mut CudaSlice<f32>,
20326 n_head: usize,
20327 t: usize,
20328 scale: f32,
20329 ) -> Result<(), Box<dyn std::error::Error>> {
20330 let f = self.func("gdn_scan_s128");
20331 const S_V: u32 = 128;
20332 const WARP: u32 = 32;
20333 const COLS_PER_BLOCK: u32 = 4;
20334 let cfg = LaunchConfig {
20335 grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
20336 block_dim: (WARP, COLS_PER_BLOCK, 1),
20337 shared_mem_bytes: 0,
20338 };
20339 let (h, ti) = (n_head as i32, t as i32);
20340 let __s_b = self.gpu.stream();
20341 let mut b = __s_b.launch_builder(&f);
20342 b.arg(q)
20343 .arg(k)
20344 .arg(v)
20345 .arg(g)
20346 .arg(beta)
20347 .arg(state_in)
20348 .arg(state_out)
20349 .arg(o)
20350 .arg(&h)
20351 .arg(&ti)
20352 .arg(&scale);
20353 unsafe {
20354 b.launch(cfg)?;
20355 }
20356 Ok(())
20357 }
20358
20359 #[allow(clippy::too_many_arguments)]
20364 pub fn ssm_conv1d_fused_decode_b(
20365 &self,
20366 qkv_cols: &CudaSlice<f32>,
20367 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
20368 w: &CudaSlice<f32>,
20369 conv_outs: &mut CudaSlice<f32>,
20370 conv_dim: usize,
20371 d_conv: usize,
20372 b_n: usize,
20373 ) -> Result<(), Box<dyn std::error::Error>> {
20374 let f = self.func("ssm_conv1d_fused_decode_b_f32");
20375 let cfg = LaunchConfig {
20376 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
20377 block_dim: (256, 1, 1),
20378 shared_mem_bytes: 0,
20379 };
20380 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20381 let __s_b = self.gpu.stream();
20382 let mut b = __s_b.launch_builder(&f);
20383 b.arg(qkv_cols)
20384 .arg(conv_state_ptrs)
20385 .arg(w)
20386 .arg(conv_outs)
20387 .arg(&cd)
20388 .arg(&dc);
20389 unsafe {
20390 b.launch(cfg)?;
20391 }
20392 Ok(())
20393 }
20394
20395 #[allow(clippy::too_many_arguments)]
20396 pub fn gdn_prep_decode_b(
20397 &self,
20398 conv_outs: &CudaSlice<f32>,
20399 beta_raws: &CudaSlice<f32>,
20400 alphas: &CudaSlice<f32>,
20401 dt_bias: &CudaSlice<f32>,
20402 a: &CudaSlice<f32>,
20403 q_l2: &mut CudaSlice<f32>,
20404 k_l2: &mut CudaSlice<f32>,
20405 v_g: &mut CudaSlice<f32>,
20406 beta: &mut CudaSlice<f32>,
20407 g_log: &mut CudaSlice<f32>,
20408 d_state: usize,
20409 num_v: usize,
20410 num_k: usize,
20411 key_dim: usize,
20412 eps: f32,
20413 conv_dim: usize,
20414 b_n: usize,
20415 ) -> Result<(), Box<dyn std::error::Error>> {
20416 let f = self.func("gdn_prep_decode_b_f32");
20417 let cfg = LaunchConfig {
20418 grid_dim: (num_v as u32, 1, b_n as u32),
20419 block_dim: (32, 4, 1),
20420 shared_mem_bytes: 0,
20421 };
20422 let (ds, nv, nk, kd, cd) = (
20423 d_state as i32,
20424 num_v as i32,
20425 num_k as i32,
20426 key_dim as i32,
20427 conv_dim as i32,
20428 );
20429 let __s_b = self.gpu.stream();
20430 let mut b = __s_b.launch_builder(&f);
20431 b.arg(conv_outs)
20432 .arg(beta_raws)
20433 .arg(alphas)
20434 .arg(dt_bias)
20435 .arg(a)
20436 .arg(q_l2)
20437 .arg(k_l2)
20438 .arg(v_g)
20439 .arg(beta)
20440 .arg(g_log)
20441 .arg(&ds)
20442 .arg(&nv)
20443 .arg(&nk)
20444 .arg(&kd)
20445 .arg(&eps)
20446 .arg(&cd);
20447 unsafe {
20448 b.launch(cfg)?;
20449 }
20450 Ok(())
20451 }
20452
20453 #[allow(clippy::too_many_arguments)]
20454 pub fn gdn_scan_s128_batched(
20455 &self,
20456 q: &CudaSlice<f32>,
20457 k: &CudaSlice<f32>,
20458 v: &CudaSlice<f32>,
20459 g: &CudaSlice<f32>,
20460 beta: &CudaSlice<f32>,
20461 state_in_ptrs: &cudarc::driver::CudaView<u64>,
20462 state_out_ptrs: &cudarc::driver::CudaView<u64>,
20463 o: &mut CudaSlice<f32>,
20464 n_head: usize,
20465 b_n: usize,
20466 scale: f32,
20467 ) -> Result<(), Box<dyn std::error::Error>> {
20468 let f = self.func("gdn_scan_s128_b");
20469 const S_V: u32 = 128;
20470 const WARP: u32 = 32;
20471 const COLS_PER_BLOCK: u32 = 4;
20472 let cfg = LaunchConfig {
20473 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
20474 block_dim: (WARP, COLS_PER_BLOCK, 1),
20475 shared_mem_bytes: 0,
20476 };
20477 let h = n_head as i32;
20478 let __s_b = self.gpu.stream();
20479 let mut b = __s_b.launch_builder(&f);
20480 b.arg(q)
20481 .arg(k)
20482 .arg(v)
20483 .arg(g)
20484 .arg(beta)
20485 .arg(state_in_ptrs)
20486 .arg(state_out_ptrs)
20487 .arg(o)
20488 .arg(&h)
20489 .arg(&scale);
20490 unsafe {
20491 b.launch(cfg)?;
20492 }
20493 Ok(())
20494 }
20495
20496 #[allow(clippy::too_many_arguments)]
20502 pub fn ssm_conv1d_fused_decode_b_view(
20503 &self,
20504 qkv_cols: &cudarc::driver::CudaView<f32>,
20505 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
20506 w: &CudaSlice<f32>,
20507 conv_outs: &mut CudaSlice<f32>,
20508 conv_dim: usize,
20509 d_conv: usize,
20510 b_n: usize,
20511 ) -> Result<(), Box<dyn std::error::Error>> {
20512 let f = self.func("ssm_conv1d_fused_decode_b_f32");
20513 let cfg = LaunchConfig {
20514 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
20515 block_dim: (256, 1, 1),
20516 shared_mem_bytes: 0,
20517 };
20518 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20519 let __s_b = self.gpu.stream();
20520 let mut b = __s_b.launch_builder(&f);
20521 b.arg(qkv_cols)
20522 .arg(conv_state_ptrs)
20523 .arg(w)
20524 .arg(conv_outs)
20525 .arg(&cd)
20526 .arg(&dc);
20527 unsafe {
20528 b.launch(cfg)?;
20529 }
20530 Ok(())
20531 }
20532
20533 #[allow(clippy::too_many_arguments)]
20534 pub fn gdn_prep_decode_b_view(
20535 &self,
20536 conv_outs: &CudaSlice<f32>,
20537 beta_raws: &cudarc::driver::CudaView<f32>,
20538 alphas: &cudarc::driver::CudaView<f32>,
20539 dt_bias: &CudaSlice<f32>,
20540 a: &CudaSlice<f32>,
20541 q_l2: &mut CudaSlice<f32>,
20542 k_l2: &mut CudaSlice<f32>,
20543 v_g: &mut CudaSlice<f32>,
20544 beta: &mut CudaSlice<f32>,
20545 g_log: &mut CudaSlice<f32>,
20546 d_state: usize,
20547 num_v: usize,
20548 num_k: usize,
20549 key_dim: usize,
20550 eps: f32,
20551 conv_dim: usize,
20552 b_n: usize,
20553 ) -> Result<(), Box<dyn std::error::Error>> {
20554 let f = self.func("gdn_prep_decode_b_f32");
20555 let cfg = LaunchConfig {
20556 grid_dim: (num_v as u32, 1, b_n as u32),
20557 block_dim: (32, 4, 1),
20558 shared_mem_bytes: 0,
20559 };
20560 let (ds, nv, nk, kd, cd) = (
20561 d_state as i32,
20562 num_v as i32,
20563 num_k as i32,
20564 key_dim as i32,
20565 conv_dim as i32,
20566 );
20567 let __s_b = self.gpu.stream();
20568 let mut b = __s_b.launch_builder(&f);
20569 b.arg(conv_outs)
20570 .arg(beta_raws)
20571 .arg(alphas)
20572 .arg(dt_bias)
20573 .arg(a)
20574 .arg(q_l2)
20575 .arg(k_l2)
20576 .arg(v_g)
20577 .arg(beta)
20578 .arg(g_log)
20579 .arg(&ds)
20580 .arg(&nv)
20581 .arg(&nk)
20582 .arg(&kd)
20583 .arg(&eps)
20584 .arg(&cd);
20585 unsafe {
20586 b.launch(cfg)?;
20587 }
20588 Ok(())
20589 }
20590
20591 #[allow(clippy::too_many_arguments)]
20592 pub fn gdn_scan_s128_batched_view(
20593 &self,
20594 q: &CudaSlice<f32>,
20595 k: &CudaSlice<f32>,
20596 v: &CudaSlice<f32>,
20597 g: &CudaSlice<f32>,
20598 beta: &CudaSlice<f32>,
20599 state_in_ptrs: &cudarc::driver::CudaView<u64>,
20600 state_out_ptrs: &cudarc::driver::CudaView<u64>,
20601 o: &mut cudarc::driver::CudaViewMut<f32>,
20602 n_head: usize,
20603 b_n: usize,
20604 scale: f32,
20605 ) -> Result<(), Box<dyn std::error::Error>> {
20606 let f = self.func("gdn_scan_s128_b");
20607 const S_V: u32 = 128;
20608 const WARP: u32 = 32;
20609 const COLS_PER_BLOCK: u32 = 4;
20610 let cfg = LaunchConfig {
20611 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
20612 block_dim: (WARP, COLS_PER_BLOCK, 1),
20613 shared_mem_bytes: 0,
20614 };
20615 let h = n_head as i32;
20616 let __s_b = self.gpu.stream();
20617 let mut b = __s_b.launch_builder(&f);
20618 b.arg(q)
20619 .arg(k)
20620 .arg(v)
20621 .arg(g)
20622 .arg(beta)
20623 .arg(state_in_ptrs)
20624 .arg(state_out_ptrs)
20625 .arg(o)
20626 .arg(&h)
20627 .arg(&scale);
20628 unsafe {
20629 b.launch(cfg)?;
20630 }
20631 Ok(())
20632 }
20633
20634 pub fn gdn_chunked_enabled() -> bool {
20643 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
20644 *E.get_or_init(|| {
20645 std::env::var("MEMRA_GDN_CHUNKED")
20646 .map(|v| v != "0")
20647 .unwrap_or(true)
20648 })
20649 }
20650
20651 pub fn gdn_chunk_size() -> usize {
20656 static C: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
20657 *C.get_or_init(|| {
20658 let c: usize = std::env::var("MEMRA_GDN_CHUNK")
20659 .ok()
20660 .and_then(|v| v.parse().ok())
20661 .unwrap_or(32);
20662 c.clamp(32, 128) / 32 * 32
20663 })
20664 }
20665
20666 #[allow(clippy::too_many_arguments)]
20671 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
20674 #[allow(clippy::too_many_arguments)]
20675 pub fn gdn_chunk_k123(
20676 &self,
20677 q: &CudaSlice<f32>,
20678 k: &CudaSlice<f32>,
20679 v: &CudaSlice<f32>,
20680 g: &CudaSlice<f32>,
20681 beta: &CudaSlice<f32>,
20682 wb16: Option<&mut CudaSlice<u8>>,
20683 n_head: usize,
20684 t: usize,
20685 c: usize,
20686 hk: usize,
20687 k2w: Option<(&CudaSlice<u8>, &CudaSlice<u8>, &mut CudaSlice<u8>)>,
20688 ) -> Result<
20689 (
20690 CudaSlice<f32>,
20691 CudaSlice<f32>,
20692 CudaSlice<f32>,
20693 CudaSlice<f32>,
20694 ),
20695 Box<dyn std::error::Error>,
20696 > {
20697 const D: usize = 128;
20698 let h = n_head;
20699 let nc = (t + c - 1) / c;
20700 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
20701 let mut gcum = self.uninit(t * h)?;
20702 let mut a = self.uninit(nc * h * c * c)?;
20703 let mut p = self.uninit(nc * h * c * c)?;
20704 let mut u = self.uninit(nc * h * c * D)?;
20705 let mut w = self.uninit(nc * h * c * D)?;
20706 {
20707 let f = self.func("gdn_chunk_cumgate_f32");
20709 let cfg = LaunchConfig {
20710 grid_dim: (nc as u32, h as u32, 1),
20711 block_dim: (32, 1, 1),
20712 shared_mem_bytes: 0,
20713 };
20714 let __s_b = self.gpu.stream();
20715 let mut b = __s_b.launch_builder(&f);
20716 b.arg(g).arg(&mut gcum).arg(&hi).arg(&ti).arg(&ci);
20717 unsafe {
20718 b.launch(cfg)?;
20719 }
20720 }
20721 if let Some((qb, kb, pb)) = k2w {
20722 assert!(c == 32, "gdn_k2_wgmma is a C==32 tile");
20725 let f = self.func("gdn_k2_wgmma");
20726 let cfg = LaunchConfig {
20727 grid_dim: (nc as u32, h as u32, 1),
20728 block_dim: (128, 1, 1),
20729 shared_mem_bytes: 0,
20730 };
20731 let hki = hk as i32;
20732 let __s_b = self.gpu.stream();
20733 let mut b = __s_b.launch_builder(&f);
20734 b.arg(qb)
20735 .arg(kb)
20736 .arg(&gcum)
20737 .arg(beta)
20738 .arg(&mut a)
20739 .arg(&mut *pb)
20740 .arg(&hi)
20741 .arg(&ti)
20742 .arg(&ci)
20743 .arg(&hki);
20744 unsafe {
20745 b.launch(cfg)?;
20746 }
20747 } else if c <= 64 && !portable_mma_gated() {
20748 let f = self.func("gdn_chunk_attn_f32");
20750 let jt = ((c + 31) / 32) as u32;
20751 let cfg = LaunchConfig {
20752 grid_dim: (nc as u32, h as u32, jt),
20753 block_dim: (256, 1, 1),
20754 shared_mem_bytes: 0,
20755 };
20756 let hki = hk as i32;
20757 let __s_b = self.gpu.stream();
20758 let mut b = __s_b.launch_builder(&f);
20759 b.arg(q)
20760 .arg(k)
20761 .arg(&gcum)
20762 .arg(beta)
20763 .arg(&mut a)
20764 .arg(&mut p)
20765 .arg(&hi)
20766 .arg(&ti)
20767 .arg(&ci)
20768 .arg(&hki);
20769 unsafe {
20770 b.launch(cfg)?;
20771 }
20772 } else {
20773 assert!(
20775 hk == h,
20776 "generic K2 is broadcast-only (de-broadcast rides C==32)"
20777 );
20778 let f = self.func("gdn_chunk_attn_g_f32");
20779 let cfg = LaunchConfig {
20780 grid_dim: (nc as u32, h as u32, 1),
20781 block_dim: (32, 8, 1),
20782 shared_mem_bytes: 0,
20783 };
20784 let __s_b = self.gpu.stream();
20785 let mut b = __s_b.launch_builder(&f);
20786 b.arg(q)
20787 .arg(k)
20788 .arg(&gcum)
20789 .arg(beta)
20790 .arg(&mut a)
20791 .arg(&mut p)
20792 .arg(&hi)
20793 .arg(&ti)
20794 .arg(&ci);
20795 unsafe {
20796 b.launch(cfg)?;
20797 }
20798 }
20799 {
20800 let cfg = LaunchConfig {
20802 grid_dim: (nc as u32, h as u32, 1),
20803 block_dim: (256, 1, 1),
20804 shared_mem_bytes: 0,
20805 };
20806 match c {
20807 32 | 64 => {
20808 let f = self.func(if c == 32 {
20809 "gdn_chunk_solve32_f32"
20810 } else {
20811 "gdn_chunk_solve64_f32"
20812 });
20813 let wb: u64 = match wb16 {
20815 Some(d) => self.addr_u8(d),
20816 None => 0,
20817 };
20818 let hki = hk as i32;
20819 let __s_b = self.gpu.stream();
20820 let mut b = __s_b.launch_builder(&f);
20821 b.arg(v)
20822 .arg(k)
20823 .arg(&a)
20824 .arg(&gcum)
20825 .arg(&mut u)
20826 .arg(&mut w)
20827 .arg(&wb)
20828 .arg(&hi)
20829 .arg(&ti)
20830 .arg(&hki);
20831 unsafe {
20832 b.launch(cfg)?;
20833 }
20834 }
20835 _ => {
20836 assert!(hk == h, "generic K3 is broadcast-only");
20837 let f = self.func("gdn_chunk_solve_f32");
20838 let __s_b = self.gpu.stream();
20839 let mut b = __s_b.launch_builder(&f);
20840 b.arg(v)
20841 .arg(k)
20842 .arg(&a)
20843 .arg(&gcum)
20844 .arg(&mut u)
20845 .arg(&mut w)
20846 .arg(&hi)
20847 .arg(&ti)
20848 .arg(&ci);
20849 unsafe {
20850 b.launch(cfg)?;
20851 }
20852 }
20853 }
20854 }
20855 Ok((gcum, p, u, w))
20856 }
20857
20858 pub fn gdn_db_on() -> bool {
20862 std::env::var("MEMRA_GDN_DB").as_deref() != Ok("0")
20863 }
20864
20865 pub fn gdn_mma_enabled(&self, c: usize) -> bool {
20868 !portable_mma_gated()
20869 && c == 32
20870 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
20871 Ok("1") => true,
20872 Ok("0") => false,
20873 _ => cfg!(memra_hopper_mma),
20874 }
20875 }
20876
20877 pub fn gdn_wgmma_on(&self, c: usize) -> bool {
20880 self.gdn_mma_enabled(c)
20881 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
20882 Ok("0") => false,
20883 Ok("1") => true,
20884 _ => cfg!(memra_hopper_mma),
20885 }
20886 }
20887
20888 #[allow(clippy::too_many_arguments)]
20893 pub fn ssm_conv1d_gdn_state_pad(
20894 &self,
20895 qkv_tm: &cudarc::driver::CudaView<f32>,
20896 conv_state: &mut CudaSlice<f32>,
20897 w: &CudaSlice<f32>,
20898 q_g: &mut CudaSlice<f32>,
20899 k_g: &mut CudaSlice<f32>,
20900 v_g: &mut CudaSlice<f32>,
20901 conv_dim: usize,
20902 t: usize,
20903 d_conv: usize,
20904 d_state: usize,
20905 num_v: usize,
20906 num_k: usize,
20907 key_dim: usize,
20908 hk: usize,
20909 pad_len: Option<&CudaSlice<i32>>,
20910 ) -> Result<(), Box<dyn std::error::Error>> {
20911 assert!(
20912 t >= d_conv - 1,
20913 "fused state conv requires T >= pad (PRIME_MIN_T gates)"
20914 );
20915 {
20916 let f = self.func("ssm_conv1d_gdn_state_f32");
20917 let cfg = LaunchConfig {
20918 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
20919 block_dim: (256, 1, 1),
20920 shared_mem_bytes: 0,
20921 };
20922 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20923 let (ds, nv, nk, kd, hki) = (
20924 d_state as i32,
20925 num_v as i32,
20926 num_k as i32,
20927 key_dim as i32,
20928 hk as i32,
20929 );
20930 let __s_b = self.gpu.stream();
20931 let mut b = __s_b.launch_builder(&f);
20932 b.arg(qkv_tm)
20933 .arg(&*conv_state)
20934 .arg(w)
20935 .arg(q_g)
20936 .arg(k_g)
20937 .arg(v_g)
20938 .arg(&cd)
20939 .arg(&ti)
20940 .arg(&dc)
20941 .arg(&ds)
20942 .arg(&nv)
20943 .arg(&nk)
20944 .arg(&kd)
20945 .arg(&hki);
20946 unsafe {
20947 b.launch(cfg)?;
20948 }
20949 }
20950 match pad_len {
20951 Some(len_d) => {
20952 let f = self.func("ssm_conv_ring_update_dev_f32");
20953 let n = conv_dim * (d_conv - 1);
20954 let cfg = LaunchConfig::for_num_elems(n as u32);
20955 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20956 let __s_b = self.gpu.stream();
20957 let mut b = __s_b.launch_builder(&f);
20958 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
20959 unsafe {
20960 b.launch(cfg)?;
20961 }
20962 }
20963 None => {
20964 let f = self.func("ssm_conv_ring_update_f32");
20965 let n = conv_dim * (d_conv - 1);
20966 let cfg = LaunchConfig::for_num_elems(n as u32);
20967 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20968 let __s_b = self.gpu.stream();
20969 let mut b = __s_b.launch_builder(&f);
20970 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
20971 unsafe {
20972 b.launch(cfg)?;
20973 }
20974 }
20975 }
20976 Ok(())
20977 }
20978
20979 pub fn gdn_chunk_alloc(
20983 &self,
20984 n_head: usize,
20985 t: usize,
20986 c: usize,
20987 hk: usize,
20988 ) -> Result<GdnChunkBufs, Box<dyn std::error::Error>> {
20989 const D: usize = 128;
20990 assert!(
20991 c == 32,
20992 "gdn_chunk_alloc: varlen chain is the C==32 mma pair"
20993 );
20994 let h = n_head;
20995 let nc = (t + c - 1) / c;
20996 Ok(GdnChunkBufs {
20997 gcum: self.uninit(t * h)?,
20998 a: self.uninit(nc * h * c * c)?,
20999 p: self.uninit(nc * h * c * c)?,
21000 u: self.uninit(nc * h * c * D)?,
21001 w: self.uninit(nc * h * c * D)?,
21002 kb16: self.alloc_u8_uninit(t * hk * D * 2)?,
21003 wb16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
21004 y16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
21005 ssnap16: self.alloc_u8_uninit(nc * h * D * D * 2)?,
21006 qb16: self.alloc_u8_uninit(t * hk * D * 2)?,
21007 pb16: self.alloc_u8_uninit(nc * h * c * c * 2)?,
21008 o: self.uninit(D * h * t)?,
21009 t,
21010 nc,
21011 })
21012 }
21013
21014 pub fn f32_to_bf16_v(
21016 &self,
21017 x: &cudarc::driver::CudaView<f32>,
21018 dst: &mut CudaSlice<u8>,
21019 n: usize,
21020 ) -> Result<(), Box<dyn std::error::Error>> {
21021 let f = self.func("f32_to_bf16_bulk");
21022 let ni = n as i64;
21023 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
21024 let __s_b = self.gpu.stream();
21025 let mut b = __s_b.launch_builder(&f);
21026 b.arg(x).arg(dst).arg(&ni);
21027 unsafe {
21028 b.launch(cfg)?;
21029 }
21030 Ok(())
21031 }
21032
21033 pub fn f32_to_bf16_into(
21035 &self,
21036 x: &CudaSlice<f32>,
21037 dst: &mut CudaSlice<u8>,
21038 n: usize,
21039 ) -> Result<(), Box<dyn std::error::Error>> {
21040 let f = self.func("f32_to_bf16_bulk");
21041 let ni = n as i64;
21042 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
21043 let __s_b = self.gpu.stream();
21044 let mut b = __s_b.launch_builder(&f);
21045 b.arg(x).arg(dst).arg(&ni);
21046 unsafe {
21047 b.launch(cfg)?;
21048 }
21049 Ok(())
21050 }
21051
21052 pub fn gdn_chunk_k123_vl8(
21055 &self,
21056 seqs: &[GdnSeqVl],
21057 n_head: usize,
21058 hk: usize,
21059 wq: Option<&GdnWVl8>,
21060 ) -> Result<(), Box<dyn std::error::Error>> {
21061 let b = seqs.len();
21062 assert!(b >= 1 && b <= 8, "gdn_chunk_k123_vl8: 1..=8 sequences");
21063 let mut packed = [GdnSeqVl::default(); 8];
21064 packed[..b].copy_from_slice(seqs);
21065 let v = GdnVl8(packed);
21066 let (hi, ci) = (n_head as i32, 32i32);
21067 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
21068 {
21069 let f = self.func("gdn_chunk_cumgate_vl");
21070 let cfg = LaunchConfig {
21071 grid_dim: (max_nc, n_head as u32, b as u32),
21072 block_dim: (32, 1, 1),
21073 shared_mem_bytes: 0,
21074 };
21075 let __s_lb = self.gpu.stream();
21076 let mut lb = __s_lb.launch_builder(&f);
21077 lb.arg(&v).arg(&hi).arg(&ci);
21078 unsafe {
21079 lb.launch(cfg)?;
21080 }
21081 }
21082 let hki = hk as i32;
21083 if let Some(w) = wq {
21084 let f = self.func("gdn_k2_wgmma_vl");
21086 let cfg = LaunchConfig {
21087 grid_dim: (max_nc, n_head as u32, b as u32),
21088 block_dim: (128, 1, 1),
21089 shared_mem_bytes: 0,
21090 };
21091 let __s_lb = self.gpu.stream();
21092 let mut lb = __s_lb.launch_builder(&f);
21093 lb.arg(&v).arg(w).arg(&hi).arg(&ci).arg(&hki);
21094 unsafe {
21095 lb.launch(cfg)?;
21096 }
21097 } else {
21098 let f = self.func("gdn_chunk_attn_vl");
21099 let cfg = LaunchConfig {
21100 grid_dim: (max_nc, n_head as u32, b as u32),
21101 block_dim: (256, 1, 1),
21102 shared_mem_bytes: 0,
21103 };
21104 let __s_lb = self.gpu.stream();
21105 let mut lb = __s_lb.launch_builder(&f);
21106 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
21107 unsafe {
21108 lb.launch(cfg)?;
21109 }
21110 }
21111 {
21112 let f = self.func("gdn_chunk_solve32_vl");
21113 let cfg = LaunchConfig {
21114 grid_dim: (max_nc, n_head as u32, b as u32),
21115 block_dim: (256, 1, 1),
21116 shared_mem_bytes: 0,
21117 };
21118 let __s_lb = self.gpu.stream();
21119 let mut lb = __s_lb.launch_builder(&f);
21120 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
21121 unsafe {
21122 lb.launch(cfg)?;
21123 }
21124 }
21125 Ok(())
21126 }
21127
21128 #[allow(clippy::too_many_arguments)]
21132 pub fn gdn_prep_vl8(
21133 &self,
21134 seqs: &[GdnPrepVl],
21135 conv_w: &CudaSlice<f32>,
21136 dt_bias: &CudaSlice<f32>,
21137 a: &CudaSlice<f32>,
21138 conv_dim: usize,
21139 d_conv: usize,
21140 d_state: usize,
21141 num_v: usize,
21142 num_k: usize,
21143 key_dim: usize,
21144 hk: usize,
21145 eps: f32,
21146 ) -> Result<(), Box<dyn std::error::Error>> {
21147 let b = seqs.len();
21148 assert!(b >= 1 && b <= 8);
21149 let mut packed = [GdnPrepVl::default(); 8];
21150 packed[..b].copy_from_slice(seqs);
21151 let v = GdnPrepVl8(packed);
21152 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
21153 let (cdi, dci) = (conv_dim as i32, d_conv as i32);
21154 let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
21155 assert!(
21156 conv_fuse || hk == num_v,
21157 "de-broadcast requires the fused conv"
21158 );
21159 if conv_fuse {
21160 let f = self.func("ssm_conv1d_gdn_state_vl");
21161 let cfg = LaunchConfig {
21162 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
21163 block_dim: (256, 1, 1),
21164 shared_mem_bytes: 0,
21165 };
21166 let (dsi, nvi, nki, kdi, hki) = (
21167 d_state as i32,
21168 num_v as i32,
21169 num_k as i32,
21170 key_dim as i32,
21171 hk as i32,
21172 );
21173 let __s_lb = self.gpu.stream();
21174 let mut lb = __s_lb.launch_builder(&f);
21175 lb.arg(&v)
21176 .arg(conv_w)
21177 .arg(&cdi)
21178 .arg(&dci)
21179 .arg(&dsi)
21180 .arg(&nvi)
21181 .arg(&nki)
21182 .arg(&kdi)
21183 .arg(&hki);
21184 unsafe {
21185 lb.launch(cfg)?;
21186 }
21187 } else {
21188 let f = self.func("ssm_conv1d_tm_state_vl");
21189 let cfg = LaunchConfig {
21190 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
21191 block_dim: (256, 1, 1),
21192 shared_mem_bytes: 0,
21193 };
21194 let __s_lb = self.gpu.stream();
21195 let mut lb = __s_lb.launch_builder(&f);
21196 lb.arg(&v).arg(conv_w).arg(&cdi).arg(&dci);
21197 unsafe {
21198 lb.launch(cfg)?;
21199 }
21200 }
21201 {
21202 let f = self.func("ssm_conv_ring_update_vl");
21203 let n = (conv_dim * (d_conv - 1)) as u32;
21204 let cfg = LaunchConfig {
21205 grid_dim: (n.div_ceil(256), 1, b as u32),
21206 block_dim: (256, 1, 1),
21207 shared_mem_bytes: 0,
21208 };
21209 let __s_lb = self.gpu.stream();
21210 let mut lb = __s_lb.launch_builder(&f);
21211 lb.arg(&v).arg(&cdi).arg(&dci);
21212 unsafe {
21213 lb.launch(cfg)?;
21214 }
21215 }
21216 if !conv_fuse {
21217 let f = self.func("qkv_to_gdn_repack_vl");
21218 let n = max_t * (num_v * d_state) as u32;
21219 let cfg = LaunchConfig {
21220 grid_dim: (n.div_ceil(256), 1, b as u32),
21221 block_dim: (256, 1, 1),
21222 shared_mem_bytes: 0,
21223 };
21224 let (dsi, nvi, nki, kdi) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
21225 let __s_lb = self.gpu.stream();
21226 let mut lb = __s_lb.launch_builder(&f);
21227 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&nki).arg(&kdi);
21228 unsafe {
21229 lb.launch(cfg)?;
21230 }
21231 }
21232 if Self::l2_v2_on(d_state) {
21233 let f = self.func("gdn_l2_v2_vl");
21234 let cfg = LaunchConfig {
21235 grid_dim: ((max_t * hk as u32).div_ceil(8), 2, b as u32),
21236 block_dim: (256, 1, 1),
21237 shared_mem_bytes: 0,
21238 };
21239 let (dsi, nvi) = (d_state as i32, hk as i32);
21240 let __s_lb = self.gpu.stream();
21241 let mut lb = __s_lb.launch_builder(&f);
21242 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
21243 unsafe {
21244 lb.launch(cfg)?;
21245 }
21246 } else {
21247 let f = self.func("gdn_l2_vl");
21248 let cfg = LaunchConfig {
21249 grid_dim: (max_t * hk as u32, 2, b as u32),
21250 block_dim: (256, 1, 1),
21251 shared_mem_bytes: 0,
21252 };
21253 let (dsi, nvi) = (d_state as i32, hk as i32);
21254 let __s_lb = self.gpu.stream();
21255 let mut lb = __s_lb.launch_builder(&f);
21256 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
21257 unsafe {
21258 lb.launch(cfg)?;
21259 }
21260 }
21261 {
21262 let f = self.func("gdn_gate_prep_vl");
21263 let n = max_t * num_v as u32;
21264 let cfg = LaunchConfig {
21265 grid_dim: (n.div_ceil(256), 1, b as u32),
21266 block_dim: (256, 1, 1),
21267 shared_mem_bytes: 0,
21268 };
21269 let nvi = num_v as i32;
21270 let __s_lb = self.gpu.stream();
21271 let mut lb = __s_lb.launch_builder(&f);
21272 lb.arg(&v).arg(dt_bias).arg(a).arg(&nvi);
21273 unsafe {
21274 lb.launch(cfg)?;
21275 }
21276 }
21277 Ok(())
21278 }
21279
21280 pub fn gdn_mirror_vl8(
21282 &self,
21283 seqs: &[GdnSeqVl],
21284 n_head: usize,
21285 which: i32,
21286 hk: usize,
21287 ) -> Result<(), Box<dyn std::error::Error>> {
21288 let b = seqs.len();
21289 assert!(b >= 1 && b <= 8);
21290 let mut packed = [GdnSeqVl::default(); 8];
21291 packed[..b].copy_from_slice(seqs);
21292 let v = GdnVl8(packed);
21293 let ept = (if which == 0 { hk } else { n_head } * 128) as i32;
21294 let max_n = seqs
21295 .iter()
21296 .map(|s| {
21297 if which == 0 {
21298 s.t as i64 * ept as i64
21299 } else {
21300 s.nc as i64 * ept as i64 * 32
21301 }
21302 })
21303 .max()
21304 .unwrap();
21305 let f = self.func("gdn_mirror_vl");
21306 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
21307 let cfg = LaunchConfig {
21308 grid_dim: (blocks, 1, b as u32),
21309 block_dim: (256, 1, 1),
21310 shared_mem_bytes: 0,
21311 };
21312 let __s_lb = self.gpu.stream();
21313 let mut lb = __s_lb.launch_builder(&f);
21314 lb.arg(&v).arg(&ept).arg(&which);
21315 unsafe {
21316 lb.launch(cfg)?;
21317 }
21318 Ok(())
21319 }
21320
21321 pub fn gdn_tail_vl8(
21323 &self,
21324 seqs: &[GdnPrepVl],
21325 norm_w: &CudaSlice<f32>,
21326 d_state: usize,
21327 num_v: usize,
21328 eps: f32,
21329 ) -> Result<(), Box<dyn std::error::Error>> {
21330 let b = seqs.len();
21331 assert!(b >= 1 && b <= 8);
21332 let mut packed = [GdnPrepVl::default(); 8];
21333 packed[..b].copy_from_slice(seqs);
21334 let v = GdnPrepVl8(packed);
21335 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
21336 let f = self.func("gated_rmsnorm_f16out_vl");
21337 let cfg = LaunchConfig {
21339 grid_dim: (max_t * num_v as u32, 1, b as u32),
21340 block_dim: (128, 1, 1),
21341 shared_mem_bytes: 0,
21342 };
21343 let (dsi, nvi) = (d_state as i32, num_v as i32);
21344 let __s_lb = self.gpu.stream();
21345 let mut lb = __s_lb.launch_builder(&f);
21346 lb.arg(&v).arg(norm_w).arg(&dsi).arg(&nvi).arg(&eps);
21347 unsafe {
21348 lb.launch(cfg)?;
21349 }
21350 Ok(())
21351 }
21352
21353 pub fn addr_f32(&self, x: &CudaSlice<f32>) -> u64 {
21356 use cudarc::driver::DevicePtr;
21357 let s = self.gpu.stream();
21358 let (p, _g) = x.device_ptr(&s);
21359 p as u64
21360 }
21361 pub fn addr_f32_mut(&self, x: &mut CudaSlice<f32>) -> u64 {
21362 use cudarc::driver::DevicePtrMut;
21363 let s = self.gpu.stream();
21364 let (p, _g) = x.device_ptr_mut(&s);
21365 p as u64
21366 }
21367 pub fn addr_f32v(&self, x: &cudarc::driver::CudaView<f32>) -> u64 {
21368 use cudarc::driver::DevicePtr;
21369 let s = self.gpu.stream();
21370 let (p, _g) = x.device_ptr(&s);
21371 p as u64
21372 }
21373 pub fn addr_u8(&self, x: &CudaSlice<u8>) -> u64 {
21374 use cudarc::driver::DevicePtr;
21375 let s = self.gpu.stream();
21376 let (p, _g) = x.device_ptr(&s);
21377 p as u64
21378 }
21379
21380 pub fn gdn_chunk_vl8(
21384 &self,
21385 seqs: &[GdnSeqVl],
21386 n_head: usize,
21387 scale: f32,
21388 hk: usize,
21389 wq: Option<&GdnWVl8>,
21390 ) -> Result<(), Box<dyn std::error::Error>> {
21391 const NSPLIT: u32 = 4;
21392 let b = seqs.len();
21393 assert!(b >= 1 && b <= 8, "gdn_chunk_vl8: 1..=8 sequences");
21394 let mut packed = [GdnSeqVl::default(); 8];
21395 packed[..b].copy_from_slice(seqs);
21396 let v = GdnVl8(packed);
21397 let (hi, ci) = (n_head as i32, 32i32);
21398 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
21399 let hki = hk as i32;
21400 if let Some(w) = wq {
21401 let f = self.func("gdn_k45_wgmma_vl");
21403 let cfg = LaunchConfig {
21404 grid_dim: (n_head as u32, NSPLIT, b as u32),
21405 block_dim: (256, 1, 1),
21406 shared_mem_bytes: 0,
21407 };
21408 let __s_lb = self.gpu.stream();
21409 let mut lb = __s_lb.launch_builder(&f);
21410 lb.arg(&v).arg(w).arg(&scale).arg(&hi).arg(&ci).arg(&hki);
21411 unsafe {
21412 lb.launch(cfg)?;
21413 }
21414 let _ = max_nc;
21415 return Ok(());
21416 }
21417 {
21418 let f = self.func("gdn_chunk_state_mma_vl");
21419 let cfg = LaunchConfig {
21420 grid_dim: (n_head as u32, NSPLIT, b as u32),
21421 block_dim: (256, 1, 1),
21422 shared_mem_bytes: 0,
21423 };
21424 let __s_lb = self.gpu.stream();
21425 let mut lb = __s_lb.launch_builder(&f);
21426 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
21427 unsafe {
21428 lb.launch(cfg)?;
21429 }
21430 }
21431 {
21432 let f = self.func("gdn_chunk_output_mma_vl");
21433 let cfg = LaunchConfig {
21434 grid_dim: (max_nc, n_head as u32, b as u32),
21435 block_dim: (256, 1, 1),
21436 shared_mem_bytes: 0,
21437 };
21438 let __s_lb = self.gpu.stream();
21439 let mut lb = __s_lb.launch_builder(&f);
21440 lb.arg(&v).arg(&hi).arg(&ci).arg(&scale).arg(&hki);
21441 unsafe {
21442 lb.launch(cfg)?;
21443 }
21444 }
21445 Ok(())
21446 }
21447 pub fn gdn_scan_chunked(
21448 &self,
21449 q: &CudaSlice<f32>,
21450 k: &CudaSlice<f32>,
21451 v: &CudaSlice<f32>,
21452 g: &CudaSlice<f32>,
21453 beta: &CudaSlice<f32>,
21454 kb16_pre: Option<&CudaSlice<u8>>,
21455 qb16_pre: Option<&CudaSlice<u8>>,
21456 state_in: &CudaSlice<f32>,
21457 state_out: &mut CudaSlice<f32>,
21458 o: &mut CudaSlice<f32>,
21459 n_head: usize,
21460 t: usize,
21461 scale: f32,
21462 c: usize,
21463 hk: usize,
21464 ) -> Result<(), Box<dyn std::error::Error>> {
21465 const D: usize = 128;
21466 const NSPLIT: u32 = 4;
21467 assert!(c >= 1 && c <= 128, "gdn_scan_chunked: C must be in 1..=128");
21468 let h = n_head;
21469 let nc = (t + c - 1) / c;
21470 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
21471 let gdn_mma_pre = !portable_mma_gated()
21475 && c == 32
21476 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
21477 Ok("1") => true,
21478 Ok("0") => false,
21479 _ => cfg!(memra_hopper_mma),
21480 };
21481 let mut wb16_pre: Option<CudaSlice<u8>> = if gdn_mma_pre {
21482 Some(self.alloc_u8_uninit(nc * h * c * D * 2)?)
21483 } else {
21484 None
21485 };
21486 let gdn_wgmma_pre = gdn_mma_pre
21490 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
21491 Ok("0") => false,
21492 Ok("1") => true,
21493 _ => cfg!(memra_hopper_mma),
21494 };
21495 let nk = t * hk * D;
21496 let mut kb16_local: Option<CudaSlice<u8>> = None;
21497 if gdn_mma_pre && kb16_pre.is_none() {
21498 let mut kb = self.alloc_u8_uninit(nk * 2)?;
21499 let f = self.func("f32_to_bf16_bulk");
21500 let n2 = nk as i64;
21501 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
21502 let __s_b = self.gpu.stream();
21503 let mut b = __s_b.launch_builder(&f);
21504 b.arg(k).arg(&mut kb).arg(&n2);
21505 unsafe {
21506 b.launch(cfg2)?;
21507 }
21508 kb16_local = Some(kb);
21509 }
21510 let kb16_ref0: Option<&CudaSlice<u8>> = kb16_local.as_ref().or(kb16_pre);
21511 if let Some(kb) = kb16_pre {
21512 assert!(kb.len() >= nk * 2, "kb16_pre too small");
21513 }
21514 let mut qb16: Option<CudaSlice<u8>> = None;
21515 let mut pb16: Option<CudaSlice<u8>> = None;
21516 if gdn_wgmma_pre {
21517 if qb16_pre.is_none() {
21520 let mut qb = self.alloc_u8_uninit(nk * 2)?;
21521 let f = self.func("f32_to_bf16_bulk");
21522 let n2 = nk as i64;
21523 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
21524 let __s_b = self.gpu.stream();
21525 let mut b = __s_b.launch_builder(&f);
21526 b.arg(q).arg(&mut qb).arg(&n2);
21527 unsafe {
21528 b.launch(cfg2)?;
21529 }
21530 qb16 = Some(qb);
21531 } else if let Some(qb) = qb16_pre {
21532 assert!(qb.len() >= nk * 2, "qb16_pre too small");
21533 }
21534 pb16 = Some(self.alloc_u8_uninit(nc * h * c * c * 2)?);
21535 }
21536 let qb16_ref0: Option<&CudaSlice<u8>> = qb16.as_ref().or(qb16_pre);
21537 let k2w = if gdn_wgmma_pre {
21538 Some((
21539 *qb16_ref0.as_ref().unwrap(),
21540 *kb16_ref0.as_ref().unwrap(),
21541 pb16.as_mut().unwrap(),
21542 ))
21543 } else {
21544 None
21545 };
21546 let (gcum, p, u, w) =
21547 self.gdn_chunk_k123(q, k, v, g, beta, wb16_pre.as_mut(), n_head, t, c, hk, k2w)?;
21548 let _ = &w;
21549 let mut y = self.uninit(nc * h * c * D)?;
21550 let mut ssnap = self.uninit(nc * h * D * D)?; let gdn_mma = !portable_mma_gated()
21562 && c == 32
21563 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
21564 Ok("1") => true,
21565 Ok("0") => false,
21566 _ => cfg!(memra_hopper_mma),
21567 };
21568 if gdn_mma {
21569 let wb16 = wb16_pre
21570 .take()
21571 .expect("mma path pre-allocates wb16 (K3 store fold)");
21572 let kb16_ref: &CudaSlice<u8> = kb16_ref0.expect("mma path pre-builds kb16 above K123");
21573 if gdn_wgmma_pre {
21585 let qb16 = qb16_ref0.unwrap();
21587 let pb16 = pb16.as_ref().unwrap();
21588 {
21589 let f = self.func("gdn_k45_wgmma");
21590 let cfg = LaunchConfig {
21591 grid_dim: (h as u32, 4, 1),
21592 block_dim: (256, 1, 1),
21593 shared_mem_bytes: 0,
21594 };
21595 let hki = hk as i32;
21596 let __s_b = self.gpu.stream();
21597 let mut b = __s_b.launch_builder(&f);
21598 b.arg(kb16_ref)
21599 .arg(&gcum)
21600 .arg(beta)
21601 .arg(&u)
21602 .arg(&wb16)
21603 .arg(qb16)
21604 .arg(pb16)
21605 .arg(o)
21606 .arg(&scale)
21607 .arg(state_in)
21608 .arg(&mut *state_out)
21609 .arg(&hi)
21610 .arg(&ti)
21611 .arg(&ci)
21612 .arg(&hki);
21613 unsafe {
21614 b.launch(cfg)?;
21615 }
21616 }
21617 return Ok(());
21618 }
21619 let mut y16 = self.alloc_u8_uninit(nc * h * c * D * 2)?;
21623 let mut ssnap16 = self.alloc_u8_uninit(nc * h * D * D * 2)?;
21624 {
21625 let f = self.func("gdn_chunk_state_mma");
21626 let cfg = LaunchConfig {
21627 grid_dim: (h as u32, NSPLIT, 1),
21628 block_dim: (256, 1, 1),
21629 shared_mem_bytes: 0,
21630 };
21631 let hki = hk as i32;
21632 let __s_b = self.gpu.stream();
21633 let mut b = __s_b.launch_builder(&f);
21634 b.arg(kb16_ref)
21635 .arg(&gcum)
21636 .arg(beta)
21637 .arg(&u)
21638 .arg(&wb16)
21639 .arg(&mut y16)
21640 .arg(&mut ssnap16)
21641 .arg(state_in)
21642 .arg(&mut *state_out)
21643 .arg(&hi)
21644 .arg(&ti)
21645 .arg(&ci)
21646 .arg(&hki);
21647 unsafe {
21648 b.launch(cfg)?;
21649 }
21650 }
21651 {
21652 let f = self.func("gdn_chunk_output_mma");
21654 let jt = ((c + 31) / 32) as u32;
21655 let cfg = LaunchConfig {
21656 grid_dim: (nc as u32, h as u32, jt),
21657 block_dim: (256, 1, 1),
21658 shared_mem_bytes: 0,
21659 };
21660 let hki = hk as i32;
21661 let __s_b = self.gpu.stream();
21662 let mut b = __s_b.launch_builder(&f);
21663 b.arg(q)
21664 .arg(&gcum)
21665 .arg(&p)
21666 .arg(&y16)
21667 .arg(&ssnap16)
21668 .arg(o)
21669 .arg(&hi)
21670 .arg(&ti)
21671 .arg(&ci)
21672 .arg(&scale)
21673 .arg(&hki);
21674 unsafe {
21675 b.launch(cfg)?;
21676 }
21677 }
21678 return Ok(());
21679 }
21680 {
21681 let f = self.func("gdn_chunk_state_f32");
21683 let cfg = LaunchConfig {
21684 grid_dim: (h as u32, NSPLIT, 1),
21685 block_dim: (256, 1, 1),
21686 shared_mem_bytes: 0,
21687 };
21688 let __s_b = self.gpu.stream();
21689 let mut b = __s_b.launch_builder(&f);
21690 b.arg(k)
21691 .arg(&gcum)
21692 .arg(beta)
21693 .arg(&u)
21694 .arg(&w)
21695 .arg(&mut y)
21696 .arg(&mut ssnap)
21697 .arg(state_in)
21698 .arg(&mut *state_out)
21699 .arg(&hi)
21700 .arg(&ti)
21701 .arg(&ci);
21702 unsafe {
21703 b.launch(cfg)?;
21704 }
21705 }
21706 {
21707 let f = self.func("gdn_chunk_output_f32");
21709 let jt = ((c + 31) / 32) as u32;
21710 let cfg = LaunchConfig {
21711 grid_dim: (nc as u32, h as u32, jt),
21712 block_dim: (256, 1, 1),
21713 shared_mem_bytes: 0,
21714 };
21715 let __s_b = self.gpu.stream();
21716 let mut b = __s_b.launch_builder(&f);
21717 b.arg(q)
21718 .arg(&gcum)
21719 .arg(&p)
21720 .arg(&y)
21721 .arg(&ssnap)
21722 .arg(o)
21723 .arg(&hi)
21724 .arg(&ti)
21725 .arg(&ci)
21726 .arg(&scale);
21727 unsafe {
21728 b.launch(cfg)?;
21729 }
21730 }
21731 Ok(())
21732 }
21733
21734 #[allow(clippy::too_many_arguments)]
21743 #[allow(clippy::too_many_arguments)]
21744 pub fn gdn_scan_prefill(
21745 &self,
21746 q: &CudaSlice<f32>,
21747 k: &CudaSlice<f32>,
21748 v: &CudaSlice<f32>,
21749 g: &CudaSlice<f32>,
21750 beta: &CudaSlice<f32>,
21751 kb16_pre: Option<&CudaSlice<u8>>,
21752 qb16_pre: Option<&CudaSlice<u8>>,
21753 state_in: &CudaSlice<f32>,
21754 state_out: &mut CudaSlice<f32>,
21755 o: &mut CudaSlice<f32>,
21756 n_head: usize,
21757 t: usize,
21758 scale: f32,
21759 hk: usize,
21760 ) -> Result<(), Box<dyn std::error::Error>> {
21761 if std::env::var("MEMRA_GDN_DIFF").is_ok() && t >= 16 {
21762 assert!(hk == n_head, "GDN_DIFF oracle is broadcast-only");
21763 return self.gdn_scan_diff(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale);
21764 }
21765 if Self::gdn_chunked_enabled() && t >= 16 {
21766 self.gdn_scan_chunked(
21767 q,
21768 k,
21769 v,
21770 g,
21771 beta,
21772 kb16_pre,
21773 qb16_pre,
21774 state_in,
21775 state_out,
21776 o,
21777 n_head,
21778 t,
21779 scale,
21780 Self::gdn_chunk_size(),
21781 hk,
21782 )
21783 } else {
21784 assert!(
21785 hk == n_head,
21786 "s128 scan is broadcast-only (prep guarantees by predicate)"
21787 );
21788 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)
21789 }
21790 }
21791
21792 #[allow(clippy::too_many_arguments)]
21794 fn gdn_scan_diff(
21795 &self,
21796 q: &CudaSlice<f32>,
21797 k: &CudaSlice<f32>,
21798 v: &CudaSlice<f32>,
21799 g: &CudaSlice<f32>,
21800 beta: &CudaSlice<f32>,
21801 state_in: &CudaSlice<f32>,
21802 state_out: &mut CudaSlice<f32>,
21803 o: &mut CudaSlice<f32>,
21804 n_head: usize,
21805 t: usize,
21806 scale: f32,
21807 ) -> Result<(), Box<dyn std::error::Error>> {
21808 static CALL: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
21809 let call = CALL.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
21810 let mut o_c = self.uninit(o.len())?;
21811 let mut st_c = self.uninit(state_out.len())?;
21812 self.gdn_scan_chunked(
21813 q,
21814 k,
21815 v,
21816 g,
21817 beta,
21818 None,
21819 None,
21820 state_in,
21821 &mut st_c,
21822 &mut o_c,
21823 n_head,
21824 t,
21825 scale,
21826 Self::gdn_chunk_size(),
21827 n_head,
21828 )?;
21829 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)?;
21830 let (oh_s, oh_c) = (self.dtoh(o)?, self.dtoh(&o_c)?);
21831 let (sh_s, sh_c) = (self.dtoh(state_out)?, self.dtoh(&st_c)?);
21832 let stats = |a: &[f32], b: &[f32]| -> (f32, f32, f64) {
21833 let mut max_abs = 0f32;
21834 let mut max_rel = 0f32;
21835 let mut sum_rel = 0f64;
21836 for (x, y) in a.iter().zip(b) {
21837 let ad = (x - y).abs();
21838 let rel = ad / x.abs().max(y.abs()).max(1e-3);
21839 if ad > max_abs {
21840 max_abs = ad;
21841 }
21842 if rel > max_rel {
21843 max_rel = rel;
21844 }
21845 sum_rel += rel as f64;
21846 }
21847 (max_abs, max_rel, sum_rel / a.len() as f64)
21848 };
21849 let (o_ma, o_mr, o_mean) = stats(&oh_s, &oh_c);
21850 let (s_ma, s_mr, s_mean) = stats(&sh_s, &sh_c);
21851 println!(
21852 "[gdn-diff call {call:3} T={t} C={}] out: max_abs={o_ma:.3e} max_rel={o_mr:.3e} mean_rel={o_mean:.3e} | \
21853 state: max_abs={s_ma:.3e} max_rel={s_mr:.3e} mean_rel={s_mean:.3e}",
21854 Self::gdn_chunk_size()
21855 );
21856 Ok(())
21857 }
21858
21859 pub fn gdn_glog(
21861 &self,
21862 alpha: &CudaSlice<f32>,
21863 dt_bias: &CudaSlice<f32>,
21864 a: &CudaSlice<f32>,
21865 g_log: &mut CudaSlice<f32>,
21866 n_head: usize,
21867 t: usize,
21868 ) -> Result<(), Box<dyn std::error::Error>> {
21869 let f = self.func("gdn_glog_f32");
21870 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
21871 let (h, ti) = (n_head as i32, t as i32);
21872 let __s_b = self.gpu.stream();
21873 let mut b = __s_b.launch_builder(&f);
21874 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
21875 unsafe {
21876 b.launch(cfg)?;
21877 }
21878 Ok(())
21879 }
21880
21881 pub fn sigmoid_v(
21884 &self,
21885 x: &cudarc::driver::CudaView<f32>,
21886 y: &mut CudaSlice<f32>,
21887 n: usize,
21888 ) -> Result<(), Box<dyn std::error::Error>> {
21889 let f = self.func("sigmoid_f32");
21890 let cfg = LaunchConfig::for_num_elems(n as u32);
21891 let ni = n as i32;
21892 let __s_b = self.gpu.stream();
21893 let mut b = __s_b.launch_builder(&f);
21894 b.arg(x).arg(y).arg(&ni);
21895 unsafe {
21896 b.launch(cfg)?;
21897 }
21898 Ok(())
21899 }
21900
21901 pub fn gdn_glog_v(
21902 &self,
21903 alpha: &cudarc::driver::CudaView<f32>,
21904 dt_bias: &CudaSlice<f32>,
21905 a: &CudaSlice<f32>,
21906 g_log: &mut CudaSlice<f32>,
21907 n_head: usize,
21908 t: usize,
21909 ) -> Result<(), Box<dyn std::error::Error>> {
21910 let f = self.func("gdn_glog_f32");
21911 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
21912 let (h, ti) = (n_head as i32, t as i32);
21913 let __s_b = self.gpu.stream();
21914 let mut b = __s_b.launch_builder(&f);
21915 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
21916 unsafe {
21917 b.launch(cfg)?;
21918 }
21919 Ok(())
21920 }
21921
21922 pub fn sigmoid(
21923 &self,
21924 x: &CudaSlice<f32>,
21925 y: &mut CudaSlice<f32>,
21926 n: usize,
21927 ) -> Result<(), Box<dyn std::error::Error>> {
21928 let f = self.func("sigmoid_f32");
21929 let cfg = LaunchConfig::for_num_elems(n as u32);
21930 let ni = n as i32;
21931 let __s_b = self.gpu.stream();
21932 let mut b = __s_b.launch_builder(&f);
21933 b.arg(x).arg(y).arg(&ni);
21934 unsafe {
21935 b.launch(cfg)?;
21936 }
21937 Ok(())
21938 }
21939
21940 pub fn sig_mul_f16out(
21943 &self,
21944 a: &CudaSlice<f32>,
21945 g: &CudaSlice<f32>,
21946 dst: &mut CudaSlice<f32>,
21947 dst16: &mut CudaSlice<u8>,
21948 n: usize,
21949 ) -> Result<(), Box<dyn std::error::Error>> {
21950 let f = self.func("sig_mul_f16out_f32");
21951 let cfg = LaunchConfig::for_num_elems(n as u32);
21952 let ni = n as i32;
21953 let __s_b = self.gpu.stream();
21954 let mut b = __s_b.launch_builder(&f);
21955 b.arg(a).arg(g).arg(dst).arg(dst16).arg(&ni);
21956 unsafe {
21957 b.launch(cfg)?;
21958 }
21959 Ok(())
21960 }
21961
21962 #[allow(clippy::too_many_arguments)]
21971 pub fn attn_head_gate(
21972 &self,
21973 a: &CudaSlice<f32>,
21974 g: &CudaSlice<f32>,
21975 dst: &mut CudaSlice<f32>,
21976 dst16: Option<&mut CudaSlice<u8>>,
21977 head_dim: usize,
21978 n_head: usize,
21979 t: usize,
21980 ) -> Result<(), Box<dyn std::error::Error>> {
21981 let f = self.func("attn_head_gate_f32");
21982 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
21983 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
21984 let d16: u64 = match dst16 {
21986 Some(d) => self.addr_u8(d),
21987 None => 0,
21988 };
21989 let __s_b = self.gpu.stream();
21990 let mut b = __s_b.launch_builder(&f);
21991 b.arg(a)
21992 .arg(g)
21993 .arg(dst)
21994 .arg(&d16)
21995 .arg(&hd)
21996 .arg(&nh)
21997 .arg(&ti);
21998 unsafe {
21999 b.launch(cfg)?;
22000 }
22001 Ok(())
22002 }
22003
22004 #[allow(clippy::too_many_arguments)]
22013 pub fn swiglu_clamped_mul_scaled(
22014 &self,
22015 gate: &CudaSlice<f32>,
22016 up: &CudaSlice<f32>,
22017 gs: f32,
22018 us: f32,
22019 limit: f32,
22020 dst: &mut CudaSlice<f32>,
22021 n: usize,
22022 ) -> Result<(), Box<dyn std::error::Error>> {
22023 debug_assert!(
22024 limit > 1e-6,
22025 "swiglu_clamped needs a live limit; use silu_mul_scaled"
22026 );
22027 let f = self.func("swiglu_clamped_mul_scaled_f32");
22028 let cfg = LaunchConfig::for_num_elems(n as u32);
22029 let ni = n as i32;
22030 let __s_b = self.gpu.stream();
22031 let mut b = __s_b.launch_builder(&f);
22032 b.arg(gate)
22033 .arg(up)
22034 .arg(&gs)
22035 .arg(&us)
22036 .arg(&limit)
22037 .arg(dst)
22038 .arg(&ni);
22039 unsafe {
22040 b.launch(cfg)?;
22041 }
22042 Ok(())
22043 }
22044
22045 pub fn gated_rmsnorm(
22047 &self,
22048 o: &CudaSlice<f32>,
22049 w: &CudaSlice<f32>,
22050 z: &CudaSlice<f32>,
22051 dst: &mut CudaSlice<f32>,
22052 ncols: usize,
22053 nrows: usize,
22054 eps: f32,
22055 ) -> Result<(), Box<dyn std::error::Error>> {
22056 let f = self.func("gated_rmsnorm_f32");
22057 let cfg = LaunchConfig {
22058 grid_dim: (nrows as u32, 1, 1),
22059 block_dim: (128, 1, 1),
22060 shared_mem_bytes: 0,
22061 };
22062 let (nc, e) = (ncols as i32, eps);
22063 let __s_b = self.gpu.stream();
22064 let mut b = __s_b.launch_builder(&f);
22065 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
22066 unsafe {
22067 b.launch(cfg)?;
22068 }
22069 Ok(())
22070 }
22071
22072 pub fn gated_rmsnorm_f16out(
22075 &self,
22076 o: &CudaSlice<f32>,
22077 w: &CudaSlice<f32>,
22078 z: &CudaSlice<f32>,
22079 dst: &mut CudaSlice<f32>,
22080 dst16: &mut CudaSlice<u8>,
22081 ncols: usize,
22082 nrows: usize,
22083 eps: f32,
22084 ) -> Result<(), Box<dyn std::error::Error>> {
22085 let f = self.func("gated_rmsnorm_f16out_f32");
22086 let cfg = LaunchConfig {
22088 grid_dim: (nrows as u32, 1, 1),
22089 block_dim: (128, 1, 1),
22090 shared_mem_bytes: 0,
22091 };
22092 let (nc, e) = (ncols as i32, eps);
22093 let __s_b = self.gpu.stream();
22094 let mut b = __s_b.launch_builder(&f);
22095 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
22096 unsafe {
22097 b.launch(cfg)?;
22098 }
22099 Ok(())
22100 }
22101
22102 #[allow(clippy::too_many_arguments)]
22106 pub fn add_rms_norm_zq8(
22107 &self,
22108 a: &CudaSlice<f32>,
22109 b_in: &CudaSlice<f32>,
22110 w: &CudaSlice<f32>,
22111 res: &mut CudaSlice<f32>,
22112 z: &mut CudaSlice<f32>,
22113 ncols: usize,
22114 nrows: usize,
22115 eps: f32,
22116 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
22117 assert!(ncols % 32 == 0);
22118 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
22119 let mut d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
22120 let f = self.func("add_rms_norm_zq8");
22121 let cfg = LaunchConfig {
22122 grid_dim: (nrows as u32, 1, 1),
22123 block_dim: (1024, 1, 1),
22124 shared_mem_bytes: 0,
22125 };
22126 let (nc, ep) = (ncols as i32, eps);
22127 let __s_b = self.gpu.stream();
22128 let mut b = __s_b.launch_builder(&f);
22129 b.arg(a)
22130 .arg(b_in)
22131 .arg(w)
22132 .arg(res)
22133 .arg(z)
22134 .arg(&mut q)
22135 .arg(&mut d)
22136 .arg(&nc)
22137 .arg(&ep);
22138 unsafe {
22139 b.launch(cfg)?;
22140 }
22141 Ok((q, d))
22142 }
22143
22144 pub fn gated_rmsnorm_zv(
22149 &self,
22150 o: &CudaSlice<f32>,
22151 w: &CudaSlice<f32>,
22152 z: &cudarc::driver::CudaView<f32>,
22153 dst: &mut CudaSlice<f32>,
22154 ncols: usize,
22155 nrows: usize,
22156 eps: f32,
22157 ) -> Result<(), Box<dyn std::error::Error>> {
22158 let f = self.func("gated_rmsnorm_f32");
22159 let cfg = LaunchConfig {
22160 grid_dim: (nrows as u32, 1, 1),
22161 block_dim: (128, 1, 1),
22162 shared_mem_bytes: 0,
22163 };
22164 let (nc, e) = (ncols as i32, eps);
22165 let __s_b = self.gpu.stream();
22166 let mut b = __s_b.launch_builder(&f);
22167 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
22168 unsafe {
22169 b.launch(cfg)?;
22170 }
22171 Ok(())
22172 }
22173
22174 pub fn gated_rmsnorm_f16out_zv(
22175 &self,
22176 o: &CudaSlice<f32>,
22177 w: &CudaSlice<f32>,
22178 z: &cudarc::driver::CudaView<f32>,
22179 dst: &mut CudaSlice<f32>,
22180 dst16: &mut CudaSlice<u8>,
22181 ncols: usize,
22182 nrows: usize,
22183 eps: f32,
22184 ) -> Result<(), Box<dyn std::error::Error>> {
22185 let f = self.func("gated_rmsnorm_f16out_f32");
22186 let cfg = LaunchConfig {
22188 grid_dim: (nrows as u32, 1, 1),
22189 block_dim: (128, 1, 1),
22190 shared_mem_bytes: 0,
22191 };
22192 let (nc, e) = (ncols as i32, eps);
22193 let __s_b = self.gpu.stream();
22194 let mut b = __s_b.launch_builder(&f);
22195 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
22196 unsafe {
22197 b.launch(cfg)?;
22198 }
22199 Ok(())
22200 }
22201
22202 pub fn gated_rmsnorm_q8_1(
22203 &self,
22204 o: &CudaSlice<f32>,
22205 w: &CudaSlice<f32>,
22206 z: &CudaSlice<f32>,
22207 ncols: usize,
22208 nrows: usize,
22209 eps: f32,
22210 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
22211 assert!(ncols % 32 == 0);
22212 let f = self.func("gated_rmsnorm_q8_1");
22213 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
22214 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
22215 let cfg = LaunchConfig {
22216 grid_dim: (nrows as u32, 1, 1),
22217 block_dim: (128, 1, 1),
22218 shared_mem_bytes: 0,
22219 };
22220 let (nc, ep) = (ncols as i32, eps);
22221 let __s_b = self.gpu.stream();
22222 let mut b = __s_b.launch_builder(&f);
22223 b.arg(o)
22224 .arg(w)
22225 .arg(z)
22226 .arg(&mut out_q)
22227 .arg(&mut out_d)
22228 .arg(&nc)
22229 .arg(&ep);
22230 unsafe {
22231 b.launch(cfg)?;
22232 }
22233 Ok((out_q, out_d))
22234 }
22235
22236 pub fn transpose(
22238 &self,
22239 inp: &CudaSlice<f32>,
22240 rows: usize,
22241 cols: usize,
22242 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22243 let f = self.func("transpose_f32");
22244 let mut out = self.zeros(rows * cols)?;
22245 let cfg = LaunchConfig::for_num_elems((rows * cols) as u32);
22246 let (r, c) = (rows as i32, cols as i32);
22247 let __s_b = self.gpu.stream();
22248 let mut b = __s_b.launch_builder(&f);
22249 b.arg(inp).arg(&mut out).arg(&r).arg(&c);
22250 unsafe {
22251 b.launch(cfg)?;
22252 }
22253 Ok(out)
22254 }
22255
22256 pub fn repeat_heads(
22258 &self,
22259 inp: &CudaSlice<f32>,
22260 out: &mut CudaSlice<f32>,
22261 head_dim: usize,
22262 n_in: usize,
22263 n_out: usize,
22264 t: usize,
22265 ) -> Result<(), Box<dyn std::error::Error>> {
22266 let f = self.func("repeat_heads_f32");
22267 let cfg = LaunchConfig::for_num_elems((head_dim * n_out * t) as u32);
22268 let (hd, ni, no, ti) = (head_dim as i32, n_in as i32, n_out as i32, t as i32);
22269 let __s_b = self.gpu.stream();
22270 let mut b = __s_b.launch_builder(&f);
22271 b.arg(inp).arg(out).arg(&hd).arg(&ni).arg(&no).arg(&ti);
22272 unsafe {
22273 b.launch(cfg)?;
22274 }
22275 Ok(())
22276 }
22277
22278 pub fn q_gate_split(
22285 &self,
22286 qf: &CudaSlice<f32>,
22287 q_out: &mut CudaSlice<f32>,
22288 gate_out: &mut CudaSlice<f32>,
22289 head_dim: usize,
22290 n_head: usize,
22291 t: usize,
22292 ) -> Result<(), Box<dyn std::error::Error>> {
22293 memra_gguf::config::check_fused_q_gate_extent(qf.len(), head_dim, n_head, t)?;
22294 let out_need = head_dim * n_head * t;
22295 if q_out.len() < out_need || gate_out.len() < out_need {
22296 return Err(format!(
22297 "q_gate_split destinations too small: need {out_need} each, have q={} gate={}",
22298 q_out.len(),
22299 gate_out.len()
22300 )
22301 .into());
22302 }
22303 let f = self.func("q_gate_split_f32");
22304 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
22305 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
22306 let __s_b = self.gpu.stream();
22307 let mut b = __s_b.launch_builder(&f);
22308 b.arg(qf)
22309 .arg(q_out)
22310 .arg(gate_out)
22311 .arg(&hd)
22312 .arg(&nh)
22313 .arg(&ti);
22314 unsafe {
22315 b.launch(cfg)?;
22316 }
22317 Ok(())
22318 }
22319
22320 pub fn qkv_to_gdn_repack(
22324 &self,
22325 conv_out: &CudaSlice<f32>,
22326 q_g: &mut CudaSlice<f32>,
22327 k_g: &mut CudaSlice<f32>,
22328 v_g: &mut CudaSlice<f32>,
22329 d_state: usize,
22330 num_v: usize,
22331 num_k: usize,
22332 key_dim: usize,
22333 t: usize,
22334 ) -> Result<(), Box<dyn std::error::Error>> {
22335 let f = self.func("qkv_to_gdn_repack_f32");
22336 let cfg = LaunchConfig::for_num_elems((d_state * num_v * t) as u32);
22337 let (ds, nv, nk, kd, ti) = (
22338 d_state as i32,
22339 num_v as i32,
22340 num_k as i32,
22341 key_dim as i32,
22342 t as i32,
22343 );
22344 let __s_b = self.gpu.stream();
22345 let mut b = __s_b.launch_builder(&f);
22346 b.arg(conv_out)
22347 .arg(q_g)
22348 .arg(k_g)
22349 .arg(v_g)
22350 .arg(&ds)
22351 .arg(&nv)
22352 .arg(&nk)
22353 .arg(&kd)
22354 .arg(&ti);
22355 unsafe {
22356 b.launch(cfg)?;
22357 }
22358 Ok(())
22359 }
22360
22361 pub fn conv_left_pad(
22364 &self,
22365 src: &CudaSlice<f32>,
22366 dst: &mut CudaSlice<f32>,
22367 conv_dim: usize,
22368 t: usize,
22369 pad: usize,
22370 ) -> Result<(), Box<dyn std::error::Error>> {
22371 let f = self.func("conv_left_pad_f32");
22372 let cfg = LaunchConfig::for_num_elems((conv_dim * t) as u32);
22373 let (cd, ti, p) = (conv_dim as i32, t as i32, pad as i32);
22374 let __s_b = self.gpu.stream();
22375 let mut b = __s_b.launch_builder(&f);
22376 b.arg(src).arg(dst).arg(&cd).arg(&ti).arg(&p);
22377 unsafe {
22378 b.launch(cfg)?;
22379 }
22380 Ok(())
22381 }
22382
22383 pub fn conv_assemble_and_roll(
22387 &self,
22388 qkv_col: &CudaSlice<f32>,
22389 conv_state: &mut CudaSlice<f32>,
22390 conv_in: &mut CudaSlice<f32>,
22391 conv_dim: usize,
22392 pad: usize,
22393 ) -> Result<(), Box<dyn std::error::Error>> {
22394 let f = self.func("conv_assemble_and_roll_f32");
22395 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
22396 let (cd, p) = (conv_dim as i32, pad as i32);
22397 let __s_b = self.gpu.stream();
22398 let mut b = __s_b.launch_builder(&f);
22399 b.arg(qkv_col).arg(conv_state).arg(conv_in).arg(&cd).arg(&p);
22400 unsafe {
22401 b.launch(cfg)?;
22402 }
22403 Ok(())
22404 }
22405
22406 pub fn ssm_conv1d_fused_decode(
22412 &self,
22413 qkv_col: &CudaSlice<f32>,
22414 conv_state: &mut CudaSlice<f32>,
22415 w: &CudaSlice<f32>,
22416 conv_out: &mut CudaSlice<f32>,
22417 conv_dim: usize,
22418 d_conv: usize,
22419 ) -> Result<(), Box<dyn std::error::Error>> {
22420 let f = self.func("ssm_conv1d_fused_decode_f32");
22421 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
22422 let (cd, dc) = (conv_dim as i32, d_conv as i32);
22423 let __s_b = self.gpu.stream();
22424 let mut b = __s_b.launch_builder(&f);
22425 b.arg(qkv_col)
22426 .arg(conv_state)
22427 .arg(w)
22428 .arg(conv_out)
22429 .arg(&cd)
22430 .arg(&dc);
22431 unsafe {
22432 b.launch(cfg)?;
22433 }
22434 Ok(())
22435 }
22436
22437 pub fn slice_range(
22440 &self,
22441 src: &CudaSlice<f32>,
22442 start: usize,
22443 len: usize,
22444 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22445 let host = self.gpu.stream().clone_dtoh(src)?;
22446 self.gpu.stream().synchronize()?;
22447 Ok(self.htod(&host[start..start + len])?)
22448 }
22449}
22450
22451#[cfg(test)]
22452mod target_dispatch_tests {
22453 use super::legacy_quant_gemm_allowed;
22454
22455 #[test]
22456 fn legacy_quant_gemm_arch_policy_honors_the_escape_hatch() {
22457 assert!(legacy_quant_gemm_allowed(false, false, false));
22459 assert!(!legacy_quant_gemm_allowed(false, false, true));
22460 assert!(!legacy_quant_gemm_allowed(true, false, false));
22462 assert!(!legacy_quant_gemm_allowed(true, false, true));
22463 assert!(legacy_quant_gemm_allowed(true, true, false));
22465 assert!(!legacy_quant_gemm_allowed(true, true, true));
22466 }
22467
22468 #[cfg(all(memra_portable_cuda, not(memra_hopper_mma)))]
22469 #[test]
22470 fn portable_build_disables_legacy_quant_gemm_without_an_env_override() {
22471 assert!(!legacy_quant_gemm_allowed(
22472 cfg!(memra_portable_cuda),
22473 cfg!(memra_hopper_mma),
22474 false
22475 ));
22476 }
22477
22478 #[cfg(memra_hopper_mma)]
22479 #[test]
22480 fn hopper_mma_build_re_admits_legacy_quant_gemm() {
22481 assert!(legacy_quant_gemm_allowed(
22482 cfg!(memra_portable_cuda),
22483 cfg!(memra_hopper_mma),
22484 false
22485 ));
22486 assert!(super::portable_mma_gated() == false);
22487 }
22488}
22489
22490impl memra_kv::KvDev for Engine {
22493 fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22494 Engine::zeros(self, n)
22495 }
22496 fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22497 Engine::uninit(self, n)
22498 }
22499 fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
22500 Engine::alloc_u8(self, n)
22501 }
22502 fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
22503 Engine::htod_i32(self, v)
22504 }
22505 fn clone_dtod(
22506 &self,
22507 src: &CudaSlice<f32>,
22508 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22509 Engine::clone_dtod(self, src)
22510 }
22511 fn copy_into(
22512 &self,
22513 dst: &mut CudaSlice<f32>,
22514 off: usize,
22515 src: &CudaSlice<f32>,
22516 len: usize,
22517 ) -> Result<(), Box<dyn std::error::Error>> {
22518 Engine::copy_into(self, dst, off, src, len)
22519 }
22520 fn set_i32_one(
22521 &self,
22522 d: &mut CudaSlice<i32>,
22523 v: i32,
22524 ) -> Result<(), Box<dyn std::error::Error>> {
22525 Engine::set_i32_one(self, d, v)
22526 }
22527}
22528
22529#[cfg(test)]
22530mod fused_gate_bounds_tests {
22531 use super::*;
22532
22533 #[test]
22546 #[ignore = "requires a CUDA GPU"]
22547 fn q_gate_split_refuses_a_separate_gate_wq_instead_of_reading_past_it() {
22548 let e = Engine::new(0).unwrap();
22549 let (head_dim, n_head, t) = (8usize, 4usize, 2usize);
22550 let fused = 2 * head_dim * n_head * t;
22551 let out_n = head_dim * n_head * t;
22552
22553 let narrow = e.htod(&vec![1.0f32; out_n]).unwrap();
22555 let mut q = e.uninit(out_n).unwrap();
22556 let mut gate = e.uninit(out_n).unwrap();
22557 let err = e
22558 .q_gate_split(&narrow, &mut q, &mut gate, head_dim, n_head, t)
22559 .expect_err("half-width wq must be refused, not read past")
22560 .to_string();
22561 assert!(err.contains("NO fused gate"), "{err}");
22562 assert!(err.contains(&format!("{fused}")), "{err}");
22563
22564 let host: Vec<f32> = (0..fused).map(|i| i as f32).collect();
22567 let wide = e.htod(&host).unwrap();
22568 e.q_gate_split(&wide, &mut q, &mut gate, head_dim, n_head, t)
22569 .expect("full-width wq splits");
22570 let (qh, gh) = (e.dtoh(&q).unwrap(), e.dtoh(&gate).unwrap());
22571 for tok in 0..t {
22572 for hh in 0..n_head {
22573 for d in 0..head_dim {
22574 let base = tok * (n_head * 2 * head_dim) + hh * (2 * head_dim);
22575 let idx = tok * (n_head * head_dim) + hh * head_dim + d;
22576 assert_eq!(qh[idx], host[base + d], "q t{tok} h{hh} d{d}");
22577 assert_eq!(gh[idx], host[base + head_dim + d], "gate t{tok} h{hh} d{d}");
22578 }
22579 }
22580 }
22581
22582 let mut small = e.uninit(out_n - 1).unwrap();
22584 assert!(
22585 e.q_gate_split(&wide, &mut small, &mut gate, head_dim, n_head, t)
22586 .is_err()
22587 );
22588 }
22589}
22590
22591#[cfg(test)]
22595mod fused_rope_width_tests {
22596 use super::Engine;
22597
22598 #[test]
22601 fn full_width_is_accepted() {
22602 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope", 256, 256).is_ok());
22603 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_cat", 512, 512).is_ok());
22604 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_append", 128, 128).is_ok());
22605 }
22606
22607 #[test]
22620 fn gemma4_official_artifact_widths_pass() {
22621 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope", 512, 512).is_ok());
22622 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_append_dc", 256, 256).is_ok());
22623 }
22624
22625 #[test]
22628 fn partial_rotary_is_refused_with_the_geometry_named() {
22629 let err = Engine::full_width_rope_only("rms_norm_qkv_rope", 64, 256)
22631 .expect_err("partial rotary must refuse");
22632 let msg = err.to_string();
22633 assert!(msg.contains("PARTIAL ROTARY REFUSED"), "{msg}");
22634 assert!(msg.contains("n_rot 64"), "{msg}");
22635 assert!(msg.contains("head_dim 256"), "{msg}");
22636 assert!(
22637 msg.contains("64..256"),
22638 "names the band it would corrupt: {msg}"
22639 );
22640 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_append_dc", 64, 128).is_err());
22642 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope", 256, 128).is_err());
22644 }
22645}