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");
1421 let (ni, nr, rs) = (n as i32, nrow as i32, row_stride as i64);
1422 let cfg = LaunchConfig {
1423 grid_dim: (nrow as u32, 1, 1),
1424 block_dim: (1024, 1, 1),
1425 shared_mem_bytes: 0,
1426 };
1427 let __s_b = self.gpu.stream();
1428 let mut b = __s_b.launch_builder(&f);
1429 b.arg(x)
1430 .arg(&rs)
1431 .arg(rows)
1432 .arg(&mut *out_th)
1433 .arg(&mut *out_z)
1434 .arg(&mut *out_max)
1435 .arg(&ni)
1436 .arg(&nr)
1437 .arg(&temp)
1438 .arg(&top_k)
1439 .arg(&top_p)
1440 .arg(&min_p);
1441 unsafe {
1442 b.launch(cfg)?;
1443 }
1444 Ok(())
1445 }
1446
1447 #[allow(clippy::too_many_arguments)]
1449 pub fn softmax_gather_filtered(
1450 &self,
1451 x: &CudaSlice<f32>,
1452 row_stride: usize,
1453 ids: &CudaSlice<u32>,
1454 rows: &CudaSlice<i32>,
1455 th: &CudaSlice<f32>,
1456 z: &CudaSlice<f32>,
1457 out: &mut CudaSlice<f32>,
1458 n: usize,
1459 npair: usize,
1460 temp: f32,
1461 ) -> Result<(), Box<dyn std::error::Error>> {
1462 let f = self.func("softmax_gather_filtered_f32");
1463 let (ni, np, rs) = (n as i32, npair as i32, row_stride as i64);
1464 let cfg = LaunchConfig {
1465 grid_dim: (npair as u32, 1, 1),
1466 block_dim: (256, 1, 1),
1467 shared_mem_bytes: 0,
1468 };
1469 let __s_b = self.gpu.stream();
1470 let mut b = __s_b.launch_builder(&f);
1471 b.arg(x)
1472 .arg(&rs)
1473 .arg(ids)
1474 .arg(rows)
1475 .arg(th)
1476 .arg(z)
1477 .arg(&mut *out)
1478 .arg(&ni)
1479 .arg(&np)
1480 .arg(&temp);
1481 unsafe {
1482 b.launch(cfg)?;
1483 }
1484 Ok(())
1485 }
1486
1487 #[allow(clippy::too_many_arguments)]
1489 pub fn residual_sample_filtered(
1490 &self,
1491 p: &CudaSlice<f32>,
1492 q: Option<&CudaSlice<f32>>,
1493 n: usize,
1494 temp: f32,
1495 seed: u64,
1496 stream_pos: u32,
1497 p_stats: (f32, f32, f32),
1498 q_stats: (f32, f32, f32),
1499 out_tok: &mut CudaSlice<u32>,
1500 ) -> Result<(), Box<dyn std::error::Error>> {
1501 let f = self.func("residual_sample_filtered_f32");
1502 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
1503 let has_q: i32 = q.is_some() as i32;
1504 let qbuf = q.unwrap_or(p);
1505 let (pm, pth, pz) = p_stats;
1506 let (qm, qth, qz) = q_stats;
1507 let cfg = LaunchConfig {
1508 grid_dim: (1, 1, 1),
1509 block_dim: (1024, 1, 1),
1510 shared_mem_bytes: 0,
1511 };
1512 let __s_b = self.gpu.stream();
1513 let mut b = __s_b.launch_builder(&f);
1514 b.arg(p)
1515 .arg(qbuf)
1516 .arg(&has_q)
1517 .arg(&ni)
1518 .arg(&temp)
1519 .arg(&slo)
1520 .arg(&shi)
1521 .arg(&stream_pos)
1522 .arg(&pm)
1523 .arg(&pth)
1524 .arg(&pz)
1525 .arg(&qm)
1526 .arg(&qth)
1527 .arg(&qz)
1528 .arg(&mut *out_tok);
1529 unsafe {
1530 b.launch(cfg)?;
1531 }
1532 Ok(())
1533 }
1534
1535 #[allow(clippy::too_many_arguments)]
1537 pub fn gumbel_perturb_filtered(
1538 &self,
1539 x: &CudaSlice<f32>,
1540 y: &mut CudaSlice<f32>,
1541 n: usize,
1542 seed: u64,
1543 stream_pos: u32,
1544 temp: f32,
1545 row_max: f32,
1546 th: f32,
1547 ) -> Result<(), Box<dyn std::error::Error>> {
1548 let f = self.func("gumbel_perturb_filtered_f32");
1549 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
1550 let cfg = LaunchConfig {
1551 grid_dim: (n.div_ceil(256) as u32, 1, 1),
1552 block_dim: (256, 1, 1),
1553 shared_mem_bytes: 0,
1554 };
1555 let __s_b = self.gpu.stream();
1556 let mut b = __s_b.launch_builder(&f);
1557 b.arg(x)
1558 .arg(&mut *y)
1559 .arg(&ni)
1560 .arg(&slo)
1561 .arg(&shi)
1562 .arg(&stream_pos)
1563 .arg(&temp)
1564 .arg(&row_max)
1565 .arg(&th);
1566 unsafe {
1567 b.launch(cfg)?;
1568 }
1569 Ok(())
1570 }
1571
1572 #[allow(clippy::too_many_arguments)]
1576 pub fn penalize_logits(
1577 &self,
1578 x: &mut CudaSlice<f32>,
1579 hist: &CudaSlice<u32>,
1580 n_hist: usize,
1581 rep: f32,
1582 freq: f32,
1583 present: f32,
1584 n: usize,
1585 ) -> Result<(), Box<dyn std::error::Error>> {
1586 if n_hist == 0 {
1587 return Ok(());
1588 }
1589 let f = self.func("penalize_logits_f32");
1590 let (nh, ni) = (n_hist as i32, n as i32);
1591 let cfg = LaunchConfig {
1592 grid_dim: (n_hist.div_ceil(128) as u32, 1, 1),
1593 block_dim: (128, 1, 1),
1594 shared_mem_bytes: 0,
1595 };
1596 let __s_b = self.gpu.stream();
1597 let mut b = __s_b.launch_builder(&f);
1598 b.arg(&mut *x)
1599 .arg(hist)
1600 .arg(&nh)
1601 .arg(&rep)
1602 .arg(&freq)
1603 .arg(&present)
1604 .arg(&ni);
1605 unsafe {
1606 b.launch(cfg)?;
1607 }
1608 Ok(())
1609 }
1610
1611 #[allow(clippy::too_many_arguments)]
1613 pub fn penalize_logits_rows(
1614 &self,
1615 x: &mut CudaSlice<f32>,
1616 hist: &CudaSlice<u32>,
1617 n_hist: usize,
1618 rep: f32,
1619 freq: f32,
1620 present: f32,
1621 n: usize,
1622 nrow: usize,
1623 ) -> Result<(), Box<dyn std::error::Error>> {
1624 if n_hist == 0 || nrow == 0 {
1625 return Ok(());
1626 }
1627 let f = self.func("penalize_logits_rows_f32");
1628 let (nh, ni, nr) = (n_hist as i32, n as i32, nrow as i32);
1629 let cfg = LaunchConfig {
1630 grid_dim: (n_hist.div_ceil(128) as u32, nrow as u32, 1),
1631 block_dim: (128, 1, 1),
1632 shared_mem_bytes: 0,
1633 };
1634 let __s_b = self.gpu.stream();
1635 let mut b = __s_b.launch_builder(&f);
1636 b.arg(&mut *x)
1637 .arg(hist)
1638 .arg(&nh)
1639 .arg(&rep)
1640 .arg(&freq)
1641 .arg(&present)
1642 .arg(&ni)
1643 .arg(&nr);
1644 unsafe {
1645 b.launch(cfg)?;
1646 }
1647 Ok(())
1648 }
1649
1650 pub fn wpf_level() -> u32 {
1658 static ON: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
1659 *ON.get_or_init(|| {
1660 std::env::var("MEMRA_WPF")
1661 .ok()
1662 .and_then(|v| v.parse().ok())
1663 .unwrap_or(1)
1664 })
1665 }
1666
1667 pub fn set_verify_exact(&self, on: bool) {
1679 self.verify_exact
1680 .store(on, std::sync::atomic::Ordering::Relaxed);
1681 }
1682 pub(crate) fn verify_exact_on(&self) -> bool {
1683 self.verify_exact.load(std::sync::atomic::Ordering::Relaxed)
1684 }
1685
1686 pub fn qkv_append_on() -> bool {
1689 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1690 *ON.get_or_init(|| {
1691 std::env::var("MEMRA_QKV_APPEND")
1692 .map(|v| v != "0")
1693 .unwrap_or(true)
1694 })
1695 }
1696
1697 pub fn pdl_wb_on() -> bool {
1700 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1701 *ON.get_or_init(|| {
1702 std::env::var("MEMRA_PDL_WB")
1703 .map(|v| v != "0")
1704 .unwrap_or(true)
1705 })
1706 }
1707
1708 pub fn pdl_mmvq_on() -> bool {
1712 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1713 *ON.get_or_init(|| {
1714 std::env::var("MEMRA_PDL_MMVQ")
1715 .map(|v| v != "0")
1716 .unwrap_or(true)
1717 })
1718 }
1719
1720 pub fn pdl_on() -> bool {
1721 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1722 *ON.get_or_init(|| std::env::var("MEMRA_PDL").map(|v| v != "0").unwrap_or(true))
1723 }
1724
1725 pub fn pdl_nvfp4q8_on() -> bool {
1731 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1732 *ON.get_or_init(|| {
1733 std::env::var("MEMRA_PDL_NVFP4")
1734 .map(|v| v != "0")
1735 .unwrap_or(true)
1736 })
1737 }
1738
1739 fn q40_mr1_on() -> bool {
1745 static Q40MR: std::sync::OnceLock<Option<u32>> = std::sync::OnceLock::new();
1746 match *Q40MR.get_or_init(|| {
1747 std::env::var("MEMRA_Q40_MR")
1748 .ok()
1749 .and_then(|v| v.parse().ok())
1750 }) {
1751 Some(v) => v == 1,
1752 None => crate::FUSED_MR1_DEFAULT.load(std::sync::atomic::Ordering::Relaxed),
1753 }
1754 }
1755
1756 fn pdl_func_flash(
1761 &self,
1762 g: bool,
1763 name: &'static str,
1764 ) -> Result<cudarc::driver::sys::CUfunction, Box<dyn std::error::Error>> {
1765 use cudarc::driver::sys as cu;
1766 static MODS: std::sync::Mutex<Option<std::collections::HashMap<(usize, bool), usize>>> =
1773 std::sync::Mutex::new(None);
1774 static FNS: std::sync::Mutex<
1775 Option<std::collections::HashMap<(usize, bool, &'static str), usize>>,
1776 > = std::sync::Mutex::new(None);
1777 let ctx_key = self.ctx().cu_ctx() as usize;
1778 if let Some(&f) = FNS
1779 .lock()
1780 .unwrap()
1781 .get_or_insert_with(Default::default)
1782 .get(&(ctx_key, g, name))
1783 {
1784 return Ok(f as cu::CUfunction);
1785 }
1786 let module = {
1787 let mut mods = MODS.lock().unwrap();
1788 let map = mods.get_or_insert_with(Default::default);
1789 match map.get(&(ctx_key, g)) {
1790 Some(&m) => m,
1791 None => {
1792 let m = self.pdl_load_module_in_ctx(if g {
1793 FLASH_FATBIN_KF8VF8
1794 } else {
1795 FLASH_FATBIN
1796 })?;
1797 map.insert((ctx_key, g), m);
1798 m
1799 }
1800 }
1801 };
1802 let cname = std::ffi::CString::new(name)?;
1803 let mut f: cu::CUfunction = std::ptr::null_mut();
1804 let r = unsafe { cu::cuModuleGetFunction(&mut f, module as cu::CUmodule, cname.as_ptr()) };
1805 if r != cu::CUresult::CUDA_SUCCESS {
1806 return Err(format!("pdl_func_flash {name} (g={g}): {r:?}").into());
1807 }
1808 FNS.lock()
1809 .unwrap()
1810 .get_or_insert_with(Default::default)
1811 .insert((ctx_key, g, name), f as usize);
1812 Ok(f)
1813 }
1814
1815 fn pdl_load_module_in_ctx(&self, bytes: &[u8]) -> Result<usize, Box<dyn std::error::Error>> {
1820 use cudarc::driver::sys as cu;
1821 let mut prev: cu::CUcontext = std::ptr::null_mut();
1822 unsafe {
1823 cu::cuCtxGetCurrent(&mut prev).result()?;
1824 }
1825 self.ctx().bind_to_thread()?;
1826 let mut m: cu::CUmodule = std::ptr::null_mut();
1827 let r = unsafe { cu::cuModuleLoadData(&mut m, bytes.as_ptr() as *const std::ffi::c_void) };
1828 let restore = if prev.is_null() {
1829 cu::CUresult::CUDA_SUCCESS
1830 } else {
1831 unsafe { cu::cuCtxSetCurrent(prev) }
1832 };
1833 if r != cu::CUresult::CUDA_SUCCESS {
1834 return Err(format!("pdl module load: {r:?}").into());
1835 }
1836 if restore != cu::CUresult::CUDA_SUCCESS {
1837 return Err(format!("pdl module load: ctx restore {restore:?}").into());
1838 }
1839 Ok(m as usize)
1840 }
1841
1842 fn pdl_func(
1843 &self,
1844 name: &'static str,
1845 ) -> Result<cudarc::driver::sys::CUfunction, Box<dyn std::error::Error>> {
1846 use cudarc::driver::sys as cu;
1847 static MODULES: std::sync::Mutex<Option<std::collections::HashMap<usize, usize>>> =
1850 std::sync::Mutex::new(None);
1851 static QMODULES: std::sync::Mutex<Option<std::collections::HashMap<usize, usize>>> =
1854 std::sync::Mutex::new(None);
1855 static FNS: std::sync::Mutex<
1856 Option<std::collections::HashMap<(usize, &'static str), usize>>,
1857 > = std::sync::Mutex::new(None);
1858 let ctx_key = self.ctx().cu_ctx() as usize;
1859 if let Some(&f) = FNS
1860 .lock()
1861 .unwrap()
1862 .get_or_insert_with(Default::default)
1863 .get(&(ctx_key, name))
1864 {
1865 return Ok(f as cu::CUfunction);
1866 }
1867 let module = {
1868 let mut mods = MODULES.lock().unwrap();
1869 let map = mods.get_or_insert_with(Default::default);
1870 match map.get(&ctx_key) {
1871 Some(&m) => m,
1872 None => {
1873 let m = self.pdl_load_module_in_ctx(FATBIN)?;
1874 map.insert(ctx_key, m);
1875 m
1876 }
1877 }
1878 };
1879 let cname = std::ffi::CString::new(name)?;
1880 let mut f: cu::CUfunction = std::ptr::null_mut();
1881 let mut r =
1882 unsafe { cu::cuModuleGetFunction(&mut f, module as cu::CUmodule, cname.as_ptr()) };
1883 if r == cu::CUresult::CUDA_ERROR_NOT_FOUND {
1884 let qmodule = {
1885 let mut mods = QMODULES.lock().unwrap();
1886 let map = mods.get_or_insert_with(Default::default);
1887 match map.get(&ctx_key) {
1888 Some(&m) => m,
1889 None => {
1890 let m = self.pdl_load_module_in_ctx(QMATVEC_FATBIN)?;
1891 map.insert(ctx_key, m);
1892 m
1893 }
1894 }
1895 };
1896 r = unsafe { cu::cuModuleGetFunction(&mut f, qmodule as cu::CUmodule, cname.as_ptr()) };
1897 }
1898 if r != cu::CUresult::CUDA_SUCCESS {
1899 return Err(format!("pdl_func {name}: {r:?}").into());
1900 }
1901 FNS.lock()
1902 .unwrap()
1903 .get_or_insert_with(Default::default)
1904 .insert((ctx_key, name), f as usize);
1905 Ok(f)
1906 }
1907
1908 unsafe fn launch_pdl_flash(
1920 &self,
1921 g: bool,
1922 name: &'static str,
1923 grid: (u32, u32, u32),
1924 block: (u32, u32, u32),
1925 smem: u32,
1926 params: &mut [*mut std::ffi::c_void],
1927 ) -> Result<(), Box<dyn std::error::Error>> {
1928 use cudarc::driver::sys as cu;
1929 let f = self.pdl_func_flash(g, name)?;
1930 if smem > 0 {
1931 let r =
1933 unsafe {
1934 cu::cuFuncSetAttribute(f,
1935 cu::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
1936 smem as i32)
1937 };
1938 if r != cu::CUresult::CUDA_SUCCESS {
1939 return Err(format!("pdl smem attr {name}: {r:?}").into());
1940 }
1941 }
1942 let mut attr = cu::CUlaunchAttribute {
1943 id: cu::CUlaunchAttributeID::CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION,
1944 pad: [0; 4],
1945 value: cu::CUlaunchAttributeValue {
1946 programmaticStreamSerializationAllowed: 1,
1947 },
1948 };
1949 let cfg = cu::CUlaunchConfig {
1950 gridDimX: grid.0,
1951 gridDimY: grid.1,
1952 gridDimZ: grid.2,
1953 blockDimX: block.0,
1954 blockDimY: block.1,
1955 blockDimZ: block.2,
1956 sharedMemBytes: smem,
1957 hStream: self.gpu.stream().cu_stream(),
1958 attrs: &mut attr,
1959 numAttrs: 1,
1960 };
1961 let r = unsafe { cu::cuLaunchKernelEx(&cfg, f, params.as_mut_ptr(), std::ptr::null_mut()) };
1962 if r != cu::CUresult::CUDA_SUCCESS {
1963 return Err(format!("launch_pdl_flash {name}: {r:?}").into());
1964 }
1965 Ok(())
1966 }
1967
1968 unsafe fn launch_pdl(
1969 &self,
1970 name: &'static str,
1971 grid: (u32, u32, u32),
1972 block: (u32, u32, u32),
1973 params: &mut [*mut std::ffi::c_void],
1974 ) -> Result<(), Box<dyn std::error::Error>> {
1975 use cudarc::driver::sys as cu;
1976 let f = self.pdl_func(name)?;
1977 let mut attr = cu::CUlaunchAttribute {
1978 id: cu::CUlaunchAttributeID::CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION,
1979 pad: [0; 4],
1980 value: cu::CUlaunchAttributeValue {
1981 programmaticStreamSerializationAllowed: 1,
1982 },
1983 };
1984 let cfg = cu::CUlaunchConfig {
1985 gridDimX: grid.0,
1986 gridDimY: grid.1,
1987 gridDimZ: grid.2,
1988 blockDimX: block.0,
1989 blockDimY: block.1,
1990 blockDimZ: block.2,
1991 sharedMemBytes: 0,
1992 hStream: self.gpu.stream().cu_stream(),
1993 attrs: &mut attr,
1994 numAttrs: 1,
1995 };
1996 let r = unsafe { cu::cuLaunchKernelEx(&cfg, f, params.as_mut_ptr(), std::ptr::null_mut()) };
1997 if r != cu::CUresult::CUDA_SUCCESS {
1998 return Err(format!("launch_pdl {name}: {r:?}").into());
1999 }
2000 Ok(())
2001 }
2002
2003 pub fn prefetch_weight_l2(
2006 &self,
2007 w: &crate::model::GpuTensor,
2008 ) -> Result<(), Box<dyn std::error::Error>> {
2009 if let crate::model::GpuTensor::Quant { bytes, rp4, .. } = w {
2010 let p = rp4.as_ref().unwrap_or(bytes);
2011 self.prefetch_l2(p, p.len())?;
2012 }
2013 Ok(())
2014 }
2015
2016 pub fn gather_row_bf16(
2019 &self,
2020 table: &CudaSlice<u8>,
2021 tok: &CudaSlice<u32>,
2022 idx: usize,
2023 dst: &mut CudaSlice<f32>,
2024 ncols: usize,
2025 ) -> Result<(), Box<dyn std::error::Error>> {
2026 let f = self.func("gather_row_bf16_f32");
2027 let cfg = LaunchConfig {
2028 grid_dim: (ncols.div_ceil(256) as u32, 1, 1),
2029 block_dim: (256, 1, 1),
2030 shared_mem_bytes: 0,
2031 };
2032 let (nc, ix) = (ncols as i32, idx as i32);
2033 let __s_b = self.gpu.stream();
2034 let mut b = __s_b.launch_builder(&f);
2035 b.arg(table).arg(tok).arg(&ix).arg(dst).arg(&nc);
2036 unsafe {
2037 b.launch(cfg)?;
2038 }
2039 Ok(())
2040 }
2041
2042 pub fn add_row_inplace(
2044 &self,
2045 logits: &mut CudaSlice<f32>,
2046 bias: &CudaSlice<f32>,
2047 n: usize,
2048 row_off: usize,
2049 ) -> Result<(), Box<dyn std::error::Error>> {
2050 let f = self.func("add_row_inplace_f32");
2051 let cfg = LaunchConfig {
2052 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2053 block_dim: (256, 1, 1),
2054 shared_mem_bytes: 0,
2055 };
2056 let (ni, off) = (n as i32, row_off as i64);
2057 let __s_b = self.gpu.stream();
2058 let mut b = __s_b.launch_builder(&f);
2059 b.arg(logits).arg(bias).arg(&ni).arg(&off);
2060 unsafe {
2061 b.launch(cfg)?;
2062 }
2063 Ok(())
2064 }
2065
2066 pub fn prefetch_l2(
2068 &self,
2069 p: &CudaSlice<u8>,
2070 n: usize,
2071 ) -> Result<(), Box<dyn std::error::Error>> {
2072 let f = self.func("prefetch_l2_bytes");
2073 let lines = n.div_ceil(128);
2074 let ni = n as i64;
2075 let cfg = LaunchConfig {
2076 grid_dim: (lines.div_ceil(256) as u32, 1, 1),
2077 block_dim: (256, 1, 1),
2078 shared_mem_bytes: 0,
2079 };
2080 let __s_b = self.gpu.stream();
2081 let mut b = __s_b.launch_builder(&f);
2082 b.arg(p).arg(&ni);
2083 unsafe {
2084 b.launch(cfg)?;
2085 }
2086 Ok(())
2087 }
2088
2089 pub fn router_gemv(
2092 &self,
2093 w: &CudaSlice<f32>,
2094 x: &CudaSlice<f32>,
2095 n_embd: usize,
2096 n_experts: usize,
2097 t: usize,
2098 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2099 let w8 = match std::env::var("MEMRA_ROUTER_V2").as_deref() {
2105 Ok("0") => false,
2106 Ok(_) => true,
2107 Err(_) => ROUTER_W8_DEFAULT.load(std::sync::atomic::Ordering::Relaxed),
2108 };
2109 let batch = w8 && t >= ROUTER_BATCH_MIN_T && router_batch_on();
2119 self.router_gemv_form(w, x, n_embd, n_experts, t, w8, batch)
2120 }
2121
2122 pub fn router_gemv_form(
2125 &self,
2126 w: &CudaSlice<f32>,
2127 x: &CudaSlice<f32>,
2128 n_embd: usize,
2129 n_experts: usize,
2130 t: usize,
2131 w8: bool,
2132 batch: bool,
2133 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2134 debug_assert!(!batch || w8, "batch twin exists for the w8 form only");
2135 let mut y = self.alloc_uninit::<f32>(t * n_experts)?;
2136 let f = if batch {
2137 self.func("router_gemv_f32_w8_batch")
2138 } else if w8 {
2139 self.func("router_gemv_f32_w8")
2140 } else {
2141 self.func("router_gemv_f32")
2142 };
2143 let (ne, nx, ti) = (n_embd as i32, n_experts as i32, t as i32);
2144 let cfg = if batch {
2145 LaunchConfig {
2146 grid_dim: (n_experts.div_ceil(8) as u32, t.div_ceil(8) as u32, 1),
2147 block_dim: (32, 8, 1),
2148 shared_mem_bytes: 0,
2149 }
2150 } else {
2151 LaunchConfig {
2152 grid_dim: (n_experts as u32, t as u32, 1),
2153 block_dim: (32, if w8 { 8 } else { 1 }, 1),
2154 shared_mem_bytes: 0,
2155 }
2156 };
2157 let __s_b = self.gpu.stream();
2158 let mut b = __s_b.launch_builder(&f);
2159 b.arg(w).arg(x).arg(&mut y).arg(&ne).arg(&nx).arg(&ti);
2160 unsafe {
2161 b.launch(cfg)?;
2162 }
2163 Ok(y)
2164 }
2165
2166 pub fn rows_permute(
2168 &self,
2169 src: &CudaSlice<f32>,
2170 idx: &CudaSlice<i32>,
2171 nrows: usize,
2172 ncols: usize,
2173 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2174 let mut dst = self.alloc_uninit::<f32>(nrows * ncols)?;
2175 let f = self.func("rows_permute_f32");
2176 let (nc, nr) = (ncols as i32, nrows as i32);
2177 let cfg = LaunchConfig {
2178 grid_dim: (nrows as u32, 1, 1),
2179 block_dim: (256, 1, 1),
2180 shared_mem_bytes: 0,
2181 };
2182 let __s_b = self.gpu.stream();
2183 let mut b = __s_b.launch_builder(&f);
2184 b.arg(src).arg(idx).arg(&mut dst).arg(&nc).arg(&nr);
2185 unsafe {
2186 b.launch(cfg)?;
2187 }
2188 Ok(dst)
2189 }
2190
2191 pub fn sigmoid_dot_rows(
2196 &self,
2197 x: &CudaSlice<f32>,
2198 w: &CudaSlice<f32>,
2199 n_embd: usize,
2200 t: usize,
2201 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2202 static OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2205 if *OFF.get_or_init(|| std::env::var("MEMRA_SHEXP_DOT").as_deref() == Ok("0")) {
2206 let gs = self.linear(x, w, t, n_embd, 1)?;
2207 let mut g = self.uninit(t)?;
2208 self.sigmoid(&gs, &mut g, t)?;
2209 return Ok(g);
2210 }
2211 let mut g = self.alloc_uninit::<f32>(t)?;
2217 let f = self.func("sigmoid_dot_rows_f32");
2218 let (ne, ti) = (n_embd as i32, t as i32);
2219 let cfg = LaunchConfig {
2220 grid_dim: (t as u32, 1, 1),
2221 block_dim: (32, 8, 1),
2222 shared_mem_bytes: 0,
2223 };
2224 let __s_b = self.gpu.stream();
2225 let mut b = __s_b.launch_builder(&f);
2226 b.arg(x).arg(w).arg(&mut g).arg(&ne).arg(&ti);
2227 unsafe {
2228 b.launch(cfg)?;
2229 }
2230 Ok(g)
2231 }
2232
2233 pub fn spec_rollback_stream(
2235 &self,
2236 len_ptrs: &CudaSlice<u64>,
2237 pos_start: &CudaSlice<i32>,
2238 acc: &CudaSlice<u32>,
2239 base: usize,
2240 n_rows: usize,
2241 ) -> Result<(), Box<dyn std::error::Error>> {
2242 let f = self.func("spec_rollback_stream");
2243 let (b, nr) = (base as i32, n_rows as i32);
2244 let cfg = LaunchConfig {
2245 grid_dim: (n_rows.div_ceil(64) as u32, 1, 1),
2246 block_dim: (64, 1, 1),
2247 shared_mem_bytes: 0,
2248 };
2249 let __s_bl = self.gpu.stream();
2250 let mut bl = __s_bl.launch_builder(&f);
2251 bl.arg(len_ptrs).arg(pos_start).arg(acc).arg(&b).arg(&nr);
2252 unsafe {
2253 bl.launch(cfg)?;
2254 }
2255 Ok(())
2256 }
2257
2258 pub fn plain_tok_ring(
2260 &self,
2261 vam: &CudaSlice<u32>,
2262 pos_start: &CudaSlice<i32>,
2263 base: usize,
2264 ring: &mut CudaSlice<u32>,
2265 ) -> Result<(), Box<dyn std::error::Error>> {
2266 let f = self.func("plain_tok_ring");
2267 let (b, cap) = (base as i32, ring.len() as i32);
2268 let cfg = LaunchConfig {
2269 grid_dim: (1, 1, 1),
2270 block_dim: (32, 1, 1),
2271 shared_mem_bytes: 0,
2272 };
2273 let __s_bl = self.gpu.stream();
2274 let mut bl = __s_bl.launch_builder(&f);
2275 bl.arg(vam).arg(pos_start).arg(&b).arg(&mut *ring).arg(&cap);
2276 unsafe {
2277 bl.launch(cfg)?;
2278 }
2279 Ok(())
2280 }
2281
2282 pub fn spec_ring_commit(
2284 &self,
2285 vtok: &CudaSlice<u32>,
2286 acc: &CudaSlice<u32>,
2287 brk: &CudaSlice<u32>,
2288 ring: &mut CudaSlice<u32>,
2289 pend: &mut CudaSlice<u32>,
2290 ) -> Result<(), Box<dyn std::error::Error>> {
2291 let f = self.func("spec_ring_commit");
2292 let cfg = LaunchConfig {
2293 grid_dim: (1, 1, 1),
2294 block_dim: (32, 1, 1),
2295 shared_mem_bytes: 0,
2296 };
2297 let __s_b = self.gpu.stream();
2298 let mut b = __s_b.launch_builder(&f);
2299 b.arg(vtok).arg(acc).arg(brk).arg(ring).arg(pend);
2300 unsafe {
2301 b.launch(cfg)?;
2302 }
2303 Ok(())
2304 }
2305 pub fn i32_copy_add(
2306 &self,
2307 src: &CudaSlice<i32>,
2308 dst: &mut CudaSlice<i32>,
2309 delta: i32,
2310 ) -> Result<(), Box<dyn std::error::Error>> {
2311 let f = self.func("i32_copy_add");
2312 let cfg = LaunchConfig {
2313 grid_dim: (1, 1, 1),
2314 block_dim: (32, 1, 1),
2315 shared_mem_bytes: 0,
2316 };
2317 let __s_b = self.gpu.stream();
2318 let mut b = __s_b.launch_builder(&f);
2319 b.arg(src).arg(dst).arg(&delta);
2320 unsafe {
2321 b.launch(cfg)?;
2322 }
2323 Ok(())
2324 }
2325 pub fn u32_copy(
2326 &self,
2327 src: &CudaSlice<u32>,
2328 dst: &mut CudaSlice<u32>,
2329 ) -> Result<(), Box<dyn std::error::Error>> {
2330 let f = self.func("u32_copy");
2331 let cfg = LaunchConfig {
2332 grid_dim: (1, 1, 1),
2333 block_dim: (32, 1, 1),
2334 shared_mem_bytes: 0,
2335 };
2336 let __s_b = self.gpu.stream();
2337 let mut b = __s_b.launch_builder(&f);
2338 b.arg(src).arg(dst);
2339 unsafe {
2340 b.launch(cfg)?;
2341 }
2342 Ok(())
2343 }
2344
2345 pub fn spec_adapt_k(
2349 &self,
2350 acc: &CudaSlice<u32>,
2351 brk: &mut CudaSlice<u32>,
2352 floor: usize,
2353 cap: usize,
2354 ) -> Result<(), Box<dyn std::error::Error>> {
2355 let f = self.func("spec_adapt_k");
2356 let (fl, cp) = (floor as i32, cap as i32);
2357 let cfg = LaunchConfig {
2358 grid_dim: (1, 1, 1),
2359 block_dim: (32, 1, 1),
2360 shared_mem_bytes: 0,
2361 };
2362 let __s_b = self.gpu.stream();
2363 let mut b = __s_b.launch_builder(&f);
2364 b.arg(acc).arg(brk).arg(&fl).arg(&cp);
2365 unsafe {
2366 b.launch(cfg)?;
2367 }
2368 Ok(())
2369 }
2370
2371 pub fn spec_accept_greedy_dc(
2373 &self,
2374 preds: &CudaSlice<u32>,
2375 vtok: &CudaSlice<u32>,
2376 last_pred: &CudaSlice<u32>,
2377 brk: &CudaSlice<u32>,
2378 out: &mut CudaSlice<u32>,
2379 ) -> Result<(), Box<dyn std::error::Error>> {
2380 let f = self.func("spec_accept_greedy_dc");
2381 let cfg = LaunchConfig {
2382 grid_dim: (1, 1, 1),
2383 block_dim: (32, 1, 1),
2384 shared_mem_bytes: 0,
2385 };
2386 let __s_b = self.gpu.stream();
2387 let mut b = __s_b.launch_builder(&f);
2388 b.arg(preds).arg(vtok).arg(last_pred).arg(brk).arg(out);
2389 unsafe {
2390 b.launch(cfg)?;
2391 }
2392 Ok(())
2393 }
2394
2395 pub fn pos_iota(
2397 &self,
2398 pos0: &CudaSlice<i32>,
2399 out: &mut CudaSlice<i32>,
2400 t: usize,
2401 ) -> Result<(), Box<dyn std::error::Error>> {
2402 let f = self.func("pos_iota_i32");
2403 let ti = t as i32;
2404 let cfg = LaunchConfig {
2405 grid_dim: (1, 1, 1),
2406 block_dim: (t.max(1) as u32, 1, 1),
2407 shared_mem_bytes: 0,
2408 };
2409 let __s_b = self.gpu.stream();
2410 let mut b = __s_b.launch_builder(&f);
2411 b.arg(pos0).arg(out).arg(&ti);
2412 unsafe {
2413 b.launch(cfg)?;
2414 }
2415 Ok(())
2416 }
2417 #[allow(clippy::too_many_arguments)]
2418 pub fn append_kv_quantized_rows_dc(
2419 &self,
2420 k_rows: &CudaSlice<f32>,
2421 v_rows: &CudaSlice<f32>,
2422 kc: &mut CudaSlice<u8>,
2423 vc: &mut CudaSlice<u8>,
2424 t0_dev: &CudaSlice<i32>,
2425 t: usize,
2426 kv_dim_k: usize,
2427 kv_dim_v: usize,
2428 k_tok_bytes: usize,
2429 v_tok_bytes: usize,
2430 g: bool,
2431 ) -> Result<(), Box<dyn std::error::Error>> {
2432 let f = if g {
2433 self.func_g("append_quantize_kv_q8_0_q5_1_rows_dc")
2434 } else {
2435 self.func("append_quantize_kv_q8_0_q5_1_rows_dc")
2436 };
2437 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
2438 let cfg = LaunchConfig {
2439 grid_dim: (nblk, t as u32, 1),
2440 block_dim: (32, 1, 1),
2441 shared_mem_bytes: 0,
2442 };
2443 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
2444 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
2445 let __s_b = self.gpu.stream();
2446 let mut b = __s_b.launch_builder(&f);
2447 b.arg(k_rows)
2448 .arg(v_rows)
2449 .arg(kc)
2450 .arg(vc)
2451 .arg(t0_dev)
2452 .arg(&kdk)
2453 .arg(&kdv)
2454 .arg(&ktb)
2455 .arg(&vtb);
2456 unsafe {
2457 b.launch(cfg)?;
2458 }
2459 Ok(())
2460 }
2461
2462 #[allow(clippy::too_many_arguments)]
2465 pub fn append_kv_quantized_row_dc_inc(
2466 &self,
2467 k_row: &CudaSlice<f32>,
2468 v_row: &CudaSlice<f32>,
2469 kc: &mut CudaSlice<u8>,
2470 vc: &mut CudaSlice<u8>,
2471 t0_dev: &mut CudaSlice<i32>,
2472 kv_dim_k: usize,
2473 kv_dim_v: usize,
2474 k_tok_bytes: usize,
2475 v_tok_bytes: usize,
2476 g: bool,
2477 ) -> Result<(), Box<dyn std::error::Error>> {
2478 let f = if g {
2479 self.func_g("append_quantize_kv_q8_0_q5_1_dc_inc")
2480 } else {
2481 self.func("append_quantize_kv_q8_0_q5_1_dc_inc")
2482 };
2483 let nthreads = ((kv_dim_k.max(kv_dim_v) / 32) * 32).min(1024) as u32;
2484 let cfg = LaunchConfig {
2485 grid_dim: (1, 1, 1),
2486 block_dim: (nthreads, 1, 1),
2487 shared_mem_bytes: 0,
2488 };
2489 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
2490 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
2491 let __s_b = self.gpu.stream();
2492 let mut b = __s_b.launch_builder(&f);
2493 b.arg(k_row)
2494 .arg(v_row)
2495 .arg(kc)
2496 .arg(vc)
2497 .arg(t0_dev)
2498 .arg(&kdk)
2499 .arg(&kdv)
2500 .arg(&ktb)
2501 .arg(&vtb);
2502 unsafe {
2503 b.launch(cfg)?;
2504 }
2505 Ok(())
2506 }
2507
2508 pub fn pack_tok_p(
2510 &self,
2511 tok: &CudaSlice<u32>,
2512 p: &CudaSlice<f32>,
2513 out: &mut CudaSlice<u32>,
2514 slot: usize,
2515 ) -> Result<(), Box<dyn std::error::Error>> {
2516 let f = self.func("pack_tok_p");
2517 let sl = slot as i32;
2518 let cfg = LaunchConfig {
2519 grid_dim: (1, 1, 1),
2520 block_dim: (32, 1, 1),
2521 shared_mem_bytes: 0,
2522 };
2523 let __s_b = self.gpu.stream();
2524 let mut b = __s_b.launch_builder(&f);
2525 b.arg(tok).arg(p).arg(out).arg(&sl);
2526 unsafe {
2527 b.launch(cfg)?;
2528 }
2529 Ok(())
2530 }
2531 pub fn tok_map_u32(
2532 &self,
2533 tok: &mut CudaSlice<u32>,
2534 map: &CudaSlice<u32>,
2535 ) -> Result<(), Box<dyn std::error::Error>> {
2536 let f = self.func("tok_map_u32");
2537 let cfg = LaunchConfig {
2538 grid_dim: (1, 1, 1),
2539 block_dim: (32, 1, 1),
2540 shared_mem_bytes: 0,
2541 };
2542 let __s_b = self.gpu.stream();
2543 let mut b = __s_b.launch_builder(&f);
2544 b.arg(tok).arg(map);
2545 unsafe {
2546 b.launch(cfg)?;
2547 }
2548 Ok(())
2549 }
2550
2551 #[allow(clippy::too_many_arguments)]
2553 pub fn spec_assemble_verify(
2554 &self,
2555 tokp: &CudaSlice<u32>,
2556 pend: &CudaSlice<u32>,
2557 d2t: Option<&CudaSlice<u32>>,
2558 vtok: &mut CudaSlice<u32>,
2559 brk: &mut CudaSlice<u32>,
2560 p_min: f32,
2561 k: usize,
2562 pmin0: bool,
2563 ) -> Result<(), Box<dyn std::error::Error>> {
2564 let f = self.func("spec_assemble_verify");
2565 let (ki, pm) = (k as i32, if pmin0 { 1i32 } else { 0i32 });
2566 let cfg = LaunchConfig {
2567 grid_dim: (1, 1, 1),
2568 block_dim: (32, 1, 1),
2569 shared_mem_bytes: 0,
2570 };
2571 let __s_b = self.gpu.stream();
2572 let mut b = __s_b.launch_builder(&f);
2573 match d2t {
2574 Some(m) => {
2575 b.arg(tokp)
2576 .arg(pend)
2577 .arg(m)
2578 .arg(vtok)
2579 .arg(brk)
2580 .arg(&p_min)
2581 .arg(&ki)
2582 .arg(&pm);
2583 unsafe {
2584 b.launch(cfg)?;
2585 }
2586 }
2587 None => {
2588 let null: u64 = 0;
2589 b.arg(tokp)
2590 .arg(pend)
2591 .arg(&null)
2592 .arg(vtok)
2593 .arg(brk)
2594 .arg(&p_min)
2595 .arg(&ki)
2596 .arg(&pm);
2597 unsafe {
2598 b.launch(cfg)?;
2599 }
2600 }
2601 }
2602 Ok(())
2603 }
2604
2605 #[allow(clippy::too_many_arguments)]
2607 pub fn ssm_conv_ring_rebuild_dc(
2608 &self,
2609 qkv_tm: &CudaSlice<f32>,
2610 ring_old: &CudaSlice<f32>,
2611 conv_state: &mut CudaSlice<f32>,
2612 conv_dim: usize,
2613 acc: &CudaSlice<u32>,
2614 base: usize,
2615 t_v: usize,
2616 d_conv: usize,
2617 ) -> Result<(), Box<dyn std::error::Error>> {
2618 let f = self.func("ssm_conv_ring_rebuild_f32_dc");
2619 let n = conv_dim * (d_conv - 1);
2620 let cfg = LaunchConfig::for_num_elems(n as u32);
2621 let (cd, b0, tv, dc) = (conv_dim as i32, base as i32, t_v as i32, d_conv as i32);
2622 let __s_b = self.gpu.stream();
2623 let mut b = __s_b.launch_builder(&f);
2624 b.arg(qkv_tm)
2625 .arg(ring_old)
2626 .arg(conv_state)
2627 .arg(&cd)
2628 .arg(acc)
2629 .arg(&b0)
2630 .arg(&tv)
2631 .arg(&dc);
2632 unsafe {
2633 b.launch(cfg)?;
2634 }
2635 Ok(())
2636 }
2637 #[allow(clippy::too_many_arguments)]
2638 pub fn gdn_scan_s128_dc(
2639 &self,
2640 q: &CudaSlice<f32>,
2641 k: &CudaSlice<f32>,
2642 v: &CudaSlice<f32>,
2643 g: &CudaSlice<f32>,
2644 beta: &CudaSlice<f32>,
2645 state_in: &CudaSlice<f32>,
2646 state_out: &mut CudaSlice<f32>,
2647 o: &mut CudaSlice<f32>,
2648 n_head: usize,
2649 acc: &CudaSlice<u32>,
2650 base: usize,
2651 t_v: usize,
2652 scale: f32,
2653 ) -> Result<(), Box<dyn std::error::Error>> {
2654 let f = self.func("gdn_scan_s128_dc");
2655 const S_V: u32 = 128;
2656 const WARP: u32 = 32;
2657 const COLS_PER_BLOCK: u32 = 4;
2658 let cfg = LaunchConfig {
2659 grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
2660 block_dim: (WARP, COLS_PER_BLOCK, 1),
2661 shared_mem_bytes: 0,
2662 };
2663 let (h, b0, tv) = (n_head as i32, base as i32, t_v as i32);
2664 let __s_b = self.gpu.stream();
2665 let mut b = __s_b.launch_builder(&f);
2666 b.arg(q)
2667 .arg(k)
2668 .arg(v)
2669 .arg(g)
2670 .arg(beta)
2671 .arg(state_in)
2672 .arg(state_out)
2673 .arg(o)
2674 .arg(&h)
2675 .arg(acc)
2676 .arg(&b0)
2677 .arg(&tv)
2678 .arg(&scale);
2679 unsafe {
2680 b.launch(cfg)?;
2681 }
2682 Ok(())
2683 }
2684
2685 pub fn spec_rollback_kv(
2687 &self,
2688 len_ptrs: &CudaSlice<u64>,
2689 saved: &CudaSlice<i32>,
2690 acc: &CudaSlice<u32>,
2691 base: usize,
2692 n_layer: usize,
2693 ) -> Result<(), Box<dyn std::error::Error>> {
2694 let f = self.func("spec_rollback_kv");
2695 let (b, nl) = (base as i32, n_layer as i32);
2696 let cfg = LaunchConfig {
2697 grid_dim: (n_layer.div_ceil(64) as u32, 1, 1),
2698 block_dim: (64, 1, 1),
2699 shared_mem_bytes: 0,
2700 };
2701 let __s_bl = self.gpu.stream();
2702 let mut bl = __s_bl.launch_builder(&f);
2703 bl.arg(len_ptrs).arg(saved).arg(acc).arg(&b).arg(&nl);
2704 unsafe {
2705 bl.launch(cfg)?;
2706 }
2707 Ok(())
2708 }
2709
2710 pub fn spec_fork_valid(
2712 &self,
2713 acc: &CudaSlice<u32>,
2714 optimistic_pending: u32,
2715 valid: &mut CudaSlice<u32>,
2716 ) -> Result<(), Box<dyn std::error::Error>> {
2717 let f = self.func("spec_fork_valid");
2718 let cfg = LaunchConfig {
2719 grid_dim: (1, 1, 1),
2720 block_dim: (1, 1, 1),
2721 shared_mem_bytes: 0,
2722 };
2723 let __s_bl = self.gpu.stream();
2724 let mut bl = __s_bl.launch_builder(&f);
2725 bl.arg(acc).arg(&optimistic_pending).arg(valid);
2726 unsafe {
2727 bl.launch(cfg)?;
2728 }
2729 Ok(())
2730 }
2731
2732 pub fn spec_fork_reconcile_kv(
2734 &self,
2735 len_ptrs: &CudaSlice<u64>,
2736 saved: &CudaSlice<i32>,
2737 acc: &CudaSlice<u32>,
2738 valid: &CudaSlice<u32>,
2739 base: usize,
2740 n_layer: usize,
2741 ) -> Result<(), Box<dyn std::error::Error>> {
2742 let f = self.func("spec_fork_reconcile_kv");
2743 let (b, nl) = (base as i32, n_layer as i32);
2744 let cfg = LaunchConfig {
2745 grid_dim: (n_layer.div_ceil(64) as u32, 1, 1),
2746 block_dim: (64, 1, 1),
2747 shared_mem_bytes: 0,
2748 };
2749 let __s_bl = self.gpu.stream();
2750 let mut bl = __s_bl.launch_builder(&f);
2751 bl.arg(len_ptrs)
2752 .arg(saved)
2753 .arg(acc)
2754 .arg(valid)
2755 .arg(&b)
2756 .arg(&nl);
2757 unsafe {
2758 bl.launch(cfg)?;
2759 }
2760 Ok(())
2761 }
2762
2763 pub fn spec_fork_restore_f32(
2765 &self,
2766 snapshot: &CudaSlice<f32>,
2767 state: &mut CudaSlice<f32>,
2768 valid: &CudaSlice<u32>,
2769 ) -> Result<(), Box<dyn std::error::Error>> {
2770 assert_eq!(
2771 snapshot.len(),
2772 state.len(),
2773 "fork recurrent snapshot shape mismatch"
2774 );
2775 let f = self.func("spec_fork_restore_f32");
2776 let n = state.len() as i32;
2777 let blocks = state.len().div_ceil(256).min(65535).max(1) as u32;
2778 let cfg = LaunchConfig {
2779 grid_dim: (blocks, 1, 1),
2780 block_dim: (256, 1, 1),
2781 shared_mem_bytes: 0,
2782 };
2783 let __s_bl = self.gpu.stream();
2784 let mut bl = __s_bl.launch_builder(&f);
2785 bl.arg(snapshot).arg(state).arg(valid).arg(&n);
2786 unsafe {
2787 bl.launch(cfg)?;
2788 }
2789 Ok(())
2790 }
2791
2792 pub fn spec_seed_gather(
2795 &self,
2796 vx: &CudaSlice<f32>,
2797 fill_prev: &CudaSlice<f32>,
2798 acc: &CudaSlice<u32>,
2799 h_seed: &mut CudaSlice<f32>,
2800 base: usize,
2801 n_embd: usize,
2802 ) -> Result<(), Box<dyn std::error::Error>> {
2803 let f = self.func("spec_seed_gather");
2804 let (b, ne) = (base as i32, n_embd as i32);
2805 let cfg = LaunchConfig {
2806 grid_dim: (n_embd.div_ceil(256) as u32, 1, 1),
2807 block_dim: (256, 1, 1),
2808 shared_mem_bytes: 0,
2809 };
2810 let __s_bl = self.gpu.stream();
2811 let mut bl = __s_bl.launch_builder(&f);
2812 bl.arg(vx)
2813 .arg(fill_prev)
2814 .arg(acc)
2815 .arg(h_seed)
2816 .arg(&b)
2817 .arg(&ne);
2818 unsafe {
2819 bl.launch(cfg)?;
2820 }
2821 Ok(())
2822 }
2823
2824 pub fn spec_accept_greedy(
2826 &self,
2827 preds: &CudaSlice<u32>,
2828 draft: &CudaSlice<u32>,
2829 last_pred: u32,
2830 base: usize,
2831 k_round: usize,
2832 out: &mut CudaSlice<u32>,
2833 ) -> Result<(), Box<dyn std::error::Error>> {
2834 let f = self.func("spec_accept_greedy");
2835 let (b, k) = (base as i32, k_round as i32);
2836 let cfg = LaunchConfig {
2837 grid_dim: (1, 1, 1),
2838 block_dim: (32, 1, 1),
2839 shared_mem_bytes: 0,
2840 };
2841 let __s_bl = self.gpu.stream();
2842 let mut bl = __s_bl.launch_builder(&f);
2843 bl.arg(preds)
2844 .arg(draft)
2845 .arg(&last_pred)
2846 .arg(&b)
2847 .arg(&k)
2848 .arg(out);
2849 unsafe {
2850 bl.launch(cfg)?;
2851 }
2852 Ok(())
2853 }
2854
2855 pub fn gumbel_perturb(
2862 &self,
2863 x: &CudaSlice<f32>,
2864 y: &mut CudaSlice<f32>,
2865 n: usize,
2866 seed: u64,
2867 stream_pos: u32,
2868 temp: f32,
2869 ) -> Result<(), Box<dyn std::error::Error>> {
2870 let f = self.func("gumbel_perturb_f32");
2871 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2872 let cfg = LaunchConfig {
2873 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2874 block_dim: (256, 1, 1),
2875 shared_mem_bytes: 0,
2876 };
2877 let __s_b = self.gpu.stream();
2878 let mut b = __s_b.launch_builder(&f);
2879 b.arg(x)
2880 .arg(&mut *y)
2881 .arg(&ni)
2882 .arg(&slo)
2883 .arg(&shi)
2884 .arg(&stream_pos)
2885 .arg(&temp);
2886 unsafe {
2887 b.launch(cfg)?;
2888 }
2889 Ok(())
2890 }
2891
2892 pub fn mask_logits_col(
2900 &self,
2901 logits: &mut CudaSlice<f32>,
2902 mask: &CudaSlice<u32>,
2903 col: usize,
2904 n: usize,
2905 mask_words: usize,
2906 ) -> Result<(), Box<dyn std::error::Error>> {
2907 let f = self.func("mask_logits_f32");
2908 let (ci, ni, mw) = (col as i32, n as i32, mask_words as i32);
2909 let cfg = LaunchConfig {
2910 grid_dim: (n.div_ceil(256).min(1024) as u32, 1, 1),
2911 block_dim: (256, 1, 1),
2912 shared_mem_bytes: 0,
2913 };
2914 let __s_b = self.gpu.stream();
2915 let mut b = __s_b.launch_builder(&f);
2916 b.arg(&mut *logits).arg(mask).arg(&ci).arg(&ni).arg(&mw);
2917 unsafe {
2918 b.launch(cfg)?;
2919 }
2920 Ok(())
2921 }
2922
2923 pub fn gumbel_perturb_col(
2930 &self,
2931 x: &CudaSlice<f32>,
2932 col: usize,
2933 y: &mut CudaSlice<f32>,
2934 n: usize,
2935 seed: u64,
2936 stream_pos: u32,
2937 temp: f32,
2938 ) -> Result<(), Box<dyn std::error::Error>> {
2939 let f = self.func("gumbel_perturb_f32");
2940 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2941 let col_view = x.slice(col * n..(col + 1) * n);
2942 let cfg = LaunchConfig {
2943 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2944 block_dim: (256, 1, 1),
2945 shared_mem_bytes: 0,
2946 };
2947 let __s_b = self.gpu.stream();
2948 let mut b = __s_b.launch_builder(&f);
2949 b.arg(&col_view)
2950 .arg(&mut *y)
2951 .arg(&ni)
2952 .arg(&slo)
2953 .arg(&shi)
2954 .arg(&stream_pos)
2955 .arg(&temp);
2956 unsafe {
2957 b.launch(cfg)?;
2958 }
2959 Ok(())
2960 }
2961
2962 #[allow(clippy::too_many_arguments)]
2968 pub fn gumbel_perturb_filtered_col(
2969 &self,
2970 x: &CudaSlice<f32>,
2971 col: usize,
2972 y: &mut CudaSlice<f32>,
2973 n: usize,
2974 seed: u64,
2975 stream_pos: u32,
2976 temp: f32,
2977 stat_max: &CudaSlice<f32>,
2978 stat_th: &CudaSlice<f32>,
2979 stat_idx: usize,
2980 ) -> Result<(), Box<dyn std::error::Error>> {
2981 let f = self.func("gumbel_perturb_filtered_col_f32");
2982 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
2983 let (ci, si) = (col as i32, stat_idx as i32);
2984 let cfg = LaunchConfig {
2985 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2986 block_dim: (256, 1, 1),
2987 shared_mem_bytes: 0,
2988 };
2989 let __s_b = self.gpu.stream();
2990 let mut b = __s_b.launch_builder(&f);
2991 b.arg(x)
2992 .arg(&ci)
2993 .arg(&mut *y)
2994 .arg(&ni)
2995 .arg(&slo)
2996 .arg(&shi)
2997 .arg(&stream_pos)
2998 .arg(&temp)
2999 .arg(stat_max)
3000 .arg(stat_th)
3001 .arg(&si);
3002 unsafe {
3003 b.launch(cfg)?;
3004 }
3005 Ok(())
3006 }
3007
3008 pub fn sctr_inc(&self, ctr: &mut CudaSlice<u32>) -> Result<(), Box<dyn std::error::Error>> {
3013 let f = self.func("memra_sctr_inc");
3014 let cfg = LaunchConfig {
3015 grid_dim: (1, 1, 1),
3016 block_dim: (1, 1, 1),
3017 shared_mem_bytes: 0,
3018 };
3019 let __s_b = self.gpu.stream();
3020 let mut b = __s_b.launch_builder(&f);
3021 b.arg(&mut *ctr);
3022 unsafe {
3023 b.launch(cfg)?;
3024 }
3025 Ok(())
3026 }
3027
3028 pub fn gumbel_perturb_ctr(
3033 &self,
3034 x: &CudaSlice<f32>,
3035 y: &mut CudaSlice<f32>,
3036 n: usize,
3037 seed: u64,
3038 ctr: &CudaSlice<u32>,
3039 temp: f32,
3040 ) -> Result<(), Box<dyn std::error::Error>> {
3041 let f = self.func("gumbel_perturb_ctr_f32");
3042 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3043 let cfg = LaunchConfig {
3044 grid_dim: (n.div_ceil(256) as u32, 1, 1),
3045 block_dim: (256, 1, 1),
3046 shared_mem_bytes: 0,
3047 };
3048 let __s_b = self.gpu.stream();
3049 let mut b = __s_b.launch_builder(&f);
3050 b.arg(x)
3051 .arg(&mut *y)
3052 .arg(&ni)
3053 .arg(&slo)
3054 .arg(&shi)
3055 .arg(ctr)
3056 .arg(&temp);
3057 unsafe {
3058 b.launch(cfg)?;
3059 }
3060 Ok(())
3061 }
3062
3063 pub fn softmax_gather(
3067 &self,
3068 x: &CudaSlice<f32>,
3069 row_stride: usize,
3070 ids: &CudaSlice<u32>,
3071 rows: &CudaSlice<i32>,
3072 out: &mut CudaSlice<f32>,
3073 n: usize,
3074 npair: usize,
3075 temp: f32,
3076 ) -> Result<(), Box<dyn std::error::Error>> {
3077 let f = self.func("softmax_gather_f32");
3078 let (ni, rs) = (n as i32, row_stride as i64);
3079 let np = npair as i32;
3080 let cfg = LaunchConfig {
3081 grid_dim: (npair as u32, 1, 1),
3082 block_dim: (256, 1, 1),
3083 shared_mem_bytes: 0,
3084 };
3085 let __s_b = self.gpu.stream();
3086 let mut b = __s_b.launch_builder(&f);
3087 b.arg(x)
3088 .arg(&rs)
3089 .arg(ids)
3090 .arg(rows)
3091 .arg(&mut *out)
3092 .arg(&ni)
3093 .arg(&np)
3094 .arg(&temp);
3095 unsafe {
3096 b.launch(cfg)?;
3097 }
3098 Ok(())
3099 }
3100
3101 pub fn residual_sample(
3105 &self,
3106 p: &CudaSlice<f32>,
3107 q: Option<&CudaSlice<f32>>,
3108 n: usize,
3109 temp: f32,
3110 seed: u64,
3111 stream_pos: u32,
3112 out_tok: &mut CudaSlice<u32>,
3113 ) -> Result<(), Box<dyn std::error::Error>> {
3114 let f = self.func("residual_sample_f32");
3115 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3116 let nth = 1024u32;
3117 let cfg = LaunchConfig {
3118 grid_dim: (1, 1, 1),
3119 block_dim: (nth, 1, 1),
3120 shared_mem_bytes: 0,
3121 };
3122 let has_q: i32 = q.is_some() as i32;
3123 let qbuf = q.unwrap_or(p); let __s_b = self.gpu.stream();
3125 let mut b = __s_b.launch_builder(&f);
3126 b.arg(p)
3127 .arg(qbuf)
3128 .arg(&has_q)
3129 .arg(&ni)
3130 .arg(&temp)
3131 .arg(&slo)
3132 .arg(&shi)
3133 .arg(&stream_pos)
3134 .arg(&mut *out_tok);
3135 unsafe {
3136 b.launch(cfg)?;
3137 }
3138 Ok(())
3139 }
3140
3141 pub fn with_moe_cache<R>(
3146 &self,
3147 max_block_bytes: usize,
3148 f: impl FnOnce(
3149 &mut crate::moe_cache::MoeSlotCache,
3150 &Engine,
3151 ) -> Result<R, Box<dyn std::error::Error>>,
3152 ) -> Result<R, Box<dyn std::error::Error>> {
3153 let mut guard = self.moe_cache.lock().unwrap();
3154 if guard.is_none() {
3155 *guard = Some(crate::moe_cache::MoeSlotCache::new(self, max_block_bytes)?);
3156 }
3157 let cache = guard.as_mut().unwrap();
3158 f(cache, self)
3159 }
3160
3161 pub fn freeze_moe_cache(&self) {
3164 if let Some(cache) = self.moe_cache.lock().unwrap().as_mut() {
3165 cache.freeze();
3166 }
3167 }
3168
3169 pub fn export_moe_residency(&self) -> Option<Vec<(u16, u8, u16)>> {
3172 self.moe_cache
3173 .lock()
3174 .unwrap()
3175 .as_ref()
3176 .map(crate::moe_cache::MoeSlotCache::export_residency)
3177 }
3178
3179 pub(crate) fn moe_cache_frozen(&self) -> bool {
3180 self.moe_cache
3181 .lock()
3182 .unwrap()
3183 .as_ref()
3184 .is_some_and(crate::moe_cache::MoeSlotCache::is_frozen)
3185 }
3186
3187 pub fn frozen_cpu_experts_prefer_tokenwise_prime(&self) -> bool {
3194 crate::cpu_experts::configured()
3195 && self.moe_cache_frozen()
3196 && std::env::var("MEMRA_CPU_EXPERT_BATCHED_PRIME").as_deref() != Ok("1")
3197 }
3198
3199 pub(crate) fn configure_moe_cache_layout(&self, block_bytes: Vec<usize>) {
3201 assert!(
3202 self.moe_cache.lock().unwrap().is_none(),
3203 "MoE cache layout configured after cache construction"
3204 );
3205 *self.moe_cache_layout.lock().unwrap() = Some(block_bytes);
3206 }
3207
3208 pub(crate) fn moe_cache_layout(&self) -> Option<Vec<usize>> {
3209 self.moe_cache_layout.lock().unwrap().clone()
3210 }
3211
3212 pub fn moe_cache_enabled() -> bool {
3214 std::env::var("MEMRA_MOE_CACHE").as_deref() != Ok("0")
3215 }
3216
3217 pub fn moe_cache_stats(&self) -> Option<(u64, u64, u64, usize)> {
3220 let guard = self.moe_cache.lock().unwrap();
3221 guard
3222 .as_ref()
3223 .map(|c| (c.hits, c.misses, c.staged_bytes, c.n_slots()))
3224 }
3225
3226 pub fn cpu_expert_stats(
3230 &self,
3231 ) -> Option<(u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64)> {
3232 crate::cpu_experts::configured().then(crate::cpu_experts::stats)
3233 }
3234
3235 pub fn cpu_expert_predictor_stats(&self) -> (u64, u64) {
3238 crate::cpu_experts::predictor_stats()
3239 }
3240
3241 pub fn cpu_expert_exposed_wait_ns(&self) -> Option<u64> {
3242 crate::cpu_experts::configured().then(crate::cpu_experts::exposed_wait_ns)
3243 }
3244
3245 pub fn cpu_expert_gpu_residency_stats(&self) -> Option<(u64, u64, u64)> {
3248 crate::cpu_experts::configured().then(crate::cpu_experts::incomplete_gpu_residency_stats)
3249 }
3250
3251 pub fn moe_pread_stats(&self) -> Option<(u64, u64, u64, u64, u64, u64, u64)> {
3254 let guard = self.moe_cache.lock().unwrap();
3255 guard
3256 .as_ref()
3257 .and_then(|cache| cache.pread_stats())
3258 .map(|stats| {
3259 (
3260 stats.reads,
3261 stats.bytes,
3262 stats.read_errors,
3263 stats.short_reads,
3264 stats.fallbacks,
3265 stats.buffer_waits,
3266 stats.ring_full,
3267 )
3268 })
3269 }
3270
3271 pub fn spill_config_fallbacks(&self) -> u64 {
3273 crate::spill_pread::config_fallbacks()
3274 }
3275
3276 pub fn moe_cache_reset_counters(&self) {
3278 if let Some(c) = self.moe_cache.lock().unwrap().as_mut() {
3279 c.reset_counters();
3280 }
3281 }
3282
3283 pub fn htod_bytes(&self, v: &[u8]) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3284 Ok(self.gpu.stream().clone_htod(v)?)
3285 }
3286
3287 pub fn htod_bytes_padded(
3291 &self,
3292 v: &[u8],
3293 pad: usize,
3294 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3295 let mut d = self.alloc_u8_uninit(v.len() + pad)?;
3296 {
3297 let mut view = d.slice_mut(0..v.len());
3298 self.gpu.stream().memcpy_htod(v, &mut view)?;
3299 }
3300 Ok(d)
3301 }
3302
3303 pub fn copy_into(
3305 &self,
3306 dst: &mut CudaSlice<f32>,
3307 off: usize,
3308 src: &CudaSlice<f32>,
3309 len: usize,
3310 ) -> Result<(), Box<dyn std::error::Error>> {
3311 let mut view = dst.slice_mut(off..off + len);
3312 self.gpu
3313 .stream()
3314 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3315 Ok(())
3316 }
3317
3318 pub fn copy_u8_into(
3321 &self,
3322 dst: &mut CudaSlice<u8>,
3323 off: usize,
3324 src: &CudaSlice<u8>,
3325 len: usize,
3326 ) -> Result<(), Box<dyn std::error::Error>> {
3327 let mut view = dst.slice_mut(off..off + len);
3328 self.gpu
3329 .stream()
3330 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3331 Ok(())
3332 }
3333
3334 pub fn copy_u8_range_into(
3336 &self,
3337 dst: &mut CudaSlice<u8>,
3338 dst_off: usize,
3339 src: &CudaSlice<u8>,
3340 src_off: usize,
3341 len: usize,
3342 ) -> Result<(), Box<dyn std::error::Error>> {
3343 let mut dst_view = dst.slice_mut(dst_off..dst_off + len);
3344 self.gpu
3345 .stream()
3346 .memcpy_dtod(&src.slice(src_off..src_off + len), &mut dst_view)?;
3347 Ok(())
3348 }
3349
3350 pub fn prepare_kv_append(
3354 &self,
3355 kv: &mut crate::cache::KvLayer,
3356 retain_from: usize,
3357 append_rows: usize,
3358 ) -> Result<usize, Box<dyn std::error::Error>> {
3359 let Some(plan) = kv
3360 .ring
3361 .as_ref()
3362 .map(|ring| ring.append_plan(kv.len, retain_from, append_rows))
3363 .transpose()?
3364 else {
3365 return Ok(kv.len);
3366 };
3367 match plan {
3368 crate::cache::KvRingAppend::Contiguous { write_row } => Ok(write_row),
3369 crate::cache::KvRingAppend::Rebase {
3370 src_row,
3371 keep_rows,
3372 new_base,
3373 write_row,
3374 } => {
3375 if keep_rows > 0 {
3376 let k_len = keep_rows * kv.k_tok_bytes;
3377 let v_len = keep_rows * kv.v_tok_bytes;
3378 let mut k_tmp = self.alloc_u8_uninit(k_len)?;
3379 let mut v_tmp = self.alloc_u8_uninit(v_len)?;
3380 self.copy_u8_range_into(&mut k_tmp, 0, &kv.k, src_row * kv.k_tok_bytes, k_len)?;
3381 self.copy_u8_range_into(&mut v_tmp, 0, &kv.v, src_row * kv.v_tok_bytes, v_len)?;
3382 self.copy_u8_into(&mut kv.k, 0, &k_tmp, k_len)?;
3383 self.copy_u8_into(&mut kv.v, 0, &v_tmp, v_len)?;
3384 }
3385 kv.ring.as_mut().unwrap().apply_rebase(new_base);
3386 Ok(write_row)
3387 }
3388 }
3389 }
3390
3391 pub fn htod_u8_into(
3394 &self,
3395 dst: &mut CudaSlice<u8>,
3396 off: usize,
3397 src: &[u8],
3398 ) -> Result<(), Box<dyn std::error::Error>> {
3399 let mut view = dst.slice_mut(off..off + src.len());
3400 self.gpu.stream().memcpy_htod(src, &mut view)?;
3401 Ok(())
3402 }
3403
3404 pub fn view<'a>(&self, b: &'a CudaSlice<f32>, len: usize) -> cudarc::driver::CudaView<'a, f32> {
3405 b.slice(0..len)
3406 }
3407
3408 pub fn view_u8_range<'a>(
3411 &self,
3412 b: &'a CudaSlice<u8>,
3413 start: usize,
3414 end: usize,
3415 ) -> cudarc::driver::CudaView<'a, u8> {
3416 b.slice(start..end)
3417 }
3418 pub fn view_u8<'a>(
3419 &self,
3420 b: &'a CudaSlice<u8>,
3421 len: usize,
3422 ) -> cudarc::driver::CudaView<'a, u8> {
3423 b.slice(0..len)
3424 }
3425
3426 pub fn append_kv_quantized(
3430 &self,
3431 k_row: &CudaSlice<f32>,
3432 v_row: &CudaSlice<f32>,
3433 kc: &mut CudaSlice<u8>,
3434 vc: &mut CudaSlice<u8>,
3435 t: usize,
3436 kv_dim_k: usize,
3437 kv_dim_v: usize,
3438 k_tok_bytes: usize,
3439 v_tok_bytes: usize,
3440 g: bool,
3441 ) -> Result<(), Box<dyn std::error::Error>> {
3442 let f = if g {
3443 self.func_g("append_quantize_kv_q8_0_q5_1")
3444 } else {
3445 self.func("append_quantize_kv_q8_0_q5_1")
3446 };
3447 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3448 let cfg = LaunchConfig {
3449 grid_dim: (nblk, 1, 1),
3450 block_dim: (32, 1, 1),
3451 shared_mem_bytes: 0,
3452 };
3453 let (ti, kdk, kdv) = (t as i32, kv_dim_k as i32, kv_dim_v as i32);
3454 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3455 let __s_b = self.gpu.stream();
3456 let mut b = __s_b.launch_builder(&f);
3457 b.arg(k_row)
3458 .arg(v_row)
3459 .arg(kc)
3460 .arg(vc)
3461 .arg(&ti)
3462 .arg(&kdk)
3463 .arg(&kdv)
3464 .arg(&ktb)
3465 .arg(&vtb);
3466 unsafe {
3467 b.launch(cfg)?;
3468 }
3469 Ok(())
3470 }
3471
3472 pub fn append_kv_quantized_dc(
3476 &self,
3477 k_row: &CudaSlice<f32>,
3478 v_row: &CudaSlice<f32>,
3479 kc: &mut CudaSlice<u8>,
3480 vc: &mut CudaSlice<u8>,
3481 t_dev: &CudaSlice<i32>,
3482 kv_dim_k: usize,
3483 kv_dim_v: usize,
3484 k_tok_bytes: usize,
3485 v_tok_bytes: usize,
3486 g: bool,
3487 ) -> Result<(), Box<dyn std::error::Error>> {
3488 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3489 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
3490 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3491 if Self::pdl_on() && Self::pdl_wb_on() {
3493 use cudarc::driver::{DevicePtr, DevicePtrMut};
3494 let s = &self.gpu.stream();
3495 let (pk, _g0) = k_row.device_ptr(s);
3496 let (pv, _g1) = v_row.device_ptr(s);
3497 let (pkc, _g2) = kc.device_ptr_mut(s);
3498 let (pvc, _g3) = vc.device_ptr_mut(s);
3499 let (pt, _g4) = t_dev.device_ptr(s);
3500 let mut ps = [
3501 &pk as *const _ as *mut std::ffi::c_void,
3502 &pv as *const _ as *mut _,
3503 &pkc as *const _ as *mut _,
3504 &pvc as *const _ as *mut _,
3505 &pt as *const _ as *mut _,
3506 &kdk as *const _ as *mut _,
3507 &kdv as *const _ as *mut _,
3508 &ktb as *const _ as *mut _,
3509 &vtb as *const _ as *mut _,
3510 ];
3511 unsafe {
3512 self.launch_pdl_flash(
3513 g,
3514 "append_quantize_kv_q8_0_q5_1_dc",
3515 (nblk, 1, 1),
3516 (32, 1, 1),
3517 0,
3518 &mut ps,
3519 )?;
3520 }
3521 return Ok(());
3522 }
3523 let f = if g {
3524 self.func_g("append_quantize_kv_q8_0_q5_1_dc")
3525 } else {
3526 self.func("append_quantize_kv_q8_0_q5_1_dc")
3527 };
3528 let cfg = LaunchConfig {
3529 grid_dim: (nblk, 1, 1),
3530 block_dim: (32, 1, 1),
3531 shared_mem_bytes: 0,
3532 };
3533 let __s_b = self.gpu.stream();
3534 let mut b = __s_b.launch_builder(&f);
3535 b.arg(k_row)
3536 .arg(v_row)
3537 .arg(kc)
3538 .arg(vc)
3539 .arg(t_dev)
3540 .arg(&kdk)
3541 .arg(&kdv)
3542 .arg(&ktb)
3543 .arg(&vtb);
3544 unsafe {
3545 b.launch(cfg)?;
3546 }
3547 Ok(())
3548 }
3549
3550 #[allow(clippy::too_many_arguments)]
3557 pub fn append_kv_quantized_rows(
3558 &self,
3559 k_rows: &CudaSlice<f32>,
3560 v_rows: &CudaSlice<f32>,
3561 kc: &mut CudaSlice<u8>,
3562 vc: &mut CudaSlice<u8>,
3563 t0: usize,
3564 t: usize,
3565 kv_dim_k: usize,
3566 kv_dim_v: usize,
3567 k_tok_bytes: usize,
3568 v_tok_bytes: usize,
3569 g: bool,
3570 ) -> Result<(), Box<dyn std::error::Error>> {
3571 if std::env::var("MEMRA_PRIME_APPEND_LOOP").is_ok() {
3572 for i in 0..t {
3573 let k_row = k_rows.slice(i * kv_dim_k..(i + 1) * kv_dim_k);
3574 let v_row = v_rows.slice(i * kv_dim_v..(i + 1) * kv_dim_v);
3575 self.append_kv_quantized_view(
3576 &k_row,
3577 &v_row,
3578 kc,
3579 vc,
3580 t0 + i,
3581 kv_dim_k,
3582 kv_dim_v,
3583 k_tok_bytes,
3584 v_tok_bytes,
3585 g,
3586 )?;
3587 }
3588 return Ok(());
3589 }
3590 let f = if g {
3591 self.func_g("append_quantize_kv_q8_0_q5_1_rows")
3592 } else {
3593 self.func("append_quantize_kv_q8_0_q5_1_rows")
3594 };
3595 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3596 let cfg = LaunchConfig {
3597 grid_dim: (nblk, t as u32, 1),
3598 block_dim: (32, 1, 1),
3599 shared_mem_bytes: 0,
3600 };
3601 let (t0i, kdk, kdv) = (t0 as i32, kv_dim_k as i32, kv_dim_v as i32);
3602 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3603 let __s_b = self.gpu.stream();
3604 let mut b = __s_b.launch_builder(&f);
3605 b.arg(k_rows)
3606 .arg(v_rows)
3607 .arg(kc)
3608 .arg(vc)
3609 .arg(&t0i)
3610 .arg(&kdk)
3611 .arg(&kdv)
3612 .arg(&ktb)
3613 .arg(&vtb);
3614 unsafe {
3615 b.launch(cfg)?;
3616 }
3617 Ok(())
3618 }
3619
3620 pub fn inc_seqlen(&self, p: &mut CudaSlice<i32>) -> Result<(), Box<dyn std::error::Error>> {
3624 let f = self.func("inc_i32");
3625 let cfg = LaunchConfig {
3626 grid_dim: (1, 1, 1),
3627 block_dim: (1, 1, 1),
3628 shared_mem_bytes: 0,
3629 };
3630 let __s_b = self.gpu.stream();
3631 let mut b = __s_b.launch_builder(&f);
3632 b.arg(p);
3633 unsafe {
3634 b.launch(cfg)?;
3635 }
3636 Ok(())
3637 }
3638
3639 pub fn append_kv_quantized_view(
3642 &self,
3643 k_row: &cudarc::driver::CudaView<f32>,
3644 v_row: &cudarc::driver::CudaView<f32>,
3645 kc: &mut CudaSlice<u8>,
3646 vc: &mut CudaSlice<u8>,
3647 t: usize,
3648 kv_dim_k: usize,
3649 kv_dim_v: usize,
3650 k_tok_bytes: usize,
3651 v_tok_bytes: usize,
3652 g: bool,
3653 ) -> Result<(), Box<dyn std::error::Error>> {
3654 let f = if g {
3655 self.func_g("append_quantize_kv_q8_0_q5_1")
3656 } else {
3657 self.func("append_quantize_kv_q8_0_q5_1")
3658 };
3659 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3660 let cfg = LaunchConfig {
3661 grid_dim: (nblk, 1, 1),
3662 block_dim: (32, 1, 1),
3663 shared_mem_bytes: 0,
3664 };
3665 let (ti, kdk, kdv) = (t as i32, kv_dim_k as i32, kv_dim_v as i32);
3666 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3667 let __s_b = self.gpu.stream();
3668 let mut b = __s_b.launch_builder(&f);
3669 b.arg(k_row)
3670 .arg(v_row)
3671 .arg(kc)
3672 .arg(vc)
3673 .arg(&ti)
3674 .arg(&kdk)
3675 .arg(&kdv)
3676 .arg(&ktb)
3677 .arg(&vtb);
3678 unsafe {
3679 b.launch(cfg)?;
3680 }
3681 Ok(())
3682 }
3683
3684 pub fn copy_view_into(
3687 &self,
3688 dst: &mut CudaSlice<f32>,
3689 off: usize,
3690 src: &cudarc::driver::CudaView<f32>,
3691 len: usize,
3692 ) -> Result<(), Box<dyn std::error::Error>> {
3693 let mut view = dst.slice_mut(off..off + len);
3694 self.gpu
3695 .stream()
3696 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3697 Ok(())
3698 }
3699
3700 pub fn clone_dtod(
3704 &self,
3705 src: &CudaSlice<f32>,
3706 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3707 let mut dst = self.gpu.stream().alloc_zeros::<f32>(src.len())?;
3708 self.gpu.stream().memcpy_dtod(src, &mut dst)?;
3709 Ok(dst)
3710 }
3711
3712 pub fn dtod_copy_view(
3715 &self,
3716 src: &cudarc::driver::CudaView<f32>,
3717 dst: &mut CudaSlice<f32>,
3718 ) -> Result<(), Box<dyn std::error::Error>> {
3719 self.gpu.stream().memcpy_dtod(src, dst)?;
3720 Ok(())
3721 }
3722
3723 pub fn dtod_copy_view_i8(
3725 &self,
3726 src: &cudarc::driver::CudaView<i8>,
3727 dst: &mut CudaSlice<i8>,
3728 ) -> Result<(), Box<dyn std::error::Error>> {
3729 self.gpu.stream().memcpy_dtod(src, dst)?;
3730 Ok(())
3731 }
3732
3733 pub fn dtod_copy_into(
3735 &self,
3736 src: &CudaSlice<f32>,
3737 dst: &mut CudaSlice<f32>,
3738 offset: usize,
3739 ) -> Result<(), Box<dyn std::error::Error>> {
3740 let n = src.len();
3741 let mut dv = dst.slice_mut(offset..offset + n);
3742 self.gpu.stream().memcpy_dtod(src, &mut dv)?;
3743 Ok(())
3744 }
3745
3746 pub fn copy_batch_uniform_f32(
3752 &self,
3753 table: &CudaSlice<u64>,
3754 n: usize,
3755 words: usize,
3756 ) -> Result<(), Box<dyn std::error::Error>> {
3757 if n == 0 || words == 0 {
3758 return Ok(());
3759 }
3760 debug_assert!(
3761 table.len() >= 2 * n,
3762 "pointer table must hold n srcs + n dsts"
3763 );
3764 let f = self.func("copy_batch_uniform_f32");
3765 let chunks = (words / 4).max(1).div_ceil(256).min(48) as u32;
3768 let (ni, wi) = (n as i32, words as i32);
3769 let cfg = LaunchConfig {
3770 grid_dim: (chunks, n as u32, 1),
3771 block_dim: (256, 1, 1),
3772 shared_mem_bytes: 0,
3773 };
3774 let __s = self.gpu.stream();
3775 let mut b = __s.launch_builder(&f);
3776 b.arg(table).arg(&ni).arg(&wi);
3777 unsafe {
3778 b.launch(cfg)?;
3779 }
3780 Ok(())
3781 }
3782
3783 pub fn htod_u64_into(
3786 &self,
3787 v: &[u64],
3788 dst: &mut CudaSlice<u64>,
3789 ) -> Result<(), Box<dyn std::error::Error>> {
3790 let mut view = dst.slice_mut(0..v.len());
3791 self.gpu.stream().memcpy_htod(v, &mut view)?;
3792 Ok(())
3793 }
3794
3795 pub fn copy_indirect_src_f32(
3800 &self,
3801 src_entry: &cudarc::driver::CudaView<u64>,
3802 dst: &mut CudaSlice<f32>,
3803 dst_off: usize,
3804 words: usize,
3805 ) -> Result<(), Box<dyn std::error::Error>> {
3806 let f = self.func("copy_indirect_src_f32");
3807 let chunks = (words / 4).max(1).div_ceil(256).min(48) as u32;
3808 let wi = words as i32;
3809 let cfg = LaunchConfig {
3810 grid_dim: (chunks, 1, 1),
3811 block_dim: (256, 1, 1),
3812 shared_mem_bytes: 0,
3813 };
3814 let mut dv = dst.slice_mut(dst_off..dst_off + words);
3815 let __s = self.gpu.stream();
3816 let mut b = __s.launch_builder(&f);
3817 b.arg(src_entry).arg(&mut dv).arg(&wi);
3818 unsafe {
3819 b.launch(cfg)?;
3820 }
3821 Ok(())
3822 }
3823
3824 pub fn uninit_i8(&self, n: usize) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
3826 self.alloc_uninit::<i8>(n)
3827 }
3828
3829 pub fn qmatvec(
3831 &self,
3832 w: &CudaSlice<u8>,
3833 x: &CudaSlice<f32>,
3834 m: usize,
3835 in_f: usize,
3836 out_f: usize,
3837 qtype: i32,
3838 row_bytes: usize,
3839 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
3840 let f = self.func("qmatvec_f32");
3841 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
3843 grid_dim: (out_f as u32, m as u32, 1),
3844 block_dim: (256, 1, 1),
3845 shared_mem_bytes: 0,
3846 };
3847 let (inf, outf, mi, qt, rb) =
3848 (in_f as i32, out_f as i32, m as i32, qtype, row_bytes as i64);
3849 let __s_b = self.gpu.stream();
3850 let mut b = __s_b.launch_builder(&f);
3851 b.arg(w)
3852 .arg(x)
3853 .arg(&mut y)
3854 .arg(&inf)
3855 .arg(&outf)
3856 .arg(&mi)
3857 .arg(&qt)
3858 .arg(&rb);
3859 unsafe {
3860 b.launch(cfg)?;
3861 }
3862 Ok(y)
3863 }
3864
3865 pub fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3867 let s = self.gpu.stream().alloc_zeros::<u8>(n)?;
3868 self.keep_if_capturing(&s);
3869 Ok(s)
3870 }
3871
3872 pub fn alloc_u8_uninit(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3876 let s = unsafe { self.gpu.stream().alloc::<u8>(n)? };
3877 self.keep_if_capturing(&s);
3878 Ok(s)
3879 }
3880
3881 pub fn memset_zeros_view(
3884 &self,
3885 dst: &mut cudarc::driver::CudaViewMut<f32>,
3886 ) -> Result<(), Box<dyn std::error::Error>> {
3887 self.gpu.stream().memset_zeros(dst)?;
3888 Ok(())
3889 }
3890
3891 pub fn stage_expert(
3897 &self,
3898 host_bytes: &[u8],
3899 scratch: &mut CudaSlice<u8>,
3900 off: usize,
3901 ) -> Result<(), Box<dyn std::error::Error>> {
3902 let mut dst = scratch.slice_mut(off..off + host_bytes.len()); self.gpu.stream().memcpy_htod(host_bytes, &mut dst)?; Ok(())
3905 }
3906
3907 pub fn moe_router_topk(
3913 &self,
3914 logits: &CudaSlice<f32>,
3915 t: usize,
3916 n_expert: usize,
3917 n_used: usize,
3918 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
3919 let f = self.func("moe_router_topk_f32");
3920 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 {
3923 grid_dim: (t as u32, 1, 1),
3924 block_dim: (n_expert as u32, 1, 1),
3925 shared_mem_bytes: 0,
3926 };
3927 let (ne, nu) = (n_expert as i32, n_used as i32);
3928 let __s_b = self.gpu.stream();
3929 let mut b = __s_b.launch_builder(&f);
3930 b.arg(logits)
3931 .arg(&mut sel_idx)
3932 .arg(&mut sel_w)
3933 .arg(&ne)
3934 .arg(&nu);
3935 unsafe {
3936 b.launch(cfg)?;
3937 }
3938 Ok((sel_idx, sel_w))
3939 }
3940
3941 pub fn moe_router_topk_scaled(
3944 &self,
3945 logits: &CudaSlice<f32>,
3946 t: usize,
3947 n_expert: usize,
3948 n_used: usize,
3949 ex_scale: &CudaSlice<f32>,
3950 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
3951 let f = self.func("moe_router_topk_scaled_f32");
3956 let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?;
3957 let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?;
3958 let cfg = LaunchConfig {
3959 grid_dim: (t as u32, 1, 1),
3960 block_dim: (n_expert as u32, 1, 1),
3961 shared_mem_bytes: 0,
3962 };
3963 let (ne, nu) = (n_expert as i32, n_used as i32);
3964 let __s_b = self.gpu.stream();
3965 let mut b = __s_b.launch_builder(&f);
3966 b.arg(logits)
3967 .arg(&mut sel_idx)
3968 .arg(&mut sel_w)
3969 .arg(&ne)
3970 .arg(&nu)
3971 .arg(ex_scale);
3972 unsafe {
3973 b.launch(cfg)?;
3974 }
3975 Ok((sel_idx, sel_w))
3976 }
3977
3978 pub fn moe_router_topk_host(
3986 &self,
3987 logits: &CudaSlice<f32>,
3988 t: usize,
3989 n_expert: usize,
3990 n_used: usize,
3991 ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
3992 let f = self.func("moe_router_topk_f32");
3993 let n = t * n_used;
3994 let mut sel_idx = self.alloc_uninit::<i32>(n)?;
3995 let mut sel_w = self.alloc_uninit::<f32>(n)?;
3996 let cfg = LaunchConfig {
3997 grid_dim: (t as u32, 1, 1),
3998 block_dim: (n_expert as u32, 1, 1),
3999 shared_mem_bytes: 0,
4000 };
4001 let (ne, nu) = (n_expert as i32, n_used as i32);
4002 let __s_b = self.gpu.stream();
4003 let mut b = __s_b.launch_builder(&f);
4004 b.arg(logits)
4005 .arg(&mut sel_idx)
4006 .arg(&mut sel_w)
4007 .arg(&ne)
4008 .arg(&nu);
4009 unsafe {
4010 b.launch(cfg)?;
4011 }
4012 let bytes = n * 8;
4014 let mut guard = self.router_stage.lock().unwrap();
4015 if guard.as_ref().map(|p| p.cap < bytes).unwrap_or(true) {
4016 *guard = Some(PinnedStage::new(bytes.max(4096))?);
4017 }
4018 let stage = guard.as_mut().unwrap();
4019 let (si, sw) = unsafe {
4020 (
4021 std::slice::from_raw_parts_mut(stage.ptr as *mut i32, n),
4022 std::slice::from_raw_parts_mut(stage.ptr.add(n * 4) as *mut f32, n),
4023 )
4024 };
4025 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()))
4029 }
4030
4031 #[allow(clippy::too_many_arguments)]
4035 pub fn moe_router_sigmoid_topk(
4036 &self,
4037 logits: &CudaSlice<f32>,
4038 t: usize,
4039 n_expert: usize,
4040 n_used: usize,
4041 active_count: usize,
4042 correction_bias: &CudaSlice<f32>,
4043 active: &CudaSlice<u8>,
4044 scaling_factor: f32,
4045 route_norm: bool,
4046 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
4047 crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
4048 if n_expert == 0 || n_expert > 1024 || n_used == 0 || n_used > n_expert {
4049 return Err(format!(
4050 "sigmoid router shape unsupported: n_expert={n_expert}, n_used={n_used}",
4051 )
4052 .into());
4053 }
4054 if logits.len() < t * n_expert
4055 || correction_bias.len() != n_expert
4056 || active.len() != n_expert
4057 {
4058 return Err(format!(
4059 "sigmoid router buffer mismatch: logits={} bias={} active={} expected logits>={} row={}",
4060 logits.len(), correction_bias.len(), active.len(), t * n_expert, n_expert,
4061 ).into());
4062 }
4063 let f = self.func("moe_router_sigmoid_topk_f32");
4064 let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?;
4065 let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?;
4066 let threads = n_expert.div_ceil(32) * 32;
4067 let cfg = LaunchConfig {
4068 grid_dim: (t as u32, 1, 1),
4069 block_dim: (threads as u32, 1, 1),
4070 shared_mem_bytes: 0,
4071 };
4072 let (ne, nu, rn) = (n_expert as i32, n_used as i32, i32::from(route_norm));
4073 let __s_b = self.gpu.stream();
4074 let mut b = __s_b.launch_builder(&f);
4075 b.arg(logits)
4076 .arg(correction_bias)
4077 .arg(active)
4078 .arg(&mut sel_idx)
4079 .arg(&mut sel_w)
4080 .arg(&ne)
4081 .arg(&nu)
4082 .arg(&scaling_factor)
4083 .arg(&rn);
4084 unsafe {
4085 b.launch(cfg)?;
4086 }
4087 Ok((sel_idx, sel_w))
4088 }
4089
4090 #[allow(clippy::too_many_arguments)]
4093 pub fn moe_router_sigmoid_topk_host(
4094 &self,
4095 logits: &CudaSlice<f32>,
4096 t: usize,
4097 n_expert: usize,
4098 n_used: usize,
4099 active_count: usize,
4100 correction_bias: &CudaSlice<f32>,
4101 active: &CudaSlice<u8>,
4102 scaling_factor: f32,
4103 route_norm: bool,
4104 ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
4105 let (sel_idx, sel_w) = self.moe_router_sigmoid_topk(
4106 logits,
4107 t,
4108 n_expert,
4109 n_used,
4110 active_count,
4111 correction_bias,
4112 active,
4113 scaling_factor,
4114 route_norm,
4115 )?;
4116 let n = t * n_used;
4117 let bytes = n * 8;
4118 let mut guard = self.router_stage.lock().unwrap();
4119 if guard.as_ref().map(|p| p.cap < bytes).unwrap_or(true) {
4120 *guard = Some(PinnedStage::new(bytes.max(4096))?);
4121 }
4122 let stage = guard.as_mut().unwrap();
4123 let (si, sw) = unsafe {
4124 (
4125 std::slice::from_raw_parts_mut(stage.ptr as *mut i32, n),
4126 std::slice::from_raw_parts_mut(stage.ptr.add(n * 4) as *mut f32, n),
4127 )
4128 };
4129 self.gpu.stream().memcpy_dtoh(&sel_idx, si)?;
4130 self.gpu.stream().memcpy_dtoh(&sel_w, sw)?;
4131 self.gpu.stream().synchronize()?;
4132 Ok((si.iter().map(|&i| i as u32).collect(), sw.to_vec()))
4133 }
4134
4135 pub fn stage_expert_async(
4139 &self,
4140 host_bytes: &[u8],
4141 scratch: &mut CudaSlice<u8>,
4142 off: usize,
4143 ) -> Result<cudarc::driver::CudaEvent, Box<dyn std::error::Error>> {
4144 let mut dst = scratch.slice_mut(off..off + host_bytes.len());
4145 self.copy_stream.memcpy_htod(host_bytes, &mut dst)?;
4146 Ok(self.copy_stream.record_event(None)?)
4147 }
4148
4149 pub fn compute_wait(
4151 &self,
4152 ev: &cudarc::driver::CudaEvent,
4153 ) -> Result<(), Box<dyn std::error::Error>> {
4154 self.gpu.stream().wait(ev)?;
4155 Ok(())
4156 }
4157
4158 pub fn qmatvec_view(
4163 &self,
4164 w: &CudaSlice<u8>,
4165 range: std::ops::Range<usize>,
4166 x: &cudarc::driver::CudaView<f32>,
4167 m: usize,
4168 in_f: usize,
4169 out_f: usize,
4170 qtype: i32,
4171 row_bytes: usize,
4172 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4173 let f = self.func("qmatvec_f32");
4174 let wv = w.slice(range); let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
4177 grid_dim: (out_f as u32, m as u32, 1),
4178 block_dim: (256, 1, 1),
4179 shared_mem_bytes: 0,
4180 };
4181 let (inf, outf, mi, qt, rb) =
4182 (in_f as i32, out_f as i32, m as i32, qtype, row_bytes as i64);
4183 let __s_b = self.gpu.stream();
4184 let mut b = __s_b.launch_builder(&f);
4185 b.arg(&wv)
4186 .arg(x)
4187 .arg(&mut y)
4188 .arg(&inf)
4189 .arg(&outf)
4190 .arg(&mi)
4191 .arg(&qt)
4192 .arg(&rb);
4193 unsafe {
4194 b.launch(cfg)?;
4195 }
4196 Ok(y)
4197 }
4198
4199 #[allow(clippy::too_many_arguments)]
4206 pub fn moe_gate_up_silu8_q8(
4210 &self,
4211 gp: WPtr8,
4212 up: WPtr8,
4213 aq: &CudaSlice<i8>,
4214 ad: &CudaSlice<f32>,
4215 in_f: usize,
4216 n_ff: usize,
4217 n_used: usize,
4218 qt_g: i32,
4219 qt_u: i32,
4220 rb_g: usize,
4221 rb_u: usize,
4222 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4223 let f = self.func("moe_gate_up_silu8_q8");
4224 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
4225 let cfg = LaunchConfig {
4226 grid_dim: (n_ff as u32, n_used as u32, 1),
4227 block_dim: (32, 1, 1),
4228 shared_mem_bytes: 0,
4229 };
4230 let (inf, nff, rbg, rbu) = (in_f as i32, n_ff as i32, rb_g as i64, rb_u as i64);
4231 let __s_b = self.gpu.stream();
4232 let mut b = __s_b.launch_builder(&f);
4233 b.arg(&gp)
4234 .arg(&up)
4235 .arg(aq)
4236 .arg(ad)
4237 .arg(&mut act)
4238 .arg(&inf)
4239 .arg(&nff)
4240 .arg(&qt_g)
4241 .arg(&qt_u)
4242 .arg(&rbg)
4243 .arg(&rbu);
4244 unsafe {
4245 b.launch(cfg)?;
4246 }
4247 Ok(act)
4248 }
4249
4250 #[allow(clippy::too_many_arguments)]
4251 pub fn moe_down8_fma_q8(
4252 &self,
4253 dp: WPtr8,
4254 w: F32x8,
4255 aq2: &CudaSlice<i8>,
4256 ad2: &CudaSlice<f32>,
4257 dst: &mut cudarc::driver::CudaViewMut<f32>,
4258 in_f: usize,
4259 out_f: usize,
4260 n_used: usize,
4261 qt: i32,
4262 rb: usize,
4263 ) -> Result<(), Box<dyn std::error::Error>> {
4264 let f = self.func("moe_down8_fma_q8");
4265 let cfg = LaunchConfig {
4266 grid_dim: (out_f as u32, 1, 1),
4267 block_dim: (32, 1, 1),
4268 shared_mem_bytes: 0,
4269 };
4270 let (inf, outf, nu, rbi) = (in_f as i32, out_f as i32, n_used as i32, rb as i64);
4271 let __s_b = self.gpu.stream();
4272 let mut b = __s_b.launch_builder(&f);
4273 b.arg(&dp)
4274 .arg(&w)
4275 .arg(aq2)
4276 .arg(ad2)
4277 .arg(dst)
4278 .arg(&inf)
4279 .arg(&outf)
4280 .arg(&nu)
4281 .arg(&qt)
4282 .arg(&rbi);
4283 unsafe {
4284 b.launch(cfg)?;
4285 }
4286 Ok(())
4287 }
4288
4289 pub fn qmatvec_expert_q8(
4291 &self,
4292 w: &CudaSlice<u8>,
4293 range: std::ops::Range<usize>,
4294 aq: &CudaSlice<i8>,
4295 ad: &CudaSlice<f32>,
4296 m: usize,
4297 in_f: usize,
4298 out_f: usize,
4299 qtype: i32,
4300 row_bytes: usize,
4301 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4302 let f = self.func("qmatvec_expert_q8");
4303 let wv = w.slice(range);
4304 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
4305 const ROWS: u32 = 4; let cfg = LaunchConfig {
4307 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, m as u32, 1),
4308 block_dim: (32, ROWS, 1),
4309 shared_mem_bytes: 0,
4310 };
4311 let (inf, outf, mi, rbi) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
4312 let __s_b = self.gpu.stream();
4313 let mut b = __s_b.launch_builder(&f);
4314 b.arg(&wv)
4315 .arg(aq)
4316 .arg(ad)
4317 .arg(&mut y)
4318 .arg(&inf)
4319 .arg(&outf)
4320 .arg(&mi)
4321 .arg(&qtype)
4322 .arg(&rbi);
4323 unsafe {
4324 b.launch(cfg)?;
4325 }
4326 Ok(y)
4327 }
4328
4329 pub fn moe_gate_up_silu8(
4330 &self,
4331 gp: WPtr8,
4332 up: WPtr8,
4333 x: &cudarc::driver::CudaView<f32>,
4334 in_f: usize,
4335 n_ff: usize,
4336 n_used: usize,
4337 qt_g: i32,
4338 qt_u: i32,
4339 rb_g: usize,
4340 rb_u: usize,
4341 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4342 let f = self.func("moe_gate_up_silu8_f32");
4343 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?; let cfg = LaunchConfig {
4345 grid_dim: (n_ff as u32, n_used as u32, 1),
4346 block_dim: (256, 1, 1),
4347 shared_mem_bytes: 0,
4348 };
4349 let (inf, nff, rbg, rbu) = (in_f as i32, n_ff as i32, rb_g as i64, rb_u as i64);
4350 let __s_b = self.gpu.stream();
4351 let mut b = __s_b.launch_builder(&f);
4352 b.arg(&gp)
4353 .arg(&up)
4354 .arg(x)
4355 .arg(&mut act)
4356 .arg(&inf)
4357 .arg(&nff)
4358 .arg(&qt_g)
4359 .arg(&qt_u)
4360 .arg(&rbg)
4361 .arg(&rbu);
4362 unsafe {
4363 b.launch(cfg)?;
4364 }
4365 Ok(act)
4366 }
4367
4368 #[allow(clippy::too_many_arguments)]
4374 pub fn moe_down8_fma_into(
4375 &self,
4376 dp: WPtr8,
4377 w: F32x8,
4378 act: &CudaSlice<f32>,
4379 dst: &mut cudarc::driver::CudaViewMut<f32>,
4380 in_f: usize,
4381 out_f: usize,
4382 n_used: usize,
4383 qt: i32,
4384 rb: usize,
4385 ) -> Result<(), Box<dyn std::error::Error>> {
4386 let f = self.func("moe_down8_fma_f32");
4387 let cfg = LaunchConfig {
4388 grid_dim: (out_f as u32, 1, 1),
4389 block_dim: (256, 1, 1),
4390 shared_mem_bytes: 0,
4391 };
4392 let (inf, outf, nu, rbv) = (in_f as i32, out_f as i32, n_used as i32, rb as i64);
4393 let __s_b = self.gpu.stream();
4394 let mut b = __s_b.launch_builder(&f);
4395 b.arg(&dp)
4396 .arg(&w)
4397 .arg(act)
4398 .arg(dst)
4399 .arg(&inf)
4400 .arg(&outf)
4401 .arg(&nu)
4402 .arg(&qt)
4403 .arg(&rbv);
4404 unsafe {
4405 b.launch(cfg)?;
4406 }
4407 Ok(())
4408 }
4409
4410 #[allow(clippy::too_many_arguments)]
4415 #[allow(clippy::too_many_arguments)]
4430 #[allow(clippy::too_many_arguments)]
4432 pub fn moe_pairs_matvec_q8(
4433 &self,
4434 table: &CudaSlice<u64>,
4435 proj: i32,
4436 pair_tok: &CudaSlice<i32>,
4437 pair_ex: &CudaSlice<i32>,
4438 aq: &CudaSlice<i8>,
4439 ad: &CudaSlice<f32>,
4440 in_f: usize,
4441 out_f: usize,
4442 n_expert: usize,
4443 n_pairs: usize,
4444 qtype: i32,
4445 row_bytes: usize,
4446 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4447 let f = self.func("moe_pairs_matvec_q8");
4448 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4449 const ROWS: u32 = 4;
4450 let cfg = LaunchConfig {
4451 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_pairs as u32, 1),
4452 block_dim: (32, ROWS, 1),
4453 shared_mem_bytes: 0,
4454 };
4455 let (inf, outf, ne, np, rbi) = (
4456 in_f as i32,
4457 out_f as i32,
4458 n_expert as i32,
4459 n_pairs as i32,
4460 row_bytes as i64,
4461 );
4462 let __s_b = self.gpu.stream();
4463 let mut b = __s_b.launch_builder(&f);
4464 b.arg(table)
4465 .arg(&proj)
4466 .arg(pair_tok)
4467 .arg(pair_ex)
4468 .arg(aq)
4469 .arg(ad)
4470 .arg(&mut y)
4471 .arg(&inf)
4472 .arg(&outf)
4473 .arg(&ne)
4474 .arg(&np)
4475 .arg(&qtype)
4476 .arg(&rbi);
4477 unsafe {
4478 b.launch(cfg)?;
4479 }
4480 Ok(y)
4481 }
4482
4483 #[allow(clippy::too_many_arguments)]
4485 pub fn moe_pairs_matvec_q8_em(
4486 &self,
4487 table: &CudaSlice<u64>,
4488 proj: i32,
4489 ex_ids: &CudaSlice<i32>,
4490 ex_off: &CudaSlice<i32>,
4491 ex_pairs: &CudaSlice<i32>,
4492 pair_tok: &CudaSlice<i32>,
4493 aq: &CudaSlice<i8>,
4494 ad: &CudaSlice<f32>,
4495 in_f: usize,
4496 out_f: usize,
4497 n_expert: usize,
4498 n_active: usize,
4499 n_pairs: usize,
4500 qtype: i32,
4501 row_bytes: usize,
4502 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4503 let f = self.func("moe_pairs_matvec_q8_em");
4504 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4505 const ROWS: u32 = 4;
4506 let cfg = LaunchConfig {
4507 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_active as u32, 1),
4508 block_dim: (32, ROWS, 1),
4509 shared_mem_bytes: 0,
4510 };
4511 let (inf, outf, ne, na, rbi) = (
4512 in_f as i32,
4513 out_f as i32,
4514 n_expert as i32,
4515 n_active as i32,
4516 row_bytes as i64,
4517 );
4518 let __s_b = self.gpu.stream();
4519 let mut b = __s_b.launch_builder(&f);
4520 b.arg(table)
4521 .arg(&proj)
4522 .arg(ex_ids)
4523 .arg(ex_off)
4524 .arg(ex_pairs)
4525 .arg(pair_tok)
4526 .arg(aq)
4527 .arg(ad)
4528 .arg(&mut y)
4529 .arg(&inf)
4530 .arg(&outf)
4531 .arg(&ne)
4532 .arg(&na)
4533 .arg(&qtype)
4534 .arg(&rbi);
4535 unsafe {
4536 b.launch(cfg)?;
4537 }
4538 Ok(y)
4539 }
4540
4541 #[allow(clippy::too_many_arguments)]
4544 pub fn moe_pairs_matvec_q8_dec(
4545 &self,
4546 table: &CudaSlice<u64>,
4547 proj: i32,
4548 ex_ids: &CudaSlice<i32>,
4549 ex_off: &CudaSlice<i32>,
4550 ex_pairs: &CudaSlice<i32>,
4551 pair_tok: &CudaSlice<i32>,
4552 aq: &CudaSlice<i8>,
4553 ad: &CudaSlice<f32>,
4554 in_f: usize,
4555 out_f: usize,
4556 n_expert: usize,
4557 n_active: usize,
4558 n_pairs: usize,
4559 qtype: i32,
4560 row_bytes: usize,
4561 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4562 let f = self.func("moe_pairs_matvec_q8_dec");
4563 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
4564 const ROWS: u32 = 4;
4565 let cfg = LaunchConfig {
4566 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_active as u32, 1),
4567 block_dim: (32, ROWS, 1),
4568 shared_mem_bytes: 0,
4569 };
4570 let (inf, outf, ne, na, rbi) = (
4571 in_f as i32,
4572 out_f as i32,
4573 n_expert as i32,
4574 n_active as i32,
4575 row_bytes as i64,
4576 );
4577 let __s_b = self.gpu.stream();
4578 let mut b = __s_b.launch_builder(&f);
4579 b.arg(table)
4580 .arg(&proj)
4581 .arg(ex_ids)
4582 .arg(ex_off)
4583 .arg(ex_pairs)
4584 .arg(pair_tok)
4585 .arg(aq)
4586 .arg(ad)
4587 .arg(&mut y)
4588 .arg(&inf)
4589 .arg(&outf)
4590 .arg(&ne)
4591 .arg(&na)
4592 .arg(&qtype)
4593 .arg(&rbi);
4594 unsafe {
4595 b.launch(cfg)?;
4596 }
4597 Ok(y)
4598 }
4599
4600 pub fn moe_pairs_gelu_mul(
4601 &self,
4602 gate: &CudaSlice<f32>,
4603 up: &CudaSlice<f32>,
4604 n: usize,
4605 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4606 let f = self.func("moe_pairs_gelu_mul");
4607 let mut act = self.alloc_uninit::<f32>(n)?;
4608 let cfg = LaunchConfig::for_num_elems(n as u32);
4609 let nl = n as i64;
4610 let __s_b = self.gpu.stream();
4611 let mut b = __s_b.launch_builder(&f);
4612 b.arg(gate).arg(up).arg(&mut act).arg(&nl);
4613 unsafe {
4614 b.launch(cfg)?;
4615 }
4616 Ok(act)
4617 }
4618
4619 pub fn moe_pairs_silu_mul(
4620 &self,
4621 gate: &CudaSlice<f32>,
4622 up: &CudaSlice<f32>,
4623 n: usize,
4624 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4625 let f = self.func("moe_pairs_silu_mul");
4626 let mut act = self.alloc_uninit::<f32>(n)?;
4627 let cfg = LaunchConfig::for_num_elems(n as u32);
4628 let nl = n as i64;
4629 let __s_b = self.gpu.stream();
4630 let mut b = __s_b.launch_builder(&f);
4631 b.arg(gate).arg(up).arg(&mut act).arg(&nl);
4632 unsafe {
4633 b.launch(cfg)?;
4634 }
4635 Ok(act)
4636 }
4637
4638 #[allow(clippy::too_many_arguments)]
4639 pub fn moe_pairs_scatter(
4640 &self,
4641 y_down: &CudaSlice<f32>,
4642 pair_w: &CudaSlice<f32>,
4643 tok_pair_off: &CudaSlice<i32>,
4644 tok_pair_ids: &CudaSlice<i32>,
4645 moe_out: &mut CudaSlice<f32>,
4646 t: usize,
4647 n_embd: usize,
4648 ) -> Result<(), Box<dyn std::error::Error>> {
4649 let f = self.func("moe_pairs_scatter");
4650 let cfg = LaunchConfig {
4651 grid_dim: (((n_embd + 255) / 256) as u32, t as u32, 1),
4652 block_dim: (256, 1, 1),
4653 shared_mem_bytes: 0,
4654 };
4655 let ne = n_embd as i32;
4656 let __s_b = self.gpu.stream();
4657 let mut b = __s_b.launch_builder(&f);
4658 b.arg(y_down)
4659 .arg(pair_w)
4660 .arg(tok_pair_off)
4661 .arg(tok_pair_ids)
4662 .arg(moe_out)
4663 .arg(&ne);
4664 unsafe {
4665 b.launch(cfg)?;
4666 }
4667 Ok(())
4668 }
4669
4670 #[allow(clippy::too_many_arguments)]
4674 pub fn moe_gate_up_gelu8_dev_q8(
4675 &self,
4676 table: &CudaSlice<u64>,
4677 sel: &cudarc::driver::CudaView<i32>,
4678 aq: &CudaSlice<i8>,
4679 ad: &CudaSlice<f32>,
4680 in_f: usize,
4681 n_ff: usize,
4682 n_used: usize,
4683 n_expert: usize,
4684 qt_g: i32,
4685 qt_u: i32,
4686 rb_g: usize,
4687 rb_u: usize,
4688 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4689 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
4690 let (inf, nff, ne, rbg, rbu) = (
4691 in_f as i32,
4692 n_ff as i32,
4693 n_expert as i32,
4694 rb_g as i64,
4695 rb_u as i64,
4696 );
4697 let f = self.func("moe_gate_up_gelu8_dev_q8");
4698 let cfg = LaunchConfig {
4699 grid_dim: (n_ff as u32, n_used as u32, 1),
4700 block_dim: (32, 1, 1),
4701 shared_mem_bytes: 0,
4702 };
4703 let __s_b = self.gpu.stream();
4704 let mut b = __s_b.launch_builder(&f);
4705 b.arg(table)
4706 .arg(sel)
4707 .arg(aq)
4708 .arg(ad)
4709 .arg(&mut act)
4710 .arg(&inf)
4711 .arg(&nff)
4712 .arg(&ne)
4713 .arg(&qt_g)
4714 .arg(&qt_u)
4715 .arg(&rbg)
4716 .arg(&rbu);
4717 unsafe {
4718 b.launch(cfg)?;
4719 }
4720 Ok(act)
4721 }
4722
4723 #[allow(clippy::too_many_arguments)]
4725 pub fn moe_gate_up_gelu8_dev_q8_rows(
4726 &self,
4727 table: &CudaSlice<u64>,
4728 sel: &CudaSlice<i32>,
4729 aq: &CudaSlice<i8>,
4730 ad: &CudaSlice<f32>,
4731 t: usize,
4732 in_f: usize,
4733 n_ff: usize,
4734 n_used: usize,
4735 n_expert: usize,
4736 qt_g: i32,
4737 qt_u: i32,
4738 rb_g: usize,
4739 rb_u: usize,
4740 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4741 let mut act = self.alloc_uninit::<f32>(t * n_used * n_ff)?;
4742 let (inf, nff, ne, rbg, rbu, nu) = (
4743 in_f as i32,
4744 n_ff as i32,
4745 n_expert as i32,
4746 rb_g as i64,
4747 rb_u as i64,
4748 n_used as i32,
4749 );
4750 let f = self.func("moe_gate_up_gelu8_dev_q8_rows");
4751 let cfg = LaunchConfig {
4752 grid_dim: (n_ff as u32, n_used as u32, t as u32),
4753 block_dim: (32, 1, 1),
4754 shared_mem_bytes: 0,
4755 };
4756 let __s_b = self.gpu.stream();
4757 let mut b = __s_b.launch_builder(&f);
4758 b.arg(table)
4759 .arg(sel)
4760 .arg(aq)
4761 .arg(ad)
4762 .arg(&mut act)
4763 .arg(&inf)
4764 .arg(&nff)
4765 .arg(&ne)
4766 .arg(&qt_g)
4767 .arg(&qt_u)
4768 .arg(&rbg)
4769 .arg(&rbu)
4770 .arg(&nu);
4771 unsafe {
4772 b.launch(cfg)?;
4773 }
4774 Ok(act)
4775 }
4776
4777 #[allow(clippy::too_many_arguments)]
4779 pub fn moe_gate_up_gelu8_dev_q8_csr(
4780 &self,
4781 table: &CudaSlice<u64>,
4782 sel: &CudaSlice<i32>,
4783 aq: &CudaSlice<i8>,
4784 ad: &CudaSlice<f32>,
4785 n_pairs: usize,
4786 in_f: usize,
4787 n_ff: usize,
4788 n_used: usize,
4789 n_expert: usize,
4790 qt_g: i32,
4791 qt_u: i32,
4792 rb_g: usize,
4793 rb_u: usize,
4794 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4795 let mut act = self.alloc_uninit::<f32>(n_pairs * n_ff)?;
4796 let (inf, nff, ne, rbg, rbu, nu, npi) = (
4797 in_f as i32,
4798 n_ff as i32,
4799 n_expert as i32,
4800 rb_g as i64,
4801 rb_u as i64,
4802 n_used as i32,
4803 n_pairs as i32,
4804 );
4805 let f = self.func("moe_gate_up_gelu8_dev_q8_csr");
4806 let cfg = LaunchConfig {
4807 grid_dim: (n_ff as u32, n_pairs as u32, 1),
4808 block_dim: (32, 1, 1),
4809 shared_mem_bytes: 0,
4810 };
4811 let __s_b = self.gpu.stream();
4812 let mut b = __s_b.launch_builder(&f);
4813 b.arg(table)
4814 .arg(sel)
4815 .arg(aq)
4816 .arg(ad)
4817 .arg(&mut act)
4818 .arg(&inf)
4819 .arg(&nff)
4820 .arg(&ne)
4821 .arg(&qt_g)
4822 .arg(&qt_u)
4823 .arg(&rbg)
4824 .arg(&rbu)
4825 .arg(&nu)
4826 .arg(&npi);
4827 unsafe {
4828 b.launch(cfg)?;
4829 }
4830 Ok(act)
4831 }
4832
4833 #[allow(clippy::too_many_arguments)]
4835 pub fn moe_down8_fma_dev_q8_rows_g(
4836 &self,
4837 table: &CudaSlice<u64>,
4838 sel: &CudaSlice<i32>,
4839 w: &CudaSlice<f32>,
4840 aq2: &CudaSlice<i8>,
4841 ad2: &CudaSlice<f32>,
4842 dst: &mut CudaSlice<f32>,
4843 t: usize,
4844 in_f: usize,
4845 out_f: usize,
4846 n_used: usize,
4847 n_expert: usize,
4848 qt: i32,
4849 rb: usize,
4850 ) -> Result<(), Box<dyn std::error::Error>> {
4851 let (inf, outf, nu, ne, rbi) = (
4852 in_f as i32,
4853 out_f as i32,
4854 n_used as i32,
4855 n_expert as i32,
4856 rb as i64,
4857 );
4858 let step_b1_w8 = t == 1 && in_f == 1280 && out_f == 4096 && n_used == 8 && qt == QT_IQ4_XS;
4862 let f = self.func(if step_b1_w8 {
4863 "moe_down8_fma_dev_q8_rows_w8"
4864 } else {
4865 "moe_down8_fma_dev_q8_rows_g"
4866 });
4867 let cfg = LaunchConfig {
4868 grid_dim: (out_f as u32, 1, t as u32),
4869 block_dim: (32, if step_b1_w8 { 8 } else { 1 }, 1),
4870 shared_mem_bytes: 0,
4871 };
4872 let __s_b = self.gpu.stream();
4873 let mut b = __s_b.launch_builder(&f);
4874 b.arg(table)
4875 .arg(sel)
4876 .arg(w)
4877 .arg(aq2)
4878 .arg(ad2)
4879 .arg(dst)
4880 .arg(&inf)
4881 .arg(&outf)
4882 .arg(&nu)
4883 .arg(&ne)
4884 .arg(&qt)
4885 .arg(&rbi);
4886 unsafe {
4887 b.launch(cfg)?;
4888 }
4889 Ok(())
4890 }
4891
4892 pub fn rp_probe_q4(&self, m: usize) -> Result<(f64, f64), Box<dyn std::error::Error>> {
4896 let (out_f, in_f) = (2048usize, 2816usize);
4897 let nblk = in_f / 32;
4898 let mut seed = 0x9E3779B97F4A7C15u64;
4899 let mut rng = move || {
4900 seed = seed
4901 .wrapping_mul(6364136223846793005)
4902 .wrapping_add(1442695040888963407);
4903 (seed >> 33) as u8
4904 };
4905 let mut w = vec![0u8; out_f * nblk * 18];
4906 for b in w.iter_mut() {
4907 *b = rng();
4908 }
4909 for r in 0..out_f {
4910 for g in 0..nblk {
4911 let off = (r * nblk + g) * 18;
4912 w[off] = 0x00;
4913 w[off + 1] = 0x2C; }
4915 }
4916 let qplane = out_f * nblk * 16;
4917 let mut wrp = vec![0u8; w.len()];
4918 for r in 0..out_f {
4919 for g in 0..nblk {
4920 let src = &w[(r * nblk + g) * 18..(r * nblk + g) * 18 + 18];
4921 wrp[qplane + (r * nblk + g) * 2..qplane + (r * nblk + g) * 2 + 2]
4922 .copy_from_slice(&src[0..2]);
4923 wrp[(r * nblk + g) * 16..(r * nblk + g) * 16 + 16].copy_from_slice(&src[2..18]);
4924 }
4925 }
4926 let w_d = self.htod_bytes(&w)?;
4927 let wrp_d = self.htod_bytes(&wrp)?;
4928 let mut aq = vec![0i8; m * in_f];
4929 for v in aq.iter_mut() {
4930 *v = rng() as i8;
4931 }
4932 let aq_d = self.htod_i8(&aq)?;
4933 let ad_d = self.htod(&vec![0.03125f32; m * nblk])?;
4934 let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
4935 let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
4936 const RPB: u32 = 4;
4937 let cfg = LaunchConfig {
4938 grid_dim: ((out_f as u32).div_ceil(RPB), 1, 1),
4939 block_dim: (32, RPB, 1),
4940 shared_mem_bytes: 0,
4941 };
4942 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
4943 let (rb, qp) = ((nblk * 18) as i64, qplane as i64);
4944 let fb = self.func("qmatvec_q4_0_mmvq_b4");
4945 let fr = self.func("qmatvec_q4_0_mmvq_b4_rp");
4946 {
4947 let __s_b = self.gpu.stream();
4948 let mut b = __s_b.launch_builder(&fb);
4949 b.arg(&w_d)
4950 .arg(&aq_d)
4951 .arg(&ad_d)
4952 .arg(&mut y0)
4953 .arg(&inf)
4954 .arg(&outf)
4955 .arg(&mi)
4956 .arg(&rb);
4957 unsafe {
4958 b.launch(cfg)?;
4959 }
4960 let __s_b = self.gpu.stream();
4961 let mut b = __s_b.launch_builder(&fr);
4962 b.arg(&wrp_d)
4963 .arg(&aq_d)
4964 .arg(&ad_d)
4965 .arg(&mut y1)
4966 .arg(&inf)
4967 .arg(&outf)
4968 .arg(&mi)
4969 .arg(&qp);
4970 unsafe {
4971 b.launch(cfg)?;
4972 }
4973 }
4974 self.gpu.stream().synchronize()?;
4975 let (h0, h1) = (self.dtoh(&y0)?, self.dtoh(&y1)?);
4976 let nd = h0
4977 .iter()
4978 .zip(&h1)
4979 .filter(|(a, b)| a.to_bits() != b.to_bits())
4980 .count();
4981 if nd != 0 {
4982 return Err(format!("rp twin not bitwise: {nd}/{} diffs", h0.len()).into());
4983 }
4984 let mut time = |rp: bool| -> Result<f64, Box<dyn std::error::Error>> {
4985 self.gpu.stream().synchronize()?;
4986 let t0 = std::time::Instant::now();
4987 for _ in 0..500 {
4988 if rp {
4989 let __s_b = self.gpu.stream();
4990 let mut b = __s_b.launch_builder(&fr);
4991 b.arg(&wrp_d)
4992 .arg(&aq_d)
4993 .arg(&ad_d)
4994 .arg(&mut y1)
4995 .arg(&inf)
4996 .arg(&outf)
4997 .arg(&mi)
4998 .arg(&qp);
4999 unsafe {
5000 b.launch(cfg)?;
5001 }
5002 } else {
5003 let __s_b = self.gpu.stream();
5004 let mut b = __s_b.launch_builder(&fb);
5005 b.arg(&w_d)
5006 .arg(&aq_d)
5007 .arg(&ad_d)
5008 .arg(&mut y0)
5009 .arg(&inf)
5010 .arg(&outf)
5011 .arg(&mi)
5012 .arg(&rb);
5013 unsafe {
5014 b.launch(cfg)?;
5015 }
5016 }
5017 }
5018 self.gpu.stream().synchronize()?;
5019 Ok(t0.elapsed().as_secs_f64() * 1e6 / 500.0)
5020 };
5021 let _ = time(false)?;
5022 let _ = time(true)?; Ok((time(false)?, time(true)?))
5024 }
5025
5026 pub fn build_q4_rp4(
5031 &self,
5032 t: &mut crate::model::GpuTensor,
5033 ) -> Result<(), Box<dyn std::error::Error>> {
5034 use crate::model::GpuTensor;
5035 let GpuTensor::Quant {
5036 bytes,
5037 qtype,
5038 row_bytes,
5039 ne,
5040 rp4,
5041 ..
5042 } = t
5043 else {
5044 return Ok(());
5045 };
5046 if *qtype != QT_Q4_0 || rp4.is_some() || ne.len() != 2 {
5047 return Ok(());
5048 }
5049 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5050 if in_f % 32 != 0 || *row_bytes != (in_f / 32) * 18 {
5051 return Ok(());
5052 }
5053 let nblk = in_f / 32;
5054 let mut dst = self.alloc_uninit::<u8>(out_f * nblk * 18)?;
5055 let f = self.func("q4_0_split_rp_build");
5056 let n = (out_f * nblk) as i32;
5057 let cfg = LaunchConfig {
5058 grid_dim: (((out_f * nblk) as u32).div_ceil(256), 1, 1),
5059 block_dim: (256, 1, 1),
5060 shared_mem_bytes: 0,
5061 };
5062 let (of, nb) = (out_f as i32, nblk as i32);
5063 let _ = n;
5064 let __s_b = self.gpu.stream();
5065 let mut b = __s_b.launch_builder(&f);
5066 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
5067 unsafe {
5068 b.launch(cfg)?;
5069 }
5070 *rp4 = Some(dst);
5071 Ok(())
5072 }
5073
5074 pub fn build_q8_rp4(
5079 &self,
5080 t: &mut crate::model::GpuTensor,
5081 ) -> Result<(), Box<dyn std::error::Error>> {
5082 use crate::model::GpuTensor;
5083 let GpuTensor::Quant {
5084 bytes,
5085 qtype,
5086 row_bytes,
5087 ne,
5088 rp4,
5089 ..
5090 } = t
5091 else {
5092 return Ok(());
5093 };
5094 if *qtype != QT_Q8_0 || rp4.is_some() || ne.len() != 2 {
5095 return Ok(());
5096 }
5097 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5098 if in_f % 32 != 0 || *row_bytes != (in_f / 32) * 34 {
5099 return Ok(());
5100 }
5101 *rp4 = Some(self.build_q8_rp4_raw(bytes, in_f, out_f)?);
5102 Ok(())
5103 }
5104
5105 pub fn build_q8_rp4_raw(
5108 &self,
5109 bytes: &CudaSlice<u8>,
5110 in_f: usize,
5111 out_f: usize,
5112 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
5113 assert!(in_f % 32 == 0);
5114 let nblk = in_f / 32;
5115 let mut dst = self.alloc_uninit::<u8>(out_f * nblk * 34)?;
5116 let f = self.func("q8_0_split_rp_build");
5117 let cfg = LaunchConfig {
5118 grid_dim: (((out_f * nblk) as u32).div_ceil(256), 1, 1),
5119 block_dim: (256, 1, 1),
5120 shared_mem_bytes: 0,
5121 };
5122 let (of, nb) = (out_f as i32, nblk as i32);
5123 let __s_b = self.gpu.stream();
5124 let mut b = __s_b.launch_builder(&f);
5125 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
5126 unsafe {
5127 b.launch(cfg)?;
5128 }
5129 Ok(dst)
5130 }
5131
5132 pub fn build_q4k_rp4(
5140 &self,
5141 t: &mut crate::model::GpuTensor,
5142 ) -> Result<(), Box<dyn std::error::Error>> {
5143 use crate::model::GpuTensor;
5144 let GpuTensor::Quant {
5145 bytes,
5146 qtype,
5147 row_bytes,
5148 ne,
5149 rp4,
5150 ..
5151 } = t
5152 else {
5153 return Ok(());
5154 };
5155 if *qtype != QT_Q4_K || rp4.is_some() || ne.len() != 2 {
5156 return Ok(());
5157 }
5158 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5159 if in_f % 256 != 0 || *row_bytes != (in_f / 256) * 144 {
5160 return Ok(());
5161 }
5162 *rp4 = Some(self.build_kq_rp4_raw(bytes, in_f, out_f, QT_Q4_K)?);
5163 Ok(())
5164 }
5165
5166 pub fn build_q6k_rp4(
5167 &self,
5168 t: &mut crate::model::GpuTensor,
5169 ) -> Result<(), Box<dyn std::error::Error>> {
5170 use crate::model::GpuTensor;
5171 let GpuTensor::Quant {
5172 bytes,
5173 qtype,
5174 row_bytes,
5175 ne,
5176 rp4,
5177 ..
5178 } = t
5179 else {
5180 return Ok(());
5181 };
5182 if *qtype != QT_Q6_K || rp4.is_some() || ne.len() != 2 {
5183 return Ok(());
5184 }
5185 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5186 if in_f % 256 != 0 || *row_bytes != (in_f / 256) * 210 {
5187 return Ok(());
5188 }
5189 *rp4 = Some(self.build_kq_rp4_raw(bytes, in_f, out_f, QT_Q6_K)?);
5190 Ok(())
5191 }
5192
5193 pub fn build_kq_rp4_raw(
5195 &self,
5196 bytes: &CudaSlice<u8>,
5197 in_f: usize,
5198 out_f: usize,
5199 qtype: i32,
5200 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
5201 assert!(in_f % 256 == 0);
5202 let nsbk = in_f / 256;
5203 let (sb_bytes, kname) = match qtype {
5204 QT_Q4_K => (144usize, "q4_K_split_rp_build"),
5205 QT_Q6_K => (210usize, "q6_K_split_rp_build"),
5206 _ => return Err(format!("build_kq_rp4_raw: qtype {qtype} has no rp mirror").into()),
5207 };
5208 let mut dst = self.alloc_uninit::<u8>(out_f * nsbk * sb_bytes)?;
5209 let f = self.func(kname);
5210 let cfg = LaunchConfig {
5211 grid_dim: (((out_f * nsbk) as u32).div_ceil(256), 1, 1),
5212 block_dim: (256, 1, 1),
5213 shared_mem_bytes: 0,
5214 };
5215 let (of, nb) = (out_f as i32, nsbk as i32);
5216 let __s_b = self.gpu.stream();
5217 let mut b = __s_b.launch_builder(&f);
5218 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
5219 unsafe {
5220 b.launch(cfg)?;
5221 }
5222 Ok(dst)
5223 }
5224
5225 pub fn kqrp_enabled() -> bool {
5229 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5230 *ON.get_or_init(|| match std::env::var("MEMRA_KQRP").as_deref() {
5231 Ok("0") => false,
5232 Ok(_) => true,
5233 Err(_) => cfg!(memra_hopper_mma),
5234 })
5235 }
5236
5237 pub fn build_q4_rp_swap(
5243 &self,
5244 t: &mut crate::model::GpuTensor,
5245 ) -> Result<bool, Box<dyn std::error::Error>> {
5246 use crate::model::GpuTensor;
5247 if !matches!(t, GpuTensor::Quant { qtype, .. } if *qtype == QT_Q4_0) {
5257 return Ok(false);
5258 }
5259 self.build_q4_rp4(t)?;
5260 self.gpu.stream().synchronize()?; let GpuTensor::Quant { bytes, rp4, rp, .. } = t else {
5262 return Ok(false);
5263 };
5264 match rp4.take() {
5265 Some(split) => {
5266 *bytes = split; *rp = true;
5268 Ok(true)
5269 }
5270 None => Ok(false),
5271 }
5272 }
5273
5274 pub fn q4rp_enabled() -> bool {
5276 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5277 *ON.get_or_init(|| {
5278 std::env::var("MEMRA_Q4RP")
5279 .map(|v| v != "0")
5280 .unwrap_or(true)
5281 })
5282 }
5283
5284 pub fn copy_rows_strided(
5287 &self,
5288 src: &CudaSlice<f32>,
5289 dst: &mut CudaSlice<f32>,
5290 row_elems: usize,
5291 n_rows: usize,
5292 src_stride: usize,
5293 src_off: usize,
5294 ) -> Result<(), Box<dyn std::error::Error>> {
5295 let f = self.func("copy_rows_strided_f32");
5296 let cfg = LaunchConfig {
5297 grid_dim: (((row_elems as u32 + 255) / 256).max(1), n_rows as u32, 1),
5298 block_dim: (256, 1, 1),
5299 shared_mem_bytes: 0,
5300 };
5301 let (re, nr) = (row_elems as i32, n_rows as i32);
5302 let (st, off) = (src_stride as i64, src_off as i64);
5303 let __s_b = self.gpu.stream();
5304 let mut b = __s_b.launch_builder(&f);
5305 b.arg(src)
5306 .arg(&mut *dst)
5307 .arg(&re)
5308 .arg(&nr)
5309 .arg(&st)
5310 .arg(&off);
5311 unsafe {
5312 b.launch(cfg)?;
5313 }
5314 Ok(())
5315 }
5316
5317 pub fn u32_set_k(
5319 &self,
5320 dst: &mut CudaSlice<u32>,
5321 v: u32,
5322 idx: usize,
5323 ) -> Result<(), Box<dyn std::error::Error>> {
5324 let f = self.func("u32_set_k");
5325 let cfg = LaunchConfig {
5326 grid_dim: (1, 1, 1),
5327 block_dim: (1, 1, 1),
5328 shared_mem_bytes: 0,
5329 };
5330 let ii = idx as i32;
5331 let __s_b = self.gpu.stream();
5332 let mut b = __s_b.launch_builder(&f);
5333 b.arg(dst).arg(&v).arg(&ii);
5334 unsafe {
5335 b.launch(cfg)?;
5336 }
5337 Ok(())
5338 }
5339
5340 pub fn i32_add_k(
5342 &self,
5343 d: &mut CudaSlice<i32>,
5344 v: i32,
5345 ) -> Result<(), Box<dyn std::error::Error>> {
5346 let f = self.func("i32_add_k");
5347 let cfg = LaunchConfig {
5348 grid_dim: (1, 1, 1),
5349 block_dim: (32, 1, 1),
5350 shared_mem_bytes: 0,
5351 };
5352 let __s_b = self.gpu.stream();
5353 let mut b = __s_b.launch_builder(&f);
5354 b.arg(d).arg(&v);
5355 unsafe {
5356 b.launch(cfg)?;
5357 }
5358 Ok(())
5359 }
5360
5361 pub fn i32_iota_from(
5363 &self,
5364 ctr: &CudaSlice<i32>,
5365 dst: &mut CudaSlice<i32>,
5366 n: usize,
5367 ) -> Result<(), Box<dyn std::error::Error>> {
5368 let f = self.func("i32_iota_from");
5369 let cfg = LaunchConfig::for_num_elems(n as u32);
5370 let ni = n as i32;
5371 let __s_b = self.gpu.stream();
5372 let mut b = __s_b.launch_builder(&f);
5373 b.arg(ctr).arg(dst).arg(&ni);
5374 unsafe {
5375 b.launch(cfg)?;
5376 }
5377 Ok(())
5378 }
5379
5380 pub fn u32_map_k(
5382 &self,
5383 buf: &mut CudaSlice<u32>,
5384 map: &CudaSlice<u32>,
5385 idx: usize,
5386 ) -> Result<(), Box<dyn std::error::Error>> {
5387 let f = self.func("u32_map_k");
5388 let cfg = LaunchConfig {
5389 grid_dim: (1, 1, 1),
5390 block_dim: (1, 1, 1),
5391 shared_mem_bytes: 0,
5392 };
5393 let ii = idx as i32;
5394 let __s_b = self.gpu.stream();
5395 let mut b = __s_b.launch_builder(&f);
5396 b.arg(buf).arg(map).arg(&ii);
5397 unsafe {
5398 b.launch(cfg)?;
5399 }
5400 Ok(())
5401 }
5402
5403 #[allow(clippy::too_many_arguments)]
5405 pub fn u32_pack2(
5406 &self,
5407 a: &CudaSlice<u32>,
5408 off_a: usize,
5409 n1: usize,
5410 b_in: &CudaSlice<u32>,
5411 n2: usize,
5412 out: &mut CudaSlice<u32>,
5413 ) -> Result<(), Box<dyn std::error::Error>> {
5414 let f = self.func("u32_pack2");
5415 let cfg = LaunchConfig::for_num_elems((n1 + n2) as u32);
5416 let (oa, i1, i2) = (off_a as i32, n1 as i32, n2 as i32);
5417 let __s_b = self.gpu.stream();
5418 let mut b = __s_b.launch_builder(&f);
5419 b.arg(a).arg(&oa).arg(&i1).arg(b_in).arg(&i2).arg(out);
5420 unsafe {
5421 b.launch(cfg)?;
5422 }
5423 Ok(())
5424 }
5425
5426 pub fn moe_w_exscale(
5428 &self,
5429 w: &mut CudaSlice<f32>,
5430 sel: &CudaSlice<i32>,
5431 s: &CudaSlice<f32>,
5432 n: usize,
5433 ) -> Result<(), Box<dyn std::error::Error>> {
5434 let f = self.func("moe_w_exscale");
5435 let cfg = LaunchConfig::for_num_elems(n as u32);
5436 let ni = n as i32;
5437 let __s_b = self.gpu.stream();
5438 let mut b = __s_b.launch_builder(&f);
5439 b.arg(w).arg(sel).arg(s).arg(&ni);
5440 unsafe {
5441 b.launch(cfg)?;
5442 }
5443 Ok(())
5444 }
5445
5446 pub fn moe_w_scale_by_expert(
5449 &self,
5450 w: &mut CudaSlice<f32>,
5451 sel: &CudaSlice<i32>,
5452 macros: &CudaSlice<f32>,
5453 n_expert: usize,
5454 n: usize,
5455 ) -> Result<(), Box<dyn std::error::Error>> {
5456 let f = self.func("moe_w_scale_by_expert");
5457 let cfg = LaunchConfig {
5458 grid_dim: (n.div_ceil(64) as u32, 1, 1),
5459 block_dim: (64, 1, 1),
5460 shared_mem_bytes: 0,
5461 };
5462 let (ne, nn) = (n_expert as i32, n as i32);
5463 let __s_b = self.gpu.stream();
5464 let mut b = __s_b.launch_builder(&f);
5465 b.arg(w).arg(sel).arg(macros).arg(&ne).arg(&nn);
5466 unsafe {
5467 b.launch(cfg)?;
5468 }
5469 Ok(())
5470 }
5471
5472 pub fn moe_gate_up_silu8_dev_q8(
5473 &self,
5474 table: &CudaSlice<u64>,
5475 sel: &cudarc::driver::CudaView<i32>,
5476 aq: &CudaSlice<i8>,
5477 ad: &CudaSlice<f32>,
5478 in_f: usize,
5479 n_ff: usize,
5480 n_used: usize,
5481 n_expert: usize,
5482 qt_g: i32,
5483 qt_u: i32,
5484 rb_g: usize,
5485 rb_u: usize,
5486 macros: &CudaSlice<f32>,
5487 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5488 static GU: std::sync::OnceLock<(String, u32)> = std::sync::OnceLock::new();
5489 let (mode, wpb) = GU.get_or_init(|| {
5490 let mode = std::env::var("MEMRA_MOE_DEVQ8_GU").unwrap_or_default();
5491 let wpb = std::env::var("MEMRA_MOE_DEVQ8_WPB")
5492 .ok()
5493 .and_then(|v| v.parse().ok())
5494 .unwrap_or(4u32)
5495 .clamp(1, 16);
5496 (mode, wpb)
5497 });
5498 let (mode, wpb) = (mode.as_str(), *wpb);
5499 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
5500 let (inf, nff, ne, rbg, rbu) = (
5501 in_f as i32,
5502 n_ff as i32,
5503 n_expert as i32,
5504 rb_g as i64,
5505 rb_u as i64,
5506 );
5507 let (f, cfg) = match mode {
5508 "1" | "2" | "4" => {
5509 let rpw: u32 = mode.parse().unwrap();
5510 let f = self.func(match rpw {
5511 1 => "moe_gate_up_silu8_dev_q8_r1",
5512 2 => "moe_gate_up_silu8_dev_q8_r2",
5513 _ => "moe_gate_up_silu8_dev_q8_r4",
5514 });
5515 let rows_per_block = (rpw * wpb) as usize;
5516 let gx = n_ff.div_ceil(rows_per_block) as u32;
5517 (
5518 f,
5519 LaunchConfig {
5520 grid_dim: (gx, n_used as u32, 1),
5521 block_dim: (32, wpb, 1),
5522 shared_mem_bytes: 0,
5523 },
5524 )
5525 }
5526 "j8" if n_used <= 32 => (
5527 self.func("moe_gate_up_silu8_dev_q8_j8"),
5528 LaunchConfig {
5529 grid_dim: (n_ff as u32, 1, 1),
5530 block_dim: (32, n_used as u32, 1),
5531 shared_mem_bytes: 0,
5532 },
5533 ),
5534 "vsm2" => {
5536 let f = self.func("moe_gate_up_silu8_dev_q8_vsm2");
5537 let sh = (rb_g + rb_u) as u32;
5538 use cudarc::driver::sys::CUfunction_attribute_enum as A;
5539 f.set_attribute(
5540 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
5541 sh as i32,
5542 )?;
5543 (
5544 f,
5545 LaunchConfig {
5546 grid_dim: (n_ff as u32, n_used as u32, 1),
5547 block_dim: (32, 1, 1),
5548 shared_mem_bytes: sh,
5549 },
5550 )
5551 }
5552 "vsm" => {
5553 let f = self.func("moe_gate_up_silu8_dev_q8_vsm");
5554 let sh = (rb_g + rb_u) as u32;
5555 use cudarc::driver::sys::CUfunction_attribute_enum as A;
5556 f.set_attribute(
5557 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
5558 sh as i32,
5559 )?;
5560 (
5561 f,
5562 LaunchConfig {
5563 grid_dim: (n_ff as u32, n_used as u32, 1),
5564 block_dim: (32, 1, 1),
5565 shared_mem_bytes: sh,
5566 },
5567 )
5568 }
5569 "sg" => (
5570 self.func("moe_gate_up_silu8_dev_q8_sg"),
5571 LaunchConfig {
5572 grid_dim: (n_ff as u32, n_used as u32, 1),
5573 block_dim: (32, 1, 1),
5574 shared_mem_bytes: 0,
5575 },
5576 ),
5577 "j8sg" if n_used <= 32 => (
5578 self.func("moe_gate_up_silu8_dev_q8_j8sg"),
5579 LaunchConfig {
5580 grid_dim: (n_ff as u32, 1, 1),
5581 block_dim: (32, n_used as u32, 1),
5582 shared_mem_bytes: 0,
5583 },
5584 ),
5585 "u64" if in_f == 2048 => (
5586 self.func("moe_gate_up_silu8_dev_q8_u64"),
5587 LaunchConfig {
5588 grid_dim: (n_ff as u32, n_used as u32, 1),
5589 block_dim: (32, 1, 1),
5590 shared_mem_bytes: 0,
5591 },
5592 ),
5593 "gs4" if in_f == 2048 => (
5594 self.func("moe_gate_up_silu8_dev_q8_gs4"),
5595 LaunchConfig {
5596 grid_dim: (n_ff as u32, n_used as u32, 1),
5597 block_dim: (32, 4, 1),
5598 shared_mem_bytes: 0,
5599 },
5600 ),
5601 "v" | "" => (
5603 self.func("moe_gate_up_silu8_dev_q8_v"),
5604 LaunchConfig {
5605 grid_dim: (n_ff as u32, n_used as u32, 1),
5606 block_dim: (32, 1, 1),
5607 shared_mem_bytes: 0,
5608 },
5609 ),
5610 "s2" => (
5611 self.func("moe_gate_up_silu8_dev_q8_s2"),
5612 LaunchConfig {
5613 grid_dim: (n_ff as u32, n_used as u32, 1),
5614 block_dim: (32, 2, 1),
5615 shared_mem_bytes: 0,
5616 },
5617 ),
5618 "s2z" => {
5619 let rz = wpb.min(16); (
5621 self.func("moe_gate_up_silu8_dev_q8_s2z"),
5622 LaunchConfig {
5623 grid_dim: (n_ff.div_ceil(rz as usize) as u32, n_used as u32, 1),
5624 block_dim: (32, 2, rz),
5625 shared_mem_bytes: 0,
5626 },
5627 )
5628 }
5629 _ => (
5630 self.func("moe_gate_up_silu8_dev_q8"),
5631 LaunchConfig {
5632 grid_dim: (n_ff as u32, n_used as u32, 1),
5633 block_dim: (32, 1, 1),
5634 shared_mem_bytes: 0,
5635 },
5636 ),
5637 };
5638 let __s_b = self.gpu.stream();
5639 let mut b = __s_b.launch_builder(&f);
5640 b.arg(table)
5641 .arg(sel)
5642 .arg(aq)
5643 .arg(ad)
5644 .arg(&mut act)
5645 .arg(&inf)
5646 .arg(&nff)
5647 .arg(&ne)
5648 .arg(&qt_g)
5649 .arg(&qt_u)
5650 .arg(&rbg)
5651 .arg(&rbu)
5652 .arg(macros);
5653 unsafe {
5654 b.launch(cfg)?;
5655 }
5656 Ok(act)
5657 }
5658
5659 #[allow(clippy::too_many_arguments)]
5660 pub fn moe_down8_fma_dev_q8(
5661 &self,
5662 table: &CudaSlice<u64>,
5663 sel: &cudarc::driver::CudaView<i32>,
5664 w: &cudarc::driver::CudaView<f32>,
5665 aq2: &CudaSlice<i8>,
5666 ad2: &CudaSlice<f32>,
5667 dst: &mut cudarc::driver::CudaViewMut<f32>,
5668 in_f: usize,
5669 out_f: usize,
5670 n_used: usize,
5671 n_expert: usize,
5672 qt: i32,
5673 rb: usize,
5674 ) -> Result<(), Box<dyn std::error::Error>> {
5675 static DOWN: std::sync::OnceLock<String> = std::sync::OnceLock::new();
5676 let mode = DOWN.get_or_init(|| std::env::var("MEMRA_MOE_DEVQ8_DOWN").unwrap_or_default());
5677 let (inf, outf, nu, ne, rbi) = (
5678 in_f as i32,
5679 out_f as i32,
5680 n_used as i32,
5681 n_expert as i32,
5682 rb as i64,
5683 );
5684 let (f, cfg) = match mode.as_str() {
5687 m @ ("1" | "2" | "4") if n_used <= 8 => {
5688 let rpw: usize = m.parse().unwrap();
5689 let f = self.func(match rpw {
5690 1 => "moe_down8_fma_dev_q8_w8r1",
5691 2 => "moe_down8_fma_dev_q8_w8r2",
5692 _ => "moe_down8_fma_dev_q8_w8r4",
5693 });
5694 (
5695 f,
5696 LaunchConfig {
5697 grid_dim: (out_f.div_ceil(rpw) as u32, 1, 1),
5698 block_dim: (32, n_used as u32, 1),
5699 shared_mem_bytes: 0,
5700 },
5701 )
5702 }
5703 "h2" if in_f == 512 => (
5704 self.func("moe_down8_fma_dev_q8_h2"),
5705 LaunchConfig {
5706 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5707 block_dim: (32, 1, 1),
5708 shared_mem_bytes: 0,
5709 },
5710 ),
5711 "" if in_f == 704 && n_used <= 8 => (
5714 self.func("moe_down8_fma_dev_q8_w8r2"),
5715 LaunchConfig {
5716 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5717 block_dim: (32, n_used as u32, 1),
5718 shared_mem_bytes: 0,
5719 },
5720 ),
5721 "w8h2v" | "" if in_f == 512 && n_used <= 8 => (
5725 self.func("moe_down8_fma_dev_q8_w8h2v"),
5726 LaunchConfig {
5727 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5728 block_dim: (32, n_used as u32, 1),
5729 shared_mem_bytes: 0,
5730 },
5731 ),
5732 "w8h2r2v" if in_f == 512 && n_used <= 8 => (
5733 self.func("moe_down8_fma_dev_q8_w8h2r2v"),
5734 LaunchConfig {
5735 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
5736 block_dim: (32, n_used as u32, 1),
5737 shared_mem_bytes: 0,
5738 },
5739 ),
5740 "w8h2r2" if in_f == 512 && n_used <= 8 => (
5741 self.func("moe_down8_fma_dev_q8_w8h2r2"),
5742 LaunchConfig {
5743 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
5744 block_dim: (32, n_used as u32, 1),
5745 shared_mem_bytes: 0,
5746 },
5747 ),
5748 "w8h2" if in_f == 512 && n_used <= 8 => (
5749 self.func("moe_down8_fma_dev_q8_w8h2"),
5750 LaunchConfig {
5751 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5752 block_dim: (32, n_used as u32, 1),
5753 shared_mem_bytes: 0,
5754 },
5755 ),
5756 _ => (
5757 self.func("moe_down8_fma_dev_q8"),
5758 LaunchConfig {
5759 grid_dim: (out_f as u32, 1, 1),
5760 block_dim: (32, 1, 1),
5761 shared_mem_bytes: 0,
5762 },
5763 ),
5764 };
5765 let __s_b = self.gpu.stream();
5766 let mut b = __s_b.launch_builder(&f);
5767 b.arg(table)
5768 .arg(sel)
5769 .arg(w)
5770 .arg(aq2)
5771 .arg(ad2)
5772 .arg(dst)
5773 .arg(&inf)
5774 .arg(&outf)
5775 .arg(&nu)
5776 .arg(&ne)
5777 .arg(&qt)
5778 .arg(&rbi);
5779 unsafe {
5780 b.launch(cfg)?;
5781 }
5782 Ok(())
5783 }
5784
5785 #[allow(clippy::too_many_arguments)]
5792 pub fn moe_gate_up_silu8_dev_q8_rows(
5793 &self,
5794 table: &CudaSlice<u64>,
5795 sel: &CudaSlice<i32>,
5796 aq: &CudaSlice<i8>,
5797 ad: &CudaSlice<f32>,
5798 t: usize,
5799 in_f: usize,
5800 n_ff: usize,
5801 n_used: usize,
5802 n_expert: usize,
5803 qt_g: i32,
5804 qt_u: i32,
5805 rb_g: usize,
5806 rb_u: usize,
5807 macros: &CudaSlice<f32>,
5808 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5809 let f = self.func("moe_gate_up_silu8_dev_q8_v_rows");
5810 let mut act = self.alloc_uninit::<f32>(t * n_used * n_ff)?;
5811 let cfg = LaunchConfig {
5812 grid_dim: (n_ff as u32, n_used as u32, t as u32),
5813 block_dim: (32, 1, 1),
5814 shared_mem_bytes: 0,
5815 };
5816 let (inf, nff, ne, nu, rbg, rbu) = (
5817 in_f as i32,
5818 n_ff as i32,
5819 n_expert as i32,
5820 n_used as i32,
5821 rb_g as i64,
5822 rb_u as i64,
5823 );
5824 let __s_b = self.gpu.stream();
5825 let mut b = __s_b.launch_builder(&f);
5826 b.arg(table)
5827 .arg(sel)
5828 .arg(aq)
5829 .arg(ad)
5830 .arg(&mut act)
5831 .arg(&inf)
5832 .arg(&nff)
5833 .arg(&ne)
5834 .arg(&qt_g)
5835 .arg(&qt_u)
5836 .arg(&rbg)
5837 .arg(&rbu)
5838 .arg(&nu)
5839 .arg(macros);
5840 unsafe {
5841 b.launch(cfg)?;
5842 }
5843 Ok(act)
5844 }
5845
5846 #[allow(clippy::too_many_arguments)]
5851 pub fn moe_down8_fma_dev_q8_rows(
5852 &self,
5853 table: &CudaSlice<u64>,
5854 sel: &CudaSlice<i32>,
5855 w: &CudaSlice<f32>,
5856 aq2: &CudaSlice<i8>,
5857 ad2: &CudaSlice<f32>,
5858 dst: &mut CudaSlice<f32>,
5859 t: usize,
5860 in_f: usize,
5861 out_f: usize,
5862 n_used: usize,
5863 n_expert: usize,
5864 qt: i32,
5865 rb: usize,
5866 ) -> Result<(), Box<dyn std::error::Error>> {
5867 assert!(
5868 in_f == 512 && n_used <= 8,
5869 "down rows twin is w8h2v shape-gated"
5870 );
5871 let f = self.func("moe_down8_fma_dev_q8_w8h2v_rows");
5872 let cfg = LaunchConfig {
5873 grid_dim: (out_f.div_ceil(2) as u32, 1, t as u32),
5874 block_dim: (32, n_used as u32, 1),
5875 shared_mem_bytes: 0,
5876 };
5877 let (inf, outf, nu, ne, rbi) = (
5878 in_f as i32,
5879 out_f as i32,
5880 n_used as i32,
5881 n_expert as i32,
5882 rb as i64,
5883 );
5884 let __s_b = self.gpu.stream();
5885 let mut b = __s_b.launch_builder(&f);
5886 b.arg(table)
5887 .arg(sel)
5888 .arg(w)
5889 .arg(aq2)
5890 .arg(ad2)
5891 .arg(dst)
5892 .arg(&inf)
5893 .arg(&outf)
5894 .arg(&nu)
5895 .arg(&ne)
5896 .arg(&qt)
5897 .arg(&rbi);
5898 unsafe {
5899 b.launch(cfg)?;
5900 }
5901 Ok(())
5902 }
5903
5904 #[allow(clippy::too_many_arguments)]
5908 pub fn moe_gate_up_silu8_dev_q8_csr(
5909 &self,
5910 table: &CudaSlice<u64>,
5911 sel: &CudaSlice<i32>,
5912 aq: &CudaSlice<i8>,
5913 ad: &CudaSlice<f32>,
5914 n_pairs: usize,
5915 in_f: usize,
5916 n_ff: usize,
5917 n_used: usize,
5918 n_expert: usize,
5919 qt_g: i32,
5920 qt_u: i32,
5921 rb_g: usize,
5922 rb_u: usize,
5923 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5924 let f = self.func("moe_gate_up_silu8_dev_q8_csr_iq4");
5925 let mut act = self.alloc_uninit::<f32>(n_pairs * n_ff)?;
5926 let cfg = LaunchConfig {
5927 grid_dim: (n_ff as u32, n_pairs as u32, 1),
5928 block_dim: (32, 1, 1),
5929 shared_mem_bytes: 0,
5930 };
5931 let (inf, nff, ne, nu, npi, rbg, rbu) = (
5932 in_f as i32,
5933 n_ff as i32,
5934 n_expert as i32,
5935 n_used as i32,
5936 n_pairs as i32,
5937 rb_g as i64,
5938 rb_u as i64,
5939 );
5940 let __s_b = self.gpu.stream();
5941 let mut b = __s_b.launch_builder(&f);
5942 b.arg(table)
5943 .arg(sel)
5944 .arg(aq)
5945 .arg(ad)
5946 .arg(&mut act)
5947 .arg(&inf)
5948 .arg(&nff)
5949 .arg(&ne)
5950 .arg(&qt_g)
5951 .arg(&qt_u)
5952 .arg(&rbg)
5953 .arg(&rbu)
5954 .arg(&nu)
5955 .arg(&npi);
5956 unsafe {
5957 b.launch(cfg)?;
5958 }
5959 Ok(act)
5960 }
5961
5962 #[allow(clippy::too_many_arguments)]
5966 pub fn moe_down8_fma_dev_q8_variant(
5967 &self,
5968 variant: &str,
5969 table: &CudaSlice<u64>,
5970 sel: &cudarc::driver::CudaView<i32>,
5971 w: &cudarc::driver::CudaView<f32>,
5972 aq2: &CudaSlice<i8>,
5973 ad2: &CudaSlice<f32>,
5974 dst: &mut cudarc::driver::CudaViewMut<f32>,
5975 in_f: usize,
5976 out_f: usize,
5977 n_used: usize,
5978 n_expert: usize,
5979 qt: i32,
5980 rb: usize,
5981 ) -> Result<(), Box<dyn std::error::Error>> {
5982 let (inf, outf, nu, ne, rbi) = (
5983 in_f as i32,
5984 out_f as i32,
5985 n_used as i32,
5986 n_expert as i32,
5987 rb as i64,
5988 );
5989 let (f, cfg) = match variant {
5990 "w8h2" | "w8h2v" => (
5991 self.func(if variant == "w8h2" {
5992 "moe_down8_fma_dev_q8_w8h2"
5993 } else {
5994 "moe_down8_fma_dev_q8_w8h2v"
5995 }),
5996 LaunchConfig {
5997 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
5998 block_dim: (32, n_used as u32, 1),
5999 shared_mem_bytes: 0,
6000 },
6001 ),
6002 "w8h2r2" | "w8h2r2v" => (
6003 self.func(if variant == "w8h2r2" {
6004 "moe_down8_fma_dev_q8_w8h2r2"
6005 } else {
6006 "moe_down8_fma_dev_q8_w8h2r2v"
6007 }),
6008 LaunchConfig {
6009 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
6010 block_dim: (32, n_used as u32, 1),
6011 shared_mem_bytes: 0,
6012 },
6013 ),
6014 _ => (
6015 self.func("moe_down8_fma_dev_q8"),
6016 LaunchConfig {
6017 grid_dim: (out_f as u32, 1, 1),
6018 block_dim: (32, 1, 1),
6019 shared_mem_bytes: 0,
6020 },
6021 ),
6022 };
6023 let __s_b = self.gpu.stream();
6024 let mut b = __s_b.launch_builder(&f);
6025 b.arg(table)
6026 .arg(sel)
6027 .arg(w)
6028 .arg(aq2)
6029 .arg(ad2)
6030 .arg(dst)
6031 .arg(&inf)
6032 .arg(&outf)
6033 .arg(&nu)
6034 .arg(&ne)
6035 .arg(&qt)
6036 .arg(&rbi);
6037 unsafe {
6038 b.launch(cfg)?;
6039 }
6040 Ok(())
6041 }
6042
6043 #[allow(clippy::too_many_arguments)]
6045 pub fn moe_gate_up_silu8_dev_q8_variant(
6046 &self,
6047 variant: &str,
6048 table: &CudaSlice<u64>,
6049 sel: &cudarc::driver::CudaView<i32>,
6050 aq: &CudaSlice<i8>,
6051 ad: &CudaSlice<f32>,
6052 in_f: usize,
6053 n_ff: usize,
6054 n_used: usize,
6055 n_expert: usize,
6056 qt_g: i32,
6057 qt_u: i32,
6058 rb_g: usize,
6059 rb_u: usize,
6060 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6061 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
6062 let (inf, nff, ne, rbg, rbu) = (
6063 in_f as i32,
6064 n_ff as i32,
6065 n_expert as i32,
6066 rb_g as i64,
6067 rb_u as i64,
6068 );
6069 let f = self.func(if variant == "v" {
6070 "moe_gate_up_silu8_dev_q8_v"
6071 } else {
6072 "moe_gate_up_silu8_dev_q8"
6073 });
6074 let cfg = LaunchConfig {
6075 grid_dim: (n_ff as u32, n_used as u32, 1),
6076 block_dim: (32, 1, 1),
6077 shared_mem_bytes: 0,
6078 };
6079 let __s_b = self.gpu.stream();
6080 let mut b = __s_b.launch_builder(&f);
6081 b.arg(table)
6082 .arg(sel)
6083 .arg(aq)
6084 .arg(ad)
6085 .arg(&mut act)
6086 .arg(&inf)
6087 .arg(&nff)
6088 .arg(&ne)
6089 .arg(&qt_g)
6090 .arg(&qt_u)
6091 .arg(&rbg)
6092 .arg(&rbu);
6093 unsafe {
6094 b.launch(cfg)?;
6095 }
6096 Ok(act)
6097 }
6098
6099 pub fn moe_gate_up_silu8_dev(
6100 &self,
6101 table: &CudaSlice<u64>,
6102 sel: &cudarc::driver::CudaView<i32>,
6103 x: &cudarc::driver::CudaView<f32>,
6104 in_f: usize,
6105 n_ff: usize,
6106 n_used: usize,
6107 n_expert: usize,
6108 qt_g: i32,
6109 qt_u: i32,
6110 rb_g: usize,
6111 rb_u: usize,
6112 macros: &CudaSlice<f32>,
6113 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6114 let f = self.func("moe_gate_up_silu8_dev");
6115 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?; let cfg = LaunchConfig {
6117 grid_dim: (n_ff as u32, n_used as u32, 1),
6118 block_dim: (256, 1, 1),
6119 shared_mem_bytes: 0,
6120 };
6121 let (inf, nff, ne, rbg, rbu) = (
6122 in_f as i32,
6123 n_ff as i32,
6124 n_expert as i32,
6125 rb_g as i64,
6126 rb_u as i64,
6127 );
6128 let __s_b = self.gpu.stream();
6129 let mut b = __s_b.launch_builder(&f);
6130 b.arg(table)
6131 .arg(sel)
6132 .arg(x)
6133 .arg(&mut act)
6134 .arg(&inf)
6135 .arg(&nff)
6136 .arg(&ne)
6137 .arg(&qt_g)
6138 .arg(&qt_u)
6139 .arg(&rbg)
6140 .arg(&rbu)
6141 .arg(macros);
6142 unsafe {
6143 b.launch(cfg)?;
6144 }
6145 Ok(act)
6146 }
6147
6148 #[allow(clippy::too_many_arguments)]
6151 pub fn moe_down8_fma_dev(
6152 &self,
6153 table: &CudaSlice<u64>,
6154 sel: &cudarc::driver::CudaView<i32>,
6155 w: &cudarc::driver::CudaView<f32>,
6156 act: &CudaSlice<f32>,
6157 dst: &mut cudarc::driver::CudaViewMut<f32>,
6158 in_f: usize,
6159 out_f: usize,
6160 n_used: usize,
6161 n_expert: usize,
6162 qt: i32,
6163 rb: usize,
6164 ) -> Result<(), Box<dyn std::error::Error>> {
6165 let f = self.func("moe_down8_fma_dev");
6166 let cfg = LaunchConfig {
6167 grid_dim: (out_f as u32, 1, 1),
6168 block_dim: (256, 1, 1),
6169 shared_mem_bytes: 0,
6170 };
6171 let (inf, outf, nu, ne, rbv) = (
6172 in_f as i32,
6173 out_f as i32,
6174 n_used as i32,
6175 n_expert as i32,
6176 rb as i64,
6177 );
6178 let __s_b = self.gpu.stream();
6179 let mut b = __s_b.launch_builder(&f);
6180 b.arg(table)
6181 .arg(sel)
6182 .arg(w)
6183 .arg(act)
6184 .arg(dst)
6185 .arg(&inf)
6186 .arg(&outf)
6187 .arg(&nu)
6188 .arg(&ne)
6189 .arg(&qt)
6190 .arg(&rbv);
6191 unsafe {
6192 b.launch(cfg)?;
6193 }
6194 Ok(())
6195 }
6196
6197 pub fn axpy_into(
6199 &self,
6200 src: &CudaSlice<f32>,
6201 alpha: f32,
6202 dst: &mut cudarc::driver::CudaViewMut<f32>,
6203 n: usize,
6204 ) -> Result<(), Box<dyn std::error::Error>> {
6205 let f = self.func("axpy_f32");
6206 let cfg = LaunchConfig::for_num_elems(n as u32);
6207 let (a, ni) = (alpha, n as i32);
6208 let __s_b = self.gpu.stream();
6209 let mut b = __s_b.launch_builder(&f);
6210 b.arg(src).arg(dst).arg(&a).arg(&ni);
6211 unsafe {
6212 b.launch(cfg)?;
6213 }
6214 Ok(())
6215 }
6216
6217 pub fn add_scaled_rows(
6219 &self,
6220 src: &CudaSlice<f32>,
6221 scale: &CudaSlice<f32>,
6222 dst: &mut CudaSlice<f32>,
6223 ncols: usize,
6224 nrows: usize,
6225 ) -> Result<(), Box<dyn std::error::Error>> {
6226 let f = self.func("add_scaled_rows_f32");
6227 let cfg = LaunchConfig::for_num_elems((ncols * nrows) as u32);
6228 let (nc, nr) = (ncols as i32, nrows as i32);
6229 let __s_b = self.gpu.stream();
6230 let mut b = __s_b.launch_builder(&f);
6231 b.arg(src).arg(scale).arg(dst).arg(&nc).arg(&nr);
6232 unsafe {
6233 b.launch(cfg)?;
6234 }
6235 Ok(())
6236 }
6237
6238 pub fn gather_rows(
6242 &self,
6243 src: &CudaSlice<f32>,
6244 idx: &CudaSlice<i32>,
6245 dst: &mut CudaSlice<f32>,
6246 ncols: usize,
6247 m_e: usize,
6248 ) -> Result<(), Box<dyn std::error::Error>> {
6249 let f = self.func("gather_rows_f32");
6250 let cfg = LaunchConfig::for_num_elems((m_e * ncols) as u32);
6251 let (nc, me) = (ncols as i32, m_e as i32);
6252 let __s_b = self.gpu.stream();
6253 let mut b = __s_b.launch_builder(&f);
6254 b.arg(src).arg(idx).arg(dst).arg(&nc).arg(&me);
6255 unsafe {
6256 b.launch(cfg)?;
6257 }
6258 Ok(())
6259 }
6260
6261 pub fn scatter_slot(
6266 &self,
6267 src: &CudaSlice<f32>,
6268 tok_idx: &CudaSlice<i32>,
6269 slot_idx: &CudaSlice<i32>,
6270 weight: &CudaSlice<f32>,
6271 dst: &mut CudaSlice<f32>,
6272 wbuf: &mut CudaSlice<f32>,
6273 ncols: usize,
6274 n_used: usize,
6275 m_e: usize,
6276 ) -> Result<(), Box<dyn std::error::Error>> {
6277 let f = self.func("scatter_add_slot_f32");
6278 let cfg = LaunchConfig::for_num_elems((m_e * ncols) as u32);
6279 let (nc, nu, me) = (ncols as i32, n_used as i32, m_e as i32);
6280 let __s_b = self.gpu.stream();
6281 let mut b = __s_b.launch_builder(&f);
6282 b.arg(src)
6283 .arg(tok_idx)
6284 .arg(slot_idx)
6285 .arg(weight)
6286 .arg(dst)
6287 .arg(wbuf)
6288 .arg(&nc)
6289 .arg(&nu)
6290 .arg(&me);
6291 unsafe {
6292 b.launch(cfg)?;
6293 }
6294 Ok(())
6295 }
6296
6297 pub fn reduce_slots(
6301 &self,
6302 slots: &CudaSlice<f32>,
6303 wbuf: &CudaSlice<f32>,
6304 dst: &mut CudaSlice<f32>,
6305 ncols: usize,
6306 n_used: usize,
6307 t: usize,
6308 ) -> Result<(), Box<dyn std::error::Error>> {
6309 let f = self.func("reduce_slots_f32");
6310 let cfg = LaunchConfig::for_num_elems((t * ncols) as u32);
6311 let (nc, nu, ti) = (ncols as i32, n_used as i32, t as i32);
6312 let __s_b = self.gpu.stream();
6313 let mut b = __s_b.launch_builder(&f);
6314 b.arg(slots).arg(wbuf).arg(dst).arg(&nc).arg(&nu).arg(&ti);
6315 unsafe {
6316 b.launch(cfg)?;
6317 }
6318 Ok(())
6319 }
6320
6321 pub fn quantize_q8_1_view(
6328 &self,
6329 x: &cudarc::driver::CudaView<f32>,
6330 m: usize,
6331 in_f: usize,
6332 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
6333 let f = self.func("quantize_q8_1");
6334 let nblk = in_f / 32;
6335 let mut q = self.alloc_uninit::<i8>(m * in_f)?;
6336 let mut d = self.alloc_uninit::<f32>(m * nblk)?;
6337 let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
6338 let (inf, mi) = (in_f as i32, m as i32);
6339 let __s_b = self.gpu.stream();
6340 let mut b = __s_b.launch_builder(&f);
6341 b.arg(x).arg(&mut q).arg(&mut d).arg(&inf).arg(&mi);
6342 unsafe {
6343 b.launch(cfg)?;
6344 }
6345 Ok((q, d))
6346 }
6347
6348 pub fn quantize_q8_1(
6349 &self,
6350 x: &CudaSlice<f32>,
6351 m: usize,
6352 in_f: usize,
6353 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
6354 let nblk = in_f / 32;
6355 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);
6359 let (inf, mi) = (in_f as i32, m as i32);
6360 if Self::pdl_on() && Self::pdl_wb_on() {
6361 {
6362 use cudarc::driver::{DevicePtr, DevicePtrMut};
6363 let s = &self.gpu.stream();
6364 let (px, _g0) = x.device_ptr(s);
6365 let (pq, _g1) = q.device_ptr_mut(s);
6366 let (pd, _g2) = d.device_ptr_mut(s);
6367 let mut ps = [
6368 &px as *const _ as *mut std::ffi::c_void,
6369 &pq as *const _ as *mut _,
6370 &pd as *const _ as *mut _,
6371 &inf as *const _ as *mut _,
6372 &mi as *const _ as *mut _,
6373 ];
6374 unsafe {
6375 self.launch_pdl("quantize_q8_1", cfg.grid_dim, cfg.block_dim, &mut ps)?;
6376 }
6377 }
6378 return Ok((q, d));
6379 }
6380 let f = self.func("quantize_q8_1");
6381 let __s_b = self.gpu.stream();
6382 let mut b = __s_b.launch_builder(&f);
6383 b.arg(x).arg(&mut q).arg(&mut d).arg(&inf).arg(&mi);
6384 unsafe {
6385 b.launch(cfg)?;
6386 }
6387 Ok((q, d))
6388 }
6389
6390 pub fn quantize_fp4_act(
6394 &self,
6395 x: &CudaSlice<f32>,
6396 m: usize,
6397 in_f: usize,
6398 ) -> Result<(CudaSlice<u32>, CudaSlice<u8>), Box<dyn std::error::Error>> {
6399 let f = self.func("quantize_fp4_act");
6400 let nb16 = in_f / 16;
6401 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);
6404 let (inf, mi) = (in_f as i32, m as i32);
6405 let __s_b = self.gpu.stream();
6406 let mut b = __s_b.launch_builder(&f);
6407 b.arg(x).arg(&mut aq4).arg(&mut ad4).arg(&inf).arg(&mi);
6408 unsafe {
6409 b.launch(cfg)?;
6410 }
6411 Ok((aq4, ad4))
6412 }
6413
6414 pub fn qmatvec_gemm_nvfp4_fp4(
6419 &self,
6420 bytes: &CudaSlice<u8>,
6421 x: &CudaSlice<f32>,
6422 m: usize,
6423 in_f: usize,
6424 out_f: usize,
6425 row_bytes: usize,
6426 scale: f32,
6427 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6428 assert!(
6429 in_f % 64 == 0,
6430 "FP4 GEMM requires in_f % 64 == 0, got {in_f}"
6431 );
6432 let (aq4, ad4) = self.quantize_fp4_act(x, m, in_f)?;
6433 let mut y = self.fp4_gemm_launch(bytes, &aq4, &ad4, m, in_f, out_f, row_bytes)?;
6434 if scale != 1.0 {
6435 self.scale_inplace(&mut y, scale, m * out_f)?;
6436 }
6437 Ok(y)
6438 }
6439
6440 fn fp4_gemm_launch(
6443 &self,
6444 bytes: &CudaSlice<u8>,
6445 aq4: &CudaSlice<u32>,
6446 ad4: &CudaSlice<u8>,
6447 m: usize,
6448 in_f: usize,
6449 out_f: usize,
6450 row_bytes: usize,
6451 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6452 let f = self.func("qmatvec_gemm_nvfp4_fp4");
6453 let mut y = self.alloc_uninit::<f32>(m * out_f)?; const BM: u32 = 64;
6455 const BN: u32 = 256;
6456 let cfg = LaunchConfig {
6457 grid_dim: ((out_f as u32 + BM - 1) / BM, (m as u32 + BN - 1) / BN, 1),
6458 block_dim: (32, 4, 1),
6459 shared_mem_bytes: 0,
6460 };
6461 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6462 let __s_b = self.gpu.stream();
6463 let mut b = __s_b.launch_builder(&f);
6464 b.arg(bytes)
6465 .arg(aq4)
6466 .arg(ad4)
6467 .arg(&mut y)
6468 .arg(&inf)
6469 .arg(&outf)
6470 .arg(&mi)
6471 .arg(&rb);
6472 unsafe {
6473 b.launch(cfg)?;
6474 }
6475 Ok(y)
6476 }
6477
6478 pub fn qmatvec_gemm_nvfp4_fp4_raw(
6480 &self,
6481 bytes: &CudaSlice<u8>,
6482 x: &CudaSlice<f32>,
6483 m: usize,
6484 in_f: usize,
6485 out_f: usize,
6486 row_bytes: usize,
6487 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6488 assert!(
6489 in_f % 64 == 0,
6490 "FP4 GEMM requires in_f % 64 == 0, got {in_f}"
6491 );
6492 let (aq4, ad4) = self.quantize_fp4_act(x, m, in_f)?;
6493 self.fp4_gemm_launch(bytes, &aq4, &ad4, m, in_f, out_f, row_bytes)
6494 }
6495
6496 pub fn qmatvec_q8_0_fast(
6498 &self,
6499 w: &CudaSlice<u8>,
6500 x: &CudaSlice<f32>,
6501 m: usize,
6502 in_f: usize,
6503 out_f: usize,
6504 row_bytes: usize,
6505 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6506 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6507 let f = self.func("qmatvec_q8_0_dp4a");
6508 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6510 grid_dim: (out_f as u32, m as u32, 1),
6511 block_dim: (128, 1, 1),
6512 shared_mem_bytes: 0,
6513 };
6514 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6515 let __s_b = self.gpu.stream();
6516 let mut b = __s_b.launch_builder(&f);
6517 b.arg(w)
6518 .arg(&aq)
6519 .arg(&ad)
6520 .arg(&mut y)
6521 .arg(&inf)
6522 .arg(&outf)
6523 .arg(&mi)
6524 .arg(&rb);
6525 unsafe {
6526 b.launch(cfg)?;
6527 }
6528 Ok(y)
6529 }
6530
6531 #[allow(non_snake_case)] pub fn qmatvec_q4_K_fast(
6534 &self,
6535 w: &CudaSlice<u8>,
6536 x: &CudaSlice<f32>,
6537 m: usize,
6538 in_f: usize,
6539 out_f: usize,
6540 row_bytes: usize,
6541 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6542 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6543 let f = self.func("qmatvec_q4_K_dp4a");
6544 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6546 grid_dim: (out_f as u32, m as u32, 1),
6547 block_dim: (128, 1, 1),
6548 shared_mem_bytes: 0,
6549 };
6550 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6551 let __s_b = self.gpu.stream();
6552 let mut b = __s_b.launch_builder(&f);
6553 b.arg(w)
6554 .arg(&aq)
6555 .arg(&ad)
6556 .arg(&mut y)
6557 .arg(&inf)
6558 .arg(&outf)
6559 .arg(&mi)
6560 .arg(&rb);
6561 unsafe {
6562 b.launch(cfg)?;
6563 }
6564 Ok(y)
6565 }
6566
6567 #[allow(non_snake_case)] pub fn qmatvec_q6_K_fast(
6570 &self,
6571 w: &CudaSlice<u8>,
6572 x: &CudaSlice<f32>,
6573 m: usize,
6574 in_f: usize,
6575 out_f: usize,
6576 row_bytes: usize,
6577 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6578 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6579 let f = self.func("qmatvec_q6_K_dp4a");
6580 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6582 grid_dim: (out_f as u32, m as u32, 1),
6583 block_dim: (128, 1, 1),
6584 shared_mem_bytes: 0,
6585 };
6586 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6587 let __s_b = self.gpu.stream();
6588 let mut b = __s_b.launch_builder(&f);
6589 b.arg(w)
6590 .arg(&aq)
6591 .arg(&ad)
6592 .arg(&mut y)
6593 .arg(&inf)
6594 .arg(&outf)
6595 .arg(&mi)
6596 .arg(&rb);
6597 unsafe {
6598 b.launch(cfg)?;
6599 }
6600 Ok(y)
6601 }
6602
6603 #[allow(non_snake_case)] pub fn qmatvec_q5_K_fast(
6606 &self,
6607 w: &CudaSlice<u8>,
6608 x: &CudaSlice<f32>,
6609 m: usize,
6610 in_f: usize,
6611 out_f: usize,
6612 row_bytes: usize,
6613 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6614 self.qmatvec_dp4a_named("qmatvec_q5_K_dp4a", w, x, m, in_f, out_f, row_bytes)
6615 }
6616 #[allow(non_snake_case)] pub fn qmatvec_q3_K_fast(
6619 &self,
6620 w: &CudaSlice<u8>,
6621 x: &CudaSlice<f32>,
6622 m: usize,
6623 in_f: usize,
6624 out_f: usize,
6625 row_bytes: usize,
6626 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6627 self.qmatvec_dp4a_named("qmatvec_q3_K_dp4a", w, x, m, in_f, out_f, row_bytes)
6628 }
6629 pub fn qmatvec_nvfp4_fast_rp(
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 assert!(
6640 in_f % 64 == 0,
6641 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
6642 );
6643 self.qmatvec_dp4a_named("qmatvec_nvfp4_dp4a_rp", w, x, m, in_f, out_f, row_bytes)
6644 }
6645 pub fn qmatvec_nvfp4_fast(
6647 &self,
6648 w: &CudaSlice<u8>,
6649 x: &CudaSlice<f32>,
6650 m: usize,
6651 in_f: usize,
6652 out_f: usize,
6653 row_bytes: usize,
6654 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6655 assert!(
6658 in_f % 64 == 0,
6659 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
6660 );
6661 self.qmatvec_dp4a_named("qmatvec_nvfp4_dp4a", w, x, m, in_f, out_f, row_bytes)
6662 }
6663 #[allow(non_snake_case)] pub fn qmatvec_iq4_XS_fast(
6666 &self,
6667 w: &CudaSlice<u8>,
6668 x: &CudaSlice<f32>,
6669 m: usize,
6670 in_f: usize,
6671 out_f: usize,
6672 row_bytes: usize,
6673 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6674 self.qmatvec_dp4a_named("qmatvec_iq4_XS_dp4a", w, x, m, in_f, out_f, row_bytes)
6675 }
6676
6677 fn qmatvec_dp4a_named(
6679 &self,
6680 name: &str,
6681 w: &CudaSlice<u8>,
6682 x: &CudaSlice<f32>,
6683 m: usize,
6684 in_f: usize,
6685 out_f: usize,
6686 row_bytes: usize,
6687 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6688 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
6689 let f = self.func(name);
6690 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
6692 grid_dim: (out_f as u32, m as u32, 1),
6693 block_dim: (128, 1, 1),
6694 shared_mem_bytes: 0,
6695 };
6696 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
6697 let __s_b = self.gpu.stream();
6698 let mut b = __s_b.launch_builder(&f);
6699 b.arg(w)
6700 .arg(&aq)
6701 .arg(&ad)
6702 .arg(&mut y)
6703 .arg(&inf)
6704 .arg(&outf)
6705 .arg(&mi)
6706 .arg(&rb);
6707 unsafe {
6708 b.launch(cfg)?;
6709 }
6710 Ok(y)
6711 }
6712
6713 pub fn htod(&self, v: &[f32]) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6714 Ok(self.gpu.stream().clone_htod(v)?)
6715 }
6716 pub fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
6717 Ok(self.gpu.stream().clone_htod(v)?)
6718 }
6719 pub fn htod_i8(&self, v: &[i8]) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
6721 Ok(self.gpu.stream().clone_htod(v)?)
6722 }
6723 pub fn htod_u64(&self, v: &[u64]) -> Result<CudaSlice<u64>, Box<dyn std::error::Error>> {
6724 Ok(self.gpu.stream().clone_htod(v)?)
6725 }
6726 pub fn dtoh_view(
6728 &self,
6729 d: &cudarc::driver::CudaView<f32>,
6730 ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6731 let v = self.gpu.stream().clone_dtoh(d)?;
6732 self.gpu.stream().synchronize()?;
6733 Ok(v)
6734 }
6735 pub fn dtoh(&self, d: &CudaSlice<f32>) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
6736 let v = self.gpu.stream().clone_dtoh(d)?;
6737 self.gpu.stream().synchronize()?;
6738 Ok(v)
6739 }
6740 pub fn dtoh_pair(
6744 &self,
6745 a: &CudaSlice<f32>,
6746 b: &CudaSlice<f32>,
6747 ) -> Result<(Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
6748 let av = self.gpu.stream().clone_dtoh(a)?;
6749 let bv = self.gpu.stream().clone_dtoh(b)?;
6750 self.gpu.stream().synchronize()?;
6751 Ok((av, bv))
6752 }
6753 pub fn dtoh_i32(&self, d: &CudaSlice<i32>) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
6755 let v = self.gpu.stream().clone_dtoh(d)?;
6756 self.gpu.stream().synchronize()?;
6757 Ok(v)
6758 }
6759 pub fn dtoh_u8(&self, d: &CudaSlice<u8>) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
6761 let v = self.gpu.stream().clone_dtoh(d)?;
6762 self.gpu.stream().synchronize()?;
6763 Ok(v)
6764 }
6765 pub fn dtoh_u8_view(
6766 &self,
6767 d: &cudarc::driver::CudaView<u8>,
6768 ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
6769 let v = self.gpu.stream().clone_dtoh(d)?;
6770 self.gpu.stream().synchronize()?;
6771 Ok(v)
6772 }
6773 pub fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6774 let s = self.gpu.stream().alloc_zeros::<f32>(n)?;
6775 self.keep_if_capturing(&s);
6776 Ok(s)
6777 }
6778
6779 pub fn prob_of_token_device(
6788 &self,
6789 logits: &CudaSlice<f32>,
6790 tok: &CudaSlice<u32>,
6791 n_vocab: usize,
6792 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6793 let nb = ARGMAX_NB;
6794 let mut part = self.alloc_uninit::<f32>(nb)?;
6795 let mut p = self.alloc_uninit::<f32>(1)?;
6796 let f1 = self.func("prob_of_token_partial_f32");
6797 let cfg1 = LaunchConfig {
6798 grid_dim: (nb as u32, 1, 1),
6799 block_dim: (256, 1, 1),
6800 shared_mem_bytes: 0,
6801 };
6802 let nv = n_vocab as i32;
6803 let __s_b1 = self.gpu.stream();
6804 let mut b1 = __s_b1.launch_builder(&f1);
6805 b1.arg(logits).arg(tok).arg(&mut part).arg(&nv);
6806 unsafe {
6807 b1.launch(cfg1)?;
6808 }
6809 let f2 = self.func("prob_of_token_final_f32");
6810 let cfg2 = LaunchConfig {
6811 grid_dim: (1, 1, 1),
6812 block_dim: (256, 1, 1),
6813 shared_mem_bytes: 0,
6814 };
6815 let nbi = nb as i32;
6816 let __s_b2 = self.gpu.stream();
6817 let mut b2 = __s_b2.launch_builder(&f2);
6818 b2.arg(&part).arg(&mut p).arg(&nbi);
6819 unsafe {
6820 b2.launch(cfg2)?;
6821 }
6822 Ok(p)
6823 }
6824
6825 pub fn prob_of_token_device_col(
6832 &self,
6833 logits: &CudaSlice<f32>,
6834 tok_all: &CudaSlice<u32>,
6835 tok_idx: usize,
6836 p_out: &mut CudaSlice<f32>,
6837 p_idx: usize,
6838 n_vocab: usize,
6839 ) -> Result<(), Box<dyn std::error::Error>> {
6840 let tok_v = tok_all.slice(tok_idx..tok_idx + 1);
6841 let mut p_v = p_out.slice_mut(p_idx..p_idx + 1);
6842 let nb = ARGMAX_NB;
6843 let mut part = self.alloc_uninit::<f32>(nb)?;
6844 let f1 = self.func("prob_of_token_partial_f32");
6845 let cfg1 = LaunchConfig {
6846 grid_dim: (nb as u32, 1, 1),
6847 block_dim: (256, 1, 1),
6848 shared_mem_bytes: 0,
6849 };
6850 let nv = n_vocab as i32;
6851 let __s_b1 = self.gpu.stream();
6852 let mut b1 = __s_b1.launch_builder(&f1);
6853 b1.arg(logits).arg(&tok_v).arg(&mut part).arg(&nv);
6854 unsafe {
6855 b1.launch(cfg1)?;
6856 }
6857 let f2 = self.func("prob_of_token_final_f32");
6858 let cfg2 = LaunchConfig {
6859 grid_dim: (1, 1, 1),
6860 block_dim: (256, 1, 1),
6861 shared_mem_bytes: 0,
6862 };
6863 let nbi = nb as i32;
6864 let __s_b2 = self.gpu.stream();
6865 let mut b2 = __s_b2.launch_builder(&f2);
6866 b2.arg(&part).arg(&mut p_v).arg(&nbi);
6867 unsafe {
6868 b2.launch(cfg2)?;
6869 }
6870 Ok(())
6871 }
6872
6873 pub fn prob_of_token_device_into(
6874 &self,
6875 logits: &CudaSlice<f32>,
6876 tok: &CudaSlice<u32>,
6877 p_out: &mut CudaSlice<f32>,
6878 n_vocab: usize,
6879 ) -> Result<(), Box<dyn std::error::Error>> {
6880 let nb = ARGMAX_NB;
6881 let mut part = self.alloc_uninit::<f32>(nb)?;
6882 let f1 = self.func("prob_of_token_partial_f32");
6883 let cfg1 = LaunchConfig {
6884 grid_dim: (nb as u32, 1, 1),
6885 block_dim: (256, 1, 1),
6886 shared_mem_bytes: 0,
6887 };
6888 let nv = n_vocab as i32;
6889 let __s_b1 = self.gpu.stream();
6890 let mut b1 = __s_b1.launch_builder(&f1);
6891 b1.arg(logits).arg(tok).arg(&mut part).arg(&nv);
6892 unsafe {
6893 b1.launch(cfg1)?;
6894 }
6895 let f2 = self.func("prob_of_token_final_f32");
6896 let cfg2 = LaunchConfig {
6897 grid_dim: (1, 1, 1),
6898 block_dim: (256, 1, 1),
6899 shared_mem_bytes: 0,
6900 };
6901 let nbi = nb as i32;
6902 let __s_b2 = self.gpu.stream();
6903 let mut b2 = __s_b2.launch_builder(&f2);
6904 b2.arg(&part).arg(p_out).arg(&nbi);
6905 unsafe {
6906 b2.launch(cfg2)?;
6907 }
6908 Ok(())
6909 }
6910
6911 pub fn argmax_token_device(
6912 &self,
6913 logits: &CudaSlice<f32>,
6914 n_vocab: usize,
6915 ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
6916 let mut tok = unsafe { self.gpu.stream().alloc::<u32>(1)? };
6917 self.argmax_token_device_into(logits, &mut tok, n_vocab)?;
6918 Ok(tok)
6919 }
6920 pub fn argmax_token_device_into(
6927 &self,
6928 logits: &CudaSlice<f32>,
6929 tok: &mut CudaSlice<u32>,
6930 n_vocab: usize,
6931 ) -> Result<(), Box<dyn std::error::Error>> {
6932 let nb = ARGMAX_NB;
6933 let f1 = self.func("argmax_partial_f32");
6934 let f2 = self.func("argmax_final_f32");
6935 let mut guard = self.argmax_partials.lock().unwrap();
6936 if guard.is_none() {
6937 let pv = self.gpu.stream().alloc_zeros::<f32>(nb)?;
6940 let pi = self.gpu.stream().alloc_zeros::<i32>(nb)?;
6941 *guard = Some((pv, pi));
6942 }
6943 let (part_v, part_i) = guard.as_mut().unwrap();
6944 let nv = n_vocab as i32;
6945 let nbi = nb as i32;
6946 let cfg1 = LaunchConfig {
6948 grid_dim: (nb as u32, 1, 1),
6949 block_dim: (256, 1, 1),
6950 shared_mem_bytes: 0,
6951 };
6952 let __s_b1 = self.gpu.stream();
6953 let mut b1 = __s_b1.launch_builder(&f1);
6954 b1.arg(logits).arg(&mut *part_v).arg(&mut *part_i).arg(&nv);
6955 unsafe {
6956 b1.launch(cfg1)?;
6957 }
6958 let cfg2 = LaunchConfig {
6960 grid_dim: (1, 1, 1),
6961 block_dim: (256, 1, 1),
6962 shared_mem_bytes: 0,
6963 };
6964 let __s_b2 = self.gpu.stream();
6965 let mut b2 = __s_b2.launch_builder(&f2);
6966 b2.arg(&*part_v).arg(&*part_i).arg(tok).arg(&nbi);
6967 unsafe {
6968 b2.launch(cfg2)?;
6969 }
6970 Ok(())
6971 }
6972 pub fn argmax_token_device_col(
6978 &self,
6979 logits: &CudaSlice<f32>,
6980 col: usize,
6981 n_vocab: usize,
6982 toks: &mut CudaSlice<u32>,
6983 out_idx: usize,
6984 ) -> Result<(), Box<dyn std::error::Error>> {
6985 let nb = ARGMAX_NB;
6986 let f1 = self.func("argmax_partial_f32");
6987 let f2 = self.func("argmax_final_f32");
6988 let mut guard = self.argmax_partials.lock().unwrap();
6989 if guard.is_none() {
6990 let pv = self.gpu.stream().alloc_zeros::<f32>(nb)?;
6991 let pi = self.gpu.stream().alloc_zeros::<i32>(nb)?;
6992 *guard = Some((pv, pi));
6993 }
6994 let (part_v, part_i) = guard.as_mut().unwrap();
6995 let col_view = logits.slice(col * n_vocab..(col + 1) * n_vocab);
6996 let nv = n_vocab as i32;
6997 let nbi = nb as i32;
6998 let cfg1 = LaunchConfig {
6999 grid_dim: (nb as u32, 1, 1),
7000 block_dim: (256, 1, 1),
7001 shared_mem_bytes: 0,
7002 };
7003 let __s_b1 = self.gpu.stream();
7004 let mut b1 = __s_b1.launch_builder(&f1);
7005 b1.arg(&col_view)
7006 .arg(&mut *part_v)
7007 .arg(&mut *part_i)
7008 .arg(&nv);
7009 unsafe {
7010 b1.launch(cfg1)?;
7011 }
7012 let mut tok_view = toks.slice_mut(out_idx..out_idx + 1);
7013 let cfg2 = LaunchConfig {
7014 grid_dim: (1, 1, 1),
7015 block_dim: (256, 1, 1),
7016 shared_mem_bytes: 0,
7017 };
7018 let __s_b2 = self.gpu.stream();
7019 let mut b2 = __s_b2.launch_builder(&f2);
7020 b2.arg(&*part_v).arg(&*part_i).arg(&mut tok_view).arg(&nbi);
7021 unsafe {
7022 b2.launch(cfg2)?;
7023 }
7024 Ok(())
7025 }
7026 pub fn htod_u32_v(&self, v: &[u32]) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
7028 Ok(self.gpu.stream().clone_htod(v)?)
7029 }
7030 pub fn dtoh_u32(&self, d: &CudaSlice<u32>) -> Result<Vec<u32>, Box<dyn std::error::Error>> {
7031 let v = self.gpu.stream().clone_dtoh(d)?;
7032 self.gpu.stream().synchronize()?;
7033 Ok(v)
7034 }
7035 pub fn htod_u32_into(
7039 &self,
7040 dst: &mut CudaSlice<u32>,
7041 src: &[u32],
7042 ) -> Result<(), Box<dyn std::error::Error>> {
7043 let mut view = dst.slice_mut(0..src.len());
7044 self.gpu.stream().memcpy_htod(src, &mut view)?;
7045 Ok(())
7046 }
7047
7048 pub fn htod_i32_into(
7051 &self,
7052 dst: &mut CudaSlice<i32>,
7053 src: &[i32],
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 alloc_u32_zeroed(&self, n: usize) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
7061 let s = self.gpu.stream().alloc_zeros::<u32>(n)?;
7062 self.keep_if_capturing(&s);
7063 Ok(s)
7064 }
7065 pub fn embed_gather_device_into(
7068 &self,
7069 embd: &CudaSlice<u8>,
7070 token_d: &CudaSlice<u32>,
7071 x_out: &mut CudaSlice<f32>,
7072 n_embd: usize,
7073 qtype: i32,
7074 row_bytes: usize,
7075 ) -> Result<(), Box<dyn std::error::Error>> {
7076 let f = self.func("embed_gather_u32");
7077 let cfg = LaunchConfig {
7078 grid_dim: (((n_embd as u32 + 255) / 256).max(1), 1, 1),
7079 block_dim: (256, 1, 1),
7080 shared_mem_bytes: 0,
7081 };
7082 let (ne, qt, rb) = (n_embd as i32, qtype, row_bytes as i64);
7083 let __s_b = self.gpu.stream();
7084 let mut b = __s_b.launch_builder(&f);
7085 b.arg(embd)
7086 .arg(token_d)
7087 .arg(x_out)
7088 .arg(&ne)
7089 .arg(&qt)
7090 .arg(&rb);
7091 unsafe {
7092 b.launch(cfg)?;
7093 }
7094 Ok(())
7095 }
7096 pub fn dtoh_i32_one(&self, d: &CudaSlice<i32>) -> Result<i32, Box<dyn std::error::Error>> {
7098 let v = self.gpu.stream().clone_dtoh(d)?;
7099 self.gpu.stream().synchronize()?;
7100 Ok(v[0])
7101 }
7102 pub fn i32_set_k(
7109 &self,
7110 dst: &mut CudaSlice<i32>,
7111 v: i32,
7112 ) -> Result<(), Box<dyn std::error::Error>> {
7113 let f = self.func("i32_set_k");
7114 let cfg = LaunchConfig {
7115 grid_dim: (1, 1, 1),
7116 block_dim: (1, 1, 1),
7117 shared_mem_bytes: 0,
7118 };
7119 let idx = 0i32;
7120 let __s_b = self.gpu.stream();
7121 let mut b = __s_b.launch_builder(&f);
7122 b.arg(dst).arg(&v).arg(&idx);
7123 unsafe {
7124 b.launch(cfg)?;
7125 }
7126 Ok(())
7127 }
7128
7129 pub fn set_i32_one(
7130 &self,
7131 d: &mut CudaSlice<i32>,
7132 v: i32,
7133 ) -> Result<(), Box<dyn std::error::Error>> {
7134 self.gpu.stream().memcpy_htod(&[v], d)?;
7135 Ok(())
7136 }
7137 pub fn set_u32_one(
7140 &self,
7141 d: &mut CudaSlice<u32>,
7142 v: u32,
7143 ) -> Result<(), Box<dyn std::error::Error>> {
7144 self.gpu.stream().memcpy_htod(&[v], d)?;
7145 Ok(())
7146 }
7147 pub fn dtoh_u32_one(&self, d: &CudaSlice<u32>) -> Result<u32, Box<dyn std::error::Error>> {
7149 let v = self.gpu.stream().clone_dtoh(d)?;
7150 self.gpu.stream().synchronize()?;
7151 Ok(v[0])
7152 }
7153 pub fn upload_u8(&self, bytes: &[u8]) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
7155 Ok(self.gpu.stream().clone_htod(bytes)?)
7156 }
7157 pub fn embed_gather_device(
7161 &self,
7162 embd: &CudaSlice<u8>,
7163 token_d: &CudaSlice<u32>,
7164 n_embd: usize,
7165 qtype: i32,
7166 row_bytes: usize,
7167 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7168 let f = self.func("embed_gather_u32");
7169 let mut x = self.alloc_uninit::<f32>(n_embd)?;
7170 let cfg = LaunchConfig {
7171 grid_dim: (((n_embd as u32 + 255) / 256).max(1), 1, 1),
7172 block_dim: (256, 1, 1),
7173 shared_mem_bytes: 0,
7174 };
7175 let (ne, qt, rb) = (n_embd as i32, qtype, row_bytes as i64);
7176 let __s_b = self.gpu.stream();
7177 let mut b = __s_b.launch_builder(&f);
7178 b.arg(embd)
7179 .arg(token_d)
7180 .arg(&mut x)
7181 .arg(&ne)
7182 .arg(&qt)
7183 .arg(&rb);
7184 unsafe {
7185 b.launch(cfg)?;
7186 }
7187 Ok(x)
7188 }
7189
7190 pub fn embed_gather_device_t(
7194 &self,
7195 embd: &CudaSlice<u8>,
7196 tokens: &[u32],
7197 n_embd: usize,
7198 qtype: i32,
7199 row_bytes: usize,
7200 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7201 let t = tokens.len();
7202 let tok_d = self.gpu.stream().clone_htod(tokens)?;
7203 let f = self.func("embed_gather_u32_t");
7204 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7205 let cfg = LaunchConfig {
7206 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7207 block_dim: (256, 1, 1),
7208 shared_mem_bytes: 0,
7209 };
7210 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7211 let __s_b = self.gpu.stream();
7212 let mut b = __s_b.launch_builder(&f);
7213 b.arg(embd)
7214 .arg(&tok_d)
7215 .arg(&mut x)
7216 .arg(&ne)
7217 .arg(&qt)
7218 .arg(&rb)
7219 .arg(&ti);
7220 unsafe {
7221 b.launch(cfg)?;
7222 }
7223 Ok(x)
7224 }
7225
7226 pub fn embed_gather_device_tv(
7231 &self,
7232 embd: &CudaSlice<u8>,
7233 tok_v: &cudarc::driver::CudaView<u32>,
7234 t: usize,
7235 n_embd: usize,
7236 qtype: i32,
7237 row_bytes: usize,
7238 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7239 let f = self.func("embed_gather_u32_t");
7240 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7241 let cfg = LaunchConfig {
7242 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7243 block_dim: (256, 1, 1),
7244 shared_mem_bytes: 0,
7245 };
7246 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7247 let __s_b = self.gpu.stream();
7248 let mut b = __s_b.launch_builder(&f);
7249 b.arg(embd)
7250 .arg(tok_v)
7251 .arg(&mut x)
7252 .arg(&ne)
7253 .arg(&qt)
7254 .arg(&rb)
7255 .arg(&ti);
7256 unsafe {
7257 b.launch(cfg)?;
7258 }
7259 Ok(x)
7260 }
7261
7262 pub fn embed_gather_device_td(
7263 &self,
7264 embd: &CudaSlice<u8>,
7265 tok_d: &CudaSlice<u32>,
7266 t: usize,
7267 n_embd: usize,
7268 qtype: i32,
7269 row_bytes: usize,
7270 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7271 let f = self.func("embed_gather_u32_t");
7272 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
7273 let cfg = LaunchConfig {
7274 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
7275 block_dim: (256, 1, 1),
7276 shared_mem_bytes: 0,
7277 };
7278 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
7279 let __s_b = self.gpu.stream();
7280 let mut b = __s_b.launch_builder(&f);
7281 b.arg(embd)
7282 .arg(tok_d)
7283 .arg(&mut x)
7284 .arg(&ne)
7285 .arg(&qt)
7286 .arg(&rb)
7287 .arg(&ti);
7288 unsafe {
7289 b.launch(cfg)?;
7290 }
7291 Ok(x)
7292 }
7293
7294 #[inline]
7300 fn keep_if_capturing<T: cudarc::driver::DeviceRepr + Send + 'static>(&self, s: &CudaSlice<T>) {
7302 if self
7303 .capture_keep_on
7304 .load(std::sync::atomic::Ordering::Relaxed)
7305 {
7306 self.capture_keep.lock().unwrap().push(Box::new(s.clone()));
7307 }
7308 }
7309
7310 fn alloc_uninit<T: cudarc::driver::DeviceRepr + Send + 'static>(
7311 &self,
7312 n: usize,
7313 ) -> Result<CudaSlice<T>, Box<dyn std::error::Error>> {
7314 let mut s = unsafe { self.gpu.stream().alloc::<T>(n)? };
7315 {
7319 static Z: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7320 if *Z.get_or_init(|| std::env::var("MEMRA_DEBUG_ZERO_ALLOCS").as_deref() == Ok("1")) {
7321 use cudarc::driver::DevicePtrMut;
7323 let n_bytes = s.len() * std::mem::size_of::<T>();
7324 let stream = self.gpu.stream();
7325 let (p_, _g) = s.device_ptr_mut(&stream);
7326 unsafe {
7327 cudarc::driver::sys::cuMemsetD8Async(p_, 0, n_bytes, stream.cu_stream())
7328 .result()?;
7329 }
7330 }
7331 }
7332 self.keep_if_capturing(&s);
7333 Ok(s)
7334 }
7335
7336 pub fn uninit_q8_pair(
7341 &self,
7342 n: usize,
7343 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7344 Ok((
7345 self.alloc_uninit::<i8>(n)?,
7346 self.alloc_uninit::<f32>(n / 32)?,
7347 ))
7348 }
7349
7350 pub fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7351 self.alloc_uninit::<f32>(n)
7352 }
7353
7354 pub fn alloc_i8_uninit(&self, n: usize) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
7356 self.alloc_uninit::<i8>(n)
7357 }
7358
7359 #[allow(clippy::too_many_arguments)]
7363 pub fn rms_norm3(
7364 &self,
7365 x: &CudaSlice<f32>,
7366 w0: &CudaSlice<f32>,
7367 w1: &CudaSlice<f32>,
7368 w2: &CudaSlice<f32>,
7369 d0: &mut CudaSlice<f32>,
7370 d1: &mut CudaSlice<f32>,
7371 d2: &mut CudaSlice<f32>,
7372 ncols: usize,
7373 nrows: usize,
7374 eps: f32,
7375 ) -> Result<(), Box<dyn std::error::Error>> {
7376 let f = self.func("rms_norm3_f32");
7377 let cfg = LaunchConfig {
7378 grid_dim: (nrows as u32, 1, 1),
7379 block_dim: (rms_block(), 1, 1),
7380 shared_mem_bytes: 0,
7381 };
7382 let (nc, e) = (ncols as i32, eps);
7383 let __s_b = self.gpu.stream();
7384 let mut b = __s_b.launch_builder(&f);
7385 b.arg(x)
7386 .arg(w0)
7387 .arg(w1)
7388 .arg(w2)
7389 .arg(d0)
7390 .arg(d1)
7391 .arg(d2)
7392 .arg(&nc)
7393 .arg(&e);
7394 unsafe {
7395 b.launch(cfg)?;
7396 }
7397 Ok(())
7398 }
7399
7400 #[allow(clippy::too_many_arguments)]
7402 pub fn qkvnorm_w_on_prefill(rows: usize, ncols: usize) -> bool {
7405 static WARP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7406 *WARP_ON.get_or_init(|| {
7407 std::env::var("MEMRA_QKVNORM_W")
7408 .map(|v| v != "0")
7409 .unwrap_or(true)
7410 }) && ncols % 4 == 0
7411 && rows >= 64
7412 }
7413
7414 #[allow(clippy::too_many_arguments)]
7417 pub fn rms_norm_qkv_w4b(
7418 &self,
7419 q: &CudaSlice<f32>,
7420 k: &CudaSlice<f32>,
7421 v: &CudaSlice<f32>,
7422 wq: &CudaSlice<f32>,
7423 wk: &CudaSlice<f32>,
7424 wv: &CudaSlice<f32>,
7425 dq: &mut CudaSlice<f32>,
7426 dk: &mut CudaSlice<f32>,
7427 dv: &mut CudaSlice<f32>,
7428 dvb: &mut CudaSlice<u8>,
7429 ncols: usize,
7430 rq: usize,
7431 rk: usize,
7432 eps: f32,
7433 vf16: bool,
7434 ) -> Result<(), Box<dyn std::error::Error>> {
7435 assert!(ncols % 4 == 0 && rq + 2 * rk >= 64);
7436 let f = self.func("rms_norm_qkv_w4b_f32");
7437 let rows = (rq + 2 * rk) as u32;
7438 let cfg = LaunchConfig {
7439 grid_dim: (rows.div_ceil(8), 1, 1),
7440 block_dim: (256, 1, 1),
7441 shared_mem_bytes: 0,
7442 };
7443 let (nc, rqi, rki, rvi, e) = (ncols as i32, rq as i32, rk as i32, rk as i32, eps);
7444 let vf = vf16 as i32;
7445 let __s_b = self.gpu.stream();
7446 let mut b = __s_b.launch_builder(&f);
7447 b.arg(q)
7448 .arg(k)
7449 .arg(v)
7450 .arg(wq)
7451 .arg(wk)
7452 .arg(wv)
7453 .arg(dq)
7454 .arg(dk)
7455 .arg(dv)
7456 .arg(&mut *dvb)
7457 .arg(&nc)
7458 .arg(&rqi)
7459 .arg(&rki)
7460 .arg(&rvi)
7461 .arg(&e)
7462 .arg(&vf);
7463 unsafe {
7464 b.launch(cfg)?;
7465 }
7466 Ok(())
7467 }
7468
7469 pub fn rms_norm_qkv(
7470 &self,
7471 q: &CudaSlice<f32>,
7472 k: &CudaSlice<f32>,
7473 v: &CudaSlice<f32>,
7474 wq: &CudaSlice<f32>,
7475 wk: &CudaSlice<f32>,
7476 wv: &CudaSlice<f32>,
7477 dq: &mut CudaSlice<f32>,
7478 dk: &mut CudaSlice<f32>,
7479 dv: &mut CudaSlice<f32>,
7480 ncols: usize,
7481 rq: usize,
7482 rk: usize,
7483 eps: f32,
7484 ) -> Result<(), Box<dyn std::error::Error>> {
7485 static WARP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7489 let warp_on = *WARP_ON.get_or_init(|| {
7490 std::env::var("MEMRA_QKVNORM_W")
7491 .map(|v| v != "0")
7492 .unwrap_or(true)
7493 });
7494 if warp_on && ncols % 4 == 0 && rq + 2 * rk >= 64 {
7497 let f = self.func("rms_norm_qkv_w4_f32");
7498 let rows = (rq + 2 * rk) as u32;
7499 let cfg = LaunchConfig {
7500 grid_dim: (rows.div_ceil(8), 1, 1),
7501 block_dim: (256, 1, 1),
7502 shared_mem_bytes: 0,
7503 };
7504 let (nc, rqi, rki, rvi, e) = (ncols as i32, rq as i32, rk as i32, rk as i32, eps);
7505 let __s_b = self.gpu.stream();
7506 let mut b = __s_b.launch_builder(&f);
7507 b.arg(q)
7508 .arg(k)
7509 .arg(v)
7510 .arg(wq)
7511 .arg(wk)
7512 .arg(wv)
7513 .arg(dq)
7514 .arg(dk)
7515 .arg(dv)
7516 .arg(&nc)
7517 .arg(&rqi)
7518 .arg(&rki)
7519 .arg(&rvi)
7520 .arg(&e);
7521 unsafe {
7522 b.launch(cfg)?;
7523 }
7524 return Ok(());
7525 }
7526 let f = self.func("rms_norm_qkv_f32");
7527 let grid = (rq + 2 * rk) as u32;
7528 let cfg = LaunchConfig {
7529 grid_dim: (grid, 1, 1),
7530 block_dim: (rms_block(), 1, 1),
7531 shared_mem_bytes: 0,
7532 };
7533 let (nc, rqi, rki, e) = (ncols as i32, rq as i32, rk as i32, eps);
7534 let __s_b = self.gpu.stream();
7535 let mut b = __s_b.launch_builder(&f);
7536 b.arg(q)
7537 .arg(k)
7538 .arg(v)
7539 .arg(wq)
7540 .arg(wk)
7541 .arg(wv)
7542 .arg(dq)
7543 .arg(dk)
7544 .arg(dv)
7545 .arg(&nc)
7546 .arg(&rqi)
7547 .arg(&rki)
7548 .arg(&e);
7549 unsafe {
7550 b.launch(cfg)?;
7551 }
7552 Ok(())
7553 }
7554
7555 #[allow(clippy::too_many_arguments)]
7557 pub fn rms_norm2x(
7558 &self,
7559 a: &CudaSlice<f32>,
7560 bb: &CudaSlice<f32>,
7561 wa: &CudaSlice<f32>,
7562 wb: &CudaSlice<f32>,
7563 da: &mut CudaSlice<f32>,
7564 db: &mut CudaSlice<f32>,
7565 ncols: usize,
7566 nrows: usize,
7567 eps: f32,
7568 ) -> Result<(), Box<dyn std::error::Error>> {
7569 let f = self.func("rms_norm2x_f32");
7570 let cfg = LaunchConfig {
7571 grid_dim: (2 * nrows as u32, 1, 1),
7572 block_dim: (rms_block(), 1, 1),
7573 shared_mem_bytes: 0,
7574 };
7575 let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
7576 let __s_b = self.gpu.stream();
7577 let mut b = __s_b.launch_builder(&f);
7578 b.arg(a)
7579 .arg(bb)
7580 .arg(wa)
7581 .arg(wb)
7582 .arg(da)
7583 .arg(db)
7584 .arg(&nc)
7585 .arg(&nr)
7586 .arg(&e);
7587 unsafe {
7588 b.launch(cfg)?;
7589 }
7590 Ok(())
7591 }
7592
7593 pub fn softcap(
7595 &self,
7596 y: &mut CudaSlice<f32>,
7597 cap: f32,
7598 n: usize,
7599 ) -> Result<(), Box<dyn std::error::Error>> {
7600 let f = self.func("softcap_f32");
7601 let cfg = LaunchConfig::for_num_elems(n as u32);
7602 let ni = n as i32;
7603 let __s_b = self.gpu.stream();
7604 let mut b = __s_b.launch_builder(&f);
7605 b.arg(y).arg(&cap).arg(&ni);
7606 unsafe {
7607 b.launch(cfg)?;
7608 }
7609 Ok(())
7610 }
7611
7612 pub fn mask_ids_rows(
7615 &self,
7616 y: &mut CudaSlice<f32>,
7617 ids: &CudaSlice<i32>,
7618 n_ids: usize,
7619 n_vocab: usize,
7620 t: usize,
7621 ) -> Result<(), Box<dyn std::error::Error>> {
7622 let f = self.func("mask_ids_rows_f32");
7623 let cfg = LaunchConfig::for_num_elems((n_ids * t) as u32);
7624 let (ni, nv, ti) = (n_ids as i32, n_vocab as i32, t as i32);
7625 let __s_b = self.gpu.stream();
7626 let mut b = __s_b.launch_builder(&f);
7627 b.arg(y).arg(ids).arg(&ni).arg(&nv).arg(&ti);
7628 unsafe {
7629 b.launch(cfg)?;
7630 }
7631 Ok(())
7632 }
7633
7634 #[allow(clippy::too_many_arguments)]
7636 pub fn add_scale_rms_norm(
7637 &self,
7638 a: &CudaSlice<f32>,
7639 b_in: &CudaSlice<f32>,
7640 c: f32,
7641 w: &CudaSlice<f32>,
7642 res: &mut CudaSlice<f32>,
7643 dst: &mut CudaSlice<f32>,
7644 ncols: usize,
7645 nrows: usize,
7646 eps: f32,
7647 ) -> Result<(), Box<dyn std::error::Error>> {
7648 let f = self.func("add_scale_rms_norm_f32");
7649 let cfg = LaunchConfig {
7650 grid_dim: (nrows as u32, 1, 1),
7651 block_dim: (rms_block(), 1, 1),
7652 shared_mem_bytes: 0,
7653 };
7654 let (nc, e2) = (ncols as i32, eps);
7655 let __s_b = self.gpu.stream();
7656 let mut b = __s_b.launch_builder(&f);
7657 b.arg(a)
7658 .arg(b_in)
7659 .arg(&c)
7660 .arg(w)
7661 .arg(res)
7662 .arg(dst)
7663 .arg(&nc)
7664 .arg(&e2);
7665 unsafe {
7666 b.launch(cfg)?;
7667 }
7668 Ok(())
7669 }
7670
7671 #[allow(clippy::too_many_arguments)]
7674 pub fn add_scale_rms_norm_q8_1(
7675 &self,
7676 a: &CudaSlice<f32>,
7677 b_in: &CudaSlice<f32>,
7678 c: f32,
7679 w: &CudaSlice<f32>,
7680 res: &mut CudaSlice<f32>,
7681 ncols: usize,
7682 nrows: usize,
7683 eps: f32,
7684 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7685 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7686 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7687 let (nc, e2) = (ncols as i32, eps);
7688 if Self::pdl_on() && Self::pdl_wb_on() {
7689 {
7690 use cudarc::driver::{DevicePtr, DevicePtrMut};
7691 let s = &self.gpu.stream();
7692 let (pa, _g0) = a.device_ptr(s);
7693 let (pb, _g1) = b_in.device_ptr(s);
7694 let (pw, _g2) = w.device_ptr(s);
7695 let (pr, _g3) = res.device_ptr_mut(s);
7696 let (pq, _g4) = out_q.device_ptr_mut(s);
7697 let (pd, _g5) = out_d.device_ptr_mut(s);
7698 let mut ps = [
7699 &pa as *const _ as *mut std::ffi::c_void,
7700 &pb as *const _ as *mut _,
7701 &c as *const _ as *mut _,
7702 &pw as *const _ as *mut _,
7703 &pr as *const _ as *mut _,
7704 &pq as *const _ as *mut _,
7705 &pd as *const _ as *mut _,
7706 &nc as *const _ as *mut _,
7707 &e2 as *const _ as *mut _,
7708 ];
7709 unsafe {
7710 self.launch_pdl(
7711 "add_scale_rms_norm_q8_1",
7712 (nrows as u32, 1, 1),
7713 (rms_block(), 1, 1),
7714 &mut ps,
7715 )?;
7716 }
7717 }
7718 return Ok((out_q, out_d));
7719 }
7720 let f = self.func("add_scale_rms_norm_q8_1");
7721 let cfg = LaunchConfig {
7722 grid_dim: (nrows as u32, 1, 1),
7723 block_dim: (rms_block(), 1, 1),
7724 shared_mem_bytes: 0,
7725 };
7726 let __s_b = self.gpu.stream();
7727 let mut b = __s_b.launch_builder(&f);
7728 b.arg(a)
7729 .arg(b_in)
7730 .arg(&c)
7731 .arg(w)
7732 .arg(res)
7733 .arg(&mut out_q)
7734 .arg(&mut out_d)
7735 .arg(&nc)
7736 .arg(&e2);
7737 unsafe {
7738 b.launch(cfg)?;
7739 }
7740 Ok((out_q, out_d))
7741 }
7742
7743 #[allow(clippy::too_many_arguments)]
7745 pub fn add_scale_rms_norm_q8_1_into(
7746 &self,
7747 a: &CudaSlice<f32>,
7748 b_in: &CudaSlice<f32>,
7749 c: f32,
7750 w: &CudaSlice<f32>,
7751 res: &mut CudaSlice<f32>,
7752 ncols: usize,
7753 nrows: usize,
7754 eps: f32,
7755 out_q: &mut CudaSlice<i8>,
7756 out_d: &mut CudaSlice<f32>,
7757 ) -> Result<(), Box<dyn std::error::Error>> {
7758 debug_assert!(out_q.len() >= nrows * ncols && out_d.len() >= nrows * (ncols / 32));
7759 let (nc, e2) = (ncols as i32, eps);
7760 if Self::pdl_on() && Self::pdl_wb_on() {
7761 use cudarc::driver::{DevicePtr, DevicePtrMut};
7762 let s = &self.gpu.stream();
7763 let (pa, _g0) = a.device_ptr(s);
7764 let (pb, _g1) = b_in.device_ptr(s);
7765 let (pw, _g2) = w.device_ptr(s);
7766 let (pr, _g3) = res.device_ptr_mut(s);
7767 let (pq, _g4) = out_q.device_ptr_mut(s);
7768 let (pd, _g5) = out_d.device_ptr_mut(s);
7769 let mut ps = [
7770 &pa as *const _ as *mut std::ffi::c_void,
7771 &pb as *const _ as *mut _,
7772 &c as *const _ as *mut _,
7773 &pw as *const _ as *mut _,
7774 &pr as *const _ as *mut _,
7775 &pq as *const _ as *mut _,
7776 &pd as *const _ as *mut _,
7777 &nc as *const _ as *mut _,
7778 &e2 as *const _ as *mut _,
7779 ];
7780 unsafe {
7781 self.launch_pdl(
7782 "add_scale_rms_norm_q8_1",
7783 (nrows as u32, 1, 1),
7784 (rms_block(), 1, 1),
7785 &mut ps,
7786 )?;
7787 }
7788 return Ok(());
7789 }
7790 let f = self.func("add_scale_rms_norm_q8_1");
7791 let cfg = LaunchConfig {
7792 grid_dim: (nrows as u32, 1, 1),
7793 block_dim: (rms_block(), 1, 1),
7794 shared_mem_bytes: 0,
7795 };
7796 let __s_b = self.gpu.stream();
7797 let mut b = __s_b.launch_builder(&f);
7798 b.arg(a)
7799 .arg(b_in)
7800 .arg(&c)
7801 .arg(w)
7802 .arg(res)
7803 .arg(&mut *out_q)
7804 .arg(&mut *out_d)
7805 .arg(&nc)
7806 .arg(&e2);
7807 unsafe {
7808 b.launch(cfg)?;
7809 }
7810 Ok(())
7811 }
7812
7813 #[allow(clippy::too_many_arguments)]
7816 pub fn rms_pre_add_scale_rms_norm_q8_1(
7817 &self,
7818 a: &CudaSlice<f32>,
7819 wa: &CudaSlice<f32>,
7820 b_in: &CudaSlice<f32>,
7821 c: f32,
7822 w: &CudaSlice<f32>,
7823 res: &mut CudaSlice<f32>,
7824 ncols: usize,
7825 nrows: usize,
7826 eps: f32,
7827 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7828 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7829 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7830 let (nc, e2) = (ncols as i32, eps);
7831 if Self::pdl_on() {
7832 {
7833 use cudarc::driver::{DevicePtr, DevicePtrMut};
7834 let s = &self.gpu.stream();
7835 let (pa, _g0) = a.device_ptr(s);
7836 let (pwa, _g1) = wa.device_ptr(s);
7837 let (pb, _g2) = b_in.device_ptr(s);
7838 let (pw, _g3) = w.device_ptr(s);
7839 let (pr, _g4) = res.device_ptr_mut(s);
7840 let (pq, _g5) = out_q.device_ptr_mut(s);
7841 let (pd, _g6) = out_d.device_ptr_mut(s);
7842 let mut ps = [
7843 &pa as *const _ as *mut std::ffi::c_void,
7844 &pwa as *const _ as *mut _,
7845 &pb as *const _ as *mut _,
7846 &c as *const _ as *mut _,
7847 &pw as *const _ as *mut _,
7848 &pr as *const _ as *mut _,
7849 &pq as *const _ as *mut _,
7850 &pd as *const _ as *mut _,
7851 &nc as *const _ as *mut _,
7852 &e2 as *const _ as *mut _,
7853 ];
7854 unsafe {
7855 self.launch_pdl(
7856 "rms_pre_add_scale_rms_norm_q8_1",
7857 (nrows as u32, 1, 1),
7858 (rms_block(), 1, 1),
7859 &mut ps,
7860 )?;
7861 }
7862 }
7863 return Ok((out_q, out_d));
7864 }
7865 let f = self.func("rms_pre_add_scale_rms_norm_q8_1");
7866 let cfg = LaunchConfig {
7867 grid_dim: (nrows as u32, 1, 1),
7868 block_dim: (rms_block(), 1, 1),
7869 shared_mem_bytes: 0,
7870 };
7871 let __s_b = self.gpu.stream();
7872 let mut b = __s_b.launch_builder(&f);
7873 b.arg(a)
7874 .arg(wa)
7875 .arg(b_in)
7876 .arg(&c)
7877 .arg(w)
7878 .arg(res)
7879 .arg(&mut out_q)
7880 .arg(&mut out_d)
7881 .arg(&nc)
7882 .arg(&e2);
7883 unsafe {
7884 b.launch(cfg)?;
7885 }
7886 Ok((out_q, out_d))
7887 }
7888
7889 pub fn gelu_tanh_mul_q8_1(
7892 &self,
7893 gate: &CudaSlice<f32>,
7894 up: &cudarc::driver::CudaView<f32>,
7895 act: &mut CudaSlice<f32>,
7896 ncols: usize,
7897 nrows: usize,
7898 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7899 debug_assert!(ncols % 128 == 0);
7900 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
7901 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
7902 let nc = ncols as i32;
7903 if Self::pdl_on() {
7904 {
7905 use cudarc::driver::{DevicePtr, DevicePtrMut};
7906 let s = &self.gpu.stream();
7907 let (pg, _g0) = gate.device_ptr(s);
7908 let (pu, _g1) = up.device_ptr(s);
7909 let (pact, _g2) = act.device_ptr_mut(s);
7910 let (pq, _g3) = out_q.device_ptr_mut(s);
7911 let (pd, _g4) = out_d.device_ptr_mut(s);
7912 let mut ps = [
7913 &pg as *const _ as *mut std::ffi::c_void,
7914 &pu as *const _ as *mut _,
7915 &pact as *const _ as *mut _,
7916 &pq as *const _ as *mut _,
7917 &pd as *const _ as *mut _,
7918 &nc as *const _ as *mut _,
7919 ];
7920 unsafe {
7921 self.launch_pdl(
7922 "gelu_tanh_mul_q8_1",
7923 (nrows as u32, 1, 1),
7924 (rms_block(), 1, 1),
7925 &mut ps,
7926 )?;
7927 }
7928 }
7929 return Ok((out_q, out_d));
7930 }
7931 let f = self.func("gelu_tanh_mul_q8_1");
7932 let cfg = LaunchConfig {
7933 grid_dim: (nrows as u32, 1, 1),
7934 block_dim: (rms_block(), 1, 1),
7935 shared_mem_bytes: 0,
7936 };
7937 let __s_b = self.gpu.stream();
7938 let mut b = __s_b.launch_builder(&f);
7939 b.arg(gate)
7940 .arg(up)
7941 .arg(act)
7942 .arg(&mut out_q)
7943 .arg(&mut out_d)
7944 .arg(&nc);
7945 unsafe {
7946 b.launch(cfg)?;
7947 }
7948 Ok((out_q, out_d))
7949 }
7950
7951 #[allow(clippy::too_many_arguments)]
7953 pub fn gelu_tanh_mul_q8_1_into(
7954 &self,
7955 gate: &CudaSlice<f32>,
7956 up: &cudarc::driver::CudaView<f32>,
7957 act: &mut CudaSlice<f32>,
7958 ncols: usize,
7959 nrows: usize,
7960 out_q: &mut CudaSlice<i8>,
7961 out_d: &mut CudaSlice<f32>,
7962 ) -> Result<(), Box<dyn std::error::Error>> {
7963 debug_assert!(ncols % 128 == 0);
7964 debug_assert!(out_q.len() >= nrows * ncols && out_d.len() >= nrows * (ncols / 32));
7965 let nc = ncols as i32;
7966 if Self::pdl_on() {
7967 use cudarc::driver::{DevicePtr, DevicePtrMut};
7968 let s = &self.gpu.stream();
7969 let (pg, _g0) = gate.device_ptr(s);
7970 let (pu, _g1) = up.device_ptr(s);
7971 let (pact, _g2) = act.device_ptr_mut(s);
7972 let (pq, _g3) = out_q.device_ptr_mut(s);
7973 let (pd, _g4) = out_d.device_ptr_mut(s);
7974 let mut ps = [
7975 &pg as *const _ as *mut std::ffi::c_void,
7976 &pu as *const _ as *mut _,
7977 &pact as *const _ as *mut _,
7978 &pq as *const _ as *mut _,
7979 &pd as *const _ as *mut _,
7980 &nc as *const _ as *mut _,
7981 ];
7982 unsafe {
7983 self.launch_pdl(
7984 "gelu_tanh_mul_q8_1",
7985 (nrows as u32, 1, 1),
7986 (rms_block(), 1, 1),
7987 &mut ps,
7988 )?;
7989 }
7990 return Ok(());
7991 }
7992 let f = self.func("gelu_tanh_mul_q8_1");
7993 let cfg = LaunchConfig {
7994 grid_dim: (nrows as u32, 1, 1),
7995 block_dim: (rms_block(), 1, 1),
7996 shared_mem_bytes: 0,
7997 };
7998 let __s_b = self.gpu.stream();
7999 let mut b = __s_b.launch_builder(&f);
8000 b.arg(gate)
8001 .arg(up)
8002 .arg(&mut *act)
8003 .arg(&mut *out_q)
8004 .arg(&mut *out_d)
8005 .arg(&nc);
8006 unsafe {
8007 b.launch(cfg)?;
8008 }
8009 Ok(())
8010 }
8011
8012 #[allow(clippy::too_many_arguments)]
8014 pub fn add_rms_norm3_q8z(
8015 &self,
8016 a: &CudaSlice<f32>,
8017 b_in: &CudaSlice<f32>,
8018 w0: &CudaSlice<f32>,
8019 w1: &CudaSlice<f32>,
8020 w2: &CudaSlice<f32>,
8021 res: &mut CudaSlice<f32>,
8022 out1: &mut CudaSlice<f32>,
8023 ncols: usize,
8024 nrows: usize,
8025 eps: f32,
8026 ) -> Result<
8027 (
8028 (CudaSlice<i8>, CudaSlice<f32>),
8029 (CudaSlice<i8>, CudaSlice<f32>),
8030 ),
8031 Box<dyn std::error::Error>,
8032 > {
8033 let mut q0 = self.alloc_uninit::<i8>(nrows * ncols)?;
8034 let mut d0 = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
8035 let mut q2 = self.alloc_uninit::<i8>(nrows * ncols)?;
8036 let mut d2 = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
8037 let f = self.func("add_rms_norm3_q8z_f32");
8038 let cfg = LaunchConfig {
8039 grid_dim: (nrows as u32, 1, 1),
8040 block_dim: (rms_block(), 1, 1),
8041 shared_mem_bytes: 0,
8042 };
8043 let (nc, e2) = (ncols as i32, eps);
8044 let __s_b = self.gpu.stream();
8045 let mut b = __s_b.launch_builder(&f);
8046 b.arg(a)
8047 .arg(b_in)
8048 .arg(w0)
8049 .arg(w1)
8050 .arg(w2)
8051 .arg(res)
8052 .arg(&mut q0)
8053 .arg(&mut d0)
8054 .arg(out1)
8055 .arg(&mut q2)
8056 .arg(&mut d2)
8057 .arg(&nc)
8058 .arg(&e2);
8059 unsafe {
8060 b.launch(cfg)?;
8061 }
8062 Ok(((q0, d0), (q2, d2)))
8063 }
8064
8065 #[allow(clippy::too_many_arguments)]
8067 pub fn add_rms_norm3(
8068 &self,
8069 a: &CudaSlice<f32>,
8070 b_in: &CudaSlice<f32>,
8071 w0: &CudaSlice<f32>,
8072 w1: &CudaSlice<f32>,
8073 w2: &CudaSlice<f32>,
8074 res: &mut CudaSlice<f32>,
8075 d0: &mut CudaSlice<f32>,
8076 d1: &mut CudaSlice<f32>,
8077 d2: &mut CudaSlice<f32>,
8078 ncols: usize,
8079 nrows: usize,
8080 eps: f32,
8081 ) -> Result<(), Box<dyn std::error::Error>> {
8082 let f = self.func("add_rms_norm3_f32");
8083 let cfg = LaunchConfig {
8084 grid_dim: (nrows as u32, 1, 1),
8085 block_dim: (rms_block(), 1, 1),
8086 shared_mem_bytes: 0,
8087 };
8088 let (nc, e2) = (ncols as i32, eps);
8089 let __s_b = self.gpu.stream();
8090 let mut b = __s_b.launch_builder(&f);
8091 b.arg(a)
8092 .arg(b_in)
8093 .arg(w0)
8094 .arg(w1)
8095 .arg(w2)
8096 .arg(res)
8097 .arg(d0)
8098 .arg(d1)
8099 .arg(d2)
8100 .arg(&nc)
8101 .arg(&e2);
8102 unsafe {
8103 b.launch(cfg)?;
8104 }
8105 Ok(())
8106 }
8107
8108 pub fn add_scale(
8110 &self,
8111 a: &CudaSlice<f32>,
8112 b_in: &CudaSlice<f32>,
8113 c: f32,
8114 dst: &mut CudaSlice<f32>,
8115 n: usize,
8116 ) -> Result<(), Box<dyn std::error::Error>> {
8117 let f = self.func("add_scale_f32");
8118 let cfg = LaunchConfig::for_num_elems(n as u32);
8119 let ni = n as i32;
8120 let __s_b = self.gpu.stream();
8121 let mut b = __s_b.launch_builder(&f);
8122 b.arg(a).arg(b_in).arg(&c).arg(dst).arg(&ni);
8123 unsafe {
8124 b.launch(cfg)?;
8125 }
8126 Ok(())
8127 }
8128
8129 pub fn layer_norm_bias(
8131 &self,
8132 x: &CudaSlice<f32>,
8133 w: &CudaSlice<f32>,
8134 b: &CudaSlice<f32>,
8135 dst: &mut CudaSlice<f32>,
8136 ncols: usize,
8137 nrows: usize,
8138 eps: f32,
8139 ) -> Result<(), Box<dyn std::error::Error>> {
8140 let f = self.func("layer_norm_bias_f32");
8141 let (nc, e) = (ncols as i32, eps);
8142 let cfg = LaunchConfig {
8143 grid_dim: (nrows as u32, 1, 1),
8144 block_dim: (256, 1, 1),
8145 shared_mem_bytes: 0,
8146 };
8147 let __s_b = self.gpu.stream();
8148 let mut lb = __s_b.launch_builder(&f);
8149 lb.arg(x).arg(w).arg(b).arg(&mut *dst).arg(&nc).arg(&e);
8150 unsafe {
8151 lb.launch(cfg)?;
8152 }
8153 Ok(())
8154 }
8155
8156 pub fn gelu_tanh(
8158 &self,
8159 x: &CudaSlice<f32>,
8160 dst: &mut CudaSlice<f32>,
8161 n: usize,
8162 ) -> Result<(), Box<dyn std::error::Error>> {
8163 let f = self.func("gelu_tanh_f32");
8164 let ni = n as i64;
8165 let cfg = LaunchConfig {
8166 grid_dim: (n.div_ceil(256) as u32, 1, 1),
8167 block_dim: (256, 1, 1),
8168 shared_mem_bytes: 0,
8169 };
8170 let __s_b = self.gpu.stream();
8171 let mut lb = __s_b.launch_builder(&f);
8172 lb.arg(x).arg(&mut *dst).arg(&ni);
8173 unsafe {
8174 lb.launch(cfg)?;
8175 }
8176 Ok(())
8177 }
8178
8179 pub fn row_softmax(
8181 &self,
8182 x: &mut CudaSlice<f32>,
8183 ncols: usize,
8184 nrows: usize,
8185 ) -> Result<(), Box<dyn std::error::Error>> {
8186 let f = self.func("row_softmax_f32");
8187 let nc = ncols as i32;
8188 let cfg = LaunchConfig {
8189 grid_dim: (nrows as u32, 1, 1),
8190 block_dim: (256, 1, 1),
8191 shared_mem_bytes: 0,
8192 };
8193 let __s_b = self.gpu.stream();
8194 let mut lb = __s_b.launch_builder(&f);
8195 lb.arg(&mut *x).arg(&nc);
8196 unsafe {
8197 lb.launch(cfg)?;
8198 }
8199 Ok(())
8200 }
8201
8202 pub fn rms_norm(
8203 &self,
8204 x: &CudaSlice<f32>,
8205 w: &CudaSlice<f32>,
8206 dst: &mut CudaSlice<f32>,
8207 ncols: usize,
8208 nrows: usize,
8209 eps: f32,
8210 ) -> Result<(), Box<dyn std::error::Error>> {
8211 let (nc, e) = (ncols as i32, eps);
8212 if Self::pdl_on() && Self::pdl_wb_on() {
8213 use cudarc::driver::{DevicePtr, DevicePtrMut};
8214 let s = &self.gpu.stream();
8215 let (px, _g0) = x.device_ptr(s);
8216 let (pw, _g1) = w.device_ptr(s);
8217 let (pd, _g2) = dst.device_ptr_mut(s);
8218 let mut ps = [
8219 &px as *const _ as *mut std::ffi::c_void,
8220 &pw as *const _ as *mut _,
8221 &pd as *const _ as *mut _,
8222 &nc as *const _ as *mut _,
8223 &e as *const _ as *mut _,
8224 ];
8225 unsafe {
8226 self.launch_pdl(
8227 "rms_norm_f32",
8228 (nrows as u32, 1, 1),
8229 (rms_block(), 1, 1),
8230 &mut ps,
8231 )?;
8232 }
8233 return Ok(());
8234 }
8235 let f = self.func("rms_norm_f32");
8236 let cfg = LaunchConfig {
8237 grid_dim: (nrows as u32, 1, 1),
8238 block_dim: (rms_block(), 1, 1),
8239 shared_mem_bytes: 0,
8240 };
8241 let __s_b = self.gpu.stream();
8242 let mut b = __s_b.launch_builder(&f);
8243 b.arg(x).arg(w).arg(dst).arg(&nc).arg(&e);
8244 unsafe {
8245 b.launch(cfg)?;
8246 }
8247 Ok(())
8248 }
8249
8250 pub fn rms_norm_decode(
8258 &self,
8259 x: &CudaSlice<f32>,
8260 w: &CudaSlice<f32>,
8261 dst: &mut CudaSlice<f32>,
8262 ncols: usize,
8263 nrows: usize,
8264 eps: f32,
8265 ) -> Result<(), Box<dyn std::error::Error>> {
8266 let f = self.func("rms_norm_f32");
8267 let cfg = LaunchConfig {
8268 grid_dim: (nrows as u32, 1, 1),
8269 block_dim: (1024, 1, 1),
8270 shared_mem_bytes: 0,
8271 };
8272 let (nc, e) = (ncols as i32, eps);
8273 let __s_b = self.gpu.stream();
8274 let mut b = __s_b.launch_builder(&f);
8275 b.arg(x).arg(w).arg(dst).arg(&nc).arg(&e);
8276 unsafe {
8277 b.launch(cfg)?;
8278 }
8279 Ok(())
8280 }
8281
8282 pub fn rms_norm_q8_1(
8286 &self,
8287 x: &CudaSlice<f32>,
8288 w: &CudaSlice<f32>,
8289 ncols: usize,
8290 nrows: usize,
8291 eps: f32,
8292 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8293 let nblk = ncols / 32;
8294 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
8295 let mut d = self.alloc_uninit::<f32>(nrows * nblk)?;
8296 let (nc, e) = (ncols as i32, eps);
8297 if Self::pdl_on() {
8298 {
8299 use cudarc::driver::{DevicePtr, DevicePtrMut};
8300 let s = &self.gpu.stream();
8301 let (px, _g0) = x.device_ptr(s);
8302 let (pw, _g1) = w.device_ptr(s);
8303 let (pq, _g2) = q.device_ptr_mut(s);
8304 let (pd, _g3) = d.device_ptr_mut(s);
8305 let mut ps = [
8306 &px as *const _ as *mut std::ffi::c_void,
8307 &pw as *const _ as *mut _,
8308 &pq as *const _ as *mut _,
8309 &pd as *const _ as *mut _,
8310 &nc as *const _ as *mut _,
8311 &e as *const _ as *mut _,
8312 ];
8313 unsafe {
8314 self.launch_pdl("rms_norm_q8_1", (nrows as u32, 1, 1), (1024, 1, 1), &mut ps)?;
8315 }
8316 }
8317 return Ok((q, d));
8318 }
8319 let f = self.func("rms_norm_q8_1");
8320 let cfg = LaunchConfig {
8323 grid_dim: (nrows as u32, 1, 1),
8324 block_dim: (1024, 1, 1),
8325 shared_mem_bytes: 0,
8326 };
8327 let __s_b = self.gpu.stream();
8328 let mut b = __s_b.launch_builder(&f);
8329 b.arg(x).arg(w).arg(&mut q).arg(&mut d).arg(&nc).arg(&e);
8330 unsafe {
8331 b.launch(cfg)?;
8332 }
8333 Ok((q, d))
8334 }
8335
8336 pub fn rms_norm_q8_1_into(
8339 &self,
8340 x: &CudaSlice<f32>,
8341 w: &CudaSlice<f32>,
8342 ncols: usize,
8343 nrows: usize,
8344 eps: f32,
8345 q: &mut CudaSlice<i8>,
8346 d: &mut CudaSlice<f32>,
8347 ) -> Result<(), Box<dyn std::error::Error>> {
8348 let nblk = ncols / 32;
8349 debug_assert!(q.len() >= nrows * ncols && d.len() >= nrows * nblk);
8350 let (nc, e) = (ncols as i32, eps);
8351 if Self::pdl_on() {
8352 use cudarc::driver::{DevicePtr, DevicePtrMut};
8353 let s = &self.gpu.stream();
8354 let (px, _g0) = x.device_ptr(s);
8355 let (pw, _g1) = w.device_ptr(s);
8356 let (pq, _g2) = q.device_ptr_mut(s);
8357 let (pd, _g3) = d.device_ptr_mut(s);
8358 let mut ps = [
8359 &px as *const _ as *mut std::ffi::c_void,
8360 &pw as *const _ as *mut _,
8361 &pq as *const _ as *mut _,
8362 &pd as *const _ as *mut _,
8363 &nc as *const _ as *mut _,
8364 &e as *const _ as *mut _,
8365 ];
8366 unsafe {
8367 self.launch_pdl("rms_norm_q8_1", (nrows as u32, 1, 1), (1024, 1, 1), &mut ps)?;
8368 }
8369 return Ok(());
8370 }
8371 let f = self.func("rms_norm_q8_1");
8372 let cfg = LaunchConfig {
8373 grid_dim: (nrows as u32, 1, 1),
8374 block_dim: (1024, 1, 1),
8375 shared_mem_bytes: 0,
8376 };
8377 let __s_b = self.gpu.stream();
8378 let mut b = __s_b.launch_builder(&f);
8379 b.arg(x).arg(w).arg(&mut *q).arg(&mut *d).arg(&nc).arg(&e);
8380 unsafe {
8381 b.launch(cfg)?;
8382 }
8383 Ok(())
8384 }
8385
8386 pub fn quantize_q8_1_into(
8388 &self,
8389 x: &CudaSlice<f32>,
8390 m: usize,
8391 in_f: usize,
8392 q: &mut CudaSlice<i8>,
8393 d: &mut CudaSlice<f32>,
8394 ) -> Result<(), Box<dyn std::error::Error>> {
8395 let nblk = in_f / 32;
8396 debug_assert!(q.len() >= m * in_f && d.len() >= m * nblk);
8397 let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
8398 let (inf, mi) = (in_f as i32, m as i32);
8399 if Self::pdl_on() && Self::pdl_wb_on() {
8400 use cudarc::driver::{DevicePtr, DevicePtrMut};
8401 let s = &self.gpu.stream();
8402 let (px, _g0) = x.device_ptr(s);
8403 let (pq, _g1) = q.device_ptr_mut(s);
8404 let (pd, _g2) = d.device_ptr_mut(s);
8405 let mut ps = [
8406 &px as *const _ as *mut std::ffi::c_void,
8407 &pq as *const _ as *mut _,
8408 &pd as *const _ as *mut _,
8409 &inf as *const _ as *mut _,
8410 &mi as *const _ as *mut _,
8411 ];
8412 unsafe {
8413 self.launch_pdl("quantize_q8_1", cfg.grid_dim, cfg.block_dim, &mut ps)?;
8414 }
8415 return Ok(());
8416 }
8417 let f = self.func("quantize_q8_1");
8418 let __s_b = self.gpu.stream();
8419 let mut b = __s_b.launch_builder(&f);
8420 b.arg(x).arg(&mut *q).arg(&mut *d).arg(&inf).arg(&mi);
8421 unsafe {
8422 b.launch(cfg)?;
8423 }
8424 Ok(())
8425 }
8426
8427 pub fn add_rms_norm_q8_1(
8431 &self,
8432 a: &CudaSlice<f32>,
8433 b_in: &CudaSlice<f32>,
8434 w: &CudaSlice<f32>,
8435 res: &mut CudaSlice<f32>,
8436 ncols: usize,
8437 nrows: usize,
8438 eps: f32,
8439 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8440 let nblk = ncols / 32;
8441 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
8442 let mut d = self.alloc_uninit::<f32>(nrows * nblk)?;
8443 let f = self.func("add_rms_norm_q8_1");
8444 let cfg = LaunchConfig {
8446 grid_dim: (nrows as u32, 1, 1),
8447 block_dim: (1024, 1, 1),
8448 shared_mem_bytes: 0,
8449 };
8450 let (nc, e) = (ncols as i32, eps);
8451 let __s_bld = self.gpu.stream();
8452 let mut bld = __s_bld.launch_builder(&f);
8453 bld.arg(a)
8454 .arg(b_in)
8455 .arg(w)
8456 .arg(res)
8457 .arg(&mut q)
8458 .arg(&mut d)
8459 .arg(&nc)
8460 .arg(&e);
8461 unsafe {
8462 bld.launch(cfg)?;
8463 }
8464 Ok((q, d))
8465 }
8466
8467 pub fn add_rms_norm(
8471 &self,
8472 a: &CudaSlice<f32>,
8473 b: &CudaSlice<f32>,
8474 w: &CudaSlice<f32>,
8475 res: &mut CudaSlice<f32>,
8476 dst: &mut CudaSlice<f32>,
8477 ncols: usize,
8478 nrows: usize,
8479 eps: f32,
8480 ) -> Result<(), Box<dyn std::error::Error>> {
8481 let (nc, e) = (ncols as i32, eps);
8482 if Self::pdl_on() && Self::pdl_wb_on() {
8483 use cudarc::driver::{DevicePtr, DevicePtrMut};
8484 let s = &self.gpu.stream();
8485 let (pa, _g0) = a.device_ptr(s);
8486 let (pb, _g1) = b.device_ptr(s);
8487 let (pw, _g2) = w.device_ptr(s);
8488 let (pr, _g3) = res.device_ptr_mut(s);
8489 let (pd, _g4) = dst.device_ptr_mut(s);
8490 let mut ps = [
8491 &pa as *const _ as *mut std::ffi::c_void,
8492 &pb as *const _ as *mut _,
8493 &pw as *const _ as *mut _,
8494 &pr as *const _ as *mut _,
8495 &pd as *const _ as *mut _,
8496 &nc as *const _ as *mut _,
8497 &e as *const _ as *mut _,
8498 ];
8499 unsafe {
8500 self.launch_pdl(
8501 "add_rms_norm_f32",
8502 (nrows as u32, 1, 1),
8503 (rms_block(), 1, 1),
8504 &mut ps,
8505 )?;
8506 }
8507 return Ok(());
8508 }
8509 let f = self.func("add_rms_norm_f32");
8510 let cfg = LaunchConfig {
8511 grid_dim: (nrows as u32, 1, 1),
8512 block_dim: (rms_block(), 1, 1),
8513 shared_mem_bytes: 0,
8514 };
8515 let __s_b2 = self.gpu.stream();
8516 let mut b2 = __s_b2.launch_builder(&f);
8517 b2.arg(a)
8518 .arg(b)
8519 .arg(w)
8520 .arg(&mut *res)
8521 .arg(&mut *dst)
8522 .arg(&nc)
8523 .arg(&e);
8524 unsafe {
8525 b2.launch(cfg)?;
8526 }
8527 Ok(())
8528 }
8529
8530 #[allow(clippy::too_many_arguments)]
8533 pub fn rms_pre_add_rms_norm(
8534 &self,
8535 a: &CudaSlice<f32>,
8536 wa: &CudaSlice<f32>,
8537 b: &CudaSlice<f32>,
8538 w: &CudaSlice<f32>,
8539 res: &mut CudaSlice<f32>,
8540 dst: &mut CudaSlice<f32>,
8541 ncols: usize,
8542 nrows: usize,
8543 eps: f32,
8544 ) -> Result<(), Box<dyn std::error::Error>> {
8545 let f = self.func("rms_pre_add_rms_norm_f32");
8546 let cfg = LaunchConfig {
8547 grid_dim: (nrows as u32, 1, 1),
8548 block_dim: (rms_block(), 1, 1),
8549 shared_mem_bytes: 0,
8550 };
8551 let (nc, e) = (ncols as i32, eps);
8552 let __s_b2 = self.gpu.stream();
8553 let mut b2 = __s_b2.launch_builder(&f);
8554 b2.arg(a)
8555 .arg(wa)
8556 .arg(b)
8557 .arg(w)
8558 .arg(&mut *res)
8559 .arg(&mut *dst)
8560 .arg(&nc)
8561 .arg(&e);
8562 unsafe {
8563 b2.launch(cfg)?;
8564 }
8565 Ok(())
8566 }
8567
8568 #[allow(clippy::too_many_arguments)]
8570 pub fn rms_pre_add_rms_norm_q8z(
8571 &self,
8572 a: &CudaSlice<f32>,
8573 wa: &CudaSlice<f32>,
8574 b: &CudaSlice<f32>,
8575 w: &CudaSlice<f32>,
8576 res: &mut CudaSlice<f32>,
8577 dst: &mut CudaSlice<f32>,
8578 ncols: usize,
8579 nrows: usize,
8580 eps: f32,
8581 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8582 debug_assert!(ncols % 128 == 0);
8583 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
8584 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
8585 let (nc, e) = (ncols as i32, eps);
8586 if Self::pdl_on() {
8587 {
8588 use cudarc::driver::{DevicePtr, DevicePtrMut};
8589 let s = &self.gpu.stream();
8590 let (pa, _g0) = a.device_ptr(s);
8591 let (pwa, _g1) = wa.device_ptr(s);
8592 let (pb, _g2) = b.device_ptr(s);
8593 let (pw, _g3) = w.device_ptr(s);
8594 let (pr, _g4) = res.device_ptr_mut(s);
8595 let (pdst, _g5) = dst.device_ptr_mut(s);
8596 let (pq, _g6) = out_q.device_ptr_mut(s);
8597 let (pd, _g7) = out_d.device_ptr_mut(s);
8598 let mut ps = [
8599 &pa as *const _ as *mut std::ffi::c_void,
8600 &pwa as *const _ as *mut _,
8601 &pb as *const _ as *mut _,
8602 &pw as *const _ as *mut _,
8603 &pr as *const _ as *mut _,
8604 &pdst as *const _ as *mut _,
8605 &pq as *const _ as *mut _,
8606 &pd as *const _ as *mut _,
8607 &nc as *const _ as *mut _,
8608 &e as *const _ as *mut _,
8609 ];
8610 unsafe {
8611 self.launch_pdl(
8612 "rms_pre_add_rms_norm_q8z_f32",
8613 (nrows as u32, 1, 1),
8614 (rms_block(), 1, 1),
8615 &mut ps,
8616 )?;
8617 }
8618 }
8619 return Ok((out_q, out_d));
8620 }
8621 let f = self.func("rms_pre_add_rms_norm_q8z_f32");
8622 let cfg = LaunchConfig {
8623 grid_dim: (nrows as u32, 1, 1),
8624 block_dim: (rms_block(), 1, 1),
8625 shared_mem_bytes: 0,
8626 };
8627 let __s_b2 = self.gpu.stream();
8628 let mut b2 = __s_b2.launch_builder(&f);
8629 b2.arg(a)
8630 .arg(wa)
8631 .arg(b)
8632 .arg(w)
8633 .arg(&mut *res)
8634 .arg(&mut *dst)
8635 .arg(&mut out_q)
8636 .arg(&mut out_d)
8637 .arg(&nc)
8638 .arg(&e);
8639 unsafe {
8640 b2.launch(cfg)?;
8641 }
8642 Ok((out_q, out_d))
8643 }
8644
8645 #[allow(clippy::too_many_arguments)]
8649 pub fn rms_pre_add_rms_norm_q8z_into(
8650 &self,
8651 a: &CudaSlice<f32>,
8652 wa: &CudaSlice<f32>,
8653 b: &CudaSlice<f32>,
8654 w: &CudaSlice<f32>,
8655 res: &mut CudaSlice<f32>,
8656 dst: &mut CudaSlice<f32>,
8657 ncols: usize,
8658 nrows: usize,
8659 eps: f32,
8660 out_q: &mut CudaSlice<i8>,
8661 out_d: &mut CudaSlice<f32>,
8662 ) -> Result<(), Box<dyn std::error::Error>> {
8663 debug_assert!(ncols % 128 == 0);
8664 let (nc, e) = (ncols as i32, eps);
8665 let f = self.func("rms_pre_add_rms_norm_q8z_f32");
8666 let cfg = LaunchConfig {
8667 grid_dim: (nrows as u32, 1, 1),
8668 block_dim: (rms_block(), 1, 1),
8669 shared_mem_bytes: 0,
8670 };
8671 let __s_b = self.gpu.stream();
8672 let mut b2 = __s_b.launch_builder(&f);
8673 b2.arg(a)
8674 .arg(wa)
8675 .arg(b)
8676 .arg(w)
8677 .arg(&mut *res)
8678 .arg(&mut *dst)
8679 .arg(&mut *out_q)
8680 .arg(&mut *out_d)
8681 .arg(&nc)
8682 .arg(&e);
8683 unsafe {
8684 b2.launch(cfg)?;
8685 }
8686 Ok(())
8687 }
8688
8689 #[allow(clippy::too_many_arguments)]
8692 pub fn rms_pre_add_scale_rms_norm_q8_1_into(
8693 &self,
8694 a: &CudaSlice<f32>,
8695 wa: &CudaSlice<f32>,
8696 b_in: &CudaSlice<f32>,
8697 c: f32,
8698 w: &CudaSlice<f32>,
8699 res: &mut CudaSlice<f32>,
8700 ncols: usize,
8701 nrows: usize,
8702 eps: f32,
8703 out_q: &mut CudaSlice<i8>,
8704 out_d: &mut CudaSlice<f32>,
8705 ) -> Result<(), Box<dyn std::error::Error>> {
8706 debug_assert!(ncols % 128 == 0);
8707 let (nc, e2) = (ncols as i32, eps);
8708 let f = self.func("rms_pre_add_scale_rms_norm_q8_1");
8709 let cfg = LaunchConfig {
8710 grid_dim: (nrows as u32, 1, 1),
8711 block_dim: (rms_block(), 1, 1),
8712 shared_mem_bytes: 0,
8713 };
8714 let __s_b = self.gpu.stream();
8715 let mut b2 = __s_b.launch_builder(&f);
8716 b2.arg(a)
8717 .arg(wa)
8718 .arg(b_in)
8719 .arg(&c)
8720 .arg(w)
8721 .arg(&mut *res)
8722 .arg(&mut *out_q)
8723 .arg(&mut *out_d)
8724 .arg(&nc)
8725 .arg(&e2);
8726 unsafe {
8727 b2.launch(cfg)?;
8728 }
8729 Ok(())
8730 }
8731
8732 pub fn g4_pnfold_on() -> bool {
8740 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8741 *ON.get_or_init(|| {
8742 std::env::var("MEMRA_G4_PNFOLD")
8743 .map(|v| v != "0")
8744 .unwrap_or(true)
8745 })
8746 }
8747
8748 pub fn build_q4_out_concat3(
8752 &self,
8753 w0: &crate::model::GpuTensor,
8754 w1: &crate::model::GpuTensor,
8755 w2: &crate::model::GpuTensor,
8756 ) -> Result<Option<crate::model::GpuTensor>, Box<dyn std::error::Error>> {
8757 use crate::model::GpuTensor;
8758 let part = |w: &GpuTensor| -> Option<(usize, usize)> {
8759 match w {
8760 GpuTensor::Quant {
8761 qtype,
8762 row_bytes,
8763 rp,
8764 ..
8765 } if *qtype == QT_Q4_0 && !*rp => Some((*row_bytes, w.out_features())),
8766 _ => None,
8767 }
8768 };
8769 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (part(w0), part(w1), part(w2))
8770 else {
8771 return Ok(None);
8772 };
8773 if rb0 != rb1
8774 || rb0 != rb2
8775 || w0.in_features() != w1.in_features()
8776 || w0.in_features() != w2.in_features()
8777 {
8778 return Ok(None);
8779 }
8780 fn bytes_of(w: &crate::model::GpuTensor) -> &CudaSlice<u8> {
8781 match w {
8782 crate::model::GpuTensor::Quant { bytes, .. } => bytes,
8783 _ => unreachable!(),
8784 }
8785 }
8786 let (b0, b1, b2) = (bytes_of(w0), bytes_of(w1), bytes_of(w2));
8787 let total = rb0 * (o0 + o1 + o2);
8788 let mut cat = self.alloc_u8(total)?;
8789 self.copy_u8_into(&mut cat, 0, b0, rb0 * o0)?;
8790 self.copy_u8_into(&mut cat, rb0 * o0, b1, rb1 * o1)?;
8791 self.copy_u8_into(&mut cat, rb0 * (o0 + o1), b2, rb2 * o2)?;
8792 Ok(Some(GpuTensor::Quant {
8793 bytes: cat,
8794 qtype: QT_Q4_0,
8795 row_bytes: rb0,
8796 ne: vec![w0.in_features() as u64, (o0 + o1 + o2) as u64],
8797 scale: 1.0,
8798 rp: false,
8799 #[cfg(memra_cutlass)]
8800 cutlass: None,
8801 fp8: None,
8802 blk: None,
8803 rp4: None,
8804 f16: None,
8805 }))
8806 }
8807
8808 fn full_width_rope_only(
8826 kernel: &str,
8827 n_rot: usize,
8828 head_dim: usize,
8829 ) -> Result<(), Box<dyn std::error::Error>> {
8830 if n_rot == head_dim {
8831 return Ok(());
8832 }
8833 Err(format!(
8834 "{kernel}: PARTIAL ROTARY REFUSED — n_rot {n_rot} != head_dim {head_dim}. This fused \
8835 rms_norm+qkv+rope kernel carries no n_dims parameter and rotates the full head \
8836 width (half = ncols/2), so it would rotate dims {n_rot}..{head_dim} that must pass \
8837 through unrotated. Use the split path (rms_norm_qkv + rope_neox/rope_neox2 with \
8838 n_dims={n_rot}), or add an n_dims early-return to the kernel and widen this guard."
8839 )
8840 .into())
8841 }
8842
8843 #[allow(clippy::too_many_arguments)]
8847 pub fn rms_norm_qkv_rope_cat(
8848 &self,
8849 qkv: &CudaSlice<f32>,
8850 wq: &CudaSlice<f32>,
8851 wk: &CudaSlice<f32>,
8852 wv: &CudaSlice<f32>,
8853 q: &mut CudaSlice<f32>,
8854 k: &mut CudaSlice<f32>,
8855 v: &mut CudaSlice<f32>,
8856 head_dim: usize,
8857 n_rot: usize,
8858 rq: usize,
8859 rk: usize,
8860 pos: &CudaSlice<i32>,
8861 nh_q: usize,
8862 nh_k: usize,
8863 base: f32,
8864 freq_scale: f32,
8865 ff: Option<&CudaSlice<f32>>,
8866 eps: f32,
8867 ) -> Result<(), Box<dyn std::error::Error>> {
8868 Self::full_width_rope_only("rms_norm_qkv_rope_cat", n_rot, head_dim)?;
8869 let rows = rq + rk + rk;
8870 let theta_scale = base.powf(-2.0 / head_dim as f32);
8871 let (nc, rqi, rki, nhq, nhk) = (
8872 head_dim as i32,
8873 rq as i32,
8874 rk as i32,
8875 nh_q as i32,
8876 nh_k as i32,
8877 );
8878 if Self::pdl_on() {
8879 use cudarc::driver::{DevicePtr, DevicePtrMut};
8880 let s = &self.gpu.stream();
8881 let (pqkv, _g0) = qkv.device_ptr(s);
8882 let (pwq, _g1) = wq.device_ptr(s);
8883 let (pwk, _g2) = wk.device_ptr(s);
8884 let (pwv, _g3) = wv.device_ptr(s);
8885 let (pq, _g4) = q.device_ptr_mut(s);
8886 let (pk, _g5) = k.device_ptr_mut(s);
8887 let (pv, _g6) = v.device_ptr_mut(s);
8888 let (ppos, _g7) = pos.device_ptr(s);
8889 let (pff, _g8) = match ff {
8890 Some(t) => {
8891 let (p, g) = t.device_ptr(s);
8892 (p, Some(g))
8893 }
8894 None => (0, None),
8895 };
8896 let mut ps = [
8897 &pqkv as *const _ as *mut std::ffi::c_void,
8898 &pwq as *const _ as *mut _,
8899 &pwk as *const _ as *mut _,
8900 &pwv as *const _ as *mut _,
8901 &pq as *const _ as *mut _,
8902 &pk as *const _ as *mut _,
8903 &pv as *const _ as *mut _,
8904 &nc as *const _ as *mut _,
8905 &rqi as *const _ as *mut _,
8906 &rki as *const _ as *mut _,
8907 &ppos as *const _ as *mut _,
8908 &nhq as *const _ as *mut _,
8909 &nhk as *const _ as *mut _,
8910 &theta_scale as *const _ as *mut _,
8911 &freq_scale as *const _ as *mut _,
8912 &pff as *const _ as *mut _,
8913 &eps as *const _ as *mut _,
8914 ];
8915 unsafe {
8916 self.launch_pdl(
8917 "rms_norm_qkv_rope_cat_f32",
8918 (rows as u32, 1, 1),
8919 (rms_block(), 1, 1),
8920 &mut ps,
8921 )?;
8922 }
8923 return Ok(());
8924 }
8925 let f = self.func("rms_norm_qkv_rope_cat_f32");
8926 let cfg = LaunchConfig {
8927 grid_dim: (rows as u32, 1, 1),
8928 block_dim: (rms_block(), 1, 1),
8929 shared_mem_bytes: 0,
8930 };
8931 let __s_b = self.gpu.stream();
8932 let mut b = __s_b.launch_builder(&f);
8933 match ff {
8934 Some(t) => {
8935 b.arg(qkv)
8936 .arg(wq)
8937 .arg(wk)
8938 .arg(wv)
8939 .arg(&mut *q)
8940 .arg(&mut *k)
8941 .arg(&mut *v)
8942 .arg(&nc)
8943 .arg(&rqi)
8944 .arg(&rki)
8945 .arg(pos)
8946 .arg(&nhq)
8947 .arg(&nhk)
8948 .arg(&theta_scale)
8949 .arg(&freq_scale)
8950 .arg(t)
8951 .arg(&eps);
8952 unsafe {
8953 b.launch(cfg)?;
8954 }
8955 }
8956 None => {
8957 let null: u64 = 0;
8958 b.arg(qkv)
8959 .arg(wq)
8960 .arg(wk)
8961 .arg(wv)
8962 .arg(&mut *q)
8963 .arg(&mut *k)
8964 .arg(&mut *v)
8965 .arg(&nc)
8966 .arg(&rqi)
8967 .arg(&rki)
8968 .arg(pos)
8969 .arg(&nhq)
8970 .arg(&nhk)
8971 .arg(&theta_scale)
8972 .arg(&freq_scale)
8973 .arg(&null)
8974 .arg(&eps);
8975 unsafe {
8976 b.launch(cfg)?;
8977 }
8978 }
8979 }
8980 Ok(())
8981 }
8982
8983 #[allow(clippy::too_many_arguments)]
8987 pub fn rms_norm_qkv_rope(
8988 &self,
8989 q0: &CudaSlice<f32>,
8990 k0: &CudaSlice<f32>,
8991 v0: &CudaSlice<f32>,
8992 wq: &CudaSlice<f32>,
8993 wk: &CudaSlice<f32>,
8994 wv: &CudaSlice<f32>,
8995 q: &mut CudaSlice<f32>,
8996 k: &mut CudaSlice<f32>,
8997 v: &mut CudaSlice<f32>,
8998 head_dim: usize,
8999 n_rot: usize,
9000 rq: usize,
9001 rk: usize,
9002 pos: &CudaSlice<i32>,
9003 nh_q: usize,
9004 nh_k: usize,
9005 base: f32,
9006 freq_scale: f32,
9007 ff: Option<&CudaSlice<f32>>,
9008 eps: f32,
9009 ) -> Result<(), Box<dyn std::error::Error>> {
9010 Self::full_width_rope_only("rms_norm_qkv_rope", n_rot, head_dim)?;
9011 let f = self.func("rms_norm_qkv_rope_f32");
9012 let rows = rq + rk + rk; let cfg = LaunchConfig {
9014 grid_dim: (rows as u32, 1, 1),
9015 block_dim: (rms_block(), 1, 1),
9016 shared_mem_bytes: 0,
9017 };
9018 let theta_scale = base.powf(-2.0 / head_dim as f32);
9019 let (nc, rqi, rki, nhq, nhk) = (
9020 head_dim as i32,
9021 rq as i32,
9022 rk as i32,
9023 nh_q as i32,
9024 nh_k as i32,
9025 );
9026 let __s_b = self.gpu.stream();
9027 let mut b = __s_b.launch_builder(&f);
9028 match ff {
9029 Some(t) => {
9030 b.arg(q0)
9031 .arg(k0)
9032 .arg(v0)
9033 .arg(wq)
9034 .arg(wk)
9035 .arg(wv)
9036 .arg(&mut *q)
9037 .arg(&mut *k)
9038 .arg(&mut *v)
9039 .arg(&nc)
9040 .arg(&rqi)
9041 .arg(&rki)
9042 .arg(pos)
9043 .arg(&nhq)
9044 .arg(&nhk)
9045 .arg(&theta_scale)
9046 .arg(&freq_scale)
9047 .arg(t)
9048 .arg(&eps);
9049 unsafe {
9050 b.launch(cfg)?;
9051 }
9052 }
9053 None => {
9054 let null: u64 = 0;
9055 b.arg(q0)
9056 .arg(k0)
9057 .arg(v0)
9058 .arg(wq)
9059 .arg(wk)
9060 .arg(wv)
9061 .arg(&mut *q)
9062 .arg(&mut *k)
9063 .arg(&mut *v)
9064 .arg(&nc)
9065 .arg(&rqi)
9066 .arg(&rki)
9067 .arg(pos)
9068 .arg(&nhq)
9069 .arg(&nhk)
9070 .arg(&theta_scale)
9071 .arg(&freq_scale)
9072 .arg(&null)
9073 .arg(&eps);
9074 unsafe {
9075 b.launch(cfg)?;
9076 }
9077 }
9078 }
9079 Ok(())
9080 }
9081
9082 #[allow(clippy::too_many_arguments)]
9088 pub fn rms_norm_qkv_rope_append_dc(
9089 &self,
9090 q0: &CudaSlice<f32>,
9091 k0: &CudaSlice<f32>,
9092 v0: &CudaSlice<f32>,
9093 wq: &CudaSlice<f32>,
9094 wk: &CudaSlice<f32>,
9095 wv: &CudaSlice<f32>,
9096 q: &mut CudaSlice<f32>,
9097 k: &mut CudaSlice<f32>,
9098 v: &mut CudaSlice<f32>,
9099 head_dim: usize,
9100 n_rot: usize,
9101 rq: usize,
9102 rk: usize,
9103 pos: &CudaSlice<i32>,
9104 nh_q: usize,
9105 nh_k: usize,
9106 base: f32,
9107 freq_scale: f32,
9108 ff: Option<&CudaSlice<f32>>,
9109 eps: f32,
9110 kc: &mut CudaSlice<u8>,
9111 vc: &mut CudaSlice<u8>,
9112 t_dev: &CudaSlice<i32>,
9113 k_tok_bytes: usize,
9114 v_tok_bytes: usize,
9115 g: bool,
9116 ) -> Result<(), Box<dyn std::error::Error>> {
9117 Self::full_width_rope_only("rms_norm_qkv_rope_append_dc", n_rot, head_dim)?;
9118 let rows = rq + rk + rk;
9119 let theta_scale = base.powf(-2.0 / head_dim as f32);
9120 let (nc, rqi, rki, nhq, nhk) = (
9121 head_dim as i32,
9122 rq as i32,
9123 rk as i32,
9124 nh_q as i32,
9125 nh_k as i32,
9126 );
9127 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
9128 if Self::pdl_on() && Self::pdl_wb_on() {
9129 use cudarc::driver::{DevicePtr, DevicePtrMut};
9130 let s = &self.gpu.stream();
9131 let (p0, _a0) = q0.device_ptr(s);
9132 let (p1, _a1) = k0.device_ptr(s);
9133 let (p2, _a2) = v0.device_ptr(s);
9134 let (pwq, _a3) = wq.device_ptr(s);
9135 let (pwk, _a4) = wk.device_ptr(s);
9136 let (pwv, _a5) = wv.device_ptr(s);
9137 let (pq, _a6) = q.device_ptr_mut(s);
9138 let (pk, _a7) = k.device_ptr_mut(s);
9139 let (pv, _a8) = v.device_ptr_mut(s);
9140 let (pp, _a9) = pos.device_ptr(s);
9141 let pff: u64 = match ff {
9142 Some(t) => {
9143 let (p, _gg) = t.device_ptr(s);
9144 p as u64
9145 }
9146 None => 0,
9147 };
9148 let (pkc, _a10) = kc.device_ptr_mut(s);
9149 let (pvc, _a11) = vc.device_ptr_mut(s);
9150 let (pt, _a12) = t_dev.device_ptr(s);
9151 let mut ps = [
9152 &p0 as *const _ as *mut std::ffi::c_void,
9153 &p1 as *const _ as *mut _,
9154 &p2 as *const _ as *mut _,
9155 &pwq as *const _ as *mut _,
9156 &pwk as *const _ as *mut _,
9157 &pwv as *const _ as *mut _,
9158 &pq as *const _ as *mut _,
9159 &pk as *const _ as *mut _,
9160 &pv as *const _ as *mut _,
9161 &nc as *const _ as *mut _,
9162 &rqi as *const _ as *mut _,
9163 &rki as *const _ as *mut _,
9164 &pp as *const _ as *mut _,
9165 &nhq as *const _ as *mut _,
9166 &nhk as *const _ as *mut _,
9167 &theta_scale as *const _ as *mut _,
9168 &freq_scale as *const _ as *mut _,
9169 &pff as *const _ as *mut _,
9170 &eps as *const _ as *mut _,
9171 &pkc as *const _ as *mut _,
9172 &pvc as *const _ as *mut _,
9173 &pt as *const _ as *mut _,
9174 &ktb as *const _ as *mut _,
9175 &vtb as *const _ as *mut _,
9176 ];
9177 unsafe {
9178 self.launch_pdl_flash(
9179 g,
9180 "rms_norm_qkv_rope_append_dc_f32",
9181 (rows as u32, 1, 1),
9182 (rms_block(), 1, 1),
9183 0,
9184 &mut ps,
9185 )?;
9186 }
9187 return Ok(());
9188 }
9189 let f = if g {
9190 self.func_g("rms_norm_qkv_rope_append_dc_f32")
9191 } else {
9192 self.func("rms_norm_qkv_rope_append_dc_f32")
9193 };
9194 let cfg = LaunchConfig {
9195 grid_dim: (rows as u32, 1, 1),
9196 block_dim: (rms_block(), 1, 1),
9197 shared_mem_bytes: 0,
9198 };
9199 let __s_b = self.gpu.stream();
9200 let mut b = __s_b.launch_builder(&f);
9201 match ff {
9202 Some(t) => {
9203 b.arg(q0)
9204 .arg(k0)
9205 .arg(v0)
9206 .arg(wq)
9207 .arg(wk)
9208 .arg(wv)
9209 .arg(&mut *q)
9210 .arg(&mut *k)
9211 .arg(&mut *v)
9212 .arg(&nc)
9213 .arg(&rqi)
9214 .arg(&rki)
9215 .arg(pos)
9216 .arg(&nhq)
9217 .arg(&nhk)
9218 .arg(&theta_scale)
9219 .arg(&freq_scale)
9220 .arg(t)
9221 .arg(&eps)
9222 .arg(&mut *kc)
9223 .arg(&mut *vc)
9224 .arg(t_dev)
9225 .arg(&ktb)
9226 .arg(&vtb);
9227 unsafe {
9228 b.launch(cfg)?;
9229 }
9230 }
9231 None => {
9232 let null: u64 = 0;
9233 b.arg(q0)
9234 .arg(k0)
9235 .arg(v0)
9236 .arg(wq)
9237 .arg(wk)
9238 .arg(wv)
9239 .arg(&mut *q)
9240 .arg(&mut *k)
9241 .arg(&mut *v)
9242 .arg(&nc)
9243 .arg(&rqi)
9244 .arg(&rki)
9245 .arg(pos)
9246 .arg(&nhq)
9247 .arg(&nhk)
9248 .arg(&theta_scale)
9249 .arg(&freq_scale)
9250 .arg(&null)
9251 .arg(&eps)
9252 .arg(&mut *kc)
9253 .arg(&mut *vc)
9254 .arg(t_dev)
9255 .arg(&ktb)
9256 .arg(&vtb);
9257 unsafe {
9258 b.launch(cfg)?;
9259 }
9260 }
9261 }
9262 Ok(())
9263 }
9264
9265 #[allow(clippy::too_many_arguments)]
9273 pub fn rms_norm_qkv_rope_append(
9274 &self,
9275 q0: &CudaSlice<f32>,
9276 k0: &CudaSlice<f32>,
9277 v0: &CudaSlice<f32>,
9278 wq: &CudaSlice<f32>,
9279 wk: &CudaSlice<f32>,
9280 wv: &CudaSlice<f32>,
9281 q: &mut CudaSlice<f32>,
9282 k: &mut CudaSlice<f32>,
9283 v: &mut CudaSlice<f32>,
9284 head_dim: usize,
9285 n_rot: usize,
9286 rq: usize,
9287 rk: usize,
9288 pos: &CudaSlice<i32>,
9289 nh_q: usize,
9290 nh_k: usize,
9291 base: f32,
9292 freq_scale: f32,
9293 ff: Option<&CudaSlice<f32>>,
9294 eps: f32,
9295 kc: &mut CudaSlice<u8>,
9296 vc: &mut CudaSlice<u8>,
9297 t: usize,
9298 k_tok_bytes: usize,
9299 v_tok_bytes: usize,
9300 g: bool,
9301 ) -> Result<(), Box<dyn std::error::Error>> {
9302 Self::full_width_rope_only("rms_norm_qkv_rope_append", n_rot, head_dim)?;
9303 let rows = rq + rk + rk;
9304 let theta_scale = base.powf(-2.0 / head_dim as f32);
9305 let (nc, rqi, rki, nhq, nhk) = (
9306 head_dim as i32,
9307 rq as i32,
9308 rk as i32,
9309 nh_q as i32,
9310 nh_k as i32,
9311 );
9312 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
9313 let ti = t as i32;
9314 if Self::pdl_on() && Self::pdl_wb_on() {
9315 use cudarc::driver::{DevicePtr, DevicePtrMut};
9316 let s = &self.gpu.stream();
9317 let (p0, _a0) = q0.device_ptr(s);
9318 let (p1, _a1) = k0.device_ptr(s);
9319 let (p2, _a2) = v0.device_ptr(s);
9320 let (pwq, _a3) = wq.device_ptr(s);
9321 let (pwk, _a4) = wk.device_ptr(s);
9322 let (pwv, _a5) = wv.device_ptr(s);
9323 let (pq, _a6) = q.device_ptr_mut(s);
9324 let (pk, _a7) = k.device_ptr_mut(s);
9325 let (pv, _a8) = v.device_ptr_mut(s);
9326 let (pp, _a9) = pos.device_ptr(s);
9327 let pff: u64 = match ff {
9328 Some(t) => {
9329 let (p, _gg) = t.device_ptr(s);
9330 p as u64
9331 }
9332 None => 0,
9333 };
9334 let (pkc, _a10) = kc.device_ptr_mut(s);
9335 let (pvc, _a11) = vc.device_ptr_mut(s);
9336 let mut ps = [
9337 &p0 as *const _ as *mut std::ffi::c_void,
9338 &p1 as *const _ as *mut _,
9339 &p2 as *const _ as *mut _,
9340 &pwq as *const _ as *mut _,
9341 &pwk as *const _ as *mut _,
9342 &pwv as *const _ as *mut _,
9343 &pq as *const _ as *mut _,
9344 &pk as *const _ as *mut _,
9345 &pv as *const _ as *mut _,
9346 &nc as *const _ as *mut _,
9347 &rqi as *const _ as *mut _,
9348 &rki as *const _ as *mut _,
9349 &pp as *const _ as *mut _,
9350 &nhq as *const _ as *mut _,
9351 &nhk as *const _ as *mut _,
9352 &theta_scale as *const _ as *mut _,
9353 &freq_scale as *const _ as *mut _,
9354 &pff as *const _ as *mut _,
9355 &eps as *const _ as *mut _,
9356 &pkc as *const _ as *mut _,
9357 &pvc as *const _ as *mut _,
9358 &ti as *const _ as *mut _,
9359 &ktb as *const _ as *mut _,
9360 &vtb as *const _ as *mut _,
9361 ];
9362 unsafe {
9363 self.launch_pdl_flash(
9364 g,
9365 "rms_norm_qkv_rope_append_f32",
9366 (rows as u32, 1, 1),
9367 (rms_block(), 1, 1),
9368 0,
9369 &mut ps,
9370 )?;
9371 }
9372 return Ok(());
9373 }
9374 let f = if g {
9375 self.func_g("rms_norm_qkv_rope_append_f32")
9376 } else {
9377 self.func("rms_norm_qkv_rope_append_f32")
9378 };
9379 let cfg = LaunchConfig {
9380 grid_dim: (rows as u32, 1, 1),
9381 block_dim: (rms_block(), 1, 1),
9382 shared_mem_bytes: 0,
9383 };
9384 let __s_b = self.gpu.stream();
9385 let mut b = __s_b.launch_builder(&f);
9386 let null: u64 = 0;
9387 b.arg(q0)
9388 .arg(k0)
9389 .arg(v0)
9390 .arg(wq)
9391 .arg(wk)
9392 .arg(wv)
9393 .arg(&mut *q)
9394 .arg(&mut *k)
9395 .arg(&mut *v)
9396 .arg(&nc)
9397 .arg(&rqi)
9398 .arg(&rki)
9399 .arg(pos)
9400 .arg(&nhq)
9401 .arg(&nhk)
9402 .arg(&theta_scale)
9403 .arg(&freq_scale);
9404 match ff {
9405 Some(t) => {
9406 b.arg(t);
9407 }
9408 None => {
9409 b.arg(&null);
9410 }
9411 }
9412 b.arg(&eps)
9413 .arg(&mut *kc)
9414 .arg(&mut *vc)
9415 .arg(&ti)
9416 .arg(&ktb)
9417 .arg(&vtb);
9418 unsafe {
9419 b.launch(cfg)?;
9420 }
9421 Ok(())
9422 }
9423
9424 pub fn add_q8_1(
9425 &self,
9426 a: &CudaSlice<f32>,
9427 b: &CudaSlice<f32>,
9428 res: &mut CudaSlice<f32>,
9429 ncols: usize,
9430 nrows: usize,
9431 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9432 debug_assert!(ncols % 128 == 0);
9433 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
9434 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9435 let f = self.func("add_q8_1_f32");
9436 let cfg = LaunchConfig {
9437 grid_dim: (nrows as u32, 1, 1),
9438 block_dim: (rms_block(), 1, 1),
9439 shared_mem_bytes: 0,
9440 };
9441 let nc = ncols as i32;
9442 let __s_b2 = self.gpu.stream();
9443 let mut b2 = __s_b2.launch_builder(&f);
9444 b2.arg(a)
9445 .arg(b)
9446 .arg(&mut *res)
9447 .arg(&mut out_q)
9448 .arg(&mut out_d)
9449 .arg(&nc);
9450 unsafe {
9451 b2.launch(cfg)?;
9452 }
9453 Ok((out_q, out_d))
9454 }
9455
9456 pub fn rms_pre_add_q8_1(
9460 &self,
9461 a: &CudaSlice<f32>,
9462 wa: &CudaSlice<f32>,
9463 b: &CudaSlice<f32>,
9464 res: &mut CudaSlice<f32>,
9465 ncols: usize,
9466 nrows: usize,
9467 eps: f32,
9468 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9469 debug_assert!(ncols % 128 == 0);
9470 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
9471 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9472 let f = self.func("rms_pre_add_q8_1_f32");
9473 let cfg = LaunchConfig {
9474 grid_dim: (nrows as u32, 1, 1),
9475 block_dim: (rms_block(), 1, 1),
9476 shared_mem_bytes: 0,
9477 };
9478 let (nc, ep) = (ncols as i32, eps);
9479 let __s_b2 = self.gpu.stream();
9480 let mut b2 = __s_b2.launch_builder(&f);
9481 b2.arg(a)
9482 .arg(wa)
9483 .arg(b)
9484 .arg(&mut *res)
9485 .arg(&mut out_q)
9486 .arg(&mut out_d)
9487 .arg(&nc)
9488 .arg(&ep);
9489 unsafe {
9490 b2.launch(cfg)?;
9491 }
9492 Ok((out_q, out_d))
9493 }
9494
9495 pub fn l2_v2_on(ncols: usize) -> bool {
9499 ncols == 128 && std::env::var("MEMRA_L2_V2").as_deref() != Ok("0")
9500 }
9501
9502 pub fn l2_norm_pp(
9503 &self,
9504 x: &CudaSlice<f32>,
9505 dst: &mut CudaSlice<f32>,
9506 dst16: Option<&mut CudaSlice<u8>>,
9507 ncols: usize,
9508 nrows: usize,
9509 eps: f32,
9510 ) -> Result<(), Box<dyn std::error::Error>> {
9511 if Self::l2_v2_on(ncols) {
9512 let f = self.func("l2_norm_pp_v2_f32");
9513 let rows_per_block = 8u32; let cfg = LaunchConfig {
9515 grid_dim: ((nrows as u32).div_ceil(rows_per_block), 1, 1),
9516 block_dim: (256, 1, 1),
9517 shared_mem_bytes: 0,
9518 };
9519 let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
9520 let d16: u64 = match dst16 {
9522 Some(d) => self.addr_u8(d),
9523 None => 0,
9524 };
9525 let __s_b = self.gpu.stream();
9526 let mut b = __s_b.launch_builder(&f);
9527 b.arg(x).arg(dst).arg(&d16).arg(&nc).arg(&nr).arg(&e);
9528 unsafe {
9529 b.launch(cfg)?;
9530 }
9531 return Ok(());
9532 }
9533 self.l2_norm(x, dst, ncols, nrows, eps)
9534 }
9535
9536 pub fn l2_norm(
9537 &self,
9538 x: &CudaSlice<f32>,
9539 dst: &mut CudaSlice<f32>,
9540 ncols: usize,
9541 nrows: usize,
9542 eps: f32,
9543 ) -> Result<(), Box<dyn std::error::Error>> {
9544 let f = self.func("l2_norm_f32");
9545 let cfg = LaunchConfig {
9546 grid_dim: (nrows as u32, 1, 1),
9547 block_dim: (256, 1, 1),
9548 shared_mem_bytes: 0,
9549 };
9550 let (nc, e) = (ncols as i32, eps);
9551 let __s_b = self.gpu.stream();
9552 let mut b = __s_b.launch_builder(&f);
9553 b.arg(x).arg(dst).arg(&nc).arg(&e);
9554 unsafe {
9555 b.launch(cfg)?;
9556 }
9557 Ok(())
9558 }
9559
9560 pub fn l2_norm_decode(
9566 &self,
9567 x: &CudaSlice<f32>,
9568 dst: &mut CudaSlice<f32>,
9569 ncols: usize,
9570 nrows: usize,
9571 eps: f32,
9572 ) -> Result<(), Box<dyn std::error::Error>> {
9573 let f = self.func("l2_norm_f32");
9574 let cfg = LaunchConfig {
9575 grid_dim: (nrows as u32, 1, 1),
9576 block_dim: (32, 1, 1),
9577 shared_mem_bytes: 0,
9578 };
9579 let (nc, e) = (ncols as i32, eps);
9580 let __s_b = self.gpu.stream();
9581 let mut b = __s_b.launch_builder(&f);
9582 b.arg(x).arg(dst).arg(&nc).arg(&e);
9583 unsafe {
9584 b.launch(cfg)?;
9585 }
9586 Ok(())
9587 }
9588
9589 pub fn rope_neox(
9591 &self,
9592 x: &mut CudaSlice<f32>,
9593 pos: &CudaSlice<i32>,
9594 head_dim: usize,
9595 n_dims: usize,
9596 n_heads: usize,
9597 n_tokens: usize,
9598 freq_base: f32,
9599 freq_scale: f32,
9600 ) -> Result<(), Box<dyn std::error::Error>> {
9601 let f = self.func("rope_neox_f32");
9602 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9603 let grid = (n_heads * n_tokens) as u32;
9604 let cfg = LaunchConfig {
9605 grid_dim: (grid, 1, 1),
9606 block_dim: ((head_dim / 2) as u32, 1, 1),
9607 shared_mem_bytes: 0,
9608 };
9609 let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
9610 let __s_b = self.gpu.stream();
9611 let mut b = __s_b.launch_builder(&f);
9612 b.arg(x)
9613 .arg(pos)
9614 .arg(&hd)
9615 .arg(&nd)
9616 .arg(&nh)
9617 .arg(&theta_scale)
9618 .arg(&freq_scale);
9619 unsafe {
9620 b.launch(cfg)?;
9621 }
9622 Ok(())
9623 }
9624
9625 pub fn rope_neox_ff(
9627 &self,
9628 x: &mut CudaSlice<f32>,
9629 pos: &CudaSlice<i32>,
9630 head_dim: usize,
9631 n_dims: usize,
9632 n_heads: usize,
9633 n_tokens: usize,
9634 freq_base: f32,
9635 freq_scale: f32,
9636 ff: &CudaSlice<f32>,
9637 ) -> Result<(), Box<dyn std::error::Error>> {
9638 let f = self.func("rope_neox_ff_f32");
9639 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9640 let grid = (n_heads * n_tokens) as u32;
9641 let cfg = LaunchConfig {
9642 grid_dim: (grid, 1, 1),
9643 block_dim: ((head_dim / 2) as u32, 1, 1),
9644 shared_mem_bytes: 0,
9645 };
9646 let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
9647 let __s_b = self.gpu.stream();
9648 let mut b = __s_b.launch_builder(&f);
9649 b.arg(x)
9650 .arg(pos)
9651 .arg(&hd)
9652 .arg(&nd)
9653 .arg(&nh)
9654 .arg(&theta_scale)
9655 .arg(&freq_scale)
9656 .arg(ff);
9657 unsafe {
9658 b.launch(cfg)?;
9659 }
9660 Ok(())
9661 }
9662
9663 #[allow(clippy::too_many_arguments)]
9665 pub fn rope_neox2(
9666 &self,
9667 q: &mut CudaSlice<f32>,
9668 k: &mut CudaSlice<f32>,
9669 pos: &CudaSlice<i32>,
9670 head_dim: usize,
9671 n_dims: usize,
9672 nh_q: usize,
9673 nh_k: usize,
9674 n_tokens: usize,
9675 freq_base: f32,
9676 freq_scale: f32,
9677 ff: Option<&CudaSlice<f32>>,
9678 ) -> Result<(), Box<dyn std::error::Error>> {
9679 let f = self.func("rope_neox2_f32");
9680 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
9681 let grid = ((nh_q + nh_k) * n_tokens) as u32;
9682 let cfg = LaunchConfig {
9683 grid_dim: (grid, 1, 1),
9684 block_dim: ((head_dim / 2) as u32, 1, 1),
9685 shared_mem_bytes: 0,
9686 };
9687 let (hd, nd, nq, nk, nt) = (
9688 head_dim as i32,
9689 n_dims as i32,
9690 nh_q as i32,
9691 nh_k as i32,
9692 n_tokens as i32,
9693 );
9694 let __s_b = self.gpu.stream();
9695 let mut b = __s_b.launch_builder(&f);
9696 b.arg(q)
9697 .arg(k)
9698 .arg(pos)
9699 .arg(&hd)
9700 .arg(&nd)
9701 .arg(&nq)
9702 .arg(&nk)
9703 .arg(&nt)
9704 .arg(&theta_scale)
9705 .arg(&freq_scale);
9706 match ff {
9707 Some(ffv) => {
9708 b.arg(ffv);
9709 unsafe {
9710 b.launch(cfg)?;
9711 }
9712 }
9713 None => {
9714 let null: u64 = 0;
9715 b.arg(&null);
9716 unsafe {
9717 b.launch(cfg)?;
9718 }
9719 }
9720 }
9721 Ok(())
9722 }
9723
9724 pub fn gelu_tanh_mul(
9726 &self,
9727 gate: &CudaSlice<f32>,
9728 up: &CudaSlice<f32>,
9729 dst: &mut CudaSlice<f32>,
9730 n: usize,
9731 ) -> Result<(), Box<dyn std::error::Error>> {
9732 let f = self.func("gelu_tanh_mul_f32");
9733 let cfg = LaunchConfig::for_num_elems(n as u32);
9734 let ni = n as i32;
9735 let __s_b = self.gpu.stream();
9736 let mut b = __s_b.launch_builder(&f);
9737 b.arg(gate).arg(up).arg(dst).arg(&ni);
9738 unsafe {
9739 b.launch(cfg)?;
9740 }
9741 Ok(())
9742 }
9743
9744 pub fn silu_mul(
9745 &self,
9746 gate: &CudaSlice<f32>,
9747 up: &CudaSlice<f32>,
9748 dst: &mut CudaSlice<f32>,
9749 n: usize,
9750 ) -> Result<(), Box<dyn std::error::Error>> {
9751 let f = self.func("silu_mul_f32");
9752 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9754 let ni = n as i32;
9755 let __s_b = self.gpu.stream();
9756 let mut b = __s_b.launch_builder(&f);
9757 b.arg(gate).arg(up).arg(dst).arg(&ni);
9758 unsafe {
9759 b.launch(cfg)?;
9760 }
9761 Ok(())
9762 }
9763
9764 pub fn silu_mul_f16out(
9767 &self,
9768 gate: &CudaSlice<f32>,
9769 up: &CudaSlice<f32>,
9770 dst: &mut CudaSlice<f32>,
9771 dst16: &mut CudaSlice<u8>,
9772 n: usize,
9773 ) -> Result<(), Box<dyn std::error::Error>> {
9774 let f = self.func("silu_mul_f16out_f32");
9775 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9776 let ni = n as i32;
9777 let __s_b = self.gpu.stream();
9778 let mut b = __s_b.launch_builder(&f);
9779 b.arg(gate).arg(up).arg(dst).arg(dst16).arg(&ni);
9780 unsafe {
9781 b.launch(cfg)?;
9782 }
9783 Ok(())
9784 }
9785
9786 pub fn silu_mul_scaled(
9793 &self,
9794 gate: &CudaSlice<f32>,
9795 up: &CudaSlice<f32>,
9796 gs: f32,
9797 us: f32,
9798 dst: &mut CudaSlice<f32>,
9799 n: usize,
9800 ) -> Result<(), Box<dyn std::error::Error>> {
9801 let f = self.func("silu_mul_scaled_f32");
9802 let cfg = LaunchConfig::for_num_elems(n as u32);
9803 let ni = n as i32;
9804 let (gsf, usf) = (gs, us);
9805 let __s_b = self.gpu.stream();
9806 let mut b = __s_b.launch_builder(&f);
9807 b.arg(gate).arg(up).arg(&gsf).arg(&usf).arg(dst).arg(&ni);
9808 unsafe {
9809 b.launch(cfg)?;
9810 }
9811 Ok(())
9812 }
9813
9814 #[allow(clippy::too_many_arguments)]
9818 pub fn swigluoai_mul_scaled(
9819 &self,
9820 gate: &CudaSlice<f32>,
9821 up: &CudaSlice<f32>,
9822 gs: f32,
9823 us: f32,
9824 alpha: f32,
9825 limit: f32,
9826 dst: &mut CudaSlice<f32>,
9827 n: usize,
9828 ) -> Result<(), Box<dyn std::error::Error>> {
9829 let f = self.func("swigluoai_mul_scaled_f32");
9830 let cfg = LaunchConfig::for_num_elems(n as u32);
9831 let ni = n as i32;
9832 let __s_b = self.gpu.stream();
9833 let mut b = __s_b.launch_builder(&f);
9834 b.arg(gate)
9835 .arg(up)
9836 .arg(&gs)
9837 .arg(&us)
9838 .arg(&alpha)
9839 .arg(&limit)
9840 .arg(dst)
9841 .arg(&ni);
9842 unsafe {
9843 b.launch(cfg)?;
9844 }
9845 Ok(())
9846 }
9847
9848 pub fn silu_mul_scaled_q8_1(
9856 &self,
9857 gate: &CudaSlice<f32>,
9858 up: &CudaSlice<f32>,
9859 gs: f32,
9860 us: f32,
9861 n: usize,
9862 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9863 let f = self.func("silu_mul_scaled_q8_1");
9864 let nblk = n / 32;
9865 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);
9869 let (gsf, usf, ni) = (gs, us, n as i32);
9870 let __s_b = self.gpu.stream();
9871 let mut b = __s_b.launch_builder(&f);
9872 b.arg(gate)
9873 .arg(up)
9874 .arg(&gsf)
9875 .arg(&usf)
9876 .arg(&mut aq)
9877 .arg(&mut ad)
9878 .arg(&ni);
9879 unsafe {
9880 b.launch(cfg)?;
9881 }
9882 Ok((aq, ad))
9883 }
9884
9885 pub fn add(
9886 &self,
9887 a: &CudaSlice<f32>,
9888 b_in: &CudaSlice<f32>,
9889 dst: &mut CudaSlice<f32>,
9890 n: usize,
9891 ) -> Result<(), Box<dyn std::error::Error>> {
9892 let f = self.func("add_f32");
9893 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
9895 let ni = n as i32;
9896 let __s_bld = self.gpu.stream();
9897 let mut bld = __s_bld.launch_builder(&f);
9898 bld.arg(a).arg(b_in).arg(dst).arg(&ni);
9899 unsafe {
9900 bld.launch(cfg)?;
9901 }
9902 Ok(())
9903 }
9904
9905 pub fn mul(
9906 &self,
9907 a: &CudaSlice<f32>,
9908 b_in: &CudaSlice<f32>,
9909 dst: &mut CudaSlice<f32>,
9910 n: usize,
9911 ) -> Result<(), Box<dyn std::error::Error>> {
9912 let f = self.func("mul_f32");
9913 let cfg = LaunchConfig::for_num_elems(n as u32);
9914 let ni = n as i32;
9915 let __s_bld = self.gpu.stream();
9916 let mut bld = __s_bld.launch_builder(&f);
9917 bld.arg(a).arg(b_in).arg(dst).arg(&ni);
9918 unsafe {
9919 bld.launch(cfg)?;
9920 }
9921 Ok(())
9922 }
9923
9924 pub fn matmul(
9927 &self,
9928 w: &crate::model::GpuTensor,
9929 x: &CudaSlice<f32>,
9930 m: usize,
9931 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
9932 use crate::model::GpuTensor;
9933 let in_f = w.in_features();
9934 let out_f = w.out_features();
9935 #[allow(non_snake_case)]
9943 let GEMM_M_THRESHOLD = if self.verify_exact_on() {
9946 usize::MAX
9947 } else {
9948 16usize
9949 };
9950
9951 const GEMM_MIN_OUT_F: usize = 128; if m >= GEMM_M_THRESHOLD {
9976 if let Some(y) = self.try_fp8_gemm(w, x, m)? {
9977 return Ok(y);
9978 }
9979 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
9986 return Ok(y);
9987 }
9988 if let Some(y) = self.try_f16_gemm(w, x, m)? {
9991 return Ok(y);
9992 }
9993 }
9994 if let GpuTensor::Quant { qtype, .. } = w {
10009 if *qtype == QT_F8_E4M3_BLK {
10010 if m >= GEMM_M_THRESHOLD {
10011 if let Some(y) = self.try_e4m3_blk_prefill(w, x, m)? {
10012 return Ok(y);
10013 }
10014 }
10015 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10016 if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
10017 return Ok(y);
10018 }
10019 }
10020 }
10021 if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.mmq_supports(w) {
10022 return self.qmatvec_mmq(w, x, m);
10023 }
10024 if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.gemm_supports(w) {
10025 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10026 return self.qmatvec_gemm(w, &aq, &ad, m);
10027 }
10028 if m >= GEMM_M_THRESHOLD {
10031 if let Some(y) = self.try_fp4_gemm(w, x, m, in_f, out_f)? {
10032 return Ok(y);
10033 }
10034 }
10035 let fast = std::env::var("MEMRA_FAST").as_deref() != Ok("0");
10039 if m == 1 && fast {
10044 if let GpuTensor::Quant {
10045 bytes,
10046 qtype,
10047 row_bytes,
10048 rp,
10049 rp4,
10050 scale,
10051 ..
10052 } = w
10053 {
10054 if self.mmvq_supports(*qtype) {
10055 let (bytes, rp) = match rp4 {
10059 Some(m4) => (m4, true),
10060 None => (bytes, *rp),
10061 };
10062 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10063 return self.qmatvec_mmvq(
10064 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, rp,
10065 );
10066 }
10067 }
10068 }
10069 if (2..=16).contains(&m)
10085 && fast
10086 && std::env::var("MEMRA_NO_BATCHED").is_err()
10087 && (m <= 4 || Self::b8_enabled())
10088 {
10089 let m_ok = m <= 8
10099 || matches!(w, GpuTensor::Quant { qtype, .. }
10100 if *qtype == QT_Q4_0 || *qtype == QT_Q6_K || *qtype == QT_F8_E4M3
10101 || *qtype == QT_NVFP4 || *qtype == QT_Q4_K || *qtype == QT_Q5_K || *qtype == QT_Q8_0);
10102 if m_ok {
10103 if let GpuTensor::Quant {
10104 bytes,
10105 qtype,
10106 row_bytes,
10107 rp,
10108 rp4,
10109 ..
10110 } = w
10111 {
10112 if self.batched_supports(*qtype) && self.mmvq_supports(*qtype) {
10113 let (bytes, rp) = match rp4 {
10114 Some(m4) => (m4, true),
10115 None => (bytes, *rp),
10116 };
10117 let mcols = Self::batched_mcols(m);
10118 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10119 let mut y = self.qmatvec_mmvq_batched(
10120 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, mcols, 1.0, rp,
10121 )?;
10122 if let GpuTensor::Quant { scale, .. } = w {
10123 if *scale != 1.0 {
10124 self.scale_inplace(&mut y, *scale, m * out_f)?;
10125 }
10126 }
10127 return Ok(y);
10128 }
10129 }
10130 }
10131 }
10132 if fast {
10138 if let GpuTensor::Quant {
10139 bytes,
10140 qtype,
10141 row_bytes,
10142 scale,
10143 ..
10144 } = w
10145 {
10146 if *qtype == QT_F8_E4M3 {
10147 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10148 return self.qmatvec_mmvq(
10149 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, false,
10150 );
10151 }
10152 }
10153 }
10154 let mut y = match w {
10155 GpuTensor::Quant {
10156 bytes,
10157 qtype,
10158 row_bytes,
10159 ..
10160 } if fast && *qtype == QT_Q8_0 => {
10161 self.qmatvec_q8_0_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10162 }
10163 GpuTensor::Quant {
10164 bytes,
10165 qtype,
10166 row_bytes,
10167 ..
10168 } if fast && *qtype == QT_Q4_K => {
10169 self.qmatvec_q4_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10170 }
10171 GpuTensor::Quant {
10172 bytes,
10173 qtype,
10174 row_bytes,
10175 ..
10176 } if fast && *qtype == QT_Q6_K => {
10177 self.qmatvec_q6_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10178 }
10179 GpuTensor::Quant {
10180 bytes,
10181 qtype,
10182 row_bytes,
10183 ..
10184 } if fast && *qtype == QT_Q5_K => {
10185 self.qmatvec_q5_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10186 }
10187 GpuTensor::Quant {
10188 bytes,
10189 qtype,
10190 row_bytes,
10191 ..
10192 } if fast && *qtype == QT_Q3_K => {
10193 self.qmatvec_q3_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10194 }
10195 GpuTensor::Quant {
10196 bytes,
10197 qtype,
10198 row_bytes,
10199 rp,
10200 ..
10201 } if fast && *qtype == QT_NVFP4 => self.qmatvec_dp4a_named(
10202 if *rp {
10203 "qmatvec_nvfp4_dp4a_rp"
10204 } else {
10205 "qmatvec_nvfp4_dp4a"
10206 },
10207 bytes,
10208 x,
10209 m,
10210 in_f,
10211 out_f,
10212 *row_bytes,
10213 )?,
10214 GpuTensor::Quant {
10218 bytes,
10219 qtype,
10220 row_bytes,
10221 ..
10222 } if fast && *qtype == QT_IQ4_XS && Self::iq_fast_enabled() => {
10223 self.qmatvec_iq4_XS_fast(bytes, x, m, in_f, out_f, *row_bytes)?
10224 }
10225 GpuTensor::Quant {
10230 bytes,
10231 qtype,
10232 row_bytes,
10233 rp,
10234 ..
10235 } =>
10236 {
10239 self.qmatvec(
10240 bytes,
10241 x,
10242 m,
10243 in_f,
10244 out_f,
10245 if *rp && *qtype == QT_NVFP4 {
10246 QT_NVFP4_RP
10247 } else {
10248 *qtype
10249 },
10250 *row_bytes,
10251 )?
10252 }
10253 GpuTensor::Float { data, .. } => self.linear(x, data, m, in_f, out_f)?,
10254 GpuTensor::FloatBf16 { data, .. } => {
10257 self.linear_bf16_chunked(x, data, m, in_f, out_f, false)?
10258 }
10259 };
10260 if let GpuTensor::Quant { scale, .. } = w {
10262 if *scale != 1.0 {
10263 self.scale_inplace(&mut y, *scale, m * out_f)?;
10264 }
10265 }
10266 Ok(y)
10267 }
10268
10269 pub fn stage_a_raw_needed() -> bool {
10279 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10280 *ON.get_or_init(|| std::env::var("MEMRA_FAST").as_deref() == Ok("0"))
10281 }
10282
10283 pub fn uses_q8_1_fast(&self, w: &crate::model::GpuTensor) -> bool {
10286 use crate::model::GpuTensor;
10287 if std::env::var("MEMRA_FAST").as_deref() == Ok("0") {
10288 return false;
10289 }
10290 match w {
10291 GpuTensor::Quant { qtype, .. } => {
10298 matches!(
10299 *qtype,
10300 QT_Q8_0
10301 | QT_Q4_K
10302 | QT_Q6_K
10303 | QT_Q5_K
10304 | QT_Q3_K
10305 | QT_NVFP4
10306 | QT_F8_E4M3
10307 | QT_F8_E4M3_BLK
10308 | QT_Q4_0
10309 ) || (*qtype == QT_IQ4_XS && Self::iq_fast_enabled())
10310 }
10311 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
10312 }
10313 }
10314
10315 pub fn matmul_pre(
10320 &self,
10321 w: &crate::model::GpuTensor,
10322 aq: &CudaSlice<i8>,
10323 ad: &CudaSlice<f32>,
10324 x_fallback: &CudaSlice<f32>,
10325 m: usize,
10326 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10327 use crate::model::GpuTensor;
10328 let x_raw_ok = x_fallback.len() >= m * w.in_features();
10334 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
10337 if let Some(y) = self.try_fp8_gemm(w, x_fallback, m)? {
10338 return Ok(y);
10339 }
10340 if let Some(y) = self.try_fp8_blk_mmq(w, x_fallback, m)? {
10343 return Ok(y);
10344 }
10345 if let Some(y) = self.try_f16_gemm(w, x_fallback, m)? {
10347 return Ok(y);
10348 }
10349 }
10350 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
10356 if let Some(y) = self.try_e4m3_blk_prefill(w, x_fallback, m)? {
10357 return Ok(y);
10358 }
10359 }
10360 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
10361 return Ok(y);
10362 }
10363 if m >= 16
10368 && w.out_features() >= 128
10369 && self.mmq_supports(w)
10370 && !self.verify_exact_on()
10371 && x_raw_ok
10372 {
10373 return self.qmatvec_mmq(w, x_fallback, m);
10374 }
10375 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
10378 if let Some(y) =
10379 self.try_fp4_gemm(w, x_fallback, m, w.in_features(), w.out_features())?
10380 {
10381 return Ok(y);
10382 }
10383 }
10384 if m >= 16 && self.gemm_supports(w) && !self.verify_exact_on() {
10387 return self.qmatvec_gemm(w, aq, ad, m);
10388 }
10389 if !self.uses_q8_1_fast(w) {
10408 if !x_raw_ok {
10409 return Err(format!(
10410 "matmul_pre: q8_1-fast is off for this weight but x_fallback holds {} f32 \
10411 (need m*in_f = {}*{} = {}). This call site pre-quantized its activation and \
10412 dropped the f32, so there is nothing to fall back to — pass the real f32 \
10413 activation (see Engine::rms_norm_decode, which is bit-identical to \
10414 rms_norm_q8_1's reduction) or keep the weight on the q8_1 path.",
10415 x_fallback.len(),
10416 m,
10417 w.in_features(),
10418 m * w.in_features()
10419 )
10420 .into());
10421 }
10422 return self.matmul(w, x_fallback, m);
10423 }
10424 let in_f = w.in_features();
10425 let out_f = w.out_features();
10426 let (bytes, qtype, row_bytes, scale, rp) = match w {
10427 GpuTensor::Quant {
10428 bytes,
10429 qtype,
10430 row_bytes,
10431 scale,
10432 rp,
10433 ..
10434 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10435 _ => unreachable!("uses_q8_1_fast guaranteed Quant"),
10436 };
10437 let (mbytes, mrp) = match w {
10440 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10441 _ => (bytes, rp),
10442 };
10443 if m == 1 && self.mmvq_supports(qtype) {
10447 return self.qmatvec_mmvq(mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, mrp);
10448 }
10449 if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
10462 && std::env::var("MEMRA_NO_BATCHED").is_err()
10463 && (m <= 4 || Self::b8_enabled())
10464 && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_NVFP4
10468 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_F8_E4M3 || qtype == QT_Q8_0)
10469 {
10470 let mcols = Self::batched_mcols(m);
10471 return self.qmatvec_mmvq_batched(
10472 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, mrp,
10473 );
10474 }
10475 if qtype == QT_F8_E4M3 || qtype == QT_Q4_0 {
10481 let (b2, r2) = if qtype == QT_Q4_0 {
10482 (mbytes, mrp)
10483 } else {
10484 (bytes, rp)
10485 };
10486 return self.qmatvec_mmvq(b2, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, r2);
10487 }
10488 let name = match qtype {
10489 QT_Q8_0 => "qmatvec_q8_0_dp4a",
10490 QT_Q4_K => "qmatvec_q4_K_dp4a",
10491 QT_Q6_K => "qmatvec_q6_K_dp4a",
10492 QT_Q5_K => "qmatvec_q5_K_dp4a",
10493 QT_Q3_K => "qmatvec_q3_K_dp4a",
10494 QT_NVFP4 => {
10495 if rp {
10496 "qmatvec_nvfp4_dp4a_rp"
10497 } else {
10498 "qmatvec_nvfp4_dp4a"
10499 }
10500 }
10501 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
10502 _ => unreachable!(),
10503 };
10504 let f = self.func(name);
10505 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
10507 grid_dim: (out_f as u32, m as u32, 1),
10508 block_dim: (128, 1, 1),
10509 shared_mem_bytes: 0,
10510 };
10511 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
10512 let __s_b = self.gpu.stream();
10513 let mut b = __s_b.launch_builder(&f);
10514 b.arg(bytes)
10515 .arg(aq)
10516 .arg(ad)
10517 .arg(&mut y)
10518 .arg(&inf)
10519 .arg(&outf)
10520 .arg(&mi)
10521 .arg(&rb);
10522 unsafe {
10523 b.launch(cfg)?;
10524 }
10525 if scale != 1.0 {
10526 self.scale_inplace(&mut y, scale, m * out_f)?;
10527 }
10528 Ok(y)
10529 }
10530
10531 pub fn matmul_decode_exact(
10539 &self,
10540 w: &crate::model::GpuTensor,
10541 x: &CudaSlice<f32>,
10542 m: usize,
10543 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10544 use crate::model::GpuTensor;
10545 if let GpuTensor::Float { data, .. } = w {
10553 return self.linear_decode_exact(x, data, m, w.in_features(), w.out_features());
10554 }
10555 if let GpuTensor::FloatBf16 { data, .. } = w {
10558 let (in_f, out_f) = (w.in_features(), w.out_features());
10559 return self.linear_bf16_chunked(x, data, m, in_f, out_f, true);
10560 }
10561 if !self.uses_q8_1_fast(w) {
10562 return self.matmul(w, x, m);
10563 }
10564 let in_f = w.in_features();
10565 let out_f = w.out_features();
10566 let (bytes, qtype, row_bytes, scale, rp) = match w {
10567 GpuTensor::Quant {
10568 bytes,
10569 qtype,
10570 row_bytes,
10571 scale,
10572 rp,
10573 ..
10574 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10575 _ => return self.matmul(w, x, m),
10576 };
10577 let (bytes, rp) = match w {
10580 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10581 _ => (bytes, rp),
10582 };
10583 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10584 if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
10588 return Ok(y);
10589 }
10590 if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
10599 && std::env::var("MEMRA_NO_BATCHED").is_err()
10600 && (m <= 4 || Self::b8_enabled())
10601 && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_F8_E4M3
10604 || qtype == QT_NVFP4 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_Q8_0)
10605 {
10606 let mcols = Self::batched_mcols(m);
10607 return self.qmatvec_mmvq_batched(
10608 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
10609 );
10610 }
10611 if self.mmvq_supports(qtype) {
10612 return self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
10615 }
10616 self.matmul_pre(w, &aq, &ad, x, m)
10619 }
10620
10621 pub fn matmul_decode_exact_pre(
10631 &self,
10632 w: &crate::model::GpuTensor,
10633 aq: &CudaSlice<i8>,
10634 ad: &CudaSlice<f32>,
10635 m: usize,
10636 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
10637 use crate::model::GpuTensor;
10638 debug_assert!(
10639 self.uses_q8_1_fast(w),
10640 "matmul_decode_exact_pre: caller must guarantee q8_1-fast"
10641 );
10642 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
10644 return Ok(y);
10645 }
10646 let in_f = w.in_features();
10647 let out_f = w.out_features();
10648 let (bytes, qtype, row_bytes, scale, rp) = match w {
10649 GpuTensor::Quant {
10650 bytes,
10651 qtype,
10652 row_bytes,
10653 scale,
10654 rp,
10655 ..
10656 } => (bytes, *qtype, *row_bytes, *scale, *rp),
10657 _ => {
10658 return Err(
10659 "matmul_decode_exact_pre: Quant tensor required (q8_1-fast contract)".into(),
10660 );
10661 }
10662 };
10663 let (bytes, rp) = match w {
10665 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
10666 _ => (bytes, rp),
10667 };
10668 if (2..=16).contains(&m)
10670 && self.batched_supports(qtype)
10671 && self.mmvq_supports(qtype)
10672 && std::env::var("MEMRA_NO_BATCHED").is_err()
10673 && (m <= 4 || Self::b8_enabled())
10674 && (m <= 8
10675 || qtype == QT_Q4_0
10676 || qtype == QT_Q6_K
10677 || qtype == QT_F8_E4M3
10678 || qtype == QT_NVFP4
10679 || qtype == QT_Q4_K
10680 || qtype == QT_Q5_K
10681 || qtype == QT_Q8_0)
10682 {
10683 let mcols = Self::batched_mcols(m);
10684 return self.qmatvec_mmvq_batched(
10685 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
10686 );
10687 }
10688 if self.mmvq_supports(qtype) {
10689 return self.qmatvec_mmvq(bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
10690 }
10691 let x0 = self.zeros(0)?;
10694 self.matmul_pre(w, aq, ad, &x0, m)
10695 }
10696
10697 pub fn matmul_decode_exact_dual_pre(
10706 &self,
10707 w0: &crate::model::GpuTensor,
10708 w1: &crate::model::GpuTensor,
10709 aq: &CudaSlice<i8>,
10710 ad: &CudaSlice<f32>,
10711 m: usize,
10712 ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
10713 {
10714 use crate::model::GpuTensor;
10715 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10716 let on = *ON.get_or_init(|| {
10717 std::env::var("MEMRA_SPEC_DUAL_T")
10718 .map(|v| v != "0")
10719 .unwrap_or(true)
10720 });
10721 if !on
10722 || !(2..=7).contains(&m)
10723 || std::env::var("MEMRA_NO_BATCHED").is_ok()
10724 || !self.uses_q8_1_fast(w0)
10725 || !self.uses_q8_1_fast(w1)
10726 {
10727 return Ok(None);
10728 }
10729 if !self.mmvq_supports(QT_NVFP4) {
10734 return Ok(None);
10735 }
10736 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10737 if w1.in_features() != in_f || w1.out_features() != out_f {
10738 return Ok(None);
10739 }
10740 let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
10741 (
10742 GpuTensor::Quant {
10743 bytes: b0,
10744 qtype: q0,
10745 row_bytes: rb0,
10746 scale: s0,
10747 rp: rp0,
10748 rp4: None,
10749 ..
10750 },
10751 GpuTensor::Quant {
10752 bytes: b1,
10753 qtype: q1,
10754 row_bytes: rb1,
10755 scale: s1,
10756 rp: rp1,
10757 rp4: None,
10758 ..
10759 },
10760 ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
10761 (b0, b1, *rb0, *s0, *s1, *rp0)
10762 }
10763 _ => return Ok(None),
10764 };
10765 if m > 4 && !(rp && Self::b8_enabled() && std::env::var("MEMRA_B567").as_deref() != Ok("0"))
10768 {
10769 return Ok(None);
10770 }
10771 let (y0, y1) =
10772 self.qmatvec_batched_dual_raw(b0, b1, aq, ad, m, in_f, out_f, row_bytes, rp)?;
10773 Ok(Some(((y0, s0), (y1, s1))))
10774 }
10775
10776 pub fn matmul_decode_exact_dual(
10792 &self,
10793 w0: &crate::model::GpuTensor,
10794 w1: &crate::model::GpuTensor,
10795 x: &CudaSlice<f32>,
10796 m: usize,
10797 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
10798 use crate::model::GpuTensor;
10799 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10800 let on = *ON.get_or_init(|| {
10801 std::env::var("MEMRA_SPEC_DUAL_T")
10802 .map(|v| v != "0")
10803 .unwrap_or(true)
10804 });
10805 if !on
10806 || !(2..=4).contains(&m)
10807 || std::env::var("MEMRA_NO_BATCHED").is_ok()
10808 || !self.uses_q8_1_fast(w0)
10809 || !self.uses_q8_1_fast(w1)
10810 {
10811 return Ok(None);
10812 }
10813 if !self.mmvq_supports(QT_NVFP4) {
10818 return Ok(None);
10819 }
10820 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10821 if w1.in_features() != in_f || w1.out_features() != out_f {
10822 return Ok(None);
10823 }
10824 let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
10825 (
10826 GpuTensor::Quant {
10827 bytes: b0,
10828 qtype: q0,
10829 row_bytes: rb0,
10830 scale: s0,
10831 rp: rp0,
10832 rp4: None,
10833 ..
10834 },
10835 GpuTensor::Quant {
10836 bytes: b1,
10837 qtype: q1,
10838 row_bytes: rb1,
10839 scale: s1,
10840 rp: rp1,
10841 rp4: None,
10842 ..
10843 },
10844 ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
10845 (b0, b1, *rb0, *s0, *s1, *rp0)
10846 }
10847 _ => return Ok(None),
10848 };
10849 if std::env::var("MEMRA_DEBUG").is_ok() {
10852 static ONCE: std::sync::Once = std::sync::Once::new();
10853 ONCE.call_once(|| eprintln!("[memra] dual gate+up batched ENGAGED (m={m} rp={rp})"));
10854 }
10855 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
10856 let (y0, y1) =
10857 self.qmatvec_batched_dual_raw(b0, b1, &aq, &ad, m, in_f, out_f, row_bytes, rp)?;
10858 let mut y0 = y0;
10859 let mut y1 = y1;
10860 if s0 != 1.0 {
10861 self.scale_inplace(&mut y0, s0, m * out_f)?;
10862 }
10863 if s1 != 1.0 {
10864 self.scale_inplace(&mut y1, s1, m * out_f)?;
10865 }
10866 Ok(Some((y0, y1)))
10867 }
10868
10869 #[allow(clippy::too_many_arguments)]
10874 pub fn qmatvec_batched_dual_raw(
10875 &self,
10876 b0: &CudaSlice<u8>,
10877 b1: &CudaSlice<u8>,
10878 aq: &CudaSlice<i8>,
10879 ad: &CudaSlice<f32>,
10880 m: usize,
10881 in_f: usize,
10882 out_f: usize,
10883 row_bytes: usize,
10884 rp: bool,
10885 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10886 const ROWS_PER_BLOCK: u32 = 4;
10887 let mcols = Self::batched_mcols(m);
10888 let tiny_rp1 = rp
10891 && mcols == 4
10892 && out_f <= 128
10893 && std::env::var("MEMRA_NVFP4_AUX_DUAL").as_deref() != Ok("0");
10894 let (name, rows_per_block) = if tiny_rp1 {
10895 ("qmatvec_nvfp4_mmvq_dual_b4_rp", ROWS_PER_BLOCK)
10896 } else {
10897 match (mcols, rp, m) {
10898 (2, false, _) => ("qmatvec_nvfp4_mmvq_dual_b2", ROWS_PER_BLOCK),
10899 (4, false, _) => ("qmatvec_nvfp4_mmvq_dual_b4_r2", ROWS_PER_BLOCK * 2),
10900 (2, true, _) => ("qmatvec_nvfp4_mmvq_dual_b2_rp", ROWS_PER_BLOCK),
10901 (4, true, _) => ("qmatvec_nvfp4_mmvq_dual_b4_rpr2", ROWS_PER_BLOCK * 2),
10902 (8, true, 5) => ("qmatvec_nvfp4_mmvq_dual_b5_rpr2", ROWS_PER_BLOCK * 2),
10903 (8, true, 6) => ("qmatvec_nvfp4_mmvq_dual_b6_rpr2", ROWS_PER_BLOCK * 2),
10904 (8, true, 7) => ("qmatvec_nvfp4_mmvq_dual_b7_rpr2", ROWS_PER_BLOCK * 2),
10905 _ => {
10906 return Err(
10907 format!("qmatvec_batched_dual_raw: no dual kernel for m {m}").into(),
10908 );
10909 }
10910 }
10911 };
10912 let f = self.func(name);
10913 let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
10914 let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
10915 let cfg = LaunchConfig {
10916 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
10917 block_dim: (32, ROWS_PER_BLOCK, 1),
10918 shared_mem_bytes: 0,
10919 };
10920 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
10921 let __s_b = self.gpu.stream();
10922 let mut b = __s_b.launch_builder(&f);
10923 b.arg(b0)
10924 .arg(b1)
10925 .arg(aq)
10926 .arg(ad)
10927 .arg(&mut y0)
10928 .arg(&mut y1)
10929 .arg(&inf)
10930 .arg(&outf)
10931 .arg(&mi)
10932 .arg(&rb);
10933 unsafe {
10934 b.launch(cfg)?;
10935 }
10936 Ok((y0, y1))
10937 }
10938
10939 pub fn matmul_pre_dual_noscale(
10951 &self,
10952 w0: &crate::model::GpuTensor,
10953 w1: &crate::model::GpuTensor,
10954 aq: &CudaSlice<i8>,
10955 ad: &CudaSlice<f32>,
10956 m: usize,
10957 ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
10958 {
10959 use crate::model::GpuTensor;
10960 if m != 1 || !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
10961 return Ok(None);
10962 }
10963 if !self.mmvq_supports(QT_NVFP4) {
10973 return Ok(None);
10974 }
10975 let (in_f, out_f) = (w0.in_features(), w0.out_features());
10976 if w1.in_features() != in_f || w1.out_features() != out_f {
10977 return Ok(None);
10978 }
10979 let no_mirror =
10992 |w: &crate::model::GpuTensor| !matches!(w, GpuTensor::Quant { rp4: Some(_), .. });
10993 if self.q8_ffn_fuse2_on()
10994 && no_mirror(w0)
10995 && no_mirror(w1)
10996 && let Some([p0, p1]) = self.q8_fused_params(&[w0, w1])
10997 {
10998 let (y0, y1) = self.q8_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2)?;
10999 return Ok(Some(((y0, 1.0), (y1, 1.0))));
11000 }
11001 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
11011 let (y0, y1) =
11012 self.e4m3_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2, 1.0, 1.0)?;
11013 return Ok(Some(((y0, p0.3), (y1, p1.3))));
11014 }
11015 let (b0, q0, rb0, s0, rp0) = match w0 {
11016 GpuTensor::Quant {
11017 bytes,
11018 qtype,
11019 row_bytes,
11020 scale,
11021 rp,
11022 ..
11023 } => (bytes, *qtype, *row_bytes, *scale, *rp),
11024 _ => return Ok(None),
11025 };
11026 let (b1, q1, rb1, s1, rp1) = match w1 {
11027 GpuTensor::Quant {
11028 bytes,
11029 qtype,
11030 row_bytes,
11031 scale,
11032 rp,
11033 ..
11034 } => (bytes, *qtype, *row_bytes, *scale, *rp),
11035 _ => return Ok(None),
11036 };
11037 if q0 != QT_NVFP4 || q1 != QT_NVFP4 || rb0 != rb1 || rp0 != rp1 {
11038 return Ok(None);
11039 }
11040 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11042 let rows_per_block = ROWS_PER_BLOCK * RPW;
11043 let f = self.func(if rp0 {
11044 "qmatvec_nvfp4_mmvq_dual_mr2_rp"
11045 } else {
11046 "qmatvec_nvfp4_mmvq_dual_mr2"
11047 });
11048 let mut y0 = self.alloc_uninit::<f32>(out_f)?;
11049 let mut y1 = self.alloc_uninit::<f32>(out_f)?;
11050 let cfg = LaunchConfig {
11051 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
11052 block_dim: (32, ROWS_PER_BLOCK, 1),
11053 shared_mem_bytes: 0,
11054 };
11055 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, rb0 as i64);
11056 let one = 1.0f32;
11059 let __s_b = self.gpu.stream();
11060 let mut b = __s_b.launch_builder(&f);
11061 b.arg(b0)
11062 .arg(b1)
11063 .arg(aq)
11064 .arg(ad)
11065 .arg(&mut y0)
11066 .arg(&mut y1)
11067 .arg(&inf)
11068 .arg(&outf)
11069 .arg(&mi)
11070 .arg(&rb)
11071 .arg(&one)
11072 .arg(&one);
11073 unsafe {
11074 b.launch(cfg)?;
11075 }
11076 Ok(Some(((y0, s0), (y1, s1))))
11077 }
11078
11079 #[allow(clippy::too_many_arguments)]
11087 pub fn matmul_nvfp4_fused3(
11088 &self,
11089 w0: &crate::model::GpuTensor,
11090 w1: &crate::model::GpuTensor,
11091 w2: &crate::model::GpuTensor,
11092 aq: &CudaSlice<i8>,
11093 ad: &CudaSlice<f32>,
11094 m: usize,
11095 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11096 {
11097 use crate::model::GpuTensor;
11098 if m != 1
11102 || !self.mmvq_supports(QT_NVFP4)
11103 || !self.uses_q8_1_fast(w0)
11104 || !self.uses_q8_1_fast(w1)
11105 || !self.uses_q8_1_fast(w2)
11106 {
11107 return Ok(None);
11108 }
11109 let unpack = |w: &crate::model::GpuTensor| match w {
11110 GpuTensor::Quant {
11111 bytes,
11112 qtype,
11113 scale,
11114 rp,
11115 ..
11116 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
11117 _ => None,
11118 };
11119 let (Some(p0), Some(p1), Some(p2)) = (unpack(w0), unpack(w1), unpack(w2)) else {
11120 return Ok(None);
11121 };
11122 let in_f = w0.in_features();
11123 if w1.in_features() != in_f || w2.in_features() != in_f {
11124 return Ok(None);
11125 }
11126 let (o0, o1, o2) = (w0.out_features(), w1.out_features(), w2.out_features());
11127 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11129 let rows_pb = ROWS_PER_BLOCK * RPW;
11130 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
11131 let f = self.func("qmatvec_nvfp4_mmvq_fused3_rp");
11132 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11133 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11134 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
11135 let cfg = LaunchConfig {
11136 grid_dim: (nb(o0) + nb(o1) + nb(o2), m as u32, 1),
11137 block_dim: (32, ROWS_PER_BLOCK, 1),
11138 shared_mem_bytes: 0,
11139 };
11140 let (inf, oi0, oi1, oi2, mi) = (in_f as i32, o0 as i32, o1 as i32, o2 as i32, m as i32);
11141 let (b0, b1, b2) = unsafe { (&*p0.0, &*p1.0, &*p2.0) };
11144 let __s_b = self.gpu.stream();
11145 let mut b = __s_b.launch_builder(&f);
11146 b.arg(b0)
11147 .arg(b1)
11148 .arg(b2)
11149 .arg(aq)
11150 .arg(ad)
11151 .arg(&mut y0)
11152 .arg(&mut y1)
11153 .arg(&mut y2)
11154 .arg(&inf)
11155 .arg(&oi0)
11156 .arg(&oi1)
11157 .arg(&oi2)
11158 .arg(&mi)
11159 .arg(&p0.1)
11160 .arg(&p1.1)
11161 .arg(&p2.1);
11162 unsafe {
11163 b.launch(cfg)?;
11164 }
11165 Ok(Some((y0, y1, y2)))
11166 }
11167
11168 pub fn matmul_nvfp4_fused2(
11177 &self,
11178 w0: &crate::model::GpuTensor,
11179 w1: &crate::model::GpuTensor,
11180 aq: &CudaSlice<i8>,
11181 ad: &CudaSlice<f32>,
11182 m: usize,
11183 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11184 use crate::model::GpuTensor;
11185 static FUSED2_OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11186 let off =
11187 *FUSED2_OFF.get_or_init(|| std::env::var("MEMRA_NVFP4_FUSED2").as_deref() == Ok("0"));
11188 if off
11191 || m != 1
11192 || !self.mmvq_supports(QT_NVFP4)
11193 || !self.uses_q8_1_fast(w0)
11194 || !self.uses_q8_1_fast(w1)
11195 {
11196 return Ok(None);
11197 }
11198 let unpack = |w: &crate::model::GpuTensor| match w {
11199 GpuTensor::Quant {
11200 bytes,
11201 qtype,
11202 scale,
11203 rp,
11204 ..
11205 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
11206 _ => None,
11207 };
11208 let (Some(p0), Some(p1)) = (unpack(w0), unpack(w1)) else {
11209 return Ok(None);
11210 };
11211 let in_f = w0.in_features();
11212 if w1.in_features() != in_f {
11213 return Ok(None);
11214 }
11215 let (o0, o1) = (w0.out_features(), w1.out_features());
11216 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11218 let rows_pb = ROWS_PER_BLOCK * RPW;
11219 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
11220 let f = self.func("qmatvec_nvfp4_mmvq_fused2_rp");
11221 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11222 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11223 let cfg = LaunchConfig {
11224 grid_dim: (nb(o0) + nb(o1), m as u32, 1),
11225 block_dim: (32, ROWS_PER_BLOCK, 1),
11226 shared_mem_bytes: 0,
11227 };
11228 let (inf, oi0, oi1, mi) = (in_f as i32, o0 as i32, o1 as i32, m as i32);
11229 let (b0, b1) = unsafe { (&*p0.0, &*p1.0) };
11232 if Self::pdl_on() && Self::pdl_mmvq_on() && Self::pdl_nvfp4q8_on() {
11235 {
11236 use cudarc::driver::{DevicePtr, DevicePtrMut};
11237 let s = &self.gpu.stream();
11238 let (pw0, _g0) = b0.device_ptr(s);
11239 let (pw1, _g1) = b1.device_ptr(s);
11240 let (paq, _g2) = aq.device_ptr(s);
11241 let (pad, _g3) = ad.device_ptr(s);
11242 let (py0, _g4) = y0.device_ptr_mut(s);
11243 let (py1, _g5) = y1.device_ptr_mut(s);
11244 let (s0, s1) = (p0.1, p1.1);
11245 let mut ps = [
11246 &pw0 as *const _ as *mut std::ffi::c_void,
11247 &pw1 as *const _ as *mut _,
11248 &paq as *const _ as *mut _,
11249 &pad as *const _ as *mut _,
11250 &py0 as *const _ as *mut _,
11251 &py1 as *const _ as *mut _,
11252 &inf as *const _ as *mut _,
11253 &oi0 as *const _ as *mut _,
11254 &oi1 as *const _ as *mut _,
11255 &mi as *const _ as *mut _,
11256 &s0 as *const _ as *mut _,
11257 &s1 as *const _ as *mut _,
11258 ];
11259 unsafe {
11260 self.launch_pdl(
11261 "qmatvec_nvfp4_mmvq_fused2_rp",
11262 cfg.grid_dim,
11263 cfg.block_dim,
11264 &mut ps,
11265 )?;
11266 }
11267 }
11268 return Ok(Some((y0, y1)));
11269 }
11270 let __s_b = self.gpu.stream();
11271 let mut b = __s_b.launch_builder(&f);
11272 b.arg(b0)
11273 .arg(b1)
11274 .arg(aq)
11275 .arg(ad)
11276 .arg(&mut y0)
11277 .arg(&mut y1)
11278 .arg(&inf)
11279 .arg(&oi0)
11280 .arg(&oi1)
11281 .arg(&mi)
11282 .arg(&p0.1)
11283 .arg(&p1.1);
11284 unsafe {
11285 b.launch(cfg)?;
11286 }
11287 Ok(Some((y0, y1)))
11288 }
11289
11290 pub fn matmul_nvfp4_fused2_into(
11295 &self,
11296 w0: &crate::model::GpuTensor,
11297 w1: &crate::model::GpuTensor,
11298 aq: &CudaSlice<i8>,
11299 ad: &CudaSlice<f32>,
11300 y0: &mut CudaSlice<f32>,
11301 y1: &mut CudaSlice<f32>,
11302 ) -> Result<bool, Box<dyn std::error::Error>> {
11303 use crate::model::GpuTensor;
11304 static FUSED2_OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11305 let off =
11306 *FUSED2_OFF.get_or_init(|| std::env::var("MEMRA_NVFP4_FUSED2").as_deref() == Ok("0"));
11307 if off
11308 || !self.mmvq_supports(QT_NVFP4)
11309 || !self.uses_q8_1_fast(w0)
11310 || !self.uses_q8_1_fast(w1)
11311 {
11312 return Ok(false);
11313 }
11314 let unpack = |w: &crate::model::GpuTensor| match w {
11315 GpuTensor::Quant {
11316 bytes,
11317 qtype,
11318 scale,
11319 rp,
11320 ..
11321 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
11322 _ => None,
11323 };
11324 let (Some(p0), Some(p1)) = (unpack(w0), unpack(w1)) else {
11325 return Ok(false);
11326 };
11327 let in_f = w0.in_features();
11328 if w1.in_features() != in_f {
11329 return Ok(false);
11330 }
11331 let (o0, o1) = (w0.out_features(), w1.out_features());
11332 if y0.len() < o0 || y1.len() < o1 {
11333 return Ok(false);
11334 }
11335 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11337 let rows_pb = ROWS_PER_BLOCK * RPW;
11338 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
11339 let f = self.func("qmatvec_nvfp4_mmvq_fused2_rp");
11340 let cfg = LaunchConfig {
11341 grid_dim: (nb(o0) + nb(o1), 1, 1),
11342 block_dim: (32, ROWS_PER_BLOCK, 1),
11343 shared_mem_bytes: 0,
11344 };
11345 let (inf, oi0, oi1, mi) = (in_f as i32, o0 as i32, o1 as i32, 1i32);
11346 let (b0, b1) = unsafe { (&*p0.0, &*p1.0) };
11349 let __s_b = self.gpu.stream();
11350 let mut b = __s_b.launch_builder(&f);
11351 b.arg(b0)
11352 .arg(b1)
11353 .arg(aq)
11354 .arg(ad)
11355 .arg(&mut *y0)
11356 .arg(&mut *y1)
11357 .arg(&inf)
11358 .arg(&oi0)
11359 .arg(&oi1)
11360 .arg(&mi)
11361 .arg(&p0.1)
11362 .arg(&p1.1);
11363 unsafe {
11364 b.launch(cfg)?;
11365 }
11366 Ok(true)
11367 }
11368
11369 #[allow(clippy::type_complexity)]
11374 pub fn matmul_nvfp4_fused4(
11375 &self,
11376 w0: &crate::model::GpuTensor,
11377 w1: &crate::model::GpuTensor,
11378 w2: &crate::model::GpuTensor,
11379 w3: &crate::model::GpuTensor,
11380 aq: &CudaSlice<i8>,
11381 ad: &CudaSlice<f32>,
11382 m: usize,
11383 ) -> Result<
11384 Option<(
11385 CudaSlice<f32>,
11386 CudaSlice<f32>,
11387 CudaSlice<f32>,
11388 CudaSlice<f32>,
11389 )>,
11390 Box<dyn std::error::Error>,
11391 > {
11392 use crate::model::GpuTensor;
11393 if m != 1
11395 || std::env::var("MEMRA_NVFP4_FUSED4").as_deref() == Ok("0")
11396 || !self.mmvq_supports(QT_NVFP4)
11397 || !self.uses_q8_1_fast(w0)
11398 || !self.uses_q8_1_fast(w1)
11399 || !self.uses_q8_1_fast(w2)
11400 || !self.uses_q8_1_fast(w3)
11401 {
11402 return Ok(None);
11403 }
11404 let unpack = |w: &crate::model::GpuTensor| match w {
11405 GpuTensor::Quant {
11406 bytes,
11407 qtype,
11408 scale,
11409 rp,
11410 ..
11411 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
11412 _ => None,
11413 };
11414 let (Some(p0), Some(p1), Some(p2), Some(p3)) =
11415 (unpack(w0), unpack(w1), unpack(w2), unpack(w3))
11416 else {
11417 return Ok(None);
11418 };
11419 let in_f = w0.in_features();
11420 if w1.in_features() != in_f || w2.in_features() != in_f || w3.in_features() != in_f {
11421 return Ok(None);
11422 }
11423 let (o0, o1, o2, o3) = (
11424 w0.out_features(),
11425 w1.out_features(),
11426 w2.out_features(),
11427 w3.out_features(),
11428 );
11429 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
11431 let rows_pb = ROWS_PER_BLOCK * RPW;
11432 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
11433 let f = self.func("qmatvec_nvfp4_mmvq_fused4_rp");
11434 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
11435 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
11436 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
11437 let mut y3 = self.alloc_uninit::<f32>(m * o3)?;
11438 let cfg = LaunchConfig {
11439 grid_dim: (nb(o0) + nb(o1) + nb(o2) + nb(o3), m as u32, 1),
11440 block_dim: (32, ROWS_PER_BLOCK, 1),
11441 shared_mem_bytes: 0,
11442 };
11443 let (inf, oi0, oi1, oi2, oi3, mi) = (
11444 in_f as i32,
11445 o0 as i32,
11446 o1 as i32,
11447 o2 as i32,
11448 o3 as i32,
11449 m as i32,
11450 );
11451 let (b0, b1, b2, b3) = unsafe { (&*p0.0, &*p1.0, &*p2.0, &*p3.0) };
11454 let __s_b = self.gpu.stream();
11455 let mut b = __s_b.launch_builder(&f);
11456 b.arg(b0)
11457 .arg(b1)
11458 .arg(b2)
11459 .arg(b3)
11460 .arg(aq)
11461 .arg(ad)
11462 .arg(&mut y0)
11463 .arg(&mut y1)
11464 .arg(&mut y2)
11465 .arg(&mut y3)
11466 .arg(&inf)
11467 .arg(&oi0)
11468 .arg(&oi1)
11469 .arg(&oi2)
11470 .arg(&oi3)
11471 .arg(&mi)
11472 .arg(&p0.1)
11473 .arg(&p1.1)
11474 .arg(&p2.1)
11475 .arg(&p3.1);
11476 unsafe {
11477 b.launch(cfg)?;
11478 }
11479 Ok(Some((y0, y1, y2, y3)))
11480 }
11481
11482 pub fn matmul_q8_fused2(
11490 &self,
11491 w0: &crate::model::GpuTensor,
11492 w1: &crate::model::GpuTensor,
11493 aq: &CudaSlice<i8>,
11494 ad: &CudaSlice<f32>,
11495 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11496 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
11502 return Ok(Some(self.e4m3_fused2_core(
11503 p0.0,
11504 p1.0,
11505 aq,
11506 ad,
11507 w0.in_features(),
11508 p0.1,
11509 p1.1,
11510 p0.2,
11511 p0.3,
11512 p1.3,
11513 )?));
11514 }
11515 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
11516 return Ok(None);
11517 };
11518 Ok(Some(self.q8_fused2_core(
11519 p0.0,
11520 p1.0,
11521 aq,
11522 ad,
11523 w0.in_features(),
11524 p0.1,
11525 p1.1,
11526 p0.2,
11527 )?))
11528 }
11529
11530 #[allow(clippy::too_many_arguments)]
11531 fn q8_fused2_core(
11532 &self,
11533 b0: &CudaSlice<u8>,
11534 b1: &CudaSlice<u8>,
11535 aq: &CudaSlice<i8>,
11536 ad: &CudaSlice<f32>,
11537 in_f: usize,
11538 out0: usize,
11539 out1: usize,
11540 row_bytes: usize,
11541 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11542 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
11544 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
11545 let f = self.func("qmatvec_q8_0_mmvq_fused2");
11546 let mut y0 = self.alloc_uninit::<f32>(out0)?;
11547 let mut y1 = self.alloc_uninit::<f32>(out1)?;
11548 let cfg = LaunchConfig {
11549 grid_dim: (nb0 + nb1, 1, 1),
11550 block_dim: (32, ROWS_PER_BLOCK, 1),
11551 shared_mem_bytes: 0,
11552 };
11553 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
11554 let __s_b = self.gpu.stream();
11555 let mut b = __s_b.launch_builder(&f);
11556 b.arg(b0)
11557 .arg(b1)
11558 .arg(aq)
11559 .arg(ad)
11560 .arg(&mut y0)
11561 .arg(&mut y1)
11562 .arg(&inf)
11563 .arg(&o0)
11564 .arg(&o1)
11565 .arg(&rbl);
11566 unsafe {
11567 b.launch(cfg)?;
11568 }
11569 Ok((y0, y1))
11570 }
11571
11572 pub fn matmul_q8_fused2_x(
11578 &self,
11579 w0: &crate::model::GpuTensor,
11580 w1: &crate::model::GpuTensor,
11581 x: &CudaSlice<f32>,
11582 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11583 if !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
11584 return Ok(None);
11585 }
11586 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
11587 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
11588 return Ok(Some(self.e4m3_fused2_core(
11589 p0.0,
11590 p1.0,
11591 &aq,
11592 &ad,
11593 w0.in_features(),
11594 p0.1,
11595 p1.1,
11596 p0.2,
11597 p0.3,
11598 p1.3,
11599 )?));
11600 }
11601 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
11602 return Ok(None);
11603 };
11604 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
11605 Ok(Some(self.q8_fused2_core(
11606 p0.0,
11607 p1.0,
11608 &aq,
11609 &ad,
11610 w0.in_features(),
11611 p0.1,
11612 p1.1,
11613 p0.2,
11614 )?))
11615 }
11616
11617 #[allow(clippy::too_many_arguments)]
11620 pub fn qmatvec_q8_fused2_raw(
11621 &self,
11622 b0: &CudaSlice<u8>,
11623 b1: &CudaSlice<u8>,
11624 x: &CudaSlice<f32>,
11625 in_f: usize,
11626 out0: usize,
11627 out1: usize,
11628 row_bytes: usize,
11629 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11630 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
11631 self.q8_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes)
11632 }
11633
11634 pub fn matmul_q4_fused3(
11640 &self,
11641 w0: &crate::model::GpuTensor,
11642 w1: &crate::model::GpuTensor,
11643 w2: &crate::model::GpuTensor,
11644 aq: &CudaSlice<i8>,
11645 ad: &CudaSlice<f32>,
11646 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
11647 {
11648 use crate::model::GpuTensor;
11649 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11650 match w {
11651 GpuTensor::Quant {
11652 qtype, row_bytes, ..
11653 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11654 _ => None,
11655 }
11656 };
11657 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
11658 return Ok(None);
11659 };
11660 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
11661 return Ok(None);
11662 }
11663 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11667 match w {
11668 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11669 Some(m) => (m, true),
11670 None => (bytes, *rp),
11671 },
11672 _ => unreachable!(),
11673 }
11674 }
11675 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
11676 if rp0 != rp1 || rp1 != rp2 {
11677 return Ok(None);
11678 }
11679 let rp = rp0;
11680 let rpb: u32 = 4;
11681 let mr1 = rp && Self::q40_mr1_on();
11685 let nb = |o: usize| {
11686 if mr1 {
11687 (o as u32).div_ceil(rpb)
11688 } else {
11689 (o as u32).div_ceil(2).div_ceil(rpb)
11690 }
11691 };
11692 let grid = nb(o0) + nb(o1) + nb(o2);
11693 let mut y0 = self.alloc_uninit::<f32>(o0)?;
11694 let mut y1 = self.alloc_uninit::<f32>(o1)?;
11695 let mut y2 = self.alloc_uninit::<f32>(o2)?;
11696 let f = self.func(if mr1 {
11697 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
11698 } else if rp {
11699 "qmatvec_q4_0_mmvq_fused3_rp"
11700 } else {
11701 "qmatvec_q4_0_mmvq_fused3"
11702 });
11703 let cfg = LaunchConfig {
11704 grid_dim: (grid, 1, 1),
11705 block_dim: (32, rpb, 1),
11706 shared_mem_bytes: 0,
11707 };
11708 let inf = w0.in_features() as i32;
11709 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
11710 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
11711 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11714 {
11715 use cudarc::driver::{DevicePtr, DevicePtrMut};
11716 let s = &self.gpu.stream();
11717 let (p0, _g0) = b0.device_ptr(s);
11718 let (p1, _g1) = b1.device_ptr(s);
11719 let (p2, _g2) = b2.device_ptr(s);
11720 let (paq, _g3) = aq.device_ptr(s);
11721 let (pad, _g4) = ad.device_ptr(s);
11722 let (py0, _g5) = y0.device_ptr_mut(s);
11723 let (py1, _g6) = y1.device_ptr_mut(s);
11724 let (py2, _g7) = y2.device_ptr_mut(s);
11725 let mut ps = [
11726 &p0 as *const _ as *mut std::ffi::c_void,
11727 &p1 as *const _ as *mut _,
11728 &p2 as *const _ as *mut _,
11729 &paq as *const _ as *mut _,
11730 &pad as *const _ as *mut _,
11731 &py0 as *const _ as *mut _,
11732 &py1 as *const _ as *mut _,
11733 &py2 as *const _ as *mut _,
11734 &inf as *const _ as *mut _,
11735 &oo0 as *const _ as *mut _,
11736 &oo1 as *const _ as *mut _,
11737 &oo2 as *const _ as *mut _,
11738 &r0 as *const _ as *mut _,
11739 &r1 as *const _ as *mut _,
11740 &r2 as *const _ as *mut _,
11741 ];
11742 unsafe {
11743 self.launch_pdl(
11744 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
11745 (grid, 1, 1),
11746 (32, rpb, 1),
11747 &mut ps,
11748 )?;
11749 }
11750 }
11751 return Ok(Some((y0, y1, y2)));
11752 }
11753 let __s_b = self.gpu.stream();
11754 let mut b = __s_b.launch_builder(&f);
11755 b.arg(b0)
11756 .arg(b1)
11757 .arg(b2)
11758 .arg(aq)
11759 .arg(ad)
11760 .arg(&mut y0)
11761 .arg(&mut y1)
11762 .arg(&mut y2)
11763 .arg(&inf)
11764 .arg(&oo0)
11765 .arg(&oo1)
11766 .arg(&oo2)
11767 .arg(&r0)
11768 .arg(&r1)
11769 .arg(&r2);
11770 unsafe {
11771 b.launch(cfg)?;
11772 }
11773 Ok(Some((y0, y1, y2)))
11774 }
11775
11776 #[allow(clippy::too_many_arguments)]
11779 pub fn matmul_q4_fused3_into(
11780 &self,
11781 w0: &crate::model::GpuTensor,
11782 w1: &crate::model::GpuTensor,
11783 w2: &crate::model::GpuTensor,
11784 aq: &CudaSlice<i8>,
11785 ad: &CudaSlice<f32>,
11786 y0: &mut CudaSlice<f32>,
11787 y1: &mut CudaSlice<f32>,
11788 y2: &mut CudaSlice<f32>,
11789 ) -> Result<bool, Box<dyn std::error::Error>> {
11790 use crate::model::GpuTensor;
11791 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11792 match w {
11793 GpuTensor::Quant {
11794 qtype, row_bytes, ..
11795 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11796 _ => None,
11797 }
11798 };
11799 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
11800 return Ok(false);
11801 };
11802 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
11803 return Ok(false);
11804 }
11805 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11806 match w {
11807 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11808 Some(m) => (m, true),
11809 None => (bytes, *rp),
11810 },
11811 _ => unreachable!(),
11812 }
11813 }
11814 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
11815 if rp0 != rp1 || rp1 != rp2 {
11816 return Ok(false);
11817 }
11818 let rp = rp0;
11819 let rpb: u32 = 4;
11820 let mr1 = rp && Self::q40_mr1_on();
11821 let nb = |o: usize| {
11822 if mr1 {
11823 (o as u32).div_ceil(rpb)
11824 } else {
11825 (o as u32).div_ceil(2).div_ceil(rpb)
11826 }
11827 };
11828 let grid = nb(o0) + nb(o1) + nb(o2);
11829 debug_assert!(y0.len() >= o0 && y1.len() >= o1 && y2.len() >= o2);
11830 let f = self.func(if mr1 {
11831 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
11832 } else if rp {
11833 "qmatvec_q4_0_mmvq_fused3_rp"
11834 } else {
11835 "qmatvec_q4_0_mmvq_fused3"
11836 });
11837 let cfg = LaunchConfig {
11838 grid_dim: (grid, 1, 1),
11839 block_dim: (32, rpb, 1),
11840 shared_mem_bytes: 0,
11841 };
11842 let inf = w0.in_features() as i32;
11843 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
11844 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
11845 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11847 use cudarc::driver::{DevicePtr, DevicePtrMut};
11848 let s = &self.gpu.stream();
11849 let (p0, _g0) = b0.device_ptr(s);
11850 let (p1, _g1) = b1.device_ptr(s);
11851 let (p2, _g2) = b2.device_ptr(s);
11852 let (paq, _g3) = aq.device_ptr(s);
11853 let (pad, _g4) = ad.device_ptr(s);
11854 let (py0, _g5) = y0.device_ptr_mut(s);
11855 let (py1, _g6) = y1.device_ptr_mut(s);
11856 let (py2, _g7) = y2.device_ptr_mut(s);
11857 let mut ps = [
11858 &p0 as *const _ as *mut std::ffi::c_void,
11859 &p1 as *const _ as *mut _,
11860 &p2 as *const _ as *mut _,
11861 &paq as *const _ as *mut _,
11862 &pad as *const _ as *mut _,
11863 &py0 as *const _ as *mut _,
11864 &py1 as *const _ as *mut _,
11865 &py2 as *const _ as *mut _,
11866 &inf as *const _ as *mut _,
11867 &oo0 as *const _ as *mut _,
11868 &oo1 as *const _ as *mut _,
11869 &oo2 as *const _ as *mut _,
11870 &r0 as *const _ as *mut _,
11871 &r1 as *const _ as *mut _,
11872 &r2 as *const _ as *mut _,
11873 ];
11874 unsafe {
11875 self.launch_pdl(
11876 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
11877 (grid, 1, 1),
11878 (32, rpb, 1),
11879 &mut ps,
11880 )?;
11881 }
11882 return Ok(true);
11883 }
11884 let __s_b = self.gpu.stream();
11885 let mut b = __s_b.launch_builder(&f);
11886 b.arg(b0)
11887 .arg(b1)
11888 .arg(b2)
11889 .arg(aq)
11890 .arg(ad)
11891 .arg(&mut *y0)
11892 .arg(&mut *y1)
11893 .arg(&mut *y2)
11894 .arg(&inf)
11895 .arg(&oo0)
11896 .arg(&oo1)
11897 .arg(&oo2)
11898 .arg(&r0)
11899 .arg(&r1)
11900 .arg(&r2);
11901 unsafe {
11902 b.launch(cfg)?;
11903 }
11904 Ok(true)
11905 }
11906
11907 pub fn matmul_q4_fused2(
11909 &self,
11910 w0: &crate::model::GpuTensor,
11911 w1: &crate::model::GpuTensor,
11912 aq: &CudaSlice<i8>,
11913 ad: &CudaSlice<f32>,
11914 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
11915 use crate::model::GpuTensor;
11916 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
11917 match w {
11918 GpuTensor::Quant {
11919 qtype, row_bytes, ..
11920 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
11921 _ => None,
11922 }
11923 };
11924 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
11925 return Ok(None);
11926 };
11927 if w0.in_features() != w1.in_features() {
11928 return Ok(None);
11929 }
11930 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
11932 match w {
11933 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
11934 Some(m) => (m, true),
11935 None => (bytes, *rp),
11936 },
11937 _ => unreachable!(),
11938 }
11939 }
11940 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
11941 if rp0 != rp1 {
11942 return Ok(None);
11943 }
11944 let rp = rp0;
11945 let rpb: u32 = 4;
11946 let mr1 = rp && Self::q40_mr1_on();
11948 let nb = |o: usize| {
11949 if mr1 {
11950 (o as u32).div_ceil(rpb)
11951 } else {
11952 (o as u32).div_ceil(2).div_ceil(rpb)
11953 }
11954 };
11955 let grid = nb(o0) + nb(o1);
11956 let mut y0 = self.alloc_uninit::<f32>(o0)?;
11957 let mut y1 = self.alloc_uninit::<f32>(o1)?;
11958 let f = self.func(if mr1 {
11959 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
11960 } else if rp {
11961 "qmatvec_q4_0_mmvq_fused2_rp"
11962 } else {
11963 "qmatvec_q4_0_mmvq_fused2"
11964 });
11965 let cfg = LaunchConfig {
11966 grid_dim: (grid, 1, 1),
11967 block_dim: (32, rpb, 1),
11968 shared_mem_bytes: 0,
11969 };
11970 let inf = w0.in_features() as i32;
11971 let (oo0, oo1) = (o0 as i32, o1 as i32);
11972 let (r0, r1) = (rb0 as i64, rb1 as i64);
11973 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
11975 {
11976 use cudarc::driver::{DevicePtr, DevicePtrMut};
11977 let s = &self.gpu.stream();
11978 let (p0, _g0) = b0.device_ptr(s);
11979 let (p1, _g1) = b1.device_ptr(s);
11980 let (paq, _g2) = aq.device_ptr(s);
11981 let (pad, _g3) = ad.device_ptr(s);
11982 let (py0, _g4) = y0.device_ptr_mut(s);
11983 let (py1, _g5) = y1.device_ptr_mut(s);
11984 let mut ps = [
11985 &p0 as *const _ as *mut std::ffi::c_void,
11986 &p1 as *const _ as *mut _,
11987 &paq as *const _ as *mut _,
11988 &pad as *const _ as *mut _,
11989 &py0 as *const _ as *mut _,
11990 &py1 as *const _ as *mut _,
11991 &inf as *const _ as *mut _,
11992 &oo0 as *const _ as *mut _,
11993 &oo1 as *const _ as *mut _,
11994 &r0 as *const _ as *mut _,
11995 &r1 as *const _ as *mut _,
11996 ];
11997 unsafe {
11998 self.launch_pdl(
11999 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
12000 (grid, 1, 1),
12001 (32, rpb, 1),
12002 &mut ps,
12003 )?;
12004 }
12005 }
12006 return Ok(Some((y0, y1)));
12007 }
12008 let __s_b = self.gpu.stream();
12009 let mut b = __s_b.launch_builder(&f);
12010 b.arg(b0)
12011 .arg(b1)
12012 .arg(aq)
12013 .arg(ad)
12014 .arg(&mut y0)
12015 .arg(&mut y1)
12016 .arg(&inf)
12017 .arg(&oo0)
12018 .arg(&oo1)
12019 .arg(&r0)
12020 .arg(&r1);
12021 unsafe {
12022 b.launch(cfg)?;
12023 }
12024 Ok(Some((y0, y1)))
12025 }
12026
12027 pub fn matmul_q4_fused2_into(
12029 &self,
12030 w0: &crate::model::GpuTensor,
12031 w1: &crate::model::GpuTensor,
12032 aq: &CudaSlice<i8>,
12033 ad: &CudaSlice<f32>,
12034 y0: &mut CudaSlice<f32>,
12035 y1: &mut CudaSlice<f32>,
12036 ) -> Result<bool, Box<dyn std::error::Error>> {
12037 use crate::model::GpuTensor;
12038 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
12039 match w {
12040 GpuTensor::Quant {
12041 qtype, row_bytes, ..
12042 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
12043 _ => None,
12044 }
12045 };
12046 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
12047 return Ok(false);
12048 };
12049 if w0.in_features() != w1.in_features() {
12050 return Ok(false);
12051 }
12052 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
12053 match w {
12054 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
12055 Some(m) => (m, true),
12056 None => (bytes, *rp),
12057 },
12058 _ => unreachable!(),
12059 }
12060 }
12061 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
12062 if rp0 != rp1 {
12063 return Ok(false);
12064 }
12065 let rp = rp0;
12066 let rpb: u32 = 4;
12067 let mr1 = rp && Self::q40_mr1_on();
12068 let nb = |o: usize| {
12069 if mr1 {
12070 (o as u32).div_ceil(rpb)
12071 } else {
12072 (o as u32).div_ceil(2).div_ceil(rpb)
12073 }
12074 };
12075 let grid = nb(o0) + nb(o1);
12076 debug_assert!(y0.len() >= o0 && y1.len() >= o1);
12077 let f = self.func(if mr1 {
12078 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
12079 } else if rp {
12080 "qmatvec_q4_0_mmvq_fused2_rp"
12081 } else {
12082 "qmatvec_q4_0_mmvq_fused2"
12083 });
12084 let cfg = LaunchConfig {
12085 grid_dim: (grid, 1, 1),
12086 block_dim: (32, rpb, 1),
12087 shared_mem_bytes: 0,
12088 };
12089 let inf = w0.in_features() as i32;
12090 let (oo0, oo1) = (o0 as i32, o1 as i32);
12091 let (r0, r1) = (rb0 as i64, rb1 as i64);
12092 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
12094 use cudarc::driver::{DevicePtr, DevicePtrMut};
12095 let s = &self.gpu.stream();
12096 let (p0, _g0) = b0.device_ptr(s);
12097 let (p1, _g1) = b1.device_ptr(s);
12098 let (paq, _g2) = aq.device_ptr(s);
12099 let (pad, _g3) = ad.device_ptr(s);
12100 let (py0, _g4) = y0.device_ptr_mut(s);
12101 let (py1, _g5) = y1.device_ptr_mut(s);
12102 let mut ps = [
12103 &p0 as *const _ as *mut std::ffi::c_void,
12104 &p1 as *const _ as *mut _,
12105 &paq as *const _ as *mut _,
12106 &pad as *const _ as *mut _,
12107 &py0 as *const _ as *mut _,
12108 &py1 as *const _ as *mut _,
12109 &inf as *const _ as *mut _,
12110 &oo0 as *const _ as *mut _,
12111 &oo1 as *const _ as *mut _,
12112 &r0 as *const _ as *mut _,
12113 &r1 as *const _ as *mut _,
12114 ];
12115 unsafe {
12116 self.launch_pdl(
12117 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
12118 (grid, 1, 1),
12119 (32, rpb, 1),
12120 &mut ps,
12121 )?;
12122 }
12123 return Ok(true);
12124 }
12125 let __s_b = self.gpu.stream();
12126 let mut b = __s_b.launch_builder(&f);
12127 b.arg(b0)
12128 .arg(b1)
12129 .arg(aq)
12130 .arg(ad)
12131 .arg(&mut *y0)
12132 .arg(&mut *y1)
12133 .arg(&inf)
12134 .arg(&oo0)
12135 .arg(&oo1)
12136 .arg(&r0)
12137 .arg(&r1);
12138 unsafe {
12139 b.launch(cfg)?;
12140 }
12141 Ok(true)
12142 }
12143
12144 pub fn matmul_q4_fused2_batched(
12149 &self,
12150 w0: &crate::model::GpuTensor,
12151 w1: &crate::model::GpuTensor,
12152 aq: &CudaSlice<i8>,
12153 ad: &CudaSlice<f32>,
12154 m: usize,
12155 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
12156 use crate::model::GpuTensor;
12157 if m < 2 || m > 8 {
12158 return Ok(None);
12159 }
12160 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
12161 match w {
12162 GpuTensor::Quant {
12163 qtype, row_bytes, ..
12164 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
12165 _ => None,
12166 }
12167 };
12168 let (Some((rb0, o0)), Some((_rb1, o1))) = (q4(w0), q4(w1)) else {
12169 return Ok(None);
12170 };
12171 if w0.in_features() != w1.in_features() {
12172 return Ok(None);
12173 }
12174 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
12175 match w {
12176 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
12177 Some(mr) => (mr, true),
12178 None => (bytes, *rp),
12179 },
12180 _ => unreachable!(),
12181 }
12182 }
12183 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
12184 if !rp0 || !rp1 {
12185 return Ok(None);
12186 }
12187 let mcols = Self::batched_mcols(m);
12188 let rpb: u32 = 4;
12189 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
12190 let grid = nb(o0) + nb(o1);
12191 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
12192 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
12193 let f = self.func(match mcols {
12194 2 => "qmatvec_q4_0_mmvq_b2_f2_rp",
12195 4 => "qmatvec_q4_0_mmvq_b4_f2_rp",
12196 _ => "qmatvec_q4_0_mmvq_b8_f2_rp",
12197 });
12198 let cfg = LaunchConfig {
12199 grid_dim: (grid, 1, 1),
12200 block_dim: (32, rpb, 1),
12201 shared_mem_bytes: 0,
12202 };
12203 let inf = w0.in_features() as i32;
12204 let (oo0, oo1, mi) = (o0 as i32, o1 as i32, m as i32);
12205 let rb = rb0 as i64;
12206 let __s_b = self.gpu.stream();
12207 let mut b = __s_b.launch_builder(&f);
12208 b.arg(b0)
12209 .arg(b1)
12210 .arg(aq)
12211 .arg(ad)
12212 .arg(&mut y0)
12213 .arg(&mut y1)
12214 .arg(&inf)
12215 .arg(&oo0)
12216 .arg(&oo1)
12217 .arg(&mi)
12218 .arg(&rb);
12219 unsafe {
12220 b.launch(cfg)?;
12221 }
12222 Ok(Some((y0, y1)))
12223 }
12224
12225 #[allow(clippy::too_many_arguments)]
12228 pub fn matmul_q4_fused3_batched(
12229 &self,
12230 w0: &crate::model::GpuTensor,
12231 w1: &crate::model::GpuTensor,
12232 w2: &crate::model::GpuTensor,
12233 aq: &CudaSlice<i8>,
12234 ad: &CudaSlice<f32>,
12235 m: usize,
12236 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
12237 {
12238 use crate::model::GpuTensor;
12239 if m < 2 || m > 8 {
12240 return Ok(None);
12241 }
12242 let q4 = |w: &GpuTensor| -> Option<usize> {
12243 match w {
12244 GpuTensor::Quant { qtype, .. } if *qtype == QT_Q4_0 => Some(w.out_features()),
12245 _ => None,
12246 }
12247 };
12248 let (Some(o0), Some(o1), Some(o2)) = (q4(w0), q4(w1), q4(w2)) else {
12249 return Ok(None);
12250 };
12251 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
12252 return Ok(None);
12253 }
12254 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
12255 match w {
12256 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
12257 Some(mr) => (mr, true),
12258 None => (bytes, *rp),
12259 },
12260 _ => unreachable!(),
12261 }
12262 }
12263 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
12264 if !rp0 || !rp1 || !rp2 {
12265 return Ok(None);
12266 }
12267 let mcols = Self::batched_mcols(m);
12268 let rpb: u32 = 4;
12269 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
12270 let grid = nb(o0) + nb(o1) + nb(o2);
12271 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
12272 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
12273 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
12274 let f = self.func(match mcols {
12275 2 => "qmatvec_q4_0_mmvq_b2_f3_rp",
12276 4 => "qmatvec_q4_0_mmvq_b4_f3_rp",
12277 _ => "qmatvec_q4_0_mmvq_b8_f3_rp",
12278 });
12279 let cfg = LaunchConfig {
12280 grid_dim: (grid, 1, 1),
12281 block_dim: (32, rpb, 1),
12282 shared_mem_bytes: 0,
12283 };
12284 let inf = w0.in_features() as i32;
12285 let (oo0, oo1, oo2, mi) = (o0 as i32, o1 as i32, o2 as i32, m as i32);
12286 let rb = 0i64;
12287 let __s_b = self.gpu.stream();
12288 let mut b = __s_b.launch_builder(&f);
12289 b.arg(b0)
12290 .arg(b1)
12291 .arg(b2)
12292 .arg(aq)
12293 .arg(ad)
12294 .arg(&mut y0)
12295 .arg(&mut y1)
12296 .arg(&mut y2)
12297 .arg(&inf)
12298 .arg(&oo0)
12299 .arg(&oo1)
12300 .arg(&oo2)
12301 .arg(&mi)
12302 .arg(&rb);
12303 unsafe {
12304 b.launch(cfg)?;
12305 }
12306 Ok(Some((y0, y1, y2)))
12307 }
12308
12309 pub fn matmul_q8_fused3(
12310 &self,
12311 w0: &crate::model::GpuTensor,
12312 w1: &crate::model::GpuTensor,
12313 w2: &crate::model::GpuTensor,
12314 aq: &CudaSlice<i8>,
12315 ad: &CudaSlice<f32>,
12316 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
12317 {
12318 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
12321 return Ok(Some(self.e4m3_fused3_core(
12322 p0.0,
12323 p1.0,
12324 p2.0,
12325 aq,
12326 ad,
12327 w0.in_features(),
12328 p0.1,
12329 p1.1,
12330 p2.1,
12331 p0.2,
12332 p0.3,
12333 p1.3,
12334 p2.3,
12335 )?));
12336 }
12337 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
12338 return Ok(None);
12339 };
12340 Ok(Some(self.q8_fused3_core(
12341 p0.0,
12342 p1.0,
12343 p2.0,
12344 aq,
12345 ad,
12346 w0.in_features(),
12347 p0.1,
12348 p1.1,
12349 p2.1,
12350 p0.2,
12351 )?))
12352 }
12353
12354 #[allow(clippy::too_many_arguments)]
12355 fn q8_fused3_core(
12356 &self,
12357 b0: &CudaSlice<u8>,
12358 b1: &CudaSlice<u8>,
12359 b2: &CudaSlice<u8>,
12360 aq: &CudaSlice<i8>,
12361 ad: &CudaSlice<f32>,
12362 in_f: usize,
12363 out0: usize,
12364 out1: usize,
12365 out2: usize,
12366 row_bytes: usize,
12367 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12368 const ROWS_PER_BLOCK: u32 = 4;
12369 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12370 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12371 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12372 let f = self.func("qmatvec_q8_0_mmvq_fused3");
12373 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12374 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12375 let mut y2 = self.alloc_uninit::<f32>(out2)?;
12376 let cfg = LaunchConfig {
12377 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12378 block_dim: (32, ROWS_PER_BLOCK, 1),
12379 shared_mem_bytes: 0,
12380 };
12381 let (inf, o0, o1, o2, rbl) = (
12382 in_f as i32,
12383 out0 as i32,
12384 out1 as i32,
12385 out2 as i32,
12386 row_bytes as i64,
12387 );
12388 let __s_b = self.gpu.stream();
12389 let mut b = __s_b.launch_builder(&f);
12390 b.arg(b0)
12391 .arg(b1)
12392 .arg(b2)
12393 .arg(aq)
12394 .arg(ad)
12395 .arg(&mut y0)
12396 .arg(&mut y1)
12397 .arg(&mut y2)
12398 .arg(&inf)
12399 .arg(&o0)
12400 .arg(&o1)
12401 .arg(&o2)
12402 .arg(&rbl);
12403 unsafe {
12404 b.launch(cfg)?;
12405 }
12406 Ok((y0, y1, y2))
12407 }
12408
12409 #[allow(clippy::too_many_arguments)]
12411 pub fn qmatvec_q8_fused3_raw(
12412 &self,
12413 b0: &CudaSlice<u8>,
12414 b1: &CudaSlice<u8>,
12415 b2: &CudaSlice<u8>,
12416 x: &CudaSlice<f32>,
12417 in_f: usize,
12418 out0: usize,
12419 out1: usize,
12420 out2: usize,
12421 row_bytes: usize,
12422 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12423 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
12424 self.q8_fused3_core(b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes)
12425 }
12426
12427 pub fn matmul_q8_fused2_t(
12438 &self,
12439 w0: &crate::model::GpuTensor,
12440 w1: &crate::model::GpuTensor,
12441 aq: &CudaSlice<i8>,
12442 ad: &CudaSlice<f32>,
12443 m: usize,
12444 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
12445 if !(2..=8).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
12449 return Ok(None);
12450 }
12451 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
12454 if m > 4 && !Self::b8_enabled() {
12455 return Ok(None);
12456 }
12457 return Ok(Some(self.e4m3_fused2_t_core(
12458 p0.0,
12459 p1.0,
12460 aq,
12461 ad,
12462 m,
12463 w0.in_features(),
12464 p0.1,
12465 p1.1,
12466 p0.2,
12467 p0.3,
12468 p1.3,
12469 )?));
12470 }
12471 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
12472 return Ok(None);
12473 };
12474 Ok(Some(self.q8_fused2_t_core(
12475 p0.0,
12476 p1.0,
12477 aq,
12478 ad,
12479 m,
12480 w0.in_features(),
12481 p0.1,
12482 p1.1,
12483 p0.2,
12484 )?))
12485 }
12486
12487 #[allow(clippy::too_many_arguments)]
12488 fn q8_fused2_t_core(
12489 &self,
12490 b0: &CudaSlice<u8>,
12491 b1: &CudaSlice<u8>,
12492 aq: &CudaSlice<i8>,
12493 ad: &CudaSlice<f32>,
12494 m: usize,
12495 in_f: usize,
12496 out0: usize,
12497 out1: usize,
12498 row_bytes: usize,
12499 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12500 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12502 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12503 let f = self.func(match Self::batched_mcols(m) {
12504 2 => "qmatvec_q8_0_mmvq_fused2_b2",
12505 4 => "qmatvec_q8_0_mmvq_fused2_b4",
12506 _ => "qmatvec_q8_0_mmvq_fused2_b8",
12508 });
12509 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12510 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12511 let cfg = LaunchConfig {
12512 grid_dim: (nb0 + nb1, 1, 1),
12513 block_dim: (32, ROWS_PER_BLOCK, 1),
12514 shared_mem_bytes: 0,
12515 };
12516 let (inf, o0, o1, mi, rbl) = (
12517 in_f as i32,
12518 out0 as i32,
12519 out1 as i32,
12520 m as i32,
12521 row_bytes as i64,
12522 );
12523 let __s_b = self.gpu.stream();
12524 let mut b = __s_b.launch_builder(&f);
12525 b.arg(b0)
12526 .arg(b1)
12527 .arg(aq)
12528 .arg(ad)
12529 .arg(&mut y0)
12530 .arg(&mut y1)
12531 .arg(&inf)
12532 .arg(&o0)
12533 .arg(&o1)
12534 .arg(&mi)
12535 .arg(&rbl);
12536 unsafe {
12537 b.launch(cfg)?;
12538 }
12539 Ok((y0, y1))
12540 }
12541
12542 #[allow(clippy::too_many_arguments)]
12545 pub fn qmatvec_q8_fused2_t_raw(
12546 &self,
12547 b0: &CudaSlice<u8>,
12548 b1: &CudaSlice<u8>,
12549 x: &CudaSlice<f32>,
12550 m: usize,
12551 in_f: usize,
12552 out0: usize,
12553 out1: usize,
12554 row_bytes: usize,
12555 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12556 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12557 self.q8_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes)
12558 }
12559
12560 #[allow(clippy::too_many_arguments)]
12563 pub fn matmul_q8_fused3_t(
12564 &self,
12565 w0: &crate::model::GpuTensor,
12566 w1: &crate::model::GpuTensor,
12567 w2: &crate::model::GpuTensor,
12568 aq: &CudaSlice<i8>,
12569 ad: &CudaSlice<f32>,
12570 m: usize,
12571 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
12572 {
12573 if !(2..=4).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
12574 return Ok(None);
12575 }
12576 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
12577 return Ok(Some(self.e4m3_fused3_t_core(
12578 p0.0,
12579 p1.0,
12580 p2.0,
12581 aq,
12582 ad,
12583 m,
12584 w0.in_features(),
12585 p0.1,
12586 p1.1,
12587 p2.1,
12588 p0.2,
12589 p0.3,
12590 p1.3,
12591 p2.3,
12592 )?));
12593 }
12594 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
12595 return Ok(None);
12596 };
12597 Ok(Some(self.q8_fused3_t_core(
12598 p0.0,
12599 p1.0,
12600 p2.0,
12601 aq,
12602 ad,
12603 m,
12604 w0.in_features(),
12605 p0.1,
12606 p1.1,
12607 p2.1,
12608 p0.2,
12609 )?))
12610 }
12611
12612 #[allow(clippy::too_many_arguments)]
12613 fn q8_fused3_t_core(
12614 &self,
12615 b0: &CudaSlice<u8>,
12616 b1: &CudaSlice<u8>,
12617 b2: &CudaSlice<u8>,
12618 aq: &CudaSlice<i8>,
12619 ad: &CudaSlice<f32>,
12620 m: usize,
12621 in_f: usize,
12622 out0: usize,
12623 out1: usize,
12624 out2: usize,
12625 row_bytes: usize,
12626 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12627 const ROWS_PER_BLOCK: u32 = 4;
12628 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12629 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12630 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12631 let f = self.func(if Self::batched_mcols(m) == 2 {
12632 "qmatvec_q8_0_mmvq_fused3_b2"
12633 } else {
12634 "qmatvec_q8_0_mmvq_fused3_b4"
12635 });
12636 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12637 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12638 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
12639 let cfg = LaunchConfig {
12640 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12641 block_dim: (32, ROWS_PER_BLOCK, 1),
12642 shared_mem_bytes: 0,
12643 };
12644 let (inf, o0, o1, o2, mi, rbl) = (
12645 in_f as i32,
12646 out0 as i32,
12647 out1 as i32,
12648 out2 as i32,
12649 m as i32,
12650 row_bytes as i64,
12651 );
12652 let __s_b = self.gpu.stream();
12653 let mut b = __s_b.launch_builder(&f);
12654 b.arg(b0)
12655 .arg(b1)
12656 .arg(b2)
12657 .arg(aq)
12658 .arg(ad)
12659 .arg(&mut y0)
12660 .arg(&mut y1)
12661 .arg(&mut y2)
12662 .arg(&inf)
12663 .arg(&o0)
12664 .arg(&o1)
12665 .arg(&o2)
12666 .arg(&mi)
12667 .arg(&rbl);
12668 unsafe {
12669 b.launch(cfg)?;
12670 }
12671 Ok((y0, y1, y2))
12672 }
12673
12674 #[allow(clippy::too_many_arguments)]
12676 pub fn qmatvec_q8_fused3_t_raw(
12677 &self,
12678 b0: &CudaSlice<u8>,
12679 b1: &CudaSlice<u8>,
12680 b2: &CudaSlice<u8>,
12681 x: &CudaSlice<f32>,
12682 m: usize,
12683 in_f: usize,
12684 out0: usize,
12685 out1: usize,
12686 out2: usize,
12687 row_bytes: usize,
12688 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12689 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12690 self.q8_fused3_t_core(b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes)
12691 }
12692
12693 pub fn q8_ffn_fuse2_on(&self) -> bool {
12697 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12698 *ON.get_or_init(|| std::env::var("MEMRA_Q8_FFN_FUSE2").as_deref() != Ok("0"))
12699 }
12700
12701 #[allow(clippy::type_complexity)]
12707 fn q8_fused_params<'w, const N: usize>(
12708 &self,
12709 ws: &[&'w crate::model::GpuTensor; N],
12710 ) -> Option<[(&'w CudaSlice<u8>, usize, usize); N]> {
12711 use crate::model::GpuTensor;
12712 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
12713 return None;
12714 }
12715 if std::env::var("MEMRA_Q8_DUAL").is_ok_and(|v| v == "0") {
12716 return None;
12717 }
12718 let in_f = ws[0].in_features();
12719 let mut out: [Option<(&CudaSlice<u8>, usize, usize)>; N] = [None; N];
12720 for (i, w) in ws.iter().enumerate() {
12721 match w {
12722 GpuTensor::Quant {
12723 bytes,
12724 qtype,
12725 row_bytes,
12726 scale,
12727 ..
12728 } if *qtype == QT_Q8_0 && *scale == 1.0 && w.in_features() == in_f => {
12729 out[i] = Some((bytes, w.out_features(), *row_bytes))
12730 }
12731 _ => return None,
12732 }
12733 }
12734 Some(out.map(|o| o.unwrap()))
12735 }
12736
12737 pub fn e4m3_dual_on(&self) -> bool {
12740 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12741 *ON.get_or_init(|| std::env::var("MEMRA_E4M3_DUAL").as_deref() != Ok("0"))
12742 }
12743
12744 #[allow(clippy::type_complexity)]
12756 fn e4m3_fused_params<'w, const N: usize>(
12757 &self,
12758 ws: &[&'w crate::model::GpuTensor; N],
12759 ) -> Option<[(&'w CudaSlice<u8>, usize, usize, f32); N]> {
12760 use crate::model::GpuTensor;
12761 if !self.e4m3_dual_on() {
12762 return None;
12763 }
12764 let in_f = ws[0].in_features();
12765 let mut out: [Option<(&CudaSlice<u8>, usize, usize, f32)>; N] = [None; N];
12766 for (i, w) in ws.iter().enumerate() {
12767 match w {
12768 GpuTensor::Quant {
12769 bytes,
12770 qtype,
12771 row_bytes,
12772 scale,
12773 rp,
12774 rp4,
12775 ..
12776 } if *qtype == QT_F8_E4M3
12777 && w.in_features() == in_f
12778 && *row_bytes == in_f
12779 && !*rp
12780 && rp4.is_none() =>
12781 {
12782 out[i] = Some((bytes, w.out_features(), *row_bytes, *scale))
12783 }
12784 _ => return None,
12785 }
12786 }
12787 Some(out.map(|o| o.unwrap()))
12788 }
12789
12790 #[allow(clippy::too_many_arguments)]
12794 fn e4m3_fused2_core(
12795 &self,
12796 b0: &CudaSlice<u8>,
12797 b1: &CudaSlice<u8>,
12798 aq: &CudaSlice<i8>,
12799 ad: &CudaSlice<f32>,
12800 in_f: usize,
12801 out0: usize,
12802 out1: usize,
12803 row_bytes: usize,
12804 ws0: f32,
12805 ws1: f32,
12806 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12807 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12809 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12810 let f = self.func("qmatvec_e4m3_mmvq_fused2");
12811 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12812 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12813 let cfg = LaunchConfig {
12814 grid_dim: (nb0 + nb1, 1, 1),
12815 block_dim: (32, ROWS_PER_BLOCK, 1),
12816 shared_mem_bytes: 0,
12817 };
12818 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
12819 let __s_b = self.gpu.stream();
12820 let mut b = __s_b.launch_builder(&f);
12821 b.arg(b0)
12822 .arg(b1)
12823 .arg(aq)
12824 .arg(ad)
12825 .arg(&mut y0)
12826 .arg(&mut y1)
12827 .arg(&inf)
12828 .arg(&o0)
12829 .arg(&o1)
12830 .arg(&rbl)
12831 .arg(&ws0)
12832 .arg(&ws1);
12833 unsafe {
12834 b.launch(cfg)?;
12835 }
12836 Ok((y0, y1))
12837 }
12838
12839 #[allow(clippy::too_many_arguments)]
12841 fn e4m3_fused3_core(
12842 &self,
12843 b0: &CudaSlice<u8>,
12844 b1: &CudaSlice<u8>,
12845 b2: &CudaSlice<u8>,
12846 aq: &CudaSlice<i8>,
12847 ad: &CudaSlice<f32>,
12848 in_f: usize,
12849 out0: usize,
12850 out1: usize,
12851 out2: usize,
12852 row_bytes: usize,
12853 ws0: f32,
12854 ws1: f32,
12855 ws2: f32,
12856 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12857 const ROWS_PER_BLOCK: u32 = 4;
12858 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12859 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12860 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12861 let f = self.func("qmatvec_e4m3_mmvq_fused3");
12862 let mut y0 = self.alloc_uninit::<f32>(out0)?;
12863 let mut y1 = self.alloc_uninit::<f32>(out1)?;
12864 let mut y2 = self.alloc_uninit::<f32>(out2)?;
12865 let cfg = LaunchConfig {
12866 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12867 block_dim: (32, ROWS_PER_BLOCK, 1),
12868 shared_mem_bytes: 0,
12869 };
12870 let (inf, o0, o1, o2, rbl) = (
12871 in_f as i32,
12872 out0 as i32,
12873 out1 as i32,
12874 out2 as i32,
12875 row_bytes as i64,
12876 );
12877 let __s_b = self.gpu.stream();
12878 let mut b = __s_b.launch_builder(&f);
12879 b.arg(b0)
12880 .arg(b1)
12881 .arg(b2)
12882 .arg(aq)
12883 .arg(ad)
12884 .arg(&mut y0)
12885 .arg(&mut y1)
12886 .arg(&mut y2)
12887 .arg(&inf)
12888 .arg(&o0)
12889 .arg(&o1)
12890 .arg(&o2)
12891 .arg(&rbl)
12892 .arg(&ws0)
12893 .arg(&ws1)
12894 .arg(&ws2);
12895 unsafe {
12896 b.launch(cfg)?;
12897 }
12898 Ok((y0, y1, y2))
12899 }
12900
12901 #[allow(clippy::too_many_arguments)]
12905 fn e4m3_fused2_t_core(
12906 &self,
12907 b0: &CudaSlice<u8>,
12908 b1: &CudaSlice<u8>,
12909 aq: &CudaSlice<i8>,
12910 ad: &CudaSlice<f32>,
12911 m: usize,
12912 in_f: usize,
12913 out0: usize,
12914 out1: usize,
12915 row_bytes: usize,
12916 ws0: f32,
12917 ws1: f32,
12918 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12919 const ROWS_PER_BLOCK: u32 = 4;
12920 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12921 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12922 let f = self.func(match Self::batched_mcols(m) {
12923 2 => "qmatvec_e4m3_mmvq_fused2_b2",
12924 4 => "qmatvec_e4m3_mmvq_fused2_b4",
12925 _ => "qmatvec_e4m3_mmvq_fused2_b8",
12926 });
12927 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12928 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12929 let cfg = LaunchConfig {
12930 grid_dim: (nb0 + nb1, 1, 1),
12931 block_dim: (32, ROWS_PER_BLOCK, 1),
12932 shared_mem_bytes: 0,
12933 };
12934 let (inf, o0, o1, mi, rbl) = (
12935 in_f as i32,
12936 out0 as i32,
12937 out1 as i32,
12938 m as i32,
12939 row_bytes as i64,
12940 );
12941 let __s_b = self.gpu.stream();
12942 let mut b = __s_b.launch_builder(&f);
12943 b.arg(b0)
12944 .arg(b1)
12945 .arg(aq)
12946 .arg(ad)
12947 .arg(&mut y0)
12948 .arg(&mut y1)
12949 .arg(&inf)
12950 .arg(&o0)
12951 .arg(&o1)
12952 .arg(&mi)
12953 .arg(&rbl);
12954 unsafe {
12955 b.launch(cfg)?;
12956 }
12957 if ws0 != 1.0 {
12958 self.scale_inplace(&mut y0, ws0, m * out0)?;
12959 }
12960 if ws1 != 1.0 {
12961 self.scale_inplace(&mut y1, ws1, m * out1)?;
12962 }
12963 Ok((y0, y1))
12964 }
12965
12966 #[allow(clippy::too_many_arguments)]
12968 fn e4m3_fused3_t_core(
12969 &self,
12970 b0: &CudaSlice<u8>,
12971 b1: &CudaSlice<u8>,
12972 b2: &CudaSlice<u8>,
12973 aq: &CudaSlice<i8>,
12974 ad: &CudaSlice<f32>,
12975 m: usize,
12976 in_f: usize,
12977 out0: usize,
12978 out1: usize,
12979 out2: usize,
12980 row_bytes: usize,
12981 ws0: f32,
12982 ws1: f32,
12983 ws2: f32,
12984 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12985 const ROWS_PER_BLOCK: u32 = 4;
12986 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
12987 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
12988 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
12989 let f = self.func(if Self::batched_mcols(m) == 2 {
12990 "qmatvec_e4m3_mmvq_fused3_b2"
12991 } else {
12992 "qmatvec_e4m3_mmvq_fused3_b4"
12993 });
12994 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
12995 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
12996 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
12997 let cfg = LaunchConfig {
12998 grid_dim: (nb0 + nb1 + nb2, 1, 1),
12999 block_dim: (32, ROWS_PER_BLOCK, 1),
13000 shared_mem_bytes: 0,
13001 };
13002 let (inf, o0, o1, o2, mi, rbl) = (
13003 in_f as i32,
13004 out0 as i32,
13005 out1 as i32,
13006 out2 as i32,
13007 m as i32,
13008 row_bytes as i64,
13009 );
13010 let __s_b = self.gpu.stream();
13011 let mut b = __s_b.launch_builder(&f);
13012 b.arg(b0)
13013 .arg(b1)
13014 .arg(b2)
13015 .arg(aq)
13016 .arg(ad)
13017 .arg(&mut y0)
13018 .arg(&mut y1)
13019 .arg(&mut y2)
13020 .arg(&inf)
13021 .arg(&o0)
13022 .arg(&o1)
13023 .arg(&o2)
13024 .arg(&mi)
13025 .arg(&rbl);
13026 unsafe {
13027 b.launch(cfg)?;
13028 }
13029 if ws0 != 1.0 {
13030 self.scale_inplace(&mut y0, ws0, m * out0)?;
13031 }
13032 if ws1 != 1.0 {
13033 self.scale_inplace(&mut y1, ws1, m * out1)?;
13034 }
13035 if ws2 != 1.0 {
13036 self.scale_inplace(&mut y2, ws2, m * out2)?;
13037 }
13038 Ok((y0, y1, y2))
13039 }
13040
13041 pub fn qmatvec_e4m3_blk_mmvq(
13051 &self,
13052 bytes: &CudaSlice<u8>,
13053 aq: &CudaSlice<i8>,
13054 ad: &CudaSlice<f32>,
13055 scales: &CudaSlice<f32>,
13056 m: usize,
13057 in_f: usize,
13058 out_f: usize,
13059 row_bytes: usize,
13060 scale_cols: usize,
13061 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13062 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_e4m3_blk_mmvq_into(
13064 bytes, aq, ad, scales, m, in_f, out_f, row_bytes, scale_cols, &mut y,
13065 )?;
13066 Ok(y)
13067 }
13068
13069 #[allow(clippy::too_many_arguments)]
13071 pub fn qmatvec_e4m3_blk_mmvq_into(
13072 &self,
13073 bytes: &CudaSlice<u8>,
13074 aq: &CudaSlice<i8>,
13075 ad: &CudaSlice<f32>,
13076 scales: &CudaSlice<f32>,
13077 m: usize,
13078 in_f: usize,
13079 out_f: usize,
13080 row_bytes: usize,
13081 scale_cols: usize,
13082 y: &mut CudaSlice<f32>,
13083 ) -> Result<(), Box<dyn std::error::Error>> {
13084 const ROWS_PER_BLOCK: u32 = 4; let f = self.func("qmatvec_e4m3_blk_mmvq");
13086 let cfg = LaunchConfig {
13087 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), m as u32, 1),
13088 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
13091 let (inf, outf, mi, rb, sc) = (
13092 in_f as i32,
13093 out_f as i32,
13094 m as i32,
13095 row_bytes as i64,
13096 scale_cols as i32,
13097 );
13098 let __s_b = self.gpu.stream();
13099 let mut b = __s_b.launch_builder(&f);
13100 b.arg(bytes)
13101 .arg(aq)
13102 .arg(ad)
13103 .arg(scales)
13104 .arg(&mut *y)
13105 .arg(&inf)
13106 .arg(&outf)
13107 .arg(&mi)
13108 .arg(&rb)
13109 .arg(&sc);
13110 unsafe {
13111 b.launch(cfg)?;
13112 }
13113 Ok(())
13114 }
13115
13116 #[allow(clippy::too_many_arguments)]
13122 pub fn qmatvec_e4m3_blk_mmvq_batched(
13123 &self,
13124 bytes: &CudaSlice<u8>,
13125 aq: &CudaSlice<i8>,
13126 ad: &CudaSlice<f32>,
13127 scales: &CudaSlice<f32>,
13128 m: usize,
13129 in_f: usize,
13130 out_f: usize,
13131 row_bytes: usize,
13132 scale_cols: usize,
13133 mcols: usize,
13134 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13135 const ROWS_PER_BLOCK: u32 = 4; debug_assert!(mcols >= m, "blk batched: mcols {mcols} < m {m}");
13137 let name = match mcols {
13138 2 => "qmatvec_e4m3_blk_mmvq_b2",
13139 4 => "qmatvec_e4m3_blk_mmvq_b4",
13140 8 => "qmatvec_e4m3_blk_mmvq_b8",
13141 16 => "qmatvec_e4m3_blk_mmvq_b16",
13142 _ => {
13143 return Err(
13144 format!("qmatvec_e4m3_blk_mmvq_batched: no kernel for mcols {mcols}").into(),
13145 );
13146 }
13147 };
13148 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13149 let f = self.func(name);
13150 let cfg = LaunchConfig {
13151 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), 1, 1),
13152 block_dim: (32, ROWS_PER_BLOCK, 1),
13153 shared_mem_bytes: 0,
13154 };
13155 let (inf, outf, mi, rb, sc) = (
13156 in_f as i32,
13157 out_f as i32,
13158 m as i32,
13159 row_bytes as i64,
13160 scale_cols as i32,
13161 );
13162 let __s_b = self.gpu.stream();
13163 let mut b = __s_b.launch_builder(&f);
13164 b.arg(bytes)
13165 .arg(aq)
13166 .arg(ad)
13167 .arg(scales)
13168 .arg(&mut y)
13169 .arg(&inf)
13170 .arg(&outf)
13171 .arg(&mi)
13172 .arg(&rb)
13173 .arg(&sc);
13174 unsafe {
13175 b.launch(cfg)?;
13176 }
13177 Ok(y)
13178 }
13179
13180 #[allow(clippy::too_many_arguments)]
13183 pub fn qmatvec_e4m3_blk_batched_raw(
13184 &self,
13185 bytes: &CudaSlice<u8>,
13186 x: &CudaSlice<f32>,
13187 scales: &CudaSlice<f32>,
13188 m: usize,
13189 in_f: usize,
13190 out_f: usize,
13191 row_bytes: usize,
13192 scale_cols: usize,
13193 mcols: usize,
13194 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13195 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13196 self.qmatvec_e4m3_blk_mmvq_batched(
13197 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols, mcols,
13198 )
13199 }
13200
13201 #[allow(clippy::too_many_arguments)]
13204 pub fn qmatvec_e4m3_blk_mmvq_raw(
13205 &self,
13206 bytes: &CudaSlice<u8>,
13207 x: &CudaSlice<f32>,
13208 scales: &CudaSlice<f32>,
13209 m: usize,
13210 in_f: usize,
13211 out_f: usize,
13212 row_bytes: usize,
13213 scale_cols: usize,
13214 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13215 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13216 self.qmatvec_e4m3_blk_mmvq(
13217 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols,
13218 )
13219 }
13220
13221 #[allow(clippy::too_many_arguments)]
13224 pub fn qmatvec_e4m3_fused2_raw(
13225 &self,
13226 b0: &CudaSlice<u8>,
13227 b1: &CudaSlice<u8>,
13228 x: &CudaSlice<f32>,
13229 in_f: usize,
13230 out0: usize,
13231 out1: usize,
13232 row_bytes: usize,
13233 ws0: f32,
13234 ws1: f32,
13235 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13236 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
13237 self.e4m3_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes, ws0, ws1)
13238 }
13239
13240 #[allow(clippy::too_many_arguments)]
13241 pub fn qmatvec_e4m3_fused3_raw(
13242 &self,
13243 b0: &CudaSlice<u8>,
13244 b1: &CudaSlice<u8>,
13245 b2: &CudaSlice<u8>,
13246 x: &CudaSlice<f32>,
13247 in_f: usize,
13248 out0: usize,
13249 out1: usize,
13250 out2: usize,
13251 row_bytes: usize,
13252 ws0: f32,
13253 ws1: f32,
13254 ws2: f32,
13255 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13256 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
13257 self.e4m3_fused3_core(
13258 b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
13259 )
13260 }
13261
13262 #[allow(clippy::too_many_arguments)]
13263 pub fn qmatvec_e4m3_fused2_t_raw(
13264 &self,
13265 b0: &CudaSlice<u8>,
13266 b1: &CudaSlice<u8>,
13267 x: &CudaSlice<f32>,
13268 m: usize,
13269 in_f: usize,
13270 out0: usize,
13271 out1: usize,
13272 row_bytes: usize,
13273 ws0: f32,
13274 ws1: f32,
13275 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13276 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13277 self.e4m3_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes, ws0, ws1)
13278 }
13279
13280 #[allow(clippy::too_many_arguments)]
13281 pub fn qmatvec_e4m3_fused3_t_raw(
13282 &self,
13283 b0: &CudaSlice<u8>,
13284 b1: &CudaSlice<u8>,
13285 b2: &CudaSlice<u8>,
13286 x: &CudaSlice<f32>,
13287 m: usize,
13288 in_f: usize,
13289 out0: usize,
13290 out1: usize,
13291 out2: usize,
13292 row_bytes: usize,
13293 ws0: f32,
13294 ws1: f32,
13295 ws2: f32,
13296 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13297 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13298 self.e4m3_fused3_t_core(
13299 b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
13300 )
13301 }
13302
13303 fn try_e4m3_blk_pre(
13314 &self,
13315 w: &crate::model::GpuTensor,
13316 aq: &CudaSlice<i8>,
13317 ad: &CudaSlice<f32>,
13318 m: usize,
13319 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13320 use crate::model::GpuTensor;
13321 if let GpuTensor::Quant {
13322 bytes,
13323 qtype,
13324 row_bytes,
13325 blk: Some(g),
13326 ..
13327 } = w
13328 {
13329 if *qtype == QT_F8_E4M3_BLK {
13330 if (2..=16).contains(&m)
13336 && std::env::var("MEMRA_NO_BATCHED").is_err()
13337 && (m <= 4 || Self::b8_enabled())
13338 {
13339 let mcols = Self::batched_mcols(m);
13340 return Ok(Some(self.qmatvec_e4m3_blk_mmvq_batched(
13341 bytes,
13342 aq,
13343 ad,
13344 &g.scales,
13345 m,
13346 w.in_features(),
13347 w.out_features(),
13348 *row_bytes,
13349 g.cols,
13350 mcols,
13351 )?));
13352 }
13353 return Ok(Some(self.qmatvec_e4m3_blk_mmvq(
13354 bytes,
13355 aq,
13356 ad,
13357 &g.scales,
13358 m,
13359 w.in_features(),
13360 w.out_features(),
13361 *row_bytes,
13362 g.cols,
13363 )?));
13364 }
13365 }
13366 Ok(None)
13367 }
13368
13369 fn try_e4m3_blk_prefill(
13416 &self,
13417 w: &crate::model::GpuTensor,
13418 x: &CudaSlice<f32>,
13419 m: usize,
13420 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
13421 use crate::model::GpuTensor;
13422 let GpuTensor::Quant {
13423 bytes,
13424 qtype,
13425 blk: Some(g),
13426 ..
13427 } = w
13428 else {
13429 return Ok(None);
13430 };
13431 if *qtype != QT_F8_E4M3_BLK {
13432 return Ok(None);
13433 }
13434 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
13439 return Ok(Some(y));
13440 }
13441 let (in_f, out_f) = (w.in_features(), w.out_features());
13442 let slab = self.fp8_blk_dequant_q8_0_dev(bytes, &g.scales, out_f, in_f)?;
13443 let tmp = GpuTensor::Quant {
13444 bytes: slab,
13445 qtype: QT_Q8_0,
13446 row_bytes: in_f / 32 * 34,
13447 ne: vec![in_f as u64, out_f as u64],
13448 scale: 1.0,
13449 rp: false,
13450 #[cfg(memra_cutlass)]
13451 cutlass: None,
13452 fp8: None,
13453 blk: None,
13454 f16: None,
13455 rp4: None,
13456 };
13457 Ok(Some(self.matmul(&tmp, x, m)?))
13459 }
13460
13461 pub fn matmul_pre_noscale(
13462 &self,
13463 w: &crate::model::GpuTensor,
13464 aq: &CudaSlice<i8>,
13465 ad: &CudaSlice<f32>,
13466 m: usize,
13467 ) -> Result<Option<(CudaSlice<f32>, f32)>, Box<dyn std::error::Error>> {
13468 use crate::model::GpuTensor;
13469 if m == 1 {
13473 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
13474 return Ok(Some((y, 1.0)));
13475 }
13476 }
13477 if m != 1 || !self.uses_q8_1_fast(w) {
13479 return Ok(None);
13480 }
13481 let in_f = w.in_features();
13482 let out_f = w.out_features();
13483 let (bytes, qtype, row_bytes, scale, rp) = match w {
13484 GpuTensor::Quant {
13485 bytes,
13486 qtype,
13487 row_bytes,
13488 scale,
13489 rp,
13490 ..
13491 } => (bytes, *qtype, *row_bytes, *scale, *rp),
13492 _ => return Ok(None),
13493 };
13494 if self.mmvq_supports(qtype) {
13496 let (mbytes, mrp) = match w {
13498 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
13499 _ => (bytes, rp),
13500 };
13501 let y = self.qmatvec_mmvq(
13502 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, 1.0, mrp,
13503 )?;
13504 return Ok(Some((y, scale)));
13505 }
13506 let name = match qtype {
13508 QT_Q8_0 => "qmatvec_q8_0_dp4a",
13509 QT_Q4_K => "qmatvec_q4_K_dp4a",
13510 QT_Q6_K => "qmatvec_q6_K_dp4a",
13511 QT_Q5_K => "qmatvec_q5_K_dp4a",
13512 QT_Q3_K => "qmatvec_q3_K_dp4a",
13513 QT_NVFP4 => {
13514 if rp {
13515 "qmatvec_nvfp4_dp4a_rp"
13516 } else {
13517 "qmatvec_nvfp4_dp4a"
13518 }
13519 }
13520 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
13521 _ => return Ok(None),
13522 };
13523 let f = self.func(name);
13524 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
13525 let cfg = LaunchConfig {
13526 grid_dim: (out_f as u32, m as u32, 1),
13527 block_dim: (128, 1, 1),
13528 shared_mem_bytes: 0,
13529 };
13530 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13531 let __s_b = self.gpu.stream();
13532 let mut b = __s_b.launch_builder(&f);
13533 b.arg(bytes)
13534 .arg(aq)
13535 .arg(ad)
13536 .arg(&mut y)
13537 .arg(&inf)
13538 .arg(&outf)
13539 .arg(&mi)
13540 .arg(&rb);
13541 unsafe {
13542 b.launch(cfg)?;
13543 }
13544 Ok(Some((y, scale)))
13545 }
13546
13547 pub fn mmvq_supports(&self, qtype: i32) -> bool {
13550 if qtype == QT_F8_E4M3 {
13555 return true;
13556 }
13557 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
13558 return false;
13559 }
13560 matches!(
13561 qtype,
13562 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_Q4_0
13563 )
13564 }
13565
13566 pub fn qmatvec_mmvq(
13571 &self,
13572 bytes: &CudaSlice<u8>,
13573 aq: &CudaSlice<i8>,
13574 ad: &CudaSlice<f32>,
13575 m: usize,
13576 in_f: usize,
13577 out_f: usize,
13578 qtype: i32,
13579 row_bytes: usize,
13580 scale: f32,
13581 rp: bool,
13582 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13583 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_mmvq_into(
13585 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp, &mut y,
13586 )?;
13587 Ok(y)
13588 }
13589
13590 #[allow(clippy::too_many_arguments)]
13592 pub fn qmatvec_mmvq_into(
13593 &self,
13594 bytes: &CudaSlice<u8>,
13595 aq: &CudaSlice<i8>,
13596 ad: &CudaSlice<f32>,
13597 m: usize,
13598 in_f: usize,
13599 out_f: usize,
13600 qtype: i32,
13601 row_bytes: usize,
13602 scale: f32,
13603 rp: bool,
13604 y: &mut CudaSlice<f32>,
13605 ) -> Result<(), Box<dyn std::error::Error>> {
13606 debug_assert!(y.len() >= m * out_f);
13607 const ROWS_PER_BLOCK: u32 = 4; if qtype == QT_Q8_0
13613 && rp
13614 && m == 1
13615 && out_f >= 64
13616 && (out_f as u32).div_ceil(ROWS_PER_BLOCK) < 4 * self.sm_count() as u32
13617 && {
13618 static G2: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13619 *G2.get_or_init(|| std::env::var("MEMRA_Q80_G2").as_deref() != Ok("0"))
13620 }
13621 {
13622 let f = self.func("qmatvec_q8_0_mmvq_rp_g2");
13623 let cfg = LaunchConfig {
13624 grid_dim: ((out_f as u32).div_ceil(2), 1, 1),
13625 block_dim: (32, 2, 1),
13626 shared_mem_bytes: 0,
13627 };
13628 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, row_bytes as i64);
13629 let __s_b = self.gpu.stream();
13630 let mut b = __s_b.launch_builder(&f);
13631 b.arg(bytes)
13632 .arg(aq)
13633 .arg(ad)
13634 .arg(&mut *y)
13635 .arg(&inf)
13636 .arg(&outf)
13637 .arg(&mi)
13638 .arg(&rb);
13639 unsafe {
13640 b.launch(cfg)?;
13641 }
13642 if scale != 1.0 {
13643 self.scale_inplace(y, scale, out_f)?;
13644 }
13645 return Ok(());
13646 }
13647 let mut mr: u32 = if m == 1 && (qtype == QT_NVFP4 || qtype == QT_Q5_K) {
13656 2
13657 } else {
13658 1
13659 };
13660 if m == 1 && qtype == QT_Q4_0 {
13665 static Q40MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
13666 mr = *Q40MR.get_or_init(|| {
13669 std::env::var("MEMRA_Q40_MR")
13670 .ok()
13671 .and_then(|v| v.parse().ok())
13672 .unwrap_or(1)
13673 });
13674 }
13675 let q5_mode = std::env::var("MEMRA_Q5K_ISSUE").ok();
13686 let q5_force = q5_mode.as_deref() == Some("2");
13687 let q5_il = qtype == QT_Q5_K
13690 && m == 1
13691 && (q5_force || q5_mode.as_deref().map(|v| v != "0").unwrap_or(true));
13692 if q5_il && !q5_force && out_f > 65536 {
13693 mr = 1;
13694 }
13695 if qtype == QT_Q4_0 && rp && mr != 1 {
13698 mr = 2;
13699 }
13700 if qtype == QT_Q8_0 && rp {
13704 static Q80MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
13705 mr = *Q80MR.get_or_init(|| {
13706 std::env::var("MEMRA_Q80_MR")
13707 .ok()
13708 .and_then(|v| v.parse().ok())
13709 .unwrap_or(1)
13710 });
13711 }
13712 let name = match (qtype, mr, rp) {
13713 (QT_NVFP4, 2, false) => "qmatvec_nvfp4_mmvq_mr2",
13714 (QT_NVFP4, 2, true) => "qmatvec_nvfp4_mmvq_mr2_rp",
13715 (QT_NVFP4, _, true) => "qmatvec_nvfp4_mmvq_rp",
13716 (QT_Q4_0, 1, true) => "qmatvec_q4_0_mmvq_rp",
13717 (QT_Q4_0, _, true) => "qmatvec_q4_0_mmvq_mr2_rp",
13718 (QT_Q5_K, 2, _) => {
13719 if q5_il {
13720 "qmatvec_q5_K_mmvq_mr2_il"
13721 } else {
13722 "qmatvec_q5_K_mmvq_mr2"
13723 }
13724 }
13725 (QT_Q8_0, 2, true) => "qmatvec_q8_0_mmvq_mr2_rp",
13726 (QT_Q8_0, _, true)
13731 if in_f % 1024 == 0 && {
13732 static CA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13733 *CA.get_or_init(|| std::env::var("MEMRA_Q80_CA").as_deref() == Ok("1"))
13734 } =>
13735 {
13736 "qmatvec_q8_0_mmvq_rpca"
13737 }
13738 (QT_Q8_0, _, true) => "qmatvec_q8_0_mmvq_rp",
13739 (QT_Q8_0, _, _) => "qmatvec_q8_0_mmvq",
13740 (QT_Q4_K, _, true) => "qmatvec_q4_K_mmvq_rp",
13744 (QT_Q6_K, _, true) => "qmatvec_q6_K_mmvq_rp",
13745 (QT_Q4_K, _, _) => "qmatvec_q4_K_mmvq",
13746 (QT_Q4_0, 2, false) => "qmatvec_q4_0_mmvq_mr2",
13747 (QT_Q4_0, _, false) => "qmatvec_q4_0_mmvq",
13748 (QT_Q5_K, _, _) => {
13749 if q5_il {
13750 "qmatvec_q5_K_mmvq_il"
13751 } else {
13752 "qmatvec_q5_K_mmvq"
13753 }
13754 }
13755 (QT_Q6_K, _, _) => "qmatvec_q6_K_mmvq",
13756 (QT_NVFP4, _, false) => "qmatvec_nvfp4_mmvq",
13757 (QT_F8_E4M3, _, _) => "qmatvec_e4m3_mmvq",
13758 _ => panic!("qmatvec_mmvq: qtype {qtype} has no MMVQ kernel"),
13759 };
13760 let f = self.func(name);
13761 let rows_per_block = ROWS_PER_BLOCK * mr;
13763 let cfg = LaunchConfig {
13764 grid_dim: (
13765 (out_f as u32 + rows_per_block - 1) / rows_per_block,
13766 m as u32,
13767 1,
13768 ),
13769 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
13772 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
13773 let __s_b = self.gpu.stream();
13774 let mut b = __s_b.launch_builder(&f);
13775 if qtype == QT_NVFP4 || qtype == QT_F8_E4M3 {
13780 if Self::pdl_on()
13783 && Self::pdl_mmvq_on()
13784 && Self::pdl_nvfp4q8_on()
13785 && name == "qmatvec_nvfp4_mmvq_mr2_rp"
13786 {
13787 use cudarc::driver::{DevicePtr, DevicePtrMut};
13788 let s = &self.gpu.stream();
13789 let (pw, _g0) = bytes.device_ptr(s);
13790 let (paq, _g1) = aq.device_ptr(s);
13791 let (pad, _g2) = ad.device_ptr(s);
13792 let (py, _g3) = y.device_ptr_mut(s);
13793 let mut ps = [
13794 &pw as *const _ as *mut std::ffi::c_void,
13795 &paq as *const _ as *mut _,
13796 &pad as *const _ as *mut _,
13797 &py as *const _ as *mut _,
13798 &inf as *const _ as *mut _,
13799 &outf as *const _ as *mut _,
13800 &mi as *const _ as *mut _,
13801 &rb as *const _ as *mut _,
13802 &scale as *const _ as *mut _,
13803 ];
13804 unsafe {
13805 self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
13806 }
13807 return Ok(());
13808 }
13809 b.arg(bytes)
13810 .arg(aq)
13811 .arg(ad)
13812 .arg(&mut *y)
13813 .arg(&inf)
13814 .arg(&outf)
13815 .arg(&mi)
13816 .arg(&rb)
13817 .arg(&scale);
13818 unsafe {
13819 b.launch(cfg)?;
13820 }
13821 } else if Self::pdl_on()
13822 && Self::pdl_mmvq_on()
13823 && (matches!(
13824 name,
13825 "qmatvec_q4_0_mmvq_rp" | "qmatvec_q6_K_mmvq" | "qmatvec_q6_K_mmvq_rp"
13826 ) || (Self::pdl_nvfp4q8_on()
13827 && matches!(name, "qmatvec_q8_0_mmvq_rp" | "qmatvec_q8_0_mmvq_mr2_rp")))
13828 {
13829 {
13833 use cudarc::driver::{DevicePtr, DevicePtrMut};
13834 let s = &self.gpu.stream();
13835 let (pw, _g0) = bytes.device_ptr(s);
13836 let (paq, _g1) = aq.device_ptr(s);
13837 let (pad, _g2) = ad.device_ptr(s);
13838 let (py, _g3) = y.device_ptr_mut(s);
13839 let mut ps = [
13840 &pw as *const _ as *mut std::ffi::c_void,
13841 &paq as *const _ as *mut _,
13842 &pad as *const _ as *mut _,
13843 &py as *const _ as *mut _,
13844 &inf as *const _ as *mut _,
13845 &outf as *const _ as *mut _,
13846 &mi as *const _ as *mut _,
13847 &rb as *const _ as *mut _,
13848 ];
13849 unsafe {
13850 self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
13851 }
13852 }
13853 if scale != 1.0 {
13854 self.scale_inplace(y, scale, m * out_f)?;
13855 }
13856 } else {
13857 b.arg(bytes)
13858 .arg(aq)
13859 .arg(ad)
13860 .arg(&mut *y)
13861 .arg(&inf)
13862 .arg(&outf)
13863 .arg(&mi)
13864 .arg(&rb);
13865 unsafe {
13866 b.launch(cfg)?;
13867 }
13868 if scale != 1.0 {
13869 self.scale_inplace(y, scale, m * out_f)?;
13870 }
13871 }
13872 Ok(())
13873 }
13874
13875 pub fn qmatvec_mmvq_raw(
13879 &self,
13880 bytes: &CudaSlice<u8>,
13881 x: &CudaSlice<f32>,
13882 m: usize,
13883 in_f: usize,
13884 out_f: usize,
13885 qtype: i32,
13886 row_bytes: usize,
13887 rp: bool,
13888 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
13889 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
13890 self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, 1.0, rp)
13891 }
13892
13893 pub fn batched_supports(&self, qtype: i32) -> bool {
13897 matches!(
13898 qtype,
13899 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_F8_E4M3 | QT_Q4_0
13900 )
13901 }
13902
13903 pub fn iq_fast_enabled() -> bool {
13911 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13912 *ON.get_or_init(|| {
13913 std::env::var("MEMRA_IQ_FAST")
13914 .map(|v| v != "0")
13915 .unwrap_or(true)
13916 })
13917 }
13918
13919 pub fn b8_enabled() -> bool {
13922 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13923 *ON.get_or_init(|| std::env::var("MEMRA_B8").map(|v| v != "0").unwrap_or(true))
13924 }
13925
13926 pub fn batched_mcols(m: usize) -> usize {
13928 if m == 2 {
13929 2
13930 } else if m <= 4 {
13931 4
13932 } else if m <= 8 {
13933 8
13934 } else {
13935 16
13936 }
13937 }
13938
13939 fn batched_kernel_name(qtype: i32, mcols: usize) -> Option<&'static str> {
13944 Some(match (qtype, mcols) {
13945 (QT_Q8_0, 2) => "qmatvec_q8_0_mmvq_b2",
13946 (QT_Q8_0, 4) => "qmatvec_q8_0_mmvq_b4",
13947 (QT_Q8_0, 8) => "qmatvec_q8_0_mmvq_b8",
13948 (QT_Q8_0, 16) => "qmatvec_q8_0_mmvq_b16",
13954 (QT_Q4_K, 2) => "qmatvec_q4_K_mmvq_b2",
13955 (QT_Q4_K, 4) => "qmatvec_q4_K_mmvq_b4",
13956 (QT_Q4_K, 8) => "qmatvec_q4_K_mmvq_b8",
13957 (QT_Q4_K, 16) => "qmatvec_q4_K_mmvq_b16",
13960 (QT_Q5_K, 2) => "qmatvec_q5_K_mmvq_b2",
13961 (QT_Q5_K, 4) => "qmatvec_q5_K_mmvq_b4",
13962 (QT_Q5_K, 8) => "qmatvec_q5_K_mmvq_b8",
13963 (QT_Q5_K, 16) => "qmatvec_q5_K_mmvq_b16",
13966 (QT_Q6_K, 2) => "qmatvec_q6_K_mmvq_b2",
13967 (QT_Q6_K, 4) => "qmatvec_q6_K_mmvq_b4",
13968 (QT_Q6_K, 8) => "qmatvec_q6_K_mmvq_b8",
13969 (QT_Q6_K, 16) => "qmatvec_q6_K_mmvq_b16",
13970 (QT_NVFP4, 2) => "qmatvec_nvfp4_mmvq_b2",
13971 (QT_NVFP4, 4) => "qmatvec_nvfp4_mmvq_b4",
13972 (QT_NVFP4, 8) => "qmatvec_nvfp4_mmvq_b8",
13973 (QT_NVFP4, 16) => "qmatvec_nvfp4_mmvq_b16",
13977 (QT_F8_E4M3, 2) => "qmatvec_e4m3_mmvq_b2",
13978 (QT_F8_E4M3, 4) => "qmatvec_e4m3_mmvq_b4",
13979 (QT_F8_E4M3, 8) => "qmatvec_e4m3_mmvq_b8",
13980 (QT_F8_E4M3, 16) => "qmatvec_e4m3_mmvq_b16",
13984 (QT_Q4_0, 2) => "qmatvec_q4_0_mmvq_b2",
13985 (QT_Q4_0, 4) => "qmatvec_q4_0_mmvq_b4",
13986 (QT_Q4_0, 8) => "qmatvec_q4_0_mmvq_b8",
13987 (QT_Q4_0, 16) => "qmatvec_q4_0_mmvq_b16",
13988 _ => return None,
13989 })
13990 }
13991
13992 pub fn sm_count(&self) -> i32 {
14027 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
14028 *SMS.get_or_init(|| {
14029 use cudarc::driver::sys::CUdevice_attribute_enum as A;
14030 self.gpu
14031 .ctx
14032 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
14033 .unwrap_or(82)
14034 })
14035 }
14036
14037 pub fn batched_variant(
14038 &self,
14039 _m: usize,
14040 in_f: usize,
14041 out_f: usize,
14042 qtype: i32,
14043 row_bytes: usize,
14044 mcols: usize,
14045 rp: bool,
14046 ) -> &'static str {
14047 if qtype == QT_Q8_0 {
14052 return if rp { "rp" } else { "base" };
14053 }
14054 static BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
14055 let bv = *BV.get_or_init(|| match std::env::var("MEMRA_MMVQ_BV").as_deref() {
14056 Ok("base") => "base",
14057 Ok("pf") => "pf",
14058 Ok("r2") => "r2",
14059 Ok("r2w8") => "r2w8",
14060 Ok("pfr2") => "pfr2",
14061 Ok("ca") => "ca",
14062 Ok("car2") => "car2",
14063 Ok("rp") => "rp",
14066 Ok("rpr2") => "rpr2",
14067 Ok("rpr2w8") => "rpr2w8",
14068 Ok("rpca") => "rpca",
14071 Ok("rpcar2") => "rpcar2",
14072 Ok("rpsc") => "rpsc",
14079 Ok("rpms") => "rpms",
14080 Ok("rpmsc") => "rpmsc",
14081 Ok("rpks") => "rpks",
14082 Ok("rpksc") => "rpksc",
14083 _ => "auto",
14084 });
14085 let ca_ok = qtype == QT_NVFP4 && (row_bytes % 16 == 0) && (in_f % 1024 == 0);
14089 static KS_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14094 let ks_on = *KS_ON.get_or_init(|| std::env::var("MEMRA_KS").as_deref() != Ok("0"));
14095 let sc_ok = ks_on && qtype == QT_NVFP4 && (in_f % 256 == 0) && (in_f / 64 <= 272);
14096 let ks_ok = ks_on && qtype == QT_NVFP4 && (in_f % 512 == 0) && (in_f / 64 <= 272);
14097 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
14098 let sms = *SMS.get_or_init(|| {
14099 use cudarc::driver::sys::CUdevice_attribute_enum as A;
14100 self.gpu
14101 .ctx
14102 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
14103 .unwrap_or(82)
14104 });
14105 let kq_r2 = matches!(qtype, QT_Q4_K | QT_Q5_K | QT_Q6_K);
14125 static KQBV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
14128 let kq_bv = *KQBV.get_or_init(|| match std::env::var("MEMRA_KQ_BV").as_deref() {
14129 Ok("base") => "base",
14130 Ok("r2") => "r2",
14131 Ok("r2w8") => "r2w8",
14132 _ => "auto",
14133 });
14134 let variant: &'static str = if qtype == QT_Q4_0 {
14135 static Q40BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
14139 let q40 = *Q40BV.get_or_init(|| match std::env::var("MEMRA_Q40_BV").as_deref() {
14140 Ok("base") => "base",
14146 Ok("r2") => "r2",
14147 Ok("ms") => "ms",
14148 Ok("sm") => "sm",
14149 Ok("la") => "la",
14150 _ => "auto",
14151 });
14152 let v = if q40 != "auto" {
14153 q40
14154 } else if (out_f as u32).div_ceil(8) >= 4 * sms as u32 {
14155 "r2"
14156 } else {
14157 "base"
14158 };
14159 if rp {
14164 match v {
14165 "ms" => "r2ms_rp",
14166 "sm" => "r2sm_rp",
14167 "la" => "r2la_rp",
14168 "r2" => "r2_rp",
14169 _ => "rp",
14170 }
14171 } else if matches!(v, "ms" | "sm" | "la") {
14172 "r2"
14173 } else {
14174 v
14175 }
14176 } else if qtype != QT_NVFP4 && !kq_r2 {
14177 "base"
14178 } else if kq_r2 && rp {
14179 "rp"
14183 } else if kq_r2 {
14184 if kq_bv != "auto" {
14187 if kq_bv == "r2w8" && mcols != 4 {
14188 "r2"
14189 } else {
14190 kq_bv
14191 }
14192 } else if bv != "auto" {
14193 match bv {
14194 "r2" | "pfr2" | "rpr2" | "car2" => "r2",
14195 "r2w8" | "rpr2w8" => {
14196 if mcols != 4 {
14197 "r2"
14198 } else {
14199 "r2w8"
14200 }
14201 }
14202 _ => "base", }
14204 } else {
14205 let blocks = (out_f + 7) / 8;
14206 let waves = blocks as f64 / (7 * sms as usize) as f64;
14207 let filled = blocks >= 4 * sms as usize;
14208 let use_r2 = if qtype == QT_Q4_K {
14209 filled
14210 } else {
14211 waves >= 2.0
14212 };
14213 if use_r2 { "r2" } else { "base" }
14214 }
14215 } else if bv != "auto" {
14216 let v = if bv == "r2w8" && mcols == 2 {
14221 "r2"
14222 } else if bv == "ca" && (!ca_ok || mcols == 8) {
14223 "pf"
14224 } else if bv == "car2" && (!ca_ok || mcols == 8) {
14225 "r2"
14226 } else if bv == "pfr2" && mcols == 8 {
14227 "r2"
14228 } else if (bv == "rpr2w8" || bv == "rpr2") && mcols == 2 {
14229 "rpr2"
14230 }
14231 else if (bv == "rpca" || bv == "rpcar2") && (!ca_ok || mcols == 8) {
14233 if mcols == 8 { "rpr2w8" } else { "rpr2" }
14234 } else if bv == "rpcar2" && mcols == 2 {
14235 "rpca"
14236 }
14237 else if (bv == "rpsc" || bv == "rpmsc") && !sc_ok {
14240 "rpr2"
14241 } else if (bv == "rpks" || bv == "rpksc") && !ks_ok {
14242 "rpr2"
14243 } else {
14244 bv
14245 };
14246 if rp {
14247 match v {
14248 "base" | "pf" | "ca" | "rp" => "rp",
14249 "r2" | "pfr2" | "car2" | "rpr2" => "rpr2",
14250 "r2w8" | "rpr2w8" => {
14251 if mcols == 2 {
14252 "rpr2"
14253 } else {
14254 "rpr2w8"
14255 }
14256 }
14257 other => other, }
14259 } else {
14260 v
14261 }
14262 } else if mcols == 8 {
14263 if rp {
14274 if sc_ok { "rpsc" } else { "rpr2w8" }
14275 } else {
14276 "r2w8"
14277 }
14278 } else if mcols >= 4 {
14279 let blocks = (out_f + 7) / 8;
14283 let r7 = 7 * sms as usize;
14284 let r8 = 8 * sms as usize;
14285 let waves = blocks as f64 / r7 as f64;
14286 let filled = blocks >= 4 * sms as usize;
14287 if filled && blocks.div_ceil(r8) < blocks.div_ceil(r7) {
14291 if rp { "rpr2w8" } else { "r2w8" }
14295 } else if waves >= 2.0 || (waves <= 1.0 && filled) {
14296 if rp { "rpr2" } else { "r2" }
14299 } else {
14300 if rp { "rp" } else { "pf" }
14304 }
14305 } else if in_f >= 6144 {
14306 if rp { "rpr2" } else { "r2" }
14310 } else if rp {
14311 let waves = ((out_f + 7) / 8) as f64 / (7 * sms as usize) as f64;
14316 if sc_ok && waves >= 0.9 && waves <= 1.1 {
14317 "rpsc"
14318 } else {
14319 "rp"
14320 }
14321 } else {
14322 "base"
14323 };
14324 variant
14325 }
14326
14327 pub fn qmatvec_mmvq_batched(
14328 &self,
14329 bytes: &CudaSlice<u8>,
14330 aq: &CudaSlice<i8>,
14331 ad: &CudaSlice<f32>,
14332 m: usize,
14333 in_f: usize,
14334 out_f: usize,
14335 qtype: i32,
14336 row_bytes: usize,
14337 mcols: usize,
14338 scale: f32,
14339 rp: bool,
14340 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14341 const ROWS_PER_BLOCK: u32 = 4;
14342 let forced: Option<&'static str> = {
14347 static V: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
14348 V.get_or_init(|| std::env::var("MEMRA_BVAR").ok())
14349 .as_deref()
14350 .map(|s| Box::leak(s.to_string().into_boxed_str()) as &'static str)
14351 };
14352 let variant = match forced {
14353 Some(v) if !rp || v.contains("rp") => v,
14354 _ => self.batched_variant(m, in_f, out_f, qtype, row_bytes, mcols, rp),
14355 };
14356 let base_name = Self::batched_kernel_name(qtype, mcols).ok_or_else(|| {
14357 format!("qmatvec_mmvq_batched: no kernel for qtype {qtype} mcols {mcols}")
14358 })?;
14359 let variant = if mcols == 16 {
14363 if rp { "rp" } else { "base" }
14364 } else {
14365 variant
14366 };
14367 static B567: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14374 let b567 = *B567.get_or_init(|| std::env::var("MEMRA_B567").as_deref() != Ok("0"));
14375 if b567
14376 && qtype == QT_NVFP4
14377 && rp
14378 && mcols == 8
14379 && (5..=7).contains(&m)
14380 && matches!(variant, "rpsc" | "rpr2w8")
14381 {
14382 let f = self.func(&format!("qmatvec_nvfp4_mmvq_b{m}_{variant}"));
14383 let rows_per_block = ROWS_PER_BLOCK * 2; let mut y = self.alloc_uninit::<f32>(m * out_f)?;
14385 let cfg = LaunchConfig {
14386 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
14387 block_dim: (32, ROWS_PER_BLOCK, 1),
14388 shared_mem_bytes: 0,
14389 };
14390 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14391 let __s_b = self.gpu.stream();
14392 let mut b = __s_b.launch_builder(&f);
14393 b.arg(bytes)
14394 .arg(aq)
14395 .arg(ad)
14396 .arg(&mut y)
14397 .arg(&inf)
14398 .arg(&outf)
14399 .arg(&mi)
14400 .arg(&rb);
14401 unsafe {
14402 b.launch(cfg)?;
14403 }
14404 if scale != 1.0 {
14405 self.scale_inplace(&mut y, scale, m * out_f)?;
14406 }
14407 return Ok(y);
14408 }
14409 let (name, rows_per_block): (std::borrow::Cow<'static, str>, u32) = match variant {
14410 "base" => (base_name.into(), ROWS_PER_BLOCK),
14411 "pf" => (format!("{base_name}_pf").into(), ROWS_PER_BLOCK),
14412 "ca" => (format!("{base_name}_ca").into(), ROWS_PER_BLOCK),
14413 "rp" => (format!("{base_name}_rp").into(), ROWS_PER_BLOCK),
14414 "rpca" => (format!("{base_name}_rpca").into(), ROWS_PER_BLOCK), "rpks" => (format!("{base_name}_rpks").into(), ROWS_PER_BLOCK),
14418 "rpksc" => (format!("{base_name}_rpksc").into(), ROWS_PER_BLOCK),
14419 "rpms" => (format!("{base_name}_rpms").into(), ROWS_PER_BLOCK),
14420 "rpmsc" => (format!("{base_name}_rpmsc").into(), ROWS_PER_BLOCK),
14421 "r2ms_rp" => (format!("{base_name}_r2ms_rp").into(), ROWS_PER_BLOCK),
14422 "r2sm_rp" => (format!("{base_name}_r2sm_rp").into(), ROWS_PER_BLOCK * 2),
14423 "r2la_rp" => (format!("{base_name}_r2la_rp").into(), ROWS_PER_BLOCK * 2),
14424 v => (format!("{base_name}_{v}").into(), ROWS_PER_BLOCK * 2), };
14426 debug_assert!(
14427 !rp || name.contains("_rp"),
14428 "rp weight dispatched to a GGUF-layout kernel"
14429 );
14430 let f = self.func(&name);
14431 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
14432 let smem = if name.contains("_r2sm_rp") {
14434 (mcols * 32 * 9 * 4 + mcols * 32 * 4) as u32
14435 } else {
14436 0
14437 };
14438 let cfg = LaunchConfig {
14439 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
14440 block_dim: (32, ROWS_PER_BLOCK, 1),
14441 shared_mem_bytes: smem,
14442 };
14443 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14444 let __s_b = self.gpu.stream();
14445 let mut b = __s_b.launch_builder(&f);
14446 b.arg(bytes)
14447 .arg(aq)
14448 .arg(ad)
14449 .arg(&mut y)
14450 .arg(&inf)
14451 .arg(&outf)
14452 .arg(&mi)
14453 .arg(&rb);
14454 unsafe {
14455 b.launch(cfg)?;
14456 }
14457 if scale != 1.0 {
14458 self.scale_inplace(&mut y, scale, m * out_f)?;
14459 }
14460 Ok(y)
14461 }
14462
14463 pub fn qmatvec_batched_raw(
14467 &self,
14468 bytes: &CudaSlice<u8>,
14469 x: &CudaSlice<f32>,
14470 m: usize,
14471 in_f: usize,
14472 out_f: usize,
14473 qtype: i32,
14474 row_bytes: usize,
14475 mcols: usize,
14476 rp: bool,
14477 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14478 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
14479 self.qmatvec_mmvq_batched(
14480 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, 1.0, rp,
14481 )
14482 }
14483
14484 pub fn qmatvec_nvfp4_batched_raw(
14486 &self,
14487 bytes: &CudaSlice<u8>,
14488 x: &CudaSlice<f32>,
14489 m: usize,
14490 in_f: usize,
14491 out_f: usize,
14492 row_bytes: usize,
14493 mcols: usize,
14494 rp: bool,
14495 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14496 self.qmatvec_batched_raw(bytes, x, m, in_f, out_f, QT_NVFP4, row_bytes, mcols, rp)
14497 }
14498
14499 fn try_fp4_gemm(
14503 &self,
14504 w: &crate::model::GpuTensor,
14505 x: &CudaSlice<f32>,
14506 m: usize,
14507 in_f: usize,
14508 out_f: usize,
14509 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
14510 use crate::model::GpuTensor;
14511 if cfg!(memra_portable_cuda) {
14512 return Ok(None);
14513 }
14514 if std::env::var("MEMRA_FP4").is_err() {
14515 return Ok(None);
14516 }
14517 #[cfg(memra_cutlass)]
14526 if m >= 128 && std::env::var("MEMRA_FP4_CUTLASS").is_ok() {
14527 if let GpuTensor::Quant {
14528 bytes,
14529 qtype,
14530 scale,
14531 row_bytes,
14532 cutlass,
14533 ..
14534 } = w
14535 {
14536 if *qtype == QT_NVFP4 && in_f % 64 == 0 {
14537 if let Some(cw) = cutlass {
14538 let y = self.cutlass_fp4_gemm(
14540 &cw.b_packed,
14541 &cw.sfb_swizzled,
14542 x,
14543 *scale,
14544 m,
14545 out_f,
14546 in_f,
14547 )?;
14548 return Ok(Some(y));
14549 } else if std::env::var("MEMRA_FP4_CUTLASS_OTF").is_ok() {
14550 let (b_packed, sfb_sw) =
14555 self.build_cutlass_weight(bytes, out_f, in_f, *row_bytes)?;
14556 let y =
14557 self.cutlass_fp4_gemm(&b_packed, &sfb_sw, x, *scale, m, out_f, in_f)?;
14558 return Ok(Some(y));
14559 }
14560 }
14561 }
14562 }
14563 if let GpuTensor::Quant {
14564 bytes,
14565 qtype,
14566 row_bytes,
14567 scale,
14568 rp,
14569 ..
14570 } = w
14571 {
14572 if *qtype == QT_NVFP4 && in_f % 64 == 0 && !*rp {
14575 let y =
14576 self.qmatvec_gemm_nvfp4_fp4(bytes, x, m, in_f, out_f, *row_bytes, *scale)?;
14577 return Ok(Some(y));
14578 }
14579 }
14580 Ok(None)
14581 }
14582
14583 pub fn rms_norm_f16out(
14587 &self,
14588 x: &CudaSlice<f32>,
14589 w: &CudaSlice<f32>,
14590 dst: &mut CudaSlice<f32>,
14591 dst16: &mut CudaSlice<u8>,
14592 ncols: usize,
14593 nrows: usize,
14594 eps: f32,
14595 ) -> Result<(), Box<dyn std::error::Error>> {
14596 let f = self.func("rms_norm_f16out_f32");
14597 let cfg = LaunchConfig {
14598 grid_dim: (nrows as u32, 1, 1),
14599 block_dim: (rms_block(), 1, 1),
14600 shared_mem_bytes: 0,
14601 };
14602 let (nc, e) = (ncols as i32, eps);
14603 let __s_b = self.gpu.stream();
14604 let mut b = __s_b.launch_builder(&f);
14605 b.arg(x).arg(w).arg(dst).arg(dst16).arg(&nc).arg(&e);
14606 unsafe {
14607 b.launch(cfg)?;
14608 }
14609 Ok(())
14610 }
14611
14612 #[allow(clippy::too_many_arguments)]
14615 pub fn add_rms_norm_f16out(
14616 &self,
14617 a: &CudaSlice<f32>,
14618 b: &CudaSlice<f32>,
14619 w: &CudaSlice<f32>,
14620 res: &mut CudaSlice<f32>,
14621 dst: &mut CudaSlice<f32>,
14622 dst16: &mut CudaSlice<u8>,
14623 ncols: usize,
14624 nrows: usize,
14625 eps: f32,
14626 ) -> Result<(), Box<dyn std::error::Error>> {
14627 let f = self.func("add_rms_norm_f16out_f32");
14628 let cfg = LaunchConfig {
14629 grid_dim: (nrows as u32, 1, 1),
14630 block_dim: (rms_block(), 1, 1),
14631 shared_mem_bytes: 0,
14632 };
14633 let (nc, e) = (ncols as i32, eps);
14634 let __s_lb = self.gpu.stream();
14635 let mut lb = __s_lb.launch_builder(&f);
14636 lb.arg(a)
14637 .arg(b)
14638 .arg(w)
14639 .arg(res)
14640 .arg(dst)
14641 .arg(dst16)
14642 .arg(&nc)
14643 .arg(&e);
14644 unsafe {
14645 lb.launch(cfg)?;
14646 }
14647 Ok(())
14648 }
14649
14650 pub fn matmul_group_xh(
14653 &self,
14654 ws: &[&crate::model::GpuTensor],
14655 x: &CudaSlice<f32>,
14656 xh: &CudaSlice<u8>,
14657 m: usize,
14658 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
14659 let mut out = Vec::with_capacity(ws.len());
14660 let in_f = ws[0].in_features();
14661 for w in ws {
14662 if w.in_features() == in_f && m >= 16 && !self.verify_exact_on() {
14663 if let Some(y) = self.try_f16_gemm_pre(w, xh, m)? {
14664 out.push(y);
14665 continue;
14666 }
14667 }
14668 out.push(self.matmul(w, x, m)?);
14669 }
14670 Ok(out)
14671 }
14672
14673 pub fn gdn_pad_mask(
14676 &self,
14677 beta: &mut CudaSlice<f32>,
14678 g_log: &mut CudaSlice<f32>,
14679 len_d: &CudaSlice<i32>,
14680 h: usize,
14681 t: usize,
14682 ) -> Result<(), Box<dyn std::error::Error>> {
14683 let f = self.func("gdn_pad_mask_f32");
14684 let cfg = LaunchConfig::for_num_elems((t * h) as u32);
14685 let (hi, ti) = (h as i32, t as i32);
14686 let __s_b = self.gpu.stream();
14687 let mut b = __s_b.launch_builder(&f);
14688 b.arg(beta).arg(g_log).arg(len_d).arg(&hi).arg(&ti);
14689 unsafe {
14690 b.launch(cfg)?;
14691 }
14692 Ok(())
14693 }
14694
14695 pub fn row_gather_dev(
14698 &self,
14699 src: &CudaSlice<f32>,
14700 dst: &mut CudaSlice<f32>,
14701 len_d: &CudaSlice<i32>,
14702 ncols: usize,
14703 ) -> Result<(), Box<dyn std::error::Error>> {
14704 let f = self.func("row_gather_dev_f32");
14705 let cfg = LaunchConfig::for_num_elems(ncols as u32);
14706 let nc = ncols as i32;
14707 let __s_b = self.gpu.stream();
14708 let mut b = __s_b.launch_builder(&f);
14709 b.arg(src).arg(dst).arg(len_d).arg(&nc);
14710 unsafe {
14711 b.launch(cfg)?;
14712 }
14713 Ok(())
14714 }
14715
14716 pub fn matmul_group(
14723 &self,
14724 ws: &[&crate::model::GpuTensor],
14725 x: &CudaSlice<f32>,
14726 m: usize,
14727 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
14728 use crate::model::GpuTensor;
14729 let mut out = Vec::with_capacity(ws.len());
14730 let any_mirror = ws
14731 .iter()
14732 .any(|w| matches!(w, GpuTensor::Quant { f16: Some(_), .. }));
14733 if m >= 16 && any_mirror && !self.verify_exact_on() {
14734 let in_f = ws[0].in_features();
14735 let xh = self.f16_act(x, m * in_f, in_f)?;
14736 for w in ws {
14737 if w.in_features() == in_f {
14738 if let Some(y) = self.try_f16_gemm_pre(w, &xh, m)? {
14739 out.push(y);
14740 continue;
14741 }
14742 }
14743 out.push(self.matmul(w, x, m)?);
14744 }
14745 return Ok(out);
14746 }
14747 for w in ws {
14748 out.push(self.matmul(w, x, m)?);
14749 }
14750 Ok(out)
14751 }
14752
14753 pub fn matmul_group_multi(
14760 &self,
14761 ws: &[&crate::model::GpuTensor],
14762 xs: &[&CudaSlice<f32>],
14763 ms: &[usize],
14764 ) -> Result<Vec<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
14765 assert_eq!(xs.len(), ms.len());
14766 let in_f = ws[0].in_features();
14767 let total: usize = ms.iter().sum();
14768 let mut xcat = self.uninit(total * in_f)?;
14769 let mut off = 0usize;
14770 for (x, &m) in xs.iter().zip(ms) {
14771 self.copy_into(&mut xcat, off * in_f, x, m * in_f)?;
14772 off += m;
14773 }
14774 let ys = self.matmul_group(ws, &xcat, total)?;
14775 let mut out: Vec<Vec<CudaSlice<f32>>> = (0..xs.len()).map(|_| Vec::new()).collect();
14776 for (w, y) in ws.iter().zip(ys) {
14777 let out_f = w.out_features();
14778 let mut off = 0usize;
14779 for (s, &m) in ms.iter().enumerate() {
14780 let mut ys_s = self.uninit(m * out_f)?;
14781 let src = y.slice(off * out_f..(off + m) * out_f);
14782 self.gpu.stream().memcpy_dtod(&src, &mut ys_s)?;
14783 out[s].push(ys_s);
14784 off += m;
14785 }
14786 }
14787 Ok(out)
14788 }
14789
14790 pub fn gemm_supports(&self, w: &crate::model::GpuTensor) -> bool {
14800 use crate::model::GpuTensor;
14801 if !legacy_quant_gemm_allowed(
14802 cfg!(memra_portable_cuda),
14803 cfg!(memra_hopper_mma),
14804 std::env::var_os("MEMRA_NO_GEMM").is_some(),
14805 ) {
14806 return false;
14807 }
14808 match w {
14809 GpuTensor::Quant { qtype, .. } => {
14810 matches!(*qtype, QT_Q8_0 | QT_Q4_K | QT_Q6_K | QT_Q5_K | QT_Q4_0)
14811 || (*qtype == QT_NVFP4 && w.in_features() % 64 == 0)
14812 }
14813 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
14814 }
14815 }
14816
14817 pub fn qmatvec_gemm(
14824 &self,
14825 w: &crate::model::GpuTensor,
14826 aq: &CudaSlice<i8>,
14827 ad: &CudaSlice<f32>,
14828 m: usize,
14829 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14830 use crate::model::GpuTensor;
14831 let in_f = w.in_features();
14832 let out_f = w.out_features();
14833 let (bytes, qtype, row_bytes, scale, rp) = match w {
14834 GpuTensor::Quant {
14835 bytes,
14836 qtype,
14837 row_bytes,
14838 scale,
14839 rp,
14840 ..
14841 } => (bytes, *qtype, *row_bytes, *scale, *rp),
14842 _ => unreachable!("gemm_supports guaranteed Quant"),
14843 };
14844 if cfg!(memra_hopper_mma) && qtype == QT_Q8_0 && out_f % 64 == 0 && wgmma_gemm_enabled() {
14850 if let GpuTensor::Quant { rp4: Some(m4), .. } = w {
14851 let mut y = self.qmatvec_gemm_q8_0_wgmma_raw(m4, aq, ad, m, in_f, out_f)?;
14852 if scale != 1.0 {
14853 self.scale_inplace(&mut y, scale, m * out_f)?;
14854 }
14855 return Ok(y);
14856 }
14857 }
14858 let name = match qtype {
14859 QT_Q8_0 => "qmatvec_gemm_q8_0",
14860 QT_Q4_K => "qmatvec_gemm_q4_K",
14861 QT_Q4_0 => {
14862 if rp {
14863 "qmatvec_gemm_q4_0_rp"
14864 } else {
14865 "qmatvec_gemm_q4_0"
14866 }
14867 }
14868 QT_Q5_K => "qmatvec_gemm_q5_K",
14869 QT_Q6_K => "qmatvec_gemm_q6_K",
14870 QT_NVFP4 => {
14871 if rp {
14872 "qmatvec_gemm_nvfp4_rp"
14873 } else {
14874 "qmatvec_gemm_nvfp4"
14875 }
14876 }
14877 _ => unreachable!(),
14878 };
14879 let f = self.func(name);
14880 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);
14885 let k1_tile = if is_k1 {
14887 k1_launch_override().unwrap_or((128, 128, 8))
14888 } else {
14889 (128, 128, 8)
14890 };
14891 let (bm, bn): (u32, u32) = if is_k1 {
14892 (k1_tile.0, k1_tile.1)
14893 } else {
14894 (64, 256)
14895 };
14896 let warps: u32 = if is_k1 {
14897 k1_tile.2
14898 } else {
14899 match qtype {
14900 QT_NVFP4 => 8,
14901 _ => 4,
14902 }
14903 };
14904 let cfg = LaunchConfig {
14905 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
14906 block_dim: (32, warps, 1),
14907 shared_mem_bytes: 0,
14908 };
14909 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14910 let __s_b = self.gpu.stream();
14911 let mut b = __s_b.launch_builder(&f);
14912 b.arg(bytes)
14913 .arg(aq)
14914 .arg(ad)
14915 .arg(&mut y)
14916 .arg(&inf)
14917 .arg(&outf)
14918 .arg(&mi)
14919 .arg(&rb);
14920 unsafe {
14921 b.launch(cfg)?;
14922 }
14923 if scale != 1.0 {
14924 self.scale_inplace(&mut y, scale, m * out_f)?;
14925 }
14926 Ok(y)
14927 }
14928
14929 pub fn qmatvec_gemm_raw(
14934 &self,
14935 bytes: &CudaSlice<u8>,
14936 x: &CudaSlice<f32>,
14937 m: usize,
14938 in_f: usize,
14939 out_f: usize,
14940 qtype: i32,
14941 row_bytes: usize,
14942 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
14943 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
14944 let name = match qtype {
14945 QT_Q8_0 => "qmatvec_gemm_q8_0",
14946 QT_Q4_K => "qmatvec_gemm_q4_K",
14947 QT_Q4_0 => "qmatvec_gemm_q4_0",
14948 QT_Q5_K => "qmatvec_gemm_q5_K",
14949 QT_Q6_K => "qmatvec_gemm_q6_K",
14950 QT_NVFP4 => "qmatvec_gemm_nvfp4",
14951 QT_NVFP4_RP => "qmatvec_gemm_nvfp4_rp",
14952 _ => panic!("qmatvec_gemm_raw: qtype {qtype} has no GEMM kernel"),
14953 };
14954 let f = self.func(name);
14955 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);
14959 let k1_tile = if is_k1 {
14961 k1_launch_override().unwrap_or((128, 128, 8))
14962 } else {
14963 (128, 128, 8)
14964 };
14965 let (bm, bn): (u32, u32) = if is_k1 {
14966 (k1_tile.0, k1_tile.1)
14967 } else {
14968 (64, 256)
14969 };
14970 let warps: u32 = if is_k1 {
14971 k1_tile.2
14972 } else {
14973 match qtype {
14974 QT_NVFP4 | QT_NVFP4_RP => 8,
14975 _ => 4,
14976 }
14977 };
14978 let cfg = LaunchConfig {
14979 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
14980 block_dim: (32, warps, 1),
14981 shared_mem_bytes: 0,
14982 };
14983 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
14984 let __s_b = self.gpu.stream();
14985 let mut b = __s_b.launch_builder(&f);
14986 b.arg(bytes)
14987 .arg(&aq)
14988 .arg(&ad)
14989 .arg(&mut y)
14990 .arg(&inf)
14991 .arg(&outf)
14992 .arg(&mi)
14993 .arg(&rb);
14994 unsafe {
14995 b.launch(cfg)?;
14996 }
14997 Ok(y)
14998 }
14999
15000 pub fn qmatvec_gemm_q8_0_wgmma_raw(
15007 &self,
15008 rp4: &CudaSlice<u8>,
15009 aq: &CudaSlice<i8>,
15010 ad: &CudaSlice<f32>,
15011 m: usize,
15012 in_f: usize,
15013 out_f: usize,
15014 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15015 assert!(
15016 out_f % 64 == 0 && in_f % 32 == 0,
15017 "wgmma GEMM needs out_f%64==0, in_f%32==0"
15018 );
15019 let f = self.func("qmatvec_gemm_q8_0_wgmma");
15020 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
15022 grid_dim: ((out_f / 64) as u32, (m as u32).div_ceil(64), 1),
15023 block_dim: (128, 1, 1),
15024 shared_mem_bytes: 0,
15025 };
15026 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
15027 let __s_b = self.gpu.stream();
15028 let mut b = __s_b.launch_builder(&f);
15029 b.arg(rp4)
15030 .arg(aq)
15031 .arg(ad)
15032 .arg(&mut y)
15033 .arg(&inf)
15034 .arg(&outf)
15035 .arg(&mi);
15036 unsafe {
15037 b.launch(cfg)?;
15038 }
15039 Ok(y)
15040 }
15041
15042 pub fn scale_inplace(
15044 &self,
15045 y: &mut CudaSlice<f32>,
15046 s: f32,
15047 n: usize,
15048 ) -> Result<(), Box<dyn std::error::Error>> {
15049 let f = self.func("scale_f32");
15050 let cfg = LaunchConfig::for_num_elems(n as u32);
15051 let (sf, ni) = (s, n as i32);
15052 let __s_b = self.gpu.stream();
15053 let mut b = __s_b.launch_builder(&f);
15054 b.arg(y).arg(&sf).arg(&ni);
15055 unsafe {
15056 b.launch(cfg)?;
15057 }
15058 Ok(())
15059 }
15060
15061 pub fn bf16_to_f32(
15066 &self,
15067 data: &cudarc::driver::CudaView<'_, u8>,
15068 n: usize,
15069 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15070 let mut out = self.alloc_uninit::<f32>(n)?;
15071 let f = self.func("bf16_to_f32");
15072 let cfg = LaunchConfig::for_num_elems(n as u32);
15073 let ni = n as i32;
15074 let __s_b = self.gpu.stream();
15075 let mut b = __s_b.launch_builder(&f);
15076 b.arg(data).arg(&mut out).arg(&ni);
15077 unsafe {
15078 b.launch(cfg)?;
15079 }
15080 Ok(out)
15081 }
15082
15083 fn linear_bf16_chunked(
15090 &self,
15091 x: &CudaSlice<f32>,
15092 data: &CudaSlice<u8>,
15093 m: usize,
15094 in_f: usize,
15095 out_f: usize,
15096 exact: bool,
15097 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15098 const CHUNK_BYTES: usize = 256 << 20;
15099 let chunk_rows = (CHUNK_BYTES / (in_f * 4)).max(1).min(out_f);
15100 if chunk_rows >= out_f {
15101 let wf32 = self.bf16_to_f32(&data.slice(0..in_f * out_f * 2), in_f * out_f)?;
15102 return if exact {
15103 self.linear_decode_exact(x, &wf32, m, in_f, out_f)
15104 } else {
15105 self.linear(x, &wf32, m, in_f, out_f)
15106 };
15107 }
15108 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
15109 let mut r0 = 0usize;
15110 while r0 < out_f {
15111 let rows = chunk_rows.min(out_f - r0);
15112 let wslice = data.slice(r0 * in_f * 2..(r0 + rows) * in_f * 2);
15113 let wf32 = self.bf16_to_f32(&wslice, in_f * rows)?;
15114 let yc = if exact {
15115 self.linear_decode_exact(x, &wf32, m, in_f, rows)?
15116 } else {
15117 self.linear(x, &wf32, m, in_f, rows)?
15118 };
15119 for mi in 0..m {
15121 let src = yc.slice(mi * rows..(mi + 1) * rows);
15122 let mut dst = y.slice_mut(mi * out_f + r0..mi * out_f + r0 + rows);
15123 self.gpu.stream().memcpy_dtod(&src, &mut dst)?;
15124 }
15125 r0 += rows;
15126 }
15127 Ok(y)
15128 }
15129
15130 pub fn linear_decode_exact(
15137 &self,
15138 x: &CudaSlice<f32>,
15139 w: &CudaSlice<f32>,
15140 m_tokens: usize,
15141 in_f: usize,
15142 out_f: usize,
15143 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15144 if m_tokens == 1 {
15145 return self.linear(x, w, 1, in_f, out_f);
15146 }
15147 let xv = self.view(x, m_tokens * in_f);
15148 let mut y = self.alloc_uninit::<f32>(m_tokens * out_f)?;
15149 for t in 0..m_tokens {
15150 let row = xv.slice(t * in_f..(t + 1) * in_f);
15151 let mut xr = self.alloc_uninit::<f32>(in_f)?;
15152 self.copy_view_into(&mut xr, 0, &row, in_f)?;
15153 let yr = self.linear(&xr, w, 1, in_f, out_f)?;
15154 self.copy_into(&mut y, t * out_f, &yr, out_f)?;
15155 }
15156 Ok(y)
15157 }
15158
15159 pub fn linear(
15160 &self,
15161 x: &CudaSlice<f32>,
15162 w: &CudaSlice<f32>,
15163 m_tokens: usize,
15164 in_f: usize,
15165 out_f: usize,
15166 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15167 use cudarc::cublaslt::{Matmul, MatmulConfig};
15168 let mut c = self.alloc_uninit::<f32>(m_tokens * out_f)?; let cfg = MatmulConfig {
15170 transa: true,
15171 transb: false,
15172 transc: false,
15173 m: out_f as u64,
15174 n: m_tokens as u64,
15175 k: in_f as u64,
15176 alpha: 1.0,
15177 lda: in_f as i64,
15178 ldb: in_f as i64,
15179 beta: 0.0,
15180 ldc: out_f as i64,
15181 stride_a: None,
15182 stride_b: None,
15183 stride_c: None,
15184 stride_bias: None,
15185 batch_size: None,
15186 };
15187 unsafe {
15188 self.gpu.blas.matmul(cfg, w, x, &mut c, None, None)?;
15189 }
15190 Ok(c)
15191 }
15192
15193 pub fn sdpa_naive(
15195 &self,
15196 q: &CudaSlice<f32>,
15197 k: &CudaSlice<f32>,
15198 v: &CudaSlice<f32>,
15199 o: &mut CudaSlice<f32>,
15200 head_dim: usize,
15201 n_head: usize,
15202 n_head_kv: usize,
15203 t: usize,
15204 t_kv: usize,
15205 scale: f32,
15206 causal: bool,
15207 ) -> Result<(), Box<dyn std::error::Error>> {
15208 let f = self.func("sdpa_naive_f32");
15209 let cfg = LaunchConfig {
15210 grid_dim: (n_head as u32, t as u32, 1),
15211 block_dim: (128, 1, 1),
15212 shared_mem_bytes: (t_kv * 4) as u32,
15213 };
15214 let (hd, nh, nhkv, ti, tkvi, cz) = (
15215 head_dim as i32,
15216 n_head as i32,
15217 n_head_kv as i32,
15218 t as i32,
15219 t_kv as i32,
15220 causal as i32,
15221 );
15222 let __s_b = self.gpu.stream();
15223 let mut b = __s_b.launch_builder(&f);
15224 b.arg(q)
15225 .arg(k)
15226 .arg(v)
15227 .arg(o)
15228 .arg(&hd)
15229 .arg(&nh)
15230 .arg(&nhkv)
15231 .arg(&ti)
15232 .arg(&tkvi)
15233 .arg(&scale)
15234 .arg(&cz);
15235 unsafe {
15236 b.launch(cfg)?;
15237 }
15238 Ok(())
15239 }
15240
15241 #[allow(clippy::too_many_arguments)]
15246 pub fn sdpa_naive_island(
15247 &self,
15248 q: &CudaSlice<f32>,
15249 k: &CudaSlice<f32>,
15250 v: &CudaSlice<f32>,
15251 o: &mut CudaSlice<f32>,
15252 span_id: &CudaSlice<i32>,
15253 head_dim: usize,
15254 n_head: usize,
15255 n_head_kv: usize,
15256 t: usize,
15257 t_kv: usize,
15258 scale: f32,
15259 window: usize,
15260 ) -> Result<(), Box<dyn std::error::Error>> {
15261 let f = self.func("sdpa_naive_island_f32");
15262 let cfg = LaunchConfig {
15263 grid_dim: (n_head as u32, t as u32, 1),
15264 block_dim: (128, 1, 1),
15265 shared_mem_bytes: (t_kv * 4) as u32,
15266 };
15267 let (hd, nh, nhkv, ti, tkvi, wi) = (
15268 head_dim as i32,
15269 n_head as i32,
15270 n_head_kv as i32,
15271 t as i32,
15272 t_kv as i32,
15273 window as i32,
15274 );
15275 let __s_b = self.gpu.stream();
15276 let mut b = __s_b.launch_builder(&f);
15277 b.arg(q)
15278 .arg(k)
15279 .arg(v)
15280 .arg(o)
15281 .arg(span_id)
15282 .arg(&hd)
15283 .arg(&nh)
15284 .arg(&nhkv)
15285 .arg(&ti)
15286 .arg(&tkvi)
15287 .arg(&scale)
15288 .arg(&wi);
15289 unsafe {
15290 b.launch(cfg)?;
15291 }
15292 Ok(())
15293 }
15294
15295 #[allow(clippy::too_many_arguments)]
15297 pub fn sdpa_naive_w(
15298 &self,
15299 q: &CudaSlice<f32>,
15300 k: &CudaSlice<f32>,
15301 v: &CudaSlice<f32>,
15302 o: &mut CudaSlice<f32>,
15303 head_dim: usize,
15304 n_head: usize,
15305 n_head_kv: usize,
15306 t: usize,
15307 t_kv: usize,
15308 scale: f32,
15309 causal: bool,
15310 window: usize,
15311 ) -> Result<(), Box<dyn std::error::Error>> {
15312 let f = self.func("sdpa_naive_w_f32");
15313 let cfg = LaunchConfig {
15314 grid_dim: (n_head as u32, t as u32, 1),
15315 block_dim: (128, 1, 1),
15316 shared_mem_bytes: (t_kv * 4) as u32,
15317 };
15318 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15319 head_dim as i32,
15320 n_head as i32,
15321 n_head_kv as i32,
15322 t as i32,
15323 t_kv as i32,
15324 causal as i32,
15325 window as i32,
15326 );
15327 let __s_b = self.gpu.stream();
15328 let mut b = __s_b.launch_builder(&f);
15329 b.arg(q)
15330 .arg(k)
15331 .arg(v)
15332 .arg(o)
15333 .arg(&hd)
15334 .arg(&nh)
15335 .arg(&nhkv)
15336 .arg(&ti)
15337 .arg(&tkvi)
15338 .arg(&scale)
15339 .arg(&cz)
15340 .arg(&wi);
15341 unsafe {
15342 b.launch(cfg)?;
15343 }
15344 Ok(())
15345 }
15346
15347 pub fn sdpa_naive_view(
15349 &self,
15350 q: &CudaSlice<f32>,
15351 k: &cudarc::driver::CudaView<f32>,
15352 v: &cudarc::driver::CudaView<f32>,
15353 o: &mut CudaSlice<f32>,
15354 head_dim: usize,
15355 n_head: usize,
15356 n_head_kv: usize,
15357 t: usize,
15358 t_kv: usize,
15359 scale: f32,
15360 causal: bool,
15361 ) -> Result<(), Box<dyn std::error::Error>> {
15362 let f = self.func("sdpa_naive_f32");
15363 let cfg = LaunchConfig {
15364 grid_dim: (n_head as u32, t as u32, 1),
15365 block_dim: (128, 1, 1),
15366 shared_mem_bytes: (t_kv * 4) as u32,
15367 };
15368 let (hd, nh, nhkv, ti, tkvi, cz) = (
15369 head_dim as i32,
15370 n_head as i32,
15371 n_head_kv as i32,
15372 t as i32,
15373 t_kv as i32,
15374 causal as i32,
15375 );
15376 let __s_b = self.gpu.stream();
15377 let mut b = __s_b.launch_builder(&f);
15378 b.arg(q)
15379 .arg(k)
15380 .arg(v)
15381 .arg(o)
15382 .arg(&hd)
15383 .arg(&nh)
15384 .arg(&nhkv)
15385 .arg(&ti)
15386 .arg(&tkvi)
15387 .arg(&scale)
15388 .arg(&cz);
15389 unsafe {
15390 b.launch(cfg)?;
15391 }
15392 Ok(())
15393 }
15394
15395 #[allow(clippy::too_many_arguments)]
15403 pub fn fa_dequant_kv_view_f32(
15404 &self,
15405 k: &cudarc::driver::CudaView<u8>,
15406 v: &cudarc::driver::CudaView<u8>,
15407 kf: &mut CudaSlice<f32>,
15408 vf: &mut CudaSlice<f32>,
15409 kv_dim_k: usize,
15410 kv_dim_v: usize,
15411 t_kv: usize,
15412 k_tok_bytes: usize,
15413 v_tok_bytes: usize,
15414 g: bool,
15415 ) -> Result<(), Box<dyn std::error::Error>> {
15416 let f = if g {
15417 self.func_g("fa_dequant_kv_ws_f32")
15418 } else {
15419 self.func("fa_dequant_kv_ws_f32")
15420 };
15421 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
15422 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
15423 let cfg = LaunchConfig {
15424 grid_dim: (nblk.max(1), 1, 1),
15425 block_dim: (256, 1, 1),
15426 shared_mem_bytes: 0,
15427 };
15428 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
15429 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
15430 let __s_b = self.gpu.stream();
15431 let mut b = __s_b.launch_builder(&f);
15432 b.arg(k)
15433 .arg(v)
15434 .arg(&mut *kf)
15435 .arg(&mut *vf)
15436 .arg(&kdk)
15437 .arg(&kdv)
15438 .arg(&tkvi)
15439 .arg(&ktb)
15440 .arg(&vtb);
15441 unsafe {
15442 b.launch(cfg)?;
15443 }
15444 Ok(())
15445 }
15446
15447 #[allow(clippy::too_many_arguments)]
15448 pub fn sdpa_naive_quantized_view(
15449 &self,
15450 q: &CudaSlice<f32>,
15451 k: &cudarc::driver::CudaView<u8>,
15452 v: &cudarc::driver::CudaView<u8>,
15453 o: &mut CudaSlice<f32>,
15454 head_dim: usize,
15455 n_head: usize,
15456 n_head_kv: usize,
15457 t: usize,
15458 t_kv: usize,
15459 scale: f32,
15460 causal: bool,
15461 k_tok_bytes: usize,
15462 v_tok_bytes: usize,
15463 ) -> Result<(), Box<dyn std::error::Error>> {
15464 let kv_dim = n_head_kv * head_dim;
15465 let mut kf = self.uninit(t_kv * kv_dim)?;
15466 let mut vf = self.uninit(t_kv * kv_dim)?;
15467 let f = self.func("fa_dequant_kv_ws_f32");
15468 let total = (2 * t_kv * kv_dim) as u64;
15469 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
15470 let cfg = LaunchConfig {
15471 grid_dim: (nblk.max(1), 1, 1),
15472 block_dim: (256, 1, 1),
15473 shared_mem_bytes: 0,
15474 };
15475 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
15476 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
15477 let __s_b = self.gpu.stream();
15478 let mut b = __s_b.launch_builder(&f);
15479 b.arg(k)
15480 .arg(v)
15481 .arg(&mut kf)
15482 .arg(&mut vf)
15483 .arg(&kv_dim_i)
15484 .arg(&kv_dim_i)
15485 .arg(&t_kv_i)
15486 .arg(&k_tok_bytes_i)
15487 .arg(&v_tok_bytes_i);
15488 unsafe { b.launch(cfg)? };
15489 self.sdpa_naive(
15490 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
15491 )
15492 }
15493
15494 #[allow(clippy::too_many_arguments)]
15506 pub fn sdpa_naive_w_quantized_view(
15507 &self,
15508 q: &CudaSlice<f32>,
15509 k: &cudarc::driver::CudaView<u8>,
15510 v: &cudarc::driver::CudaView<u8>,
15511 o: &mut CudaSlice<f32>,
15512 head_dim: usize,
15513 n_head: usize,
15514 n_head_kv: usize,
15515 t: usize,
15516 t_kv: usize,
15517 scale: f32,
15518 causal: bool,
15519 window: usize,
15520 k_tok_bytes: usize,
15521 v_tok_bytes: usize,
15522 ) -> Result<(), Box<dyn std::error::Error>> {
15523 let kv_dim = n_head_kv * head_dim;
15524 let mut kf = self.uninit(t_kv * kv_dim)?;
15525 let mut vf = self.uninit(t_kv * kv_dim)?;
15526 let f = self.func("fa_dequant_kv_ws_f32");
15527 let total = (2 * t_kv * kv_dim) as u64;
15528 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
15529 let cfg = LaunchConfig {
15530 grid_dim: (nblk.max(1), 1, 1),
15531 block_dim: (256, 1, 1),
15532 shared_mem_bytes: 0,
15533 };
15534 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
15535 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
15536 let __s_b = self.gpu.stream();
15537 let mut b = __s_b.launch_builder(&f);
15538 b.arg(k)
15539 .arg(v)
15540 .arg(&mut kf)
15541 .arg(&mut vf)
15542 .arg(&kv_dim_i)
15543 .arg(&kv_dim_i)
15544 .arg(&t_kv_i)
15545 .arg(&k_tok_bytes_i)
15546 .arg(&v_tok_bytes_i);
15547 unsafe { b.launch(cfg)? };
15548 self.sdpa_naive_w(
15549 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
15550 )
15551 }
15552
15553 pub fn fa_prefill(
15557 &self,
15558 q: &CudaSlice<f32>,
15559 k: &CudaSlice<f32>,
15560 v: &CudaSlice<f32>,
15561 o: &mut CudaSlice<f32>,
15562 head_dim: usize,
15563 n_head: usize,
15564 n_head_kv: usize,
15565 t: usize,
15566 t_kv: usize,
15567 scale: f32,
15568 causal: bool,
15569 ) -> Result<(), Box<dyn std::error::Error>> {
15570 if portable_mma_gated() {
15571 return self.sdpa_naive(
15572 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
15573 );
15574 }
15575 let fa3_on = head_dim == 256
15583 && causal
15584 && t == t_kv
15585 && match std::env::var("MEMRA_FA3").as_deref() {
15586 Ok("0") => false,
15587 Ok("1") => true,
15588 _ => cfg!(memra_hopper_mma),
15589 };
15590 if fa3_on {
15591 let n = t * n_head * head_dim;
15592 let nkv = t * n_head_kv * head_dim;
15593 let mut q16 = self.alloc_u8_uninit(n * 2)?;
15594 let mut k16 = self.alloc_u8_uninit(nkv * 2)?;
15595 let mut v16 = self.alloc_u8_uninit(nkv * 2)?;
15596 self.f32_to_bf16_into(q, &mut q16, n)?;
15597 self.f32_to_bf16_into(k, &mut k16, nkv)?;
15598 self.f32_to_bf16_into(v, &mut v16, nkv)?;
15599 let rc = {
15600 use cudarc::driver::{DevicePtr, DevicePtrMut};
15601 let stream = self.gpu.stream();
15602 let (qp, _g1) = q16.device_ptr(&stream);
15603 let (kp, _g2) = k16.device_ptr(&stream);
15604 let (vp, _g3) = v16.device_ptr(&stream);
15605 let (op, _g4) = o.device_ptr_mut(&stream);
15606 unsafe {
15607 memra_fa3_prefill(
15608 qp as *const core::ffi::c_void,
15609 kp as *const core::ffi::c_void,
15610 vp as *const core::ffi::c_void,
15611 op as *mut f32,
15612 t as i32,
15613 n_head as i32,
15614 n_head_kv as i32,
15615 head_dim as i32,
15616 scale,
15617 stream.cu_stream() as *mut core::ffi::c_void,
15618 )
15619 }
15620 };
15621 if rc != 0 {
15622 return Err(format!("memra_fa3_prefill rc={rc}").into());
15623 }
15624 return Ok(());
15625 }
15626 static FA_P1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15631 let fa_p1 = *FA_P1.get_or_init(|| std::env::var("MEMRA_FA_P1").as_deref() == Ok("1"));
15632 if fa_p1 && head_dim == 256 && !std::env::var("MEMRA_FA_FLOOR").is_ok() {
15633 const BLOCK_Q: usize = 64;
15634 const BKX: usize = 32;
15635 let f = self.func("fa_prefill_bf16_p1");
15636 let shmem = (2 * (2 * BKX * head_dim + BLOCK_Q * BKX)
15637 + 4 * (BLOCK_Q * BKX + 2 * BLOCK_Q)) as u32;
15638 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15639 f.set_attribute(
15640 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15641 shmem as i32,
15642 )?;
15643 let cfg = LaunchConfig {
15644 grid_dim: (
15645 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15646 n_head as u32,
15647 1,
15648 ),
15649 block_dim: (32, 4, 1),
15650 shared_mem_bytes: shmem,
15651 };
15652 let (hd, nh, nhkv, ti, tkvi, cz) = (
15653 head_dim as i32,
15654 n_head as i32,
15655 n_head_kv as i32,
15656 t as i32,
15657 t_kv as i32,
15658 causal as i32,
15659 );
15660 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
15661 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
15662 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
15663 let __s_b = self.gpu.stream();
15664 let mut b = __s_b.launch_builder(&f);
15665 b.arg(&qb)
15666 .arg(&kb)
15667 .arg(&vb)
15668 .arg(o)
15669 .arg(&hd)
15670 .arg(&nh)
15671 .arg(&nhkv)
15672 .arg(&ti)
15673 .arg(&tkvi)
15674 .arg(&scale)
15675 .arg(&cz);
15676 unsafe {
15677 b.launch(cfg)?;
15678 }
15679 return Ok(());
15680 }
15681 const BK: usize = 32;
15687 let w2 = std::env::var("MEMRA_FA_PP_W2").as_deref() == Ok("1");
15690 let (block_q, warps, w2_sfx): (usize, u32, &str) =
15691 if w2 { (32, 2, "_w2") } else { (64, 4, "") };
15692 let hd_sfx = fa_hd_suffix(head_dim)?;
15696 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
15697 let bf16kv = !floor && !w2 && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0");
15702 let (kb16, vb16) = if bf16kv {
15703 let n = t_kv * n_head_kv * head_dim;
15704 let mut kb = self.alloc_u8_uninit(n * 2)?;
15705 let mut vb = self.alloc_u8_uninit(n * 2)?;
15706 let fcv = self.func("f32_to_bf16_bulk");
15707 let ni = n as i64;
15708 let cfgc = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
15709 let __s_b = self.gpu.stream();
15710 let mut b = __s_b.launch_builder(&fcv);
15711 b.arg(k).arg(&mut kb).arg(&ni);
15712 unsafe {
15713 b.launch(cfgc)?;
15714 }
15715 let __s_b = self.gpu.stream();
15716 let mut b = __s_b.launch_builder(&fcv);
15717 b.arg(v).arg(&mut vb).arg(&ni);
15718 unsafe {
15719 b.launch(cfgc)?;
15720 }
15721 (Some(kb), Some(vb))
15722 } else {
15723 (None, None)
15724 };
15725 let f = self.func(&if bf16kv {
15726 format!("fa_prefill_bf16kv_pp{hd_sfx}")
15727 } else {
15728 format!(
15729 "fa_prefill_f32{}{}{hd_sfx}",
15730 if floor { "" } else { "_pp" },
15731 if floor { "" } else { w2_sfx }
15732 )
15733 });
15734 let kv_stages = if bf16kv { 2 } else { 1 };
15737 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
15738 + 4 * (block_q * BK + 2 * block_q)) as u32;
15739 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15740 f.set_attribute(
15741 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15742 shmem as i32,
15743 )?;
15744 let cfg = LaunchConfig {
15745 grid_dim: (
15746 (t as u32 + block_q as u32 - 1) / block_q as u32,
15747 n_head as u32,
15748 1,
15749 ),
15750 block_dim: (32, warps, 1),
15751 shared_mem_bytes: shmem,
15752 };
15753 let (hd, nh, nhkv, ti, tkvi, cz) = (
15754 head_dim as i32,
15755 n_head as i32,
15756 n_head_kv as i32,
15757 t as i32,
15758 t_kv as i32,
15759 causal as i32,
15760 );
15761 let __s_b = self.gpu.stream();
15762 let mut b = __s_b.launch_builder(&f);
15763 b.arg(q);
15764 match (&kb16, &vb16) {
15765 (Some(kb), Some(vb)) => {
15766 b.arg(kb).arg(vb);
15767 }
15768 _ => {
15769 b.arg(k).arg(v);
15770 }
15771 }
15772 b.arg(o)
15773 .arg(&hd)
15774 .arg(&nh)
15775 .arg(&nhkv)
15776 .arg(&ti)
15777 .arg(&tkvi)
15778 .arg(&scale)
15779 .arg(&cz);
15780 unsafe {
15781 b.launch(cfg)?;
15782 }
15783 Ok(())
15784 }
15785
15786 #[allow(clippy::too_many_arguments)]
15790 pub fn fa_prefill_w(
15791 &self,
15792 q: &CudaSlice<f32>,
15793 k: &CudaSlice<f32>,
15794 v: &CudaSlice<f32>,
15795 o: &mut CudaSlice<f32>,
15796 head_dim: usize,
15797 n_head: usize,
15798 n_head_kv: usize,
15799 t: usize,
15800 t_kv: usize,
15801 scale: f32,
15802 causal: bool,
15803 window: usize,
15804 ) -> Result<(), Box<dyn std::error::Error>> {
15805 if portable_mma_gated() {
15808 return self.sdpa_naive_w(
15809 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
15810 );
15811 }
15812 static FAW_F32: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15816 let faw_f32 =
15817 *FAW_F32.get_or_init(|| std::env::var("MEMRA_FAW_STAGE").as_deref() == Ok("f32"));
15818 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
15819 self.fa_prefill_w_arm(
15820 q,
15821 k,
15822 v,
15823 o,
15824 head_dim,
15825 n_head,
15826 n_head_kv,
15827 t,
15828 t_kv,
15829 scale,
15830 causal,
15831 window,
15832 floor || faw_f32,
15833 floor,
15834 )
15835 }
15836
15837 #[allow(clippy::too_many_arguments)]
15840 pub fn fa_prefill_w_pre(
15841 &self,
15842 qb: &CudaSlice<u8>,
15843 kb: &CudaSlice<u8>,
15844 vb: &CudaSlice<u8>,
15845 o: &mut CudaSlice<f32>,
15846 head_dim: usize,
15847 n_head: usize,
15848 n_head_kv: usize,
15849 t: usize,
15850 t_kv: usize,
15851 scale: f32,
15852 causal: bool,
15853 window: usize,
15854 v_f16: bool,
15855 ) -> Result<(), Box<dyn std::error::Error>> {
15856 const BLOCK_Q: usize = 64;
15857 const BK: usize = 32;
15858 debug_assert_eq!(head_dim, 256);
15859 let hp = fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15860 debug_assert!(!v_f16 || hp, "f16 V emitted but the SWA hp arm is off");
15861 if hp {
15862 const BLOCK_QH: usize = 32;
15863 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
15866 let vh: &CudaSlice<u8> = if v_f16 {
15867 vb
15868 } else {
15869 let n = t_kv * n_head_kv * head_dim;
15870 if vguard.as_ref().map(|b| b.len() < n * 2).unwrap_or(true) {
15871 *vguard = Some(self.alloc_uninit::<u8>(n * 2)?);
15872 }
15873 self.bf16_to_f16_into(vb, n, vguard.as_mut().unwrap())?;
15874 vguard.as_ref().unwrap()
15875 };
15876 let f = self.func("fa_prefill_w_bf16_p1h2");
15877 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
15878 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15879 f.set_attribute(
15880 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15881 shmem as i32,
15882 )?;
15883 let cfg = LaunchConfig {
15884 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
15885 block_dim: (32, 4, 1),
15886 shared_mem_bytes: shmem,
15887 };
15888 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15889 head_dim as i32,
15890 n_head as i32,
15891 n_head_kv as i32,
15892 t as i32,
15893 t_kv as i32,
15894 causal as i32,
15895 window as i32,
15896 );
15897 let __s_b = self.gpu.stream();
15898 let mut b = __s_b.launch_builder(&f);
15899 b.arg(qb)
15900 .arg(kb)
15901 .arg(vh)
15902 .arg(o)
15903 .arg(&hd)
15904 .arg(&nh)
15905 .arg(&nhkv)
15906 .arg(&ti)
15907 .arg(&tkvi)
15908 .arg(&scale)
15909 .arg(&cz)
15910 .arg(&wi);
15911 unsafe {
15912 b.launch(cfg)?;
15913 }
15914 return Ok(());
15915 }
15916 let f = self.func("fa_prefill_w_bf16_p1");
15917 let shmem =
15918 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
15919 use cudarc::driver::sys::CUfunction_attribute_enum as A;
15920 f.set_attribute(
15921 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
15922 shmem as i32,
15923 )?;
15924 let cfg = LaunchConfig {
15925 grid_dim: (
15926 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
15927 n_head as u32,
15928 1,
15929 ),
15930 block_dim: (32, 4, 1),
15931 shared_mem_bytes: shmem,
15932 };
15933 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
15934 head_dim as i32,
15935 n_head as i32,
15936 n_head_kv as i32,
15937 t as i32,
15938 t_kv as i32,
15939 causal as i32,
15940 window as i32,
15941 );
15942 let __s_b = self.gpu.stream();
15943 let mut b = __s_b.launch_builder(&f);
15944 b.arg(qb)
15945 .arg(kb)
15946 .arg(vb)
15947 .arg(o)
15948 .arg(&hd)
15949 .arg(&nh)
15950 .arg(&nhkv)
15951 .arg(&ti)
15952 .arg(&tkvi)
15953 .arg(&scale)
15954 .arg(&cz)
15955 .arg(&wi);
15956 unsafe {
15957 b.launch(cfg)?;
15958 }
15959 Ok(())
15960 }
15961
15962 #[allow(clippy::too_many_arguments)]
15964 pub fn fa_prefill_w_arm(
15965 &self,
15966 q: &CudaSlice<f32>,
15967 k: &CudaSlice<f32>,
15968 v: &CudaSlice<f32>,
15969 o: &mut CudaSlice<f32>,
15970 head_dim: usize,
15971 n_head: usize,
15972 n_head_kv: usize,
15973 t: usize,
15974 t_kv: usize,
15975 scale: f32,
15976 causal: bool,
15977 window: usize,
15978 f32_stage: bool,
15979 floor: bool,
15980 ) -> Result<(), Box<dyn std::error::Error>> {
15981 const BLOCK_Q: usize = 64;
15982 const BK: usize = 32;
15983 debug_assert_eq!(head_dim, 256, "fa_prefill_w is stamped hd256 only");
15984 static P1_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15988 let p1 = !floor
15989 && !f32_stage
15990 && *P1_ON.get_or_init(|| {
15991 std::env::var("MEMRA_FAW_P1")
15992 .map(|v| v != "0")
15993 .unwrap_or(true)
15994 });
15995 let hp =
15996 p1 && fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
15997 if hp {
15998 const BLOCK_QH: usize = 32;
15999 let f = self.func("fa_prefill_w_bf16_p1h2");
16000 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
16001 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16002 f.set_attribute(
16003 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16004 shmem as i32,
16005 )?;
16006 let cfg = LaunchConfig {
16007 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
16008 block_dim: (32, 4, 1),
16009 shared_mem_bytes: shmem,
16010 };
16011 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
16012 head_dim as i32,
16013 n_head as i32,
16014 n_head_kv as i32,
16015 t as i32,
16016 t_kv as i32,
16017 causal as i32,
16018 window as i32,
16019 );
16020 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16021 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16022 let vh = self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?;
16023 let __s_b = self.gpu.stream();
16024 let mut b = __s_b.launch_builder(&f);
16025 b.arg(&qb)
16026 .arg(&kb)
16027 .arg(&vh)
16028 .arg(o)
16029 .arg(&hd)
16030 .arg(&nh)
16031 .arg(&nhkv)
16032 .arg(&ti)
16033 .arg(&tkvi)
16034 .arg(&scale)
16035 .arg(&cz)
16036 .arg(&wi);
16037 unsafe {
16038 b.launch(cfg)?;
16039 }
16040 return Ok(());
16041 }
16042 if p1 {
16043 let f = self.func("fa_prefill_w_bf16_p1");
16044 let shmem =
16045 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
16046 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16047 f.set_attribute(
16048 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16049 shmem as i32,
16050 )?;
16051 let cfg = LaunchConfig {
16052 grid_dim: (
16053 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16054 n_head as u32,
16055 1,
16056 ),
16057 block_dim: (32, 4, 1),
16058 shared_mem_bytes: shmem,
16059 };
16060 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
16061 head_dim as i32,
16062 n_head as i32,
16063 n_head_kv as i32,
16064 t as i32,
16065 t_kv as i32,
16066 causal as i32,
16067 window as i32,
16068 );
16069 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16070 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16071 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
16072 let __s_b = self.gpu.stream();
16073 let mut b = __s_b.launch_builder(&f);
16074 b.arg(&qb)
16075 .arg(&kb)
16076 .arg(&vb)
16077 .arg(o)
16078 .arg(&hd)
16079 .arg(&nh)
16080 .arg(&nhkv)
16081 .arg(&ti)
16082 .arg(&tkvi)
16083 .arg(&scale)
16084 .arg(&cz)
16085 .arg(&wi);
16086 unsafe {
16087 b.launch(cfg)?;
16088 }
16089 return Ok(());
16090 }
16091 static G4_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16094 let g4 = !floor
16095 && !f32_stage
16096 && n_head_kv == 1
16097 && n_head % 4 == 0
16098 && *G4_ON.get_or_init(|| {
16099 std::env::var("MEMRA_FAW_G4")
16100 .map(|v| v != "0")
16101 .unwrap_or(true)
16102 });
16103 if g4 {
16104 const SP_M: usize = 16;
16105 static O2_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16108 let o2 = *O2_ON.get_or_init(|| {
16109 std::env::var("MEMRA_FAW_O2")
16110 .map(|v| v != "0")
16111 .unwrap_or(true)
16112 });
16113 let f = self.func(if o2 {
16114 "fa_prefill_w_bf16_g4o2"
16115 } else {
16116 "fa_prefill_w_bf16_g4"
16117 });
16118 let shmem = if o2 {
16119 (2 * (4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M)) as u32
16120 } else {
16121 (2 * (2 * BK * head_dim + 4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M))
16122 as u32
16123 };
16124 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16125 f.set_attribute(
16126 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16127 shmem as i32,
16128 )?;
16129 let cfg = LaunchConfig {
16130 grid_dim: ((t as u32).div_ceil(SP_M as u32), (n_head / 4) as u32, 1),
16131 block_dim: (32, 4, 1),
16132 shared_mem_bytes: shmem,
16133 };
16134 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
16135 head_dim as i32,
16136 n_head as i32,
16137 n_head_kv as i32,
16138 t as i32,
16139 t_kv as i32,
16140 causal as i32,
16141 window as i32,
16142 );
16143 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16144 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16145 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
16146 let __s_b = self.gpu.stream();
16147 let mut b = __s_b.launch_builder(&f);
16148 b.arg(&qb)
16149 .arg(&kb)
16150 .arg(&vb)
16151 .arg(o)
16152 .arg(&hd)
16153 .arg(&nh)
16154 .arg(&nhkv)
16155 .arg(&ti)
16156 .arg(&tkvi)
16157 .arg(&scale)
16158 .arg(&cz)
16159 .arg(&wi);
16160 unsafe {
16161 b.launch(cfg)?;
16162 }
16163 return Ok(());
16164 }
16165 let f = self.func(if floor {
16166 "fa_prefill_w_f32"
16167 } else if f32_stage {
16168 "fa_prefill_w_f32_pp"
16169 } else {
16170 "fa_prefill_w_bf16_pp"
16171 });
16172 let shmem =
16173 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
16174 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16175 f.set_attribute(
16176 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16177 shmem as i32,
16178 )?;
16179 let cfg = LaunchConfig {
16180 grid_dim: (
16181 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16182 n_head as u32,
16183 1,
16184 ),
16185 block_dim: (32, 4, 1),
16186 shared_mem_bytes: shmem,
16187 };
16188 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
16189 head_dim as i32,
16190 n_head as i32,
16191 n_head_kv as i32,
16192 t as i32,
16193 t_kv as i32,
16194 causal as i32,
16195 window as i32,
16196 );
16197 if f32_stage {
16198 let __s_b = self.gpu.stream();
16199 let mut b = __s_b.launch_builder(&f);
16200 b.arg(q)
16201 .arg(k)
16202 .arg(v)
16203 .arg(o)
16204 .arg(&hd)
16205 .arg(&nh)
16206 .arg(&nhkv)
16207 .arg(&ti)
16208 .arg(&tkvi)
16209 .arg(&scale)
16210 .arg(&cz)
16211 .arg(&wi);
16212 unsafe {
16213 b.launch(cfg)?;
16214 }
16215 } else {
16216 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16217 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16218 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
16219 let __s_b = self.gpu.stream();
16220 let mut b = __s_b.launch_builder(&f);
16221 b.arg(&qb)
16222 .arg(&kb)
16223 .arg(&vb)
16224 .arg(o)
16225 .arg(&hd)
16226 .arg(&nh)
16227 .arg(&nhkv)
16228 .arg(&ti)
16229 .arg(&tkvi)
16230 .arg(&scale)
16231 .arg(&cz)
16232 .arg(&wi);
16233 unsafe {
16234 b.launch(cfg)?;
16235 }
16236 }
16237 Ok(())
16238 }
16239
16240 #[allow(clippy::too_many_arguments)]
16244 pub fn fa_prefill_hd512(
16245 &self,
16246 q: &CudaSlice<f32>,
16247 k: &CudaSlice<f32>,
16248 v: &CudaSlice<f32>,
16249 o: &mut CudaSlice<f32>,
16250 head_dim: usize,
16251 n_head: usize,
16252 n_head_kv: usize,
16253 t: usize,
16254 t_kv: usize,
16255 scale: f32,
16256 causal: bool,
16257 ) -> Result<(), Box<dyn std::error::Error>> {
16258 if portable_mma_gated() {
16260 return self.sdpa_naive(
16261 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
16262 );
16263 }
16264 static F32_STAGE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16270 let f32_stage =
16271 *F32_STAGE.get_or_init(|| std::env::var("MEMRA_FA512_STAGE").as_deref() == Ok("f32"));
16272 static SP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16276 let sp = !f32_stage
16277 && *SP_ON.get_or_init(|| {
16278 std::env::var("MEMRA_FA512_SP")
16279 .map(|v| v != "0")
16280 .unwrap_or(true)
16281 });
16282 self.fa_prefill_hd512_arm(
16283 q,
16284 k,
16285 v,
16286 o,
16287 head_dim,
16288 n_head,
16289 n_head_kv,
16290 t,
16291 t_kv,
16292 scale,
16293 causal,
16294 f32_stage,
16295 sp,
16296 sp && fa_f16pv_on(),
16297 )
16298 }
16299
16300 #[allow(clippy::too_many_arguments)]
16302 pub fn fa_prefill_hd512_pre(
16303 &self,
16304 qb: &CudaSlice<u8>,
16305 kb: &CudaSlice<u8>,
16306 vb: &CudaSlice<u8>,
16307 o: &mut CudaSlice<f32>,
16308 head_dim: usize,
16309 n_head: usize,
16310 n_head_kv: usize,
16311 t: usize,
16312 t_kv: usize,
16313 scale: f32,
16314 causal: bool,
16315 v_f16: bool,
16316 ) -> Result<(), Box<dyn std::error::Error>> {
16317 debug_assert_eq!(head_dim, 512);
16318 const SP_M: usize = 16;
16319 const BKS: usize = 32;
16320 let f16pv = fa_f16pv_on();
16324 let nw = if f16pv { fa512_wide_warps() } else { 2 };
16325 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
16326 debug_assert!(!v_f16 || f16pv, "f16 V emitted without the door on");
16327 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
16328 let vref: &CudaSlice<u8> = if f16pv && !v_f16 {
16329 let n = t_kv * n_head_kv * head_dim;
16331 let need = n * 2;
16332 if vguard.as_ref().map(|b| b.len() < need).unwrap_or(true) {
16333 *vguard = Some(self.alloc_uninit::<u8>(need)?);
16334 }
16335 let dst = vguard.as_mut().unwrap();
16336 self.bf16_to_f16_into(vb, n, dst)?;
16337 vguard.as_ref().unwrap()
16338 } else {
16339 vb
16340 };
16341 let f = self.func(if hp {
16342 "fa_prefill_bf16_hd512_sp16h2"
16343 } else {
16344 match (f16pv, nw) {
16345 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
16346 (true, _) => "fa_prefill_bf16_hd512_sp16",
16347 _ => "fa_prefill_bf16_hd512_sp",
16348 }
16349 });
16350 let (nwarp, npart) = if hp {
16351 (4usize, 4usize)
16352 } else if nw > 2 {
16353 (nw, nw)
16354 } else {
16355 (2, 1)
16356 };
16357 let shmem = if hp {
16359 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS) + 4 * (2 * npart * SP_M * BKS + 2 * SP_M))
16360 as u32
16361 } else {
16362 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
16363 + 4 * (npart * SP_M * BKS + SP_M)) as u32
16364 };
16365 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16366 f.set_attribute(
16367 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16368 shmem as i32,
16369 )?;
16370 let grid_y = if hp {
16371 (n_head / 2) as u32
16372 } else {
16373 n_head as u32
16374 };
16375 let cfg = LaunchConfig {
16376 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
16377 block_dim: (32, nwarp as u32, 1),
16378 shared_mem_bytes: shmem,
16379 };
16380 let (hd, nh, nhkv, ti, tkvi, cz) = (
16381 head_dim as i32,
16382 n_head as i32,
16383 n_head_kv as i32,
16384 t as i32,
16385 t_kv as i32,
16386 causal as i32,
16387 );
16388 let __s_b = self.gpu.stream();
16389 let mut b = __s_b.launch_builder(&f);
16390 b.arg(qb)
16391 .arg(kb)
16392 .arg(vref)
16393 .arg(o)
16394 .arg(&hd)
16395 .arg(&nh)
16396 .arg(&nhkv)
16397 .arg(&ti)
16398 .arg(&tkvi)
16399 .arg(&scale)
16400 .arg(&cz);
16401 unsafe {
16402 b.launch(cfg)?;
16403 }
16404 Ok(())
16405 }
16406
16407 #[allow(clippy::too_many_arguments)]
16410 pub fn fa_prefill_hd512_arm(
16411 &self,
16412 q: &CudaSlice<f32>,
16413 k: &CudaSlice<f32>,
16414 v: &CudaSlice<f32>,
16415 o: &mut CudaSlice<f32>,
16416 head_dim: usize,
16417 n_head: usize,
16418 n_head_kv: usize,
16419 t: usize,
16420 t_kv: usize,
16421 scale: f32,
16422 causal: bool,
16423 f32_stage: bool,
16424 sp: bool,
16425 f16pv: bool,
16426 ) -> Result<(), Box<dyn std::error::Error>> {
16427 debug_assert_eq!(head_dim, 512, "fa_prefill_hd512 is hd512 only");
16428 if sp && !f32_stage {
16429 const SP_M: usize = 16;
16433 const BKS: usize = 32;
16434 let nw = if f16pv { fa512_wide_warps() } else { 2 };
16435 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
16436 let f = self.func(if hp {
16437 "fa_prefill_bf16_hd512_sp16h2"
16438 } else {
16439 match (f16pv, nw) {
16440 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
16441 (true, _) => "fa_prefill_bf16_hd512_sp16",
16442 _ => "fa_prefill_bf16_hd512_sp",
16443 }
16444 });
16445 let (nwarp, npart) = if hp {
16446 (4usize, 4usize)
16447 } else if nw > 2 {
16448 (nw, nw)
16449 } else {
16450 (2, 1)
16451 };
16452 let shmem = if hp {
16453 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS)
16454 + 4 * (2 * npart * SP_M * BKS + 2 * SP_M)) as u32
16455 } else {
16456 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
16457 + 4 * (npart * SP_M * BKS + SP_M)) as u32
16458 };
16459 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16460 f.set_attribute(
16461 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16462 shmem as i32,
16463 )?;
16464 let grid_y = if hp {
16465 (n_head / 2) as u32
16466 } else {
16467 n_head as u32
16468 };
16469 let cfg = LaunchConfig {
16470 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
16471 block_dim: (32, nwarp as u32, 1),
16472 shared_mem_bytes: shmem,
16473 };
16474 let (hd, nh, nhkv, ti, tkvi, cz) = (
16475 head_dim as i32,
16476 n_head as i32,
16477 n_head_kv as i32,
16478 t as i32,
16479 t_kv as i32,
16480 causal as i32,
16481 );
16482 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16483 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16484 let vb = if f16pv {
16485 self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?
16486 } else {
16487 self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?
16488 };
16489 let __s_b = self.gpu.stream();
16490 let mut b = __s_b.launch_builder(&f);
16491 b.arg(&qb)
16492 .arg(&kb)
16493 .arg(&vb)
16494 .arg(o)
16495 .arg(&hd)
16496 .arg(&nh)
16497 .arg(&nhkv)
16498 .arg(&ti)
16499 .arg(&tkvi)
16500 .arg(&scale)
16501 .arg(&cz);
16502 unsafe {
16503 b.launch(cfg)?;
16504 }
16505 return Ok(());
16506 }
16507 const BLOCK_Q: usize = 32;
16508 const BK: usize = 32;
16509 const HALF: usize = 256;
16510 let f = self.func(if f32_stage {
16511 "fa_prefill_f32_hd512"
16512 } else {
16513 "fa_prefill_bf16_hd512"
16514 });
16515 let shmem = (2 * (BLOCK_Q * head_dim + BK * head_dim + BK * HALF + BLOCK_Q * BK)
16517 + 4 * BLOCK_Q) as u32;
16518 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16519 f.set_attribute(
16520 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16521 shmem as i32,
16522 )?;
16523 let cfg = LaunchConfig {
16524 grid_dim: (
16525 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16526 n_head as u32,
16527 2,
16528 ),
16529 block_dim: (32, 2, 1),
16530 shared_mem_bytes: shmem,
16531 };
16532 let (hd, nh, nhkv, ti, tkvi, cz) = (
16533 head_dim as i32,
16534 n_head as i32,
16535 n_head_kv as i32,
16536 t as i32,
16537 t_kv as i32,
16538 causal as i32,
16539 );
16540 if f32_stage {
16541 let __s_b = self.gpu.stream();
16542 let mut b = __s_b.launch_builder(&f);
16543 b.arg(q)
16544 .arg(k)
16545 .arg(v)
16546 .arg(o)
16547 .arg(&hd)
16548 .arg(&nh)
16549 .arg(&nhkv)
16550 .arg(&ti)
16551 .arg(&tkvi)
16552 .arg(&scale)
16553 .arg(&cz);
16554 unsafe {
16555 b.launch(cfg)?;
16556 }
16557 } else {
16558 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
16559 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
16560 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
16561 let __s_b = self.gpu.stream();
16562 let mut b = __s_b.launch_builder(&f);
16563 b.arg(&qb)
16564 .arg(&kb)
16565 .arg(&vb)
16566 .arg(o)
16567 .arg(&hd)
16568 .arg(&nh)
16569 .arg(&nhkv)
16570 .arg(&ti)
16571 .arg(&tkvi)
16572 .arg(&scale)
16573 .arg(&cz);
16574 unsafe {
16575 b.launch(cfg)?;
16576 }
16577 }
16578 Ok(())
16579 }
16580
16581 #[allow(clippy::too_many_arguments)]
16585 pub fn rope_neox2_bf16e(
16586 &self,
16587 q: &mut CudaSlice<f32>,
16588 k: &mut CudaSlice<f32>,
16589 qb: &mut CudaSlice<u8>,
16590 kb: &mut CudaSlice<u8>,
16591 pos: &CudaSlice<i32>,
16592 head_dim: usize,
16593 n_dims: usize,
16594 nh_q: usize,
16595 nh_k: usize,
16596 n_tokens: usize,
16597 base: f32,
16598 freq_scale: f32,
16599 ff: Option<&CudaSlice<f32>>,
16600 ) -> Result<(), Box<dyn std::error::Error>> {
16601 let f = self.func("rope_neox2_bf16e_f32");
16602 let rows = ((nh_q + nh_k) * n_tokens) as u32;
16603 let cfg = LaunchConfig {
16604 grid_dim: (rows, 1, 1),
16605 block_dim: ((head_dim / 2) as u32, 1, 1),
16606 shared_mem_bytes: 0,
16607 };
16608 let theta_scale = base.powf(-2.0 / n_dims as f32);
16609 let (hd, nd, nhq, nhk, nt) = (
16610 head_dim as i32,
16611 n_dims as i32,
16612 nh_q as i32,
16613 nh_k as i32,
16614 n_tokens as i32,
16615 );
16616 let __s_b = self.gpu.stream();
16617 let mut b = __s_b.launch_builder(&f);
16618 match ff {
16619 Some(t) => {
16620 b.arg(&mut *q)
16621 .arg(&mut *k)
16622 .arg(&mut *qb)
16623 .arg(&mut *kb)
16624 .arg(pos)
16625 .arg(&hd)
16626 .arg(&nd)
16627 .arg(&nhq)
16628 .arg(&nhk)
16629 .arg(&nt)
16630 .arg(&theta_scale)
16631 .arg(&freq_scale)
16632 .arg(t);
16633 unsafe {
16634 b.launch(cfg)?;
16635 }
16636 }
16637 None => {
16638 let null: u64 = 0;
16639 b.arg(&mut *q)
16640 .arg(&mut *k)
16641 .arg(&mut *qb)
16642 .arg(&mut *kb)
16643 .arg(pos)
16644 .arg(&hd)
16645 .arg(&nd)
16646 .arg(&nhq)
16647 .arg(&nhk)
16648 .arg(&nt)
16649 .arg(&theta_scale)
16650 .arg(&freq_scale)
16651 .arg(&null);
16652 unsafe {
16653 b.launch(cfg)?;
16654 }
16655 }
16656 }
16657 Ok(())
16658 }
16659
16660 pub fn f32_to_bf16(
16663 &self,
16664 x: &CudaSlice<f32>,
16665 n: usize,
16666 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
16667 assert!(n % 4 == 0, "f32_to_bf16 requires n % 4 == 0, got {n}");
16668 let mut y = self.alloc_uninit::<u8>(n * 2)?;
16669 let f = self.func("f32_to_bf16_flat");
16670 let n_i = n as i64;
16671 let cfg = LaunchConfig {
16672 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
16673 block_dim: (256, 1, 1),
16674 shared_mem_bytes: 0,
16675 };
16676 let __s_b = self.gpu.stream();
16677 let mut b = __s_b.launch_builder(&f);
16678 b.arg(x).arg(&mut y).arg(&n_i);
16679 unsafe {
16680 b.launch(cfg)?;
16681 }
16682 Ok(y)
16683 }
16684
16685 pub fn f32_to_f16(
16686 &self,
16687 x: &CudaSlice<f32>,
16688 n: usize,
16689 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
16690 assert!(n % 4 == 0, "f32_to_f16 requires n % 4 == 0, got {n}");
16691 let mut y = self.alloc_uninit::<u8>(n * 2)?;
16692 let f = self.func("f32_to_f16_flat");
16693 let n_i = n as i64;
16694 let cfg = LaunchConfig {
16695 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
16696 block_dim: (256, 1, 1),
16697 shared_mem_bytes: 0,
16698 };
16699 let __s_b = self.gpu.stream();
16700 let mut b = __s_b.launch_builder(&f);
16701 b.arg(x).arg(&mut y).arg(&n_i);
16702 unsafe {
16703 b.launch(cfg)?;
16704 }
16705 Ok(y)
16706 }
16707
16708 pub fn bf16_to_f16(
16710 &self,
16711 xb: &CudaSlice<u8>,
16712 n: usize,
16713 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
16714 let mut y = self.alloc_uninit::<u8>(n * 2)?;
16715 self.bf16_to_f16_into(xb, n, &mut y)?;
16716 Ok(y)
16717 }
16718
16719 pub fn bf16_to_f16_into(
16721 &self,
16722 xb: &CudaSlice<u8>,
16723 n: usize,
16724 y: &mut CudaSlice<u8>,
16725 ) -> Result<(), Box<dyn std::error::Error>> {
16726 assert!(n % 2 == 0, "bf16_to_f16 requires n % 2 == 0, got {n}");
16727 assert!(y.len() >= n * 2);
16728 let f = self.func("bf16_to_f16_flat");
16729 let n2 = (n / 2) as i64;
16730 let cfg = LaunchConfig {
16731 grid_dim: (((n / 2) as u32).div_ceil(256), 1, 1),
16732 block_dim: (256, 1, 1),
16733 shared_mem_bytes: 0,
16734 };
16735 let __s_b = self.gpu.stream();
16736 let mut b = __s_b.launch_builder(&f);
16737 b.arg(xb).arg(y).arg(&n2);
16738 unsafe {
16739 b.launch(cfg)?;
16740 }
16741 Ok(())
16742 }
16743
16744 #[allow(clippy::too_many_arguments)]
16749 pub fn fa_prefill_vl8(
16750 &self,
16751 seqs: &[FaSeqVl],
16752 head_dim: usize,
16753 n_head: usize,
16754 n_head_kv: usize,
16755 scale: f32,
16756 ) -> Result<(), Box<dyn std::error::Error>> {
16757 const BK: usize = 32;
16758 let b = seqs.len();
16759 assert!(b >= 1 && b <= 8);
16760 let mut packed = [FaSeqVl::default(); 8];
16761 packed[..b].copy_from_slice(seqs);
16762 let v = FaVl8(packed);
16763 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
16764 let ept = (n_head_kv * head_dim) as i32;
16765 {
16766 let f = self.func("fa_mirror_vl");
16767 let max_n = (max_t as i64) * ept as i64;
16768 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
16769 for which in 0..2i32 {
16770 let cfg = LaunchConfig {
16771 grid_dim: (blocks, 1, b as u32),
16772 block_dim: (256, 1, 1),
16773 shared_mem_bytes: 0,
16774 };
16775 let __s_lb = self.gpu.stream();
16776 let mut lb = __s_lb.launch_builder(&f);
16777 lb.arg(&v).arg(&ept).arg(&which);
16778 unsafe {
16779 lb.launch(cfg)?;
16780 }
16781 }
16782 }
16783 let hd_sfx = fa_hd_suffix(head_dim)?;
16784 let f = self.func(&format!("fa_prefill_bf16kv_vl{hd_sfx}"));
16785 let block_q = 64usize;
16786 let kv_stages = 2usize;
16787 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
16788 + 4 * (block_q * BK + 2 * block_q)) as u32;
16789 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16790 f.set_attribute(
16791 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16792 shmem as i32,
16793 )?;
16794 let cfg = LaunchConfig {
16795 grid_dim: (max_t.div_ceil(block_q as u32), n_head as u32, b as u32),
16796 block_dim: (32, 4, 1),
16797 shared_mem_bytes: shmem,
16798 };
16799 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
16800 let __s_lb = self.gpu.stream();
16801 let mut lb = __s_lb.launch_builder(&f);
16802 lb.arg(&v).arg(&hd).arg(&nh).arg(&nhkv).arg(&scale);
16803 unsafe {
16804 lb.launch(cfg)?;
16805 }
16806 Ok(())
16807 }
16808
16809 #[allow(clippy::too_many_arguments)]
16813 pub fn attn_pre_vl8(
16814 &self,
16815 seqs: &[AttnPreVl],
16816 wq: &CudaSlice<f32>,
16817 wk: &CudaSlice<f32>,
16818 head_dim: usize,
16819 rope_dims: usize,
16820 n_head: usize,
16821 n_head_kv: usize,
16822 eps: f32,
16823 freq_base: f32,
16824 freq_scale: f32,
16825 kv_dim_k: usize,
16826 kv_dim_v: usize,
16827 k_tok_bytes: usize,
16828 v_tok_bytes: usize,
16829 ) -> Result<(), Box<dyn std::error::Error>> {
16830 let b = seqs.len();
16831 assert!(b >= 1 && b <= 8);
16832 let mut packed = [AttnPreVl::default(); 8];
16833 packed[..b].copy_from_slice(seqs);
16834 let v = AttnPreVl8(packed);
16835 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
16836 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
16837 {
16838 let f = self.func("q_gate_split_vl");
16839 let n = max_t * (n_head * head_dim) as u32;
16840 let cfg = LaunchConfig {
16841 grid_dim: (n.div_ceil(256), 1, b as u32),
16842 block_dim: (256, 1, 1),
16843 shared_mem_bytes: 0,
16844 };
16845 let __s_lb = self.gpu.stream();
16846 let mut lb = __s_lb.launch_builder(&f);
16847 lb.arg(&v).arg(&hd).arg(&nh);
16848 unsafe {
16849 lb.launch(cfg)?;
16850 }
16851 }
16852 {
16853 let f = self.func("attn_rms_vl");
16854 let cfg = LaunchConfig {
16855 grid_dim: (max_t * n_head as u32, 2, b as u32),
16856 block_dim: (rms_block(), 1, 1),
16857 shared_mem_bytes: 0,
16858 };
16859 let __s_lb = self.gpu.stream();
16860 let mut lb = __s_lb.launch_builder(&f);
16861 lb.arg(&v)
16862 .arg(wq)
16863 .arg(wk)
16864 .arg(&hd)
16865 .arg(&nh)
16866 .arg(&nhkv)
16867 .arg(&eps);
16868 unsafe {
16869 lb.launch(cfg)?;
16870 }
16871 }
16872 {
16873 let f = self.func("attn_rope_vl");
16874 let theta_scale = freq_base.powf(-2.0 / rope_dims as f32);
16875 let nd = rope_dims as i32;
16876 let cfg = LaunchConfig {
16877 grid_dim: (max_t * n_head as u32, 2, b as u32),
16878 block_dim: ((head_dim / 2) as u32, 1, 1),
16879 shared_mem_bytes: 0,
16880 };
16881 let __s_lb = self.gpu.stream();
16882 let mut lb = __s_lb.launch_builder(&f);
16883 lb.arg(&v)
16884 .arg(&hd)
16885 .arg(&nd)
16886 .arg(&nh)
16887 .arg(&nhkv)
16888 .arg(&theta_scale)
16889 .arg(&freq_scale);
16890 unsafe {
16891 lb.launch(cfg)?;
16892 }
16893 }
16894 {
16895 let f = self.func("append_kv_vl");
16896 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
16897 let cfg = LaunchConfig {
16898 grid_dim: (nblk, max_t, b as u32),
16899 block_dim: (32, 1, 1),
16900 shared_mem_bytes: 0,
16901 };
16902 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
16903 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16904 let __s_lb = self.gpu.stream();
16905 let mut lb = __s_lb.launch_builder(&f);
16906 lb.arg(&v).arg(&kdk).arg(&kdv).arg(&ktb).arg(&vtb);
16907 unsafe {
16908 lb.launch(cfg)?;
16909 }
16910 }
16911 Ok(())
16912 }
16913
16914 pub fn fa_prefill_view(
16919 &self,
16920 q: &CudaSlice<f32>,
16921 k: &cudarc::driver::CudaView<u8>,
16922 v: &cudarc::driver::CudaView<u8>,
16923 o: &mut CudaSlice<f32>,
16924 head_dim: usize,
16925 n_head: usize,
16926 n_head_kv: usize,
16927 t: usize,
16928 t_kv: usize,
16929 scale: f32,
16930 causal: bool,
16931 k_tok_bytes: usize,
16932 v_tok_bytes: usize,
16933 g: bool,
16934 ) -> Result<(), Box<dyn std::error::Error>> {
16935 if portable_mma_gated() {
16936 return self.sdpa_naive_quantized_view(
16937 q,
16938 k,
16939 v,
16940 o,
16941 head_dim,
16942 n_head,
16943 n_head_kv,
16944 t,
16945 t_kv,
16946 scale,
16947 causal,
16948 k_tok_bytes,
16949 v_tok_bytes,
16950 );
16951 }
16952 const BLOCK_Q: usize = 64;
16953 const BK: usize = 32;
16954 let name = format!("fa_prefill_q{}", fa_hd_suffix(head_dim)?);
16957 let f = if g {
16958 self.func_g(&name)
16959 } else {
16960 self.func(&name)
16961 };
16962 let shmem =
16963 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
16964 use cudarc::driver::sys::CUfunction_attribute_enum as A;
16965 f.set_attribute(
16966 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
16967 shmem as i32,
16968 )?;
16969 let cfg = LaunchConfig {
16970 grid_dim: (
16971 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
16972 n_head as u32,
16973 1,
16974 ),
16975 block_dim: (32, 4, 1),
16976 shared_mem_bytes: shmem,
16977 };
16978 let (hd, nh, nhkv, ti, tkvi, cz) = (
16979 head_dim as i32,
16980 n_head as i32,
16981 n_head_kv as i32,
16982 t as i32,
16983 t_kv as i32,
16984 causal as i32,
16985 );
16986 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
16987 let __s_b = self.gpu.stream();
16988 let mut b = __s_b.launch_builder(&f);
16989 b.arg(q)
16990 .arg(k)
16991 .arg(v)
16992 .arg(o)
16993 .arg(&hd)
16994 .arg(&nh)
16995 .arg(&nhkv)
16996 .arg(&ti)
16997 .arg(&tkvi)
16998 .arg(&scale)
16999 .arg(&cz)
17000 .arg(&ktb)
17001 .arg(&vtb);
17002 unsafe {
17003 b.launch(cfg)?;
17004 }
17005 Ok(())
17006 }
17007
17008 #[allow(clippy::too_many_arguments)]
17018 pub fn fa_prefill_view_ws(
17019 &self,
17020 q: &CudaSlice<f32>,
17021 k: &cudarc::driver::CudaView<u8>,
17022 v: &cudarc::driver::CudaView<u8>,
17023 o: &mut CudaSlice<f32>,
17024 head_dim: usize,
17025 n_head: usize,
17026 n_head_kv: usize,
17027 t: usize,
17028 t_kv: usize,
17029 scale: f32,
17030 causal: bool,
17031 k_tok_bytes: usize,
17032 v_tok_bytes: usize,
17033 g: bool,
17034 ) -> Result<(), Box<dyn std::error::Error>> {
17035 if portable_mma_gated() {
17036 return self.sdpa_naive_quantized_view(
17037 q,
17038 k,
17039 v,
17040 o,
17041 head_dim,
17042 n_head,
17043 n_head_kv,
17044 t,
17045 t_kv,
17046 scale,
17047 causal,
17048 k_tok_bytes,
17049 v_tok_bytes,
17050 );
17051 }
17052 const BLOCK_Q: usize = 64;
17053 const BK: usize = 32;
17054 let kv_dim_k = n_head_kv * head_dim;
17055 let kv_dim_v = n_head_kv * head_dim;
17056 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
17058 let mut guard = self.prime_deqw_ws.lock().unwrap();
17060 let need_grow = match guard.as_ref() {
17061 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
17062 None => true,
17063 };
17064 if need_grow {
17065 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
17066 let (ck, cv) = guard
17067 .as_ref()
17068 .map(|(a, b)| (a.len(), b.len()))
17069 .unwrap_or((0, 0));
17070 *guard = Some((
17071 self.alloc_u8(grow(ck, k_ws_bytes))?,
17072 self.alloc_u8(grow(cv, v_ws_bytes))?,
17073 ));
17074 }
17075 let (kw, vw) = guard.as_mut().unwrap();
17076 {
17078 let f = if g {
17080 self.func_g("fa_dequant_kv_ws_bf16")
17081 } else {
17082 self.func("fa_dequant_kv_ws_bf16")
17083 };
17084 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
17085 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
17086 let cfg = LaunchConfig {
17087 grid_dim: (nblk.max(1), 1, 1),
17088 block_dim: (256, 1, 1),
17089 shared_mem_bytes: 0,
17090 };
17091 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
17092 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17093 let __s_b = self.gpu.stream();
17094 let mut b = __s_b.launch_builder(&f);
17095 b.arg(k)
17096 .arg(v)
17097 .arg(&mut *kw)
17098 .arg(&mut *vw)
17099 .arg(&kdk)
17100 .arg(&kdv)
17101 .arg(&tkvi)
17102 .arg(&ktb)
17103 .arg(&vtb);
17104 unsafe {
17105 b.launch(cfg)?;
17106 }
17107 }
17108 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
17116 .map(|v| v != "0")
17117 .unwrap_or(true);
17118 {
17119 let hd_sfx = fa_hd_suffix(head_dim)?;
17120 let f = self.func(&format!(
17121 "fa_prefill_qw{}{hd_sfx}",
17122 if db { "_db" } else { "" }
17123 ));
17124 let shmem = if db {
17125 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
17127 } else {
17128 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
17129 };
17130 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17131 f.set_attribute(
17132 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17133 shmem as i32,
17134 )?;
17135 let cfg = LaunchConfig {
17136 grid_dim: (
17137 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
17138 n_head as u32,
17139 1,
17140 ),
17141 block_dim: (32, 4, 1),
17142 shared_mem_bytes: shmem,
17143 };
17144 let (hd, nh, nhkv, ti, tkvi, cz) = (
17145 head_dim as i32,
17146 n_head as i32,
17147 n_head_kv as i32,
17148 t as i32,
17149 t_kv as i32,
17150 causal as i32,
17151 );
17152 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
17153 let __s_b = self.gpu.stream();
17154 let mut b = __s_b.launch_builder(&f);
17155 b.arg(q)
17156 .arg(&*kw)
17157 .arg(&*vw)
17158 .arg(o)
17159 .arg(&hd)
17160 .arg(&nh)
17161 .arg(&nhkv)
17162 .arg(&ti)
17163 .arg(&tkvi)
17164 .arg(&scale)
17165 .arg(&cz)
17166 .arg(&kdk)
17167 .arg(&kdv);
17168 unsafe {
17169 b.launch(cfg)?;
17170 }
17171 }
17172 Ok(())
17173 }
17174
17175 #[allow(clippy::too_many_arguments)]
17191 pub fn fa_prefill_view_ws_w_hd128(
17192 &self,
17193 q: &CudaSlice<f32>,
17194 k: &cudarc::driver::CudaView<u8>,
17195 v: &cudarc::driver::CudaView<u8>,
17196 o: &mut CudaSlice<f32>,
17197 head_dim: usize,
17198 n_head: usize,
17199 n_head_kv: usize,
17200 t: usize,
17201 t_kv: usize,
17202 scale: f32,
17203 causal: bool,
17204 window: usize,
17205 k_tok_bytes: usize,
17206 v_tok_bytes: usize,
17207 ) -> Result<(), Box<dyn std::error::Error>> {
17208 assert_eq!(
17209 head_dim, 128,
17210 "fa_prefill_view_ws_w_hd128: only the hd128 twin is stamped"
17211 );
17212 if portable_mma_gated() {
17213 return self.sdpa_naive_w_quantized_view(
17214 q,
17215 k,
17216 v,
17217 o,
17218 head_dim,
17219 n_head,
17220 n_head_kv,
17221 t,
17222 t_kv,
17223 scale,
17224 causal,
17225 window,
17226 k_tok_bytes,
17227 v_tok_bytes,
17228 );
17229 }
17230 const BLOCK_Q: usize = 64;
17231 const BK: usize = 32;
17232 let kv_dim_k = n_head_kv * head_dim;
17233 let kv_dim_v = n_head_kv * head_dim;
17234 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
17236 let mut guard = self.prime_deqw_ws.lock().unwrap();
17237 let need_grow = match guard.as_ref() {
17238 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
17239 None => true,
17240 };
17241 if need_grow {
17242 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
17243 let (ck, cv) = guard
17244 .as_ref()
17245 .map(|(a, b)| (a.len(), b.len()))
17246 .unwrap_or((0, 0));
17247 *guard = Some((
17248 self.alloc_u8(grow(ck, k_ws_bytes))?,
17249 self.alloc_u8(grow(cv, v_ws_bytes))?,
17250 ));
17251 }
17252 let (kw, vw) = guard.as_mut().unwrap();
17253 {
17256 let f = self.func("fa_dequant_kv_ws_bf16");
17257 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
17258 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
17259 let cfg = LaunchConfig {
17260 grid_dim: (nblk.max(1), 1, 1),
17261 block_dim: (256, 1, 1),
17262 shared_mem_bytes: 0,
17263 };
17264 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
17265 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17266 let __s_b = self.gpu.stream();
17267 let mut b = __s_b.launch_builder(&f);
17268 b.arg(k)
17269 .arg(v)
17270 .arg(&mut *kw)
17271 .arg(&mut *vw)
17272 .arg(&kdk)
17273 .arg(&kdv)
17274 .arg(&tkvi)
17275 .arg(&ktb)
17276 .arg(&vtb);
17277 unsafe {
17278 b.launch(cfg)?;
17279 }
17280 }
17281 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
17283 .map(|v| v != "0")
17284 .unwrap_or(true);
17285 {
17286 let f = self.func(if db {
17287 "fa_prefill_qw_db_w_hd128"
17288 } else {
17289 "fa_prefill_qw_w_hd128"
17290 });
17291 let shmem = if db {
17292 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
17293 } else {
17294 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
17295 };
17296 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17297 f.set_attribute(
17298 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17299 shmem as i32,
17300 )?;
17301 let cfg = LaunchConfig {
17302 grid_dim: (
17303 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
17304 n_head as u32,
17305 1,
17306 ),
17307 block_dim: (32, 4, 1),
17308 shared_mem_bytes: shmem,
17309 };
17310 let (hd, nh, nhkv, ti, tkvi, cz) = (
17311 head_dim as i32,
17312 n_head as i32,
17313 n_head_kv as i32,
17314 t as i32,
17315 t_kv as i32,
17316 causal as i32,
17317 );
17318 let (kdk, kdv, wnd) = (kv_dim_k as i32, kv_dim_v as i32, window as i32);
17319 let __s_b = self.gpu.stream();
17320 let mut b = __s_b.launch_builder(&f);
17321 b.arg(q)
17322 .arg(&*kw)
17323 .arg(&*vw)
17324 .arg(o)
17325 .arg(&hd)
17326 .arg(&nh)
17327 .arg(&nhkv)
17328 .arg(&ti)
17329 .arg(&tkvi)
17330 .arg(&scale)
17331 .arg(&cz)
17332 .arg(&kdk)
17333 .arg(&kdv)
17334 .arg(&wnd);
17335 unsafe {
17336 b.launch(cfg)?;
17337 }
17338 }
17339 Ok(())
17340 }
17341
17342 pub fn fa_decode(
17346 &self,
17347 q: &CudaSlice<f32>,
17348 k: &cudarc::driver::CudaView<u8>,
17349 v: &cudarc::driver::CudaView<u8>,
17350 o: &mut CudaSlice<f32>,
17351 head_dim: usize,
17352 n_head: usize,
17353 n_head_kv: usize,
17354 t_kv: usize,
17355 scale: f32,
17356 k_tok_bytes: usize,
17357 v_tok_bytes: usize,
17358 ) -> Result<(), Box<dyn std::error::Error>> {
17359 self.fa_decode_kvmod(
17360 q,
17361 k,
17362 v,
17363 o,
17364 head_dim,
17365 n_head,
17366 n_head_kv,
17367 t_kv,
17368 scale,
17369 k_tok_bytes,
17370 v_tok_bytes,
17371 false,
17372 )
17373 }
17374
17375 #[allow(clippy::too_many_arguments)]
17379 #[allow(clippy::too_many_arguments)]
17383 #[allow(clippy::too_many_arguments)]
17384 fn fa_decode_scalar_unified(
17385 &self,
17386 q: &cudarc::driver::CudaView<f32>,
17387 k: &cudarc::driver::CudaView<u8>,
17388 v: &cudarc::driver::CudaView<u8>,
17389 o: &mut cudarc::driver::CudaViewMut<f32>,
17390 head_dim: usize,
17391 n_head: usize,
17392 n_head_kv: usize,
17393 t_kv_host: usize,
17394 t_kv_dev: Option<&CudaSlice<i32>>,
17395 scale: f32,
17396 n_splits: usize,
17397 split_keys: usize,
17398 k_tok_bytes: usize,
17399 v_tok_bytes: usize,
17400 g: bool,
17401 part_o: &mut CudaSlice<f32>,
17402 part_m: &mut CudaSlice<f32>,
17403 part_l: &mut CudaSlice<f32>,
17404 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
17405 ) -> Result<(), Box<dyn std::error::Error>> {
17406 let f = if g {
17407 self.func_g("fa_decode_f32")
17408 } else {
17409 self.fa_func("fa_decode_f32", head_dim)
17410 };
17411 let cfg = LaunchConfig {
17412 grid_dim: (n_head as u32, n_splits as u32, 1),
17413 block_dim: (head_dim as u32, 1, 1),
17414 shared_mem_bytes: (4 * (head_dim + 32)) as u32,
17415 };
17416 let (hd, nh, nhkv, nsp) = (
17417 head_dim as i32,
17418 n_head as i32,
17419 n_head_kv as i32,
17420 n_splits as i32,
17421 );
17422 let (ktb, vtb, tkvi, ski) = (
17423 k_tok_bytes as i64,
17424 v_tok_bytes as i64,
17425 t_kv_host as i32,
17426 split_keys as i32,
17427 );
17428 let __s_b = self.gpu.stream();
17429 let mut b = __s_b.launch_builder(&f);
17430 match t_kv_dev {
17431 Some(d) => {
17432 b.arg(q)
17433 .arg(k)
17434 .arg(v)
17435 .arg(&mut *part_o)
17436 .arg(&mut *part_m)
17437 .arg(&mut *part_l)
17438 .arg(&hd)
17439 .arg(&nh)
17440 .arg(&nhkv)
17441 .arg(&tkvi)
17442 .arg(d)
17443 .arg(&scale)
17444 .arg(&nsp)
17445 .arg(&ski)
17446 .arg(&ktb)
17447 .arg(&vtb);
17448 unsafe {
17449 b.launch(cfg)?;
17450 }
17451 }
17452 None => {
17453 let null: u64 = 0;
17454 b.arg(q)
17455 .arg(k)
17456 .arg(v)
17457 .arg(&mut *part_o)
17458 .arg(&mut *part_m)
17459 .arg(&mut *part_l)
17460 .arg(&hd)
17461 .arg(&nh)
17462 .arg(&nhkv)
17463 .arg(&tkvi)
17464 .arg(&null)
17465 .arg(&scale)
17466 .arg(&nsp)
17467 .arg(&ski)
17468 .arg(&ktb)
17469 .arg(&vtb);
17470 unsafe {
17471 b.launch(cfg)?;
17472 }
17473 }
17474 }
17475 let cfg2 = LaunchConfig {
17476 grid_dim: (n_head as u32, 1, 1),
17477 block_dim: (head_dim as u32, 1, 1),
17478 shared_mem_bytes: 0,
17479 };
17480 if let Some((oq, od)) = q8_out {
17481 let fc = if g {
17483 self.func_g("fa_decode_combine_q8_1")
17484 } else {
17485 self.fa_func("fa_decode_combine_q8_1", head_dim)
17486 };
17487 let __s_b2 = self.gpu.stream();
17488 let mut b2 = __s_b2.launch_builder(&fc);
17489 b2.arg(&*part_o)
17490 .arg(&*part_m)
17491 .arg(&*part_l)
17492 .arg(oq)
17493 .arg(od)
17494 .arg(&hd)
17495 .arg(&nh)
17496 .arg(&nsp);
17497 unsafe {
17498 b2.launch(cfg2)?;
17499 }
17500 return Ok(());
17501 }
17502 let fc = if g {
17503 self.func_g("fa_decode_combine_f32")
17504 } else {
17505 self.fa_func("fa_decode_combine_f32", head_dim)
17506 };
17507 let __s_b2 = self.gpu.stream();
17508 let mut b2 = __s_b2.launch_builder(&fc);
17509 b2.arg(&*part_o)
17510 .arg(&*part_m)
17511 .arg(&*part_l)
17512 .arg(o)
17513 .arg(&hd)
17514 .arg(&nh)
17515 .arg(&nsp);
17516 unsafe {
17517 b2.launch(cfg2)?;
17518 }
17519 Ok(())
17520 }
17521
17522 pub fn fa_decode_kvmod(
17523 &self,
17524 q: &CudaSlice<f32>,
17525 k: &cudarc::driver::CudaView<u8>,
17526 v: &cudarc::driver::CudaView<u8>,
17527 o: &mut CudaSlice<f32>,
17528 head_dim: usize,
17529 n_head: usize,
17530 n_head_kv: usize,
17531 t_kv: usize,
17532 scale: f32,
17533 k_tok_bytes: usize,
17534 v_tok_bytes: usize,
17535 g: bool,
17536 ) -> Result<(), Box<dyn std::error::Error>> {
17537 let q_view = q.as_view();
17538 let mut o_view = o.as_view_mut();
17539 self.fa_decode_kvmod_view(
17540 &q_view,
17541 k,
17542 v,
17543 &mut o_view,
17544 head_dim,
17545 n_head,
17546 n_head_kv,
17547 t_kv,
17548 scale,
17549 k_tok_bytes,
17550 v_tok_bytes,
17551 g,
17552 )
17553 }
17554
17555 #[allow(clippy::too_many_arguments)]
17560 pub fn fa_decode_kvmod_view(
17561 &self,
17562 q: &cudarc::driver::CudaView<f32>,
17563 k: &cudarc::driver::CudaView<u8>,
17564 v: &cudarc::driver::CudaView<u8>,
17565 o: &mut cudarc::driver::CudaViewMut<f32>,
17566 head_dim: usize,
17567 n_head: usize,
17568 n_head_kv: usize,
17569 t_kv: usize,
17570 scale: f32,
17571 k_tok_bytes: usize,
17572 v_tok_bytes: usize,
17573 g: bool,
17574 ) -> Result<(), Box<dyn std::error::Error>> {
17575 let mut fa_vec = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
17596 if g && head_dim == 256 && !fa_v4_at(t_kv) {
17600 fa_vec = false;
17601 }
17602 let sp = fa_split_keys(t_kv, n_head_kv);
17603 let n_splits = if fa_vec {
17604 ((t_kv + sp - 1) / sp).max(1)
17605 } else {
17606 ((t_kv + 255) / 256).max(1)
17607 };
17608 let o_len = n_head * n_splits * head_dim;
17609 let ml_len = n_head * n_splits;
17610 let mut part_guard = self.fa_part_pool.lock().unwrap();
17611 if part_guard
17612 .as_ref()
17613 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17614 .unwrap_or(true)
17615 {
17616 let old = part_guard.take();
17627 let (co, cm) = old
17628 .as_ref()
17629 .map(|pp| (pp.0.len(), pp.1.len()))
17630 .unwrap_or((0, 0));
17631 if let Some(old) = old {
17632 self.fa_part_retired.lock().unwrap().push(old);
17633 }
17634 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17635 eprintln!(
17636 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17637 co, o_len, cm, ml_len
17638 );
17639 }
17640 *part_guard = Some((
17641 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17642 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17643 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17644 ));
17645 }
17646 let pg = part_guard.as_mut().unwrap();
17647 self.gpu
17648 .stream()
17649 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17650 self.gpu
17651 .stream()
17652 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17653 self.gpu
17654 .stream()
17655 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17656 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17657 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
17658 let (hd, nh, nhkv, tkvi, nsp) = (
17659 head_dim as i32,
17660 n_head as i32,
17661 n_head_kv as i32,
17662 t_kv as i32,
17663 n_splits as i32,
17664 );
17665 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17666 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
17670 let fa512_min = fa512_min_tkv();
17675 let deep = fa_vec
17678 && head_dim == 256
17679 && fa_v4_at(t_kv)
17680 && !g
17681 && fa_deep_at(t_kv)
17682 && !matches!(fa_v4_mode(), "noB3" | "stage");
17683 let (f, cfg) = if fa_vec && head_dim == 512 && t_kv >= fa512_min {
17684 let gqa = (n_head / n_head_kv).max(1) as u32;
17687 let fv = self.fa_func("fa_decode_vec_q_dpl16", head_dim);
17688 (
17689 fv,
17690 LaunchConfig {
17691 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17692 block_dim: (32, gqa, 1),
17693 shared_mem_bytes: 0,
17694 },
17695 )
17696 } else if fa_vec && head_dim <= 256 {
17697 let gqa = (n_head / n_head_kv).max(1) as u32;
17698 static SMEM_TKV: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
17709 let smem_tkv = *SMEM_TKV.get_or_init(|| {
17710 std::env::var("MEMRA_FA_SMEM_TKV")
17711 .ok()
17712 .and_then(|v| v.parse().ok())
17713 .unwrap_or_else(|| {
17714 FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
17715 })
17716 });
17717 if fa_v4_at(t_kv) && head_dim == 256 {
17718 let v4name = match fa_v4_mode() {
17722 "noB3" => "fa_decode_vec_q_v4_noB3", "stage" => "fa_decode_vec_q_v4_stage", _ if deep => "fa_decode_vec_q_v4_deep",
17725 _ => "fa_decode_vec_q_v4",
17726 };
17727 let fv = if g {
17728 self.func_g(v4name)
17729 } else {
17730 self.func(v4name)
17731 };
17732 let shmem = (if deep { 12160 } else { 11520 }
17735 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
17736 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17737 fv.set_attribute(
17738 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17739 shmem as i32,
17740 )?;
17741 (
17742 fv,
17743 LaunchConfig {
17744 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17745 block_dim: (32, gqa, 1),
17746 shared_mem_bytes: shmem,
17747 },
17748 )
17749 } else if fa_v3_active(head_dim) {
17750 let fv = if g {
17753 self.func_g("fa_decode_vec_q_v3")
17754 } else {
17755 self.func("fa_decode_vec_q_v3")
17756 };
17757 let shmem = (32 * head_dim * 2) as u32; (
17759 fv,
17760 LaunchConfig {
17761 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17762 block_dim: (32, gqa, 1),
17763 shared_mem_bytes: shmem,
17764 },
17765 )
17766 } else if fa_v2_on() {
17767 let fv = if g {
17771 self.func_g("fa_decode_vec_q_v2")
17772 } else {
17773 self.func("fa_decode_vec_q_v2")
17774 };
17775 let shmem = (2 * 32 * head_dim * 2) as u32; (
17777 fv,
17778 LaunchConfig {
17779 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17780 block_dim: (32, gqa, 1),
17781 shared_mem_bytes: shmem,
17782 },
17783 )
17784 } else if smem_tkv > 0 && t_kv >= smem_tkv && !g && !(head_dim == 512 && Self::gkv_on())
17785 {
17786 let fv = if g {
17790 self.func_g("fa_decode_vec_q_smem")
17791 } else {
17792 self.func("fa_decode_vec_q_smem")
17793 };
17794 let shmem = (2 * 32 * head_dim * 2) as u32; use cudarc::driver::sys::CUfunction_attribute_enum as A;
17796 fv.set_attribute(
17797 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17798 shmem as i32,
17799 )?;
17800 (
17801 fv,
17802 LaunchConfig {
17803 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17804 block_dim: (32, gqa, 1),
17805 shared_mem_bytes: shmem,
17806 },
17807 )
17808 } else {
17809 let fv = if g {
17812 self.func_g("fa_decode_vec_q")
17813 } else {
17814 self.func("fa_decode_vec_q")
17815 };
17816 (
17817 fv,
17818 LaunchConfig {
17819 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
17820 block_dim: (32, gqa, 1),
17821 shared_mem_bytes: 0,
17822 },
17823 )
17824 }
17825 } else {
17826 return self.fa_decode_scalar_unified(
17829 q,
17830 k,
17831 v,
17832 o,
17833 head_dim,
17834 n_head,
17835 n_head_kv,
17836 t_kv,
17837 None,
17838 scale,
17839 n_splits,
17840 if fa_vec { sp } else { 256 },
17841 k_tok_bytes,
17842 v_tok_bytes,
17843 g,
17844 part_o,
17845 part_m,
17846 part_l,
17847 None,
17848 );
17849 };
17850 let __s_b = self.gpu.stream();
17851 let mut b = __s_b.launch_builder(&f);
17852 b.arg(q)
17853 .arg(k)
17854 .arg(v)
17855 .arg(&mut *part_o)
17856 .arg(&mut *part_m)
17857 .arg(&mut *part_l)
17858 .arg(&hd)
17859 .arg(&nh)
17860 .arg(&nhkv)
17861 .arg(&tkvi)
17862 .arg(&scale)
17863 .arg(&nsp)
17864 .arg(&ktb)
17865 .arg(&vtb);
17866 unsafe {
17867 b.launch(cfg)?;
17868 }
17869 let (fc, cfg2) = (
17872 if g {
17873 self.func_g("fa_decode_combine_f32")
17874 } else {
17875 self.fa_func("fa_decode_combine_f32", head_dim)
17876 },
17877 LaunchConfig {
17878 grid_dim: (n_head as u32, 1, 1),
17879 block_dim: (head_dim as u32, 1, 1),
17880 shared_mem_bytes: 0,
17881 },
17882 );
17883 let __s_b2 = self.gpu.stream();
17884 let mut b2 = __s_b2.launch_builder(&fc);
17885 b2.arg(&*part_o)
17886 .arg(&*part_m)
17887 .arg(&*part_l)
17888 .arg(o)
17889 .arg(&hd)
17890 .arg(&nh)
17891 .arg(&nsp);
17892 unsafe {
17893 b2.launch(cfg2)?;
17894 }
17895 Ok(())
17896 }
17897
17898 #[allow(clippy::too_many_arguments)]
17909 pub fn fa_decode_batch_seqs_v4(
17910 &self,
17911 q: &CudaSlice<f32>,
17912 kv_ptrs: &cudarc::driver::CudaView<u64>,
17913 pos_seq: &CudaSlice<i32>,
17914 o: &mut CudaSlice<f32>,
17915 head_dim: usize,
17916 n_head: usize,
17917 n_head_kv: usize,
17918 b_n: usize,
17919 t_kv_max: usize,
17920 scale: f32,
17921 split_keys: usize,
17922 k_tok_bytes: usize,
17923 v_tok_bytes: usize,
17924 ) -> Result<(), Box<dyn std::error::Error>> {
17925 debug_assert!(head_dim == 256, "seqs twin is v4-stamped (hd256 only)");
17926 let n_splits_max = (t_kv_max + split_keys - 1) / split_keys;
17927 let o_len = b_n * n_head * n_splits_max * head_dim;
17928 let ml_len = b_n * n_head * n_splits_max;
17929 let mut part_guard = self.fa_part_pool.lock().unwrap();
17930 if part_guard
17931 .as_ref()
17932 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
17933 .unwrap_or(true)
17934 {
17935 let old = part_guard.take();
17946 let (co, cm) = old
17947 .as_ref()
17948 .map(|pp| (pp.0.len(), pp.1.len()))
17949 .unwrap_or((0, 0));
17950 if let Some(old) = old {
17951 self.fa_part_retired.lock().unwrap().push(old);
17952 }
17953 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
17954 eprintln!(
17955 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
17956 co, o_len, cm, ml_len
17957 );
17958 }
17959 *part_guard = Some((
17960 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
17961 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17962 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
17963 ));
17964 }
17965 let pg = part_guard.as_mut().unwrap();
17966 self.gpu
17967 .stream()
17968 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
17969 self.gpu
17970 .stream()
17971 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
17972 self.gpu
17973 .stream()
17974 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
17975 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
17976 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
17977 let (nspm, spk) = (n_splits_max as i32, split_keys as i32);
17978 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17979 let gqa = (n_head / n_head_kv).max(1) as u32;
17980 let f = self.func("fa_decode_vec_q_seqs_v4");
17981 let shmem = (11520 + 32 * head_dim * 2) as u32;
17983 use cudarc::driver::sys::CUfunction_attribute_enum as A;
17984 f.set_attribute(
17985 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
17986 shmem as i32,
17987 )?;
17988 let cfg = LaunchConfig {
17989 grid_dim: (n_head_kv as u32, n_splits_max as u32, b_n as u32),
17990 block_dim: (32, gqa, 1),
17991 shared_mem_bytes: shmem,
17992 };
17993 {
17994 let __s_b = self.gpu.stream();
17995 let mut b = __s_b.launch_builder(&f);
17996 b.arg(q)
17997 .arg(kv_ptrs)
17998 .arg(pos_seq)
17999 .arg(&mut *part_o)
18000 .arg(&mut *part_m)
18001 .arg(&mut *part_l)
18002 .arg(&hd)
18003 .arg(&nh)
18004 .arg(&nhkv)
18005 .arg(&scale)
18006 .arg(&nspm)
18007 .arg(&spk)
18008 .arg(&ktb)
18009 .arg(&vtb);
18010 unsafe {
18011 b.launch(cfg)?;
18012 }
18013 }
18014 let fc = self.func("fa_decode_combine_seqs");
18015 let cfg2 = LaunchConfig {
18016 grid_dim: (n_head as u32, b_n as u32, 1),
18017 block_dim: (head_dim as u32, 1, 1),
18018 shared_mem_bytes: 0,
18019 };
18020 let __s_b2 = self.gpu.stream();
18021 let mut b2 = __s_b2.launch_builder(&fc);
18022 b2.arg(&*part_o)
18023 .arg(&*part_m)
18024 .arg(&*part_l)
18025 .arg(o)
18026 .arg(&hd)
18027 .arg(&nh)
18028 .arg(pos_seq)
18029 .arg(&nspm)
18030 .arg(&spk);
18031 unsafe {
18032 b2.launch(cfg2)?;
18033 }
18034 Ok(())
18035 }
18036
18037 #[allow(clippy::too_many_arguments)]
18044 pub fn append_kv_quantized_seqs(
18045 &self,
18046 k_rows: &CudaSlice<f32>,
18047 v_rows: &CudaSlice<f32>,
18048 kv_ptrs: &cudarc::driver::CudaView<u64>,
18049 pos_seq: &CudaSlice<i32>,
18050 b_n: usize,
18051 kv_dim_k: usize,
18052 kv_dim_v: usize,
18053 k_tok_bytes: usize,
18054 v_tok_bytes: usize,
18055 ) -> Result<(), Box<dyn std::error::Error>> {
18056 let f = self.func("append_quantize_kv_q8_0_q5_1_seqs");
18057 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
18058 let cfg = LaunchConfig {
18059 grid_dim: (nblk, b_n as u32, 1),
18060 block_dim: (32, 1, 1),
18061 shared_mem_bytes: 0,
18062 };
18063 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
18064 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18065 let __s_b = self.gpu.stream();
18066 let mut b = __s_b.launch_builder(&f);
18067 b.arg(k_rows)
18068 .arg(v_rows)
18069 .arg(kv_ptrs)
18070 .arg(pos_seq)
18071 .arg(&kdk)
18072 .arg(&kdv)
18073 .arg(&ktb)
18074 .arg(&vtb);
18075 unsafe {
18076 b.launch(cfg)?;
18077 }
18078 Ok(())
18079 }
18080
18081 pub fn fa_rows_eligible(&self, base_len: usize, head_dim: usize) -> bool {
18087 std::env::var("MEMRA_NO_FA_VEC").is_err()
18088 && std::env::var("MEMRA_FA_ROWS_OFF").is_err()
18089 && base_len + 1 >= fa_vec_min_tkv()
18090 && head_dim <= 256
18091 && head_dim % 32 == 0
18092 }
18093
18094 #[allow(clippy::too_many_arguments)]
18103 pub fn fa_decode_rows(
18104 &self,
18105 q: &CudaSlice<f32>,
18106 k: &cudarc::driver::CudaView<u8>,
18107 v: &cudarc::driver::CudaView<u8>,
18108 o: &mut CudaSlice<f32>,
18109 head_dim: usize,
18110 n_head: usize,
18111 n_head_kv: usize,
18112 base_len: usize,
18113 t: usize,
18114 scale: f32,
18115 k_tok_bytes: usize,
18116 v_tok_bytes: usize,
18117 base_dev: Option<(&CudaSlice<i32>, i32)>,
18121 kv_shared: bool,
18124 g: bool,
18128 mut q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
18131 ) -> Result<(), Box<dyn std::error::Error>> {
18132 debug_assert!(base_len + 1 >= fa_vec_min_tkv() && head_dim <= 512 && head_dim % 32 == 0);
18133 let t_kv_max = base_len + t; let mut sp = fa_split_keys(t_kv_max, n_head_kv); if head_dim == 512 {
18140 static SP512: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18141 let v = *SP512.get_or_init(|| {
18144 std::env::var("MEMRA_FA_SP512")
18145 .ok()
18146 .and_then(|x| x.parse().ok())
18147 .unwrap_or(0)
18148 });
18149 sp = if v >= 8 {
18150 v
18151 } else {
18152 FA_SP512_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
18153 };
18154 }
18155 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18156 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18157 let gqa = (n_head / n_head_kv).max(1) as u32;
18158 let mut groups: Vec<(usize, usize, usize)> = Vec::new(); if head_dim == 512 || fa_split_keys(base_len + 1, n_head_kv) == sp {
18169 groups.push((0, t, sp));
18170 } else {
18171 let mut r0 = 0usize;
18172 while r0 < t {
18173 let sp_g = fa_split_keys(base_len + r0 + 1, n_head_kv);
18174 let mut r1 = r0 + 1;
18175 while r1 < t && fa_split_keys(base_len + r1 + 1, n_head_kv) == sp_g {
18176 r1 += 1;
18177 }
18178 groups.push((r0, r1 - r0, sp_g));
18179 r0 = r1;
18180 }
18181 }
18182 static SMEM_TKV_R: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18186 let smem_tkv = *SMEM_TKV_R.get_or_init(|| {
18187 std::env::var("MEMRA_FA_SMEM_TKV")
18188 .ok()
18189 .and_then(|v| v.parse().ok())
18190 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
18191 });
18192 let v4 = fa_v4_at(base_len + t) && head_dim == 256;
18193 let v3 = fa_v3_active(head_dim);
18194 let smem_rows =
18195 head_dim <= 256 && !v3 && !fa_v2_on() && smem_tkv > 0 && t_kv_max >= smem_tkv;
18196 let _ = kv_shared;
18201 let i2 = head_dim == 512 && std::env::var("MEMRA_FA_I2").as_deref() != Ok("0");
18204 static TB512: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
18218 let tb512 = head_dim == 512
18220 && sp <= 32
18221 && n_head / n_head_kv.max(1) <= 16
18222 && *TB512.get_or_init(|| std::env::var("MEMRA_FA_TB512").as_deref() != Ok("0"));
18223 let fname = if tb512 {
18224 "fa_decode_vec_q_rows_v4_512_tb"
18225 } else if i2 {
18226 "fa_decode_vec_q_rows_dpl16_i2"
18227 } else if head_dim == 512 {
18228 "fa_decode_vec_q_rows_dpl16"
18229 }
18230 else if v4 {
18232 "fa_decode_vec_q_rows_v4"
18233 } else if v3 {
18234 "fa_decode_vec_q_rows_v3"
18235 } else if fa_v2_on() {
18236 "fa_decode_vec_q_rows_v2"
18237 } else if smem_rows {
18238 "fa_decode_vec_q_rows_smem"
18239 } else {
18240 "fa_decode_vec_q_rows"
18241 };
18242 let f = if head_dim == 512 {
18243 self.fa_func(fname, head_dim)
18244 } else if g {
18245 self.func_g(if smem_rows {
18253 "fa_decode_vec_q_rows"
18254 } else {
18255 fname
18256 })
18257 } else {
18258 self.func(fname)
18259 };
18260 let shmem = if tb512 {
18261 let gk = Self::gkv_on();
18263 let sh =
18264 (8192 + 1024 + 32 * 512 + 32 * 64 + 32 * head_dim * if gk { 1 } else { 2 }) as u32;
18265 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18266 f.set_attribute(
18267 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18268 sh as i32,
18269 )?;
18270 sh
18271 } else if v4 || v3 || smem_rows || fa_v2_on() {
18272 let sh = (if v4 {
18274 11520 + 32 * head_dim * if g { 1 } else { 2 }
18275 } else if v3 {
18276 32 * head_dim * 2
18277 } else {
18278 2 * 32 * head_dim * 2
18279 }) as u32;
18280 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18281 f.set_attribute(
18282 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18283 sh as i32,
18284 )?;
18285 sh
18286 } else {
18287 0
18288 };
18289 for &(r0, t_g, sp_g) in &groups {
18293 let n_splits_g = (base_len + r0 + t_g).div_ceil(sp_g);
18294 let (nspm, spk) = (n_splits_g as i32, sp_g as i32);
18295 let base_i = (base_len + r0) as i32;
18296 let o_len = t_g * n_head * n_splits_g * head_dim;
18297 let ml_len = t_g * n_head * n_splits_g;
18298 let mut part_guard = self.fa_part_pool.lock().unwrap();
18299 if part_guard
18300 .as_ref()
18301 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18302 .unwrap_or(true)
18303 {
18304 let old = part_guard.take();
18315 let (co, cm) = old
18316 .as_ref()
18317 .map(|pp| (pp.0.len(), pp.1.len()))
18318 .unwrap_or((0, 0));
18319 if let Some(old) = old {
18320 self.fa_part_retired.lock().unwrap().push(old);
18321 }
18322 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18323 eprintln!(
18324 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18325 co, o_len, cm, ml_len
18326 );
18327 }
18328 *part_guard = Some((
18329 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18330 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18331 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18332 ));
18333 }
18334 let pg = part_guard.as_mut().unwrap();
18335 self.gpu
18336 .stream()
18337 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18338 self.gpu
18339 .stream()
18340 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18341 self.gpu
18342 .stream()
18343 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18344 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18345 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
18346 let qv = self.view(q, t * n_head * head_dim);
18347 let q_g = qv.slice(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
18348 let cfg = LaunchConfig {
18349 grid_dim: (n_head_kv as u32, n_splits_g as u32, t_g as u32),
18350 block_dim: (32, gqa, 1),
18351 shared_mem_bytes: shmem,
18352 };
18353 {
18354 let __s_b = self.gpu.stream();
18355 let mut b = __s_b.launch_builder(&f);
18356 if tb512 {
18357 let (bd, plus) =
18359 base_dev.expect("hd512 rows twin requires a device base counter");
18360 let plus_g = plus + r0 as i32;
18361 let nr = t_g as i32;
18362 if Self::pdl_on() && Self::pdl_wb_on() {
18363 use cudarc::driver::{DevicePtr, DevicePtrMut};
18365 let s = &self.gpu.stream();
18366 let (pq, _b0) = q_g.device_ptr(s);
18367 let (pk, _b1) = k.device_ptr(s);
18368 let (pv, _b2) = v.device_ptr(s);
18369 let (po, _b3) = part_o.device_ptr_mut(s);
18370 let (pm, _b4) = part_m.device_ptr_mut(s);
18371 let (pl, _b5) = part_l.device_ptr_mut(s);
18372 let (pb, _b6) = bd.device_ptr(s);
18373 let mut ps = [
18374 &pq as *const _ as *mut std::ffi::c_void,
18375 &pk as *const _ as *mut _,
18376 &pv as *const _ as *mut _,
18377 &po as *const _ as *mut _,
18378 &pm as *const _ as *mut _,
18379 &pl as *const _ as *mut _,
18380 &hd as *const _ as *mut _,
18381 &nh as *const _ as *mut _,
18382 &nhkv as *const _ as *mut _,
18383 &pb as *const _ as *mut _,
18384 &plus_g as *const _ as *mut _,
18385 &scale as *const _ as *mut _,
18386 &nspm as *const _ as *mut _,
18387 &spk as *const _ as *mut _,
18388 &ktb as *const _ as *mut _,
18389 &vtb as *const _ as *mut _,
18390 &nr as *const _ as *mut _,
18391 ];
18392 unsafe {
18393 self.launch_pdl_flash(
18394 Self::gkv_on(),
18395 "fa_decode_vec_q_rows_v4_512_tb",
18396 (n_head_kv as u32, n_splits_g as u32, 1),
18397 (32, gqa, 1),
18398 shmem,
18399 &mut ps,
18400 )?;
18401 }
18402 } else {
18403 let cfg_tb = LaunchConfig {
18404 grid_dim: (n_head_kv as u32, n_splits_g as u32, 1),
18405 block_dim: (32, gqa, 1),
18406 shared_mem_bytes: shmem,
18407 };
18408 b.arg(&q_g)
18409 .arg(k)
18410 .arg(v)
18411 .arg(&mut *part_o)
18412 .arg(&mut *part_m)
18413 .arg(&mut *part_l)
18414 .arg(&hd)
18415 .arg(&nh)
18416 .arg(&nhkv)
18417 .arg(bd)
18418 .arg(&plus_g)
18419 .arg(&scale)
18420 .arg(&nspm)
18421 .arg(&spk)
18422 .arg(&ktb)
18423 .arg(&vtb)
18424 .arg(&nr);
18425 unsafe {
18426 b.launch(cfg_tb)?;
18427 }
18428 }
18429 } else if head_dim == 512 {
18430 let (bd, plus) =
18431 base_dev.expect("hd512 rows twin requires a device base counter");
18432 let plus_g = plus + r0 as i32;
18433 b.arg(&q_g)
18434 .arg(k)
18435 .arg(v)
18436 .arg(&mut *part_o)
18437 .arg(&mut *part_m)
18438 .arg(&mut *part_l)
18439 .arg(&hd)
18440 .arg(&nh)
18441 .arg(&nhkv)
18442 .arg(bd)
18443 .arg(&plus_g)
18444 .arg(&scale)
18445 .arg(&nspm)
18446 .arg(&spk)
18447 .arg(&ktb)
18448 .arg(&vtb);
18449 unsafe {
18450 b.launch(cfg)?;
18451 }
18452 } else {
18453 b.arg(&q_g)
18454 .arg(k)
18455 .arg(v)
18456 .arg(&mut *part_o)
18457 .arg(&mut *part_m)
18458 .arg(&mut *part_l)
18459 .arg(&hd)
18460 .arg(&nh)
18461 .arg(&nhkv)
18462 .arg(&base_i)
18463 .arg(&scale)
18464 .arg(&nspm)
18465 .arg(&spk)
18466 .arg(&ktb)
18467 .arg(&vtb);
18468 unsafe {
18469 b.launch(cfg)?;
18470 }
18471 }
18472 }
18473 let cfg2 = LaunchConfig {
18474 grid_dim: (n_head as u32, t_g as u32, 1),
18475 block_dim: (head_dim as u32, 1, 1),
18476 shared_mem_bytes: 0,
18477 };
18478 let mut o_g = o.slice_mut(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
18479 if head_dim == 512 {
18480 let (bd, plus) = base_dev.unwrap();
18483 let plus_g = plus + r0 as i32;
18484 if let Some((oq, od)) = q8_out.as_mut() {
18485 debug_assert!(t == 1, "rows q8 emit is a t=1 decode arm");
18487 if Self::pdl_on() && Self::pdl_wb_on() {
18488 use cudarc::driver::{DevicePtr, DevicePtrMut};
18490 let s = &self.gpu.stream();
18491 let (po, _g0) = part_o.device_ptr(s);
18492 let (pm, _g1) = part_m.device_ptr(s);
18493 let (pl, _g2) = part_l.device_ptr(s);
18494 let (pq, _g3) = oq.device_ptr_mut(s);
18495 let (pd, _g4) = od.device_ptr_mut(s);
18496 let (pb, _g5) = bd.device_ptr(s);
18497 let mut ps = [
18498 &po as *const _ as *mut std::ffi::c_void,
18499 &pm as *const _ as *mut _,
18500 &pl as *const _ as *mut _,
18501 &pq as *const _ as *mut _,
18502 &pd as *const _ as *mut _,
18503 &hd as *const _ as *mut _,
18504 &nh as *const _ as *mut _,
18505 &pb as *const _ as *mut _,
18506 &plus_g as *const _ as *mut _,
18507 &nspm as *const _ as *mut _,
18508 &spk as *const _ as *mut _,
18509 ];
18510 unsafe {
18511 self.launch_pdl_flash(
18512 Self::gkv_on(),
18513 "fa_decode_combine_rows_dc_q8_1",
18514 cfg2.grid_dim,
18515 cfg2.block_dim,
18516 0,
18517 &mut ps,
18518 )?;
18519 }
18520 continue;
18521 }
18522 let fc = self.fa_func("fa_decode_combine_rows_dc_q8_1", head_dim);
18523 let __s_b2 = self.gpu.stream();
18524 let mut b2 = __s_b2.launch_builder(&fc);
18525 b2.arg(&*part_o)
18526 .arg(&*part_m)
18527 .arg(&*part_l)
18528 .arg(&mut **oq)
18529 .arg(&mut **od)
18530 .arg(&hd)
18531 .arg(&nh)
18532 .arg(bd)
18533 .arg(&plus_g)
18534 .arg(&nspm)
18535 .arg(&spk);
18536 unsafe {
18537 b2.launch(cfg2)?;
18538 }
18539 continue;
18540 }
18541 let fc = self.fa_func("fa_decode_combine_rows_dc", head_dim);
18542 let __s_b2 = self.gpu.stream();
18543 let mut b2 = __s_b2.launch_builder(&fc);
18544 b2.arg(&*part_o)
18545 .arg(&*part_m)
18546 .arg(&*part_l)
18547 .arg(&mut o_g)
18548 .arg(&hd)
18549 .arg(&nh)
18550 .arg(bd)
18551 .arg(&plus_g)
18552 .arg(&nspm)
18553 .arg(&spk);
18554 unsafe {
18555 b2.launch(cfg2)?;
18556 }
18557 } else {
18558 assert!(
18561 q8_out.is_none(),
18562 "rows q8 emit requires the hd512 dc combine"
18563 );
18564 let fc = self.func("fa_decode_combine_rows");
18565 let __s_b2 = self.gpu.stream();
18566 let mut b2 = __s_b2.launch_builder(&fc);
18567 b2.arg(&*part_o)
18568 .arg(&*part_m)
18569 .arg(&*part_l)
18570 .arg(&mut o_g)
18571 .arg(&hd)
18572 .arg(&nh)
18573 .arg(&base_i)
18574 .arg(&nspm)
18575 .arg(&spk);
18576 unsafe {
18577 b2.launch(cfg2)?;
18578 }
18579 }
18580 }
18581 Ok(())
18582 }
18583
18584 #[allow(clippy::too_many_arguments)]
18588 pub fn fa_decode_rows_w(
18589 &self,
18590 q: &CudaSlice<f32>,
18591 k: &cudarc::driver::CudaView<u8>,
18592 v: &cudarc::driver::CudaView<u8>,
18593 o: &mut CudaSlice<f32>,
18594 head_dim: usize,
18595 n_head: usize,
18596 n_head_kv: usize,
18597 base_dev: &CudaSlice<i32>,
18598 base_plus: i32,
18599 t: usize,
18600 scale: f32,
18601 window: usize,
18602 k_tok_bytes: usize,
18603 v_tok_bytes: usize,
18604 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
18605 ) -> Result<(), Box<dyn std::error::Error>> {
18606 debug_assert!(head_dim == 256);
18611 let sp = {
18619 static SPW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18620 let v = *SPW.get_or_init(|| {
18621 std::env::var("MEMRA_FA_SPW")
18622 .ok()
18623 .and_then(|x| x.parse().ok())
18624 .unwrap_or(0)
18625 });
18626 if v >= 8 {
18627 v
18628 } else {
18629 FA_SPW_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
18630 }
18631 };
18632 let n_splits_max = (window + sp - 1) / sp;
18633 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
18634 let (nspm, spk, wini) = (n_splits_max as i32, sp as i32, window as i32);
18635 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18636 let gqa = (n_head / n_head_kv).max(1) as u32;
18637 let o_len = t * n_head * n_splits_max * head_dim;
18638 let ml_len = t * n_head * n_splits_max;
18639 let mut part_guard = self.fa_part_pool.lock().unwrap();
18640 if part_guard
18641 .as_ref()
18642 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
18643 .unwrap_or(true)
18644 {
18645 let old = part_guard.take();
18656 let (co, cm) = old
18657 .as_ref()
18658 .map(|pp| (pp.0.len(), pp.1.len()))
18659 .unwrap_or((0, 0));
18660 if let Some(old) = old {
18661 self.fa_part_retired.lock().unwrap().push(old);
18662 }
18663 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
18664 eprintln!(
18665 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
18666 co, o_len, cm, ml_len
18667 );
18668 }
18669 *part_guard = Some((
18670 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
18671 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18672 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
18673 ));
18674 }
18675 let pg = part_guard.as_mut().unwrap();
18676 self.gpu
18677 .stream()
18678 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
18679 self.gpu
18680 .stream()
18681 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
18682 self.gpu
18683 .stream()
18684 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
18685 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
18686 static SMEM_TKV_W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
18692 let smem_tkv = *SMEM_TKV_W.get_or_init(|| {
18693 std::env::var("MEMRA_FA_SMEM_TKV")
18694 .ok()
18695 .and_then(|v| v.parse().ok())
18696 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
18697 });
18698 use cudarc::driver::sys::CUfunction_attribute_enum as A;
18704 let wg = Self::wkv_on();
18709 let sp2 =
18712 gqa <= 4 && fa_v4_at(window) && std::env::var("MEMRA_FA_SPW2").as_deref() != Ok("0");
18713 if sp2 {
18714 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
18715 if Self::pdl_on() && Self::pdl_wb_on() {
18716 use cudarc::driver::{DevicePtr, DevicePtrMut};
18718 let s = &self.gpu.stream();
18719 let (pq, _b0) = q.device_ptr(s);
18720 let (pk, _b1) = k.device_ptr(s);
18721 let (pv, _b2) = v.device_ptr(s);
18722 let (po, _b3) = part_o.device_ptr_mut(s);
18723 let (pm, _b4) = part_m.device_ptr_mut(s);
18724 let (pl, _b5) = part_l.device_ptr_mut(s);
18725 let (pb, _b6) = base_dev.device_ptr(s);
18726 let mut ps = [
18727 &pq as *const _ as *mut std::ffi::c_void,
18728 &pk as *const _ as *mut _,
18729 &pv as *const _ as *mut _,
18730 &po as *const _ as *mut _,
18731 &pm as *const _ as *mut _,
18732 &pl as *const _ as *mut _,
18733 &hd as *const _ as *mut _,
18734 &nh as *const _ as *mut _,
18735 &nhkv as *const _ as *mut _,
18736 &pb as *const _ as *mut _,
18737 &base_plus as *const _ as *mut _,
18738 &scale as *const _ as *mut _,
18739 &nspm as *const _ as *mut _,
18740 &spk as *const _ as *mut _,
18741 &ktb as *const _ as *mut _,
18742 &vtb as *const _ as *mut _,
18743 &wini as *const _ as *mut _,
18744 ];
18745 unsafe {
18746 self.launch_pdl_flash(
18747 wg,
18748 "fa_decode_vec_q_rows_v4_w_sp",
18749 (n_head_kv as u32, n_splits_max as u32, t as u32),
18750 (32, gqa + 1, 1),
18751 sh,
18752 &mut ps,
18753 )?;
18754 }
18755 } else {
18756 let f = if wg {
18757 self.func_g("fa_decode_vec_q_rows_v4_w_sp")
18758 } else {
18759 self.func("fa_decode_vec_q_rows_v4_w_sp")
18760 };
18761 f.set_attribute(
18762 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18763 sh as i32,
18764 )?;
18765 let cfg = LaunchConfig {
18766 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18767 block_dim: (32, gqa + 1, 1),
18768 shared_mem_bytes: sh,
18769 };
18770 let __s_b = self.gpu.stream();
18771 let mut b = __s_b.launch_builder(&f);
18772 b.arg(q)
18773 .arg(k)
18774 .arg(v)
18775 .arg(&mut *part_o)
18776 .arg(&mut *part_m)
18777 .arg(&mut *part_l)
18778 .arg(&hd)
18779 .arg(&nh)
18780 .arg(&nhkv)
18781 .arg(base_dev)
18782 .arg(&base_plus)
18783 .arg(&scale)
18784 .arg(&nspm)
18785 .arg(&spk)
18786 .arg(&ktb)
18787 .arg(&vtb)
18788 .arg(&wini);
18789 unsafe {
18790 b.launch(cfg)?;
18791 }
18792 }
18793 } else {
18794 if fa_v4_at(window) && Self::pdl_on() && Self::pdl_wb_on() {
18795 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
18797 use cudarc::driver::{DevicePtr, DevicePtrMut};
18798 let s = &self.gpu.stream();
18799 let (pq, _b0) = q.device_ptr(s);
18800 let (pk, _b1) = k.device_ptr(s);
18801 let (pv, _b2) = v.device_ptr(s);
18802 let (po, _b3) = part_o.device_ptr_mut(s);
18803 let (pm, _b4) = part_m.device_ptr_mut(s);
18804 let (pl, _b5) = part_l.device_ptr_mut(s);
18805 let (pb, _b6) = base_dev.device_ptr(s);
18806 let mut ps = [
18807 &pq as *const _ as *mut std::ffi::c_void,
18808 &pk as *const _ as *mut _,
18809 &pv as *const _ as *mut _,
18810 &po as *const _ as *mut _,
18811 &pm as *const _ as *mut _,
18812 &pl as *const _ as *mut _,
18813 &hd as *const _ as *mut _,
18814 &nh as *const _ as *mut _,
18815 &nhkv as *const _ as *mut _,
18816 &pb as *const _ as *mut _,
18817 &base_plus as *const _ as *mut _,
18818 &scale as *const _ as *mut _,
18819 &nspm as *const _ as *mut _,
18820 &spk as *const _ as *mut _,
18821 &ktb as *const _ as *mut _,
18822 &vtb as *const _ as *mut _,
18823 &wini as *const _ as *mut _,
18824 ];
18825 unsafe {
18826 self.launch_pdl_flash(
18827 wg,
18828 "fa_decode_vec_q_rows_v4_w",
18829 (n_head_kv as u32, n_splits_max as u32, t as u32),
18830 (32, gqa, 1),
18831 sh,
18832 &mut ps,
18833 )?;
18834 }
18835 } else {
18836 let pick = |name: &str| {
18837 if wg {
18838 self.func_g(name)
18839 } else {
18840 self.func(name)
18841 }
18842 };
18843 let (f, sh) = if fa_v4_at(window) {
18844 let f = pick("fa_decode_vec_q_rows_v4_w");
18845 (f, (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32)
18846 } else if smem_tkv > 0 && window >= smem_tkv {
18847 (
18850 pick("fa_decode_vec_q_rows_smem_w"),
18851 (2 * 32 * head_dim * 2) as u32,
18852 )
18853 } else {
18854 (pick("fa_decode_vec_q_rows_reg_w"), 0u32)
18855 };
18856 f.set_attribute(
18857 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
18858 sh as i32,
18859 )?;
18860 let cfg = LaunchConfig {
18861 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
18862 block_dim: (32, gqa, 1),
18863 shared_mem_bytes: sh,
18864 };
18865 let __s_b = self.gpu.stream();
18866 let mut b = __s_b.launch_builder(&f);
18867 b.arg(q)
18868 .arg(k)
18869 .arg(v)
18870 .arg(&mut *part_o)
18871 .arg(&mut *part_m)
18872 .arg(&mut *part_l)
18873 .arg(&hd)
18874 .arg(&nh)
18875 .arg(&nhkv)
18876 .arg(base_dev)
18877 .arg(&base_plus)
18878 .arg(&scale)
18879 .arg(&nspm)
18880 .arg(&spk)
18881 .arg(&ktb)
18882 .arg(&vtb)
18883 .arg(&wini);
18884 unsafe {
18885 b.launch(cfg)?;
18886 }
18887 }
18888 }
18889 let cfg2 = LaunchConfig {
18890 grid_dim: (n_head as u32, t as u32, 1),
18891 block_dim: (head_dim as u32, 1, 1),
18892 shared_mem_bytes: 0,
18893 };
18894 if let Some((oq, od)) = q8_out {
18895 if Self::pdl_on() && Self::pdl_wb_on() {
18898 use cudarc::driver::{DevicePtr, DevicePtrMut};
18900 let s = &self.gpu.stream();
18901 let (po, _g0) = part_o.device_ptr(s);
18902 let (pm, _g1) = part_m.device_ptr(s);
18903 let (pl, _g2) = part_l.device_ptr(s);
18904 let (pq, _g3) = oq.device_ptr_mut(s);
18905 let (pd, _g4) = od.device_ptr_mut(s);
18906 let mut ps = [
18907 &po as *const _ as *mut std::ffi::c_void,
18908 &pm as *const _ as *mut _,
18909 &pl as *const _ as *mut _,
18910 &pq as *const _ as *mut _,
18911 &pd as *const _ as *mut _,
18912 &hd as *const _ as *mut _,
18913 &nh as *const _ as *mut _,
18914 &nspm as *const _ as *mut _,
18915 &spk as *const _ as *mut _,
18916 &wini as *const _ as *mut _,
18917 ];
18918 unsafe {
18919 self.launch_pdl_flash(
18920 wg,
18921 "fa_decode_combine_rows_w_q8_1",
18922 cfg2.grid_dim,
18923 cfg2.block_dim,
18924 0,
18925 &mut ps,
18926 )?;
18927 }
18928 return Ok(());
18929 }
18930 let fc = if wg {
18931 self.func_g("fa_decode_combine_rows_w_q8_1")
18932 } else {
18933 self.func("fa_decode_combine_rows_w_q8_1")
18934 };
18935 let __s_b2 = self.gpu.stream();
18936 let mut b2 = __s_b2.launch_builder(&fc);
18937 b2.arg(&*part_o)
18938 .arg(&*part_m)
18939 .arg(&*part_l)
18940 .arg(oq)
18941 .arg(od)
18942 .arg(&hd)
18943 .arg(&nh)
18944 .arg(&nspm)
18945 .arg(&spk)
18946 .arg(&wini);
18947 unsafe {
18948 b2.launch(cfg2)?;
18949 }
18950 return Ok(());
18951 }
18952 let fc = if wg {
18953 self.func_g("fa_decode_combine_rows_w")
18954 } else {
18955 self.func("fa_decode_combine_rows_w")
18956 };
18957 let __s_b2 = self.gpu.stream();
18958 let mut b2 = __s_b2.launch_builder(&fc);
18959 b2.arg(&*part_o)
18960 .arg(&*part_m)
18961 .arg(&*part_l)
18962 .arg(o)
18963 .arg(&hd)
18964 .arg(&nh)
18965 .arg(&nspm)
18966 .arg(&spk)
18967 .arg(&wini);
18968 unsafe {
18969 b2.launch(cfg2)?;
18970 }
18971 Ok(())
18972 }
18973
18974 #[allow(clippy::too_many_arguments)]
18980 pub fn fa_decode_rows_dc(
18981 &self,
18982 q: &CudaSlice<f32>,
18983 k: &cudarc::driver::CudaView<u8>,
18984 v: &cudarc::driver::CudaView<u8>,
18985 o: &mut CudaSlice<f32>,
18986 head_dim: usize,
18987 n_head: usize,
18988 n_head_kv: usize,
18989 base_dev: &CudaSlice<i32>,
18990 t_kv_upper: usize,
18991 t: usize,
18992 scale: f32,
18993 k_tok_bytes: usize,
18994 v_tok_bytes: usize,
18995 base_plus: i32,
18996 g: bool,
18997 ) -> Result<(), Box<dyn std::error::Error>> {
18998 let v4 = head_dim == 256 && fa_v4_at(t_kv_upper);
18999 assert!(
19000 v4 || fa_v3_active(head_dim),
19001 "stream fa rows requires the v3 or v4 lane"
19002 );
19003 assert!(v4 || base_plus == 0, "v3_dc kernel takes no plus arg");
19004 if v4 {
19005 let sp = fa_split_keys(t_kv_upper, n_head_kv);
19006 let n_splits_max = (t_kv_upper + sp - 1) / sp;
19007 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
19008 let (nspm, spk) = (n_splits_max as i32, sp as i32);
19009 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
19010 let gqa = (n_head / n_head_kv).max(1) as u32;
19011 let o_len = t * n_head * n_splits_max * head_dim;
19012 let ml_len = t * n_head * n_splits_max;
19013 let mut part_guard = self.fa_part_pool.lock().unwrap();
19014 if part_guard
19015 .as_ref()
19016 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
19017 .unwrap_or(true)
19018 {
19019 let old = part_guard.take();
19030 let (co, cm) = old
19031 .as_ref()
19032 .map(|pp| (pp.0.len(), pp.1.len()))
19033 .unwrap_or((0, 0));
19034 if let Some(old) = old {
19035 self.fa_part_retired.lock().unwrap().push(old);
19036 }
19037 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
19038 eprintln!(
19039 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
19040 co, o_len, cm, ml_len
19041 );
19042 }
19043 *part_guard = Some((
19044 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
19045 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19046 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19047 ));
19048 }
19049 let pg = part_guard.as_mut().unwrap();
19050 self.gpu
19051 .stream()
19052 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
19053 self.gpu
19054 .stream()
19055 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
19056 self.gpu
19057 .stream()
19058 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
19059 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
19060 let f = if g {
19061 self.func_g("fa_decode_vec_q_rows_v4_dc")
19062 } else {
19063 self.func("fa_decode_vec_q_rows_v4_dc")
19064 };
19065 let sh = (11520 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
19066 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19067 f.set_attribute(
19068 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19069 sh as i32,
19070 )?;
19071 let cfg = LaunchConfig {
19072 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
19073 block_dim: (32, gqa, 1),
19074 shared_mem_bytes: sh,
19075 };
19076 let __s_b = self.gpu.stream();
19077 let mut b = __s_b.launch_builder(&f);
19078 b.arg(q)
19079 .arg(k)
19080 .arg(v)
19081 .arg(&mut *part_o)
19082 .arg(&mut *part_m)
19083 .arg(&mut *part_l)
19084 .arg(&hd)
19085 .arg(&nh)
19086 .arg(&nhkv)
19087 .arg(base_dev)
19088 .arg(&base_plus)
19089 .arg(&scale)
19090 .arg(&nspm)
19091 .arg(&spk)
19092 .arg(&ktb)
19093 .arg(&vtb);
19094 unsafe {
19095 b.launch(cfg)?;
19096 }
19097 let fc = self.func("fa_decode_combine_rows_dc");
19098 let cfg2 = LaunchConfig {
19099 grid_dim: (n_head as u32, t as u32, 1),
19100 block_dim: (head_dim as u32, 1, 1),
19101 shared_mem_bytes: 0,
19102 };
19103 let __s_b2 = self.gpu.stream();
19104 let mut b2 = __s_b2.launch_builder(&fc);
19105 b2.arg(&*part_o)
19106 .arg(&*part_m)
19107 .arg(&*part_l)
19108 .arg(o)
19109 .arg(&hd)
19110 .arg(&nh)
19111 .arg(base_dev)
19112 .arg(&base_plus)
19113 .arg(&nspm)
19114 .arg(&spk);
19115 unsafe {
19116 b2.launch(cfg2)?;
19117 }
19118 return Ok(());
19119 }
19120 let sp = fa_split_keys(t_kv_upper, n_head_kv);
19121 let n_splits_max = (t_kv_upper + sp - 1) / sp;
19122 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
19123 let (nspm, spk) = (n_splits_max as i32, sp as i32);
19124 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
19125 let gqa = (n_head / n_head_kv).max(1) as u32;
19126 let o_len = t * n_head * n_splits_max * head_dim;
19127 let ml_len = t * n_head * n_splits_max;
19128 let mut part_guard = self.fa_part_pool.lock().unwrap();
19129 if part_guard
19130 .as_ref()
19131 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
19132 .unwrap_or(true)
19133 {
19134 let old = part_guard.take();
19145 let (co, cm) = old
19146 .as_ref()
19147 .map(|pp| (pp.0.len(), pp.1.len()))
19148 .unwrap_or((0, 0));
19149 if let Some(old) = old {
19150 self.fa_part_retired.lock().unwrap().push(old);
19151 }
19152 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
19153 eprintln!(
19154 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
19155 co, o_len, cm, ml_len
19156 );
19157 }
19158 *part_guard = Some((
19159 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
19160 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19161 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19162 ));
19163 }
19164 let pg = part_guard.as_mut().unwrap();
19165 self.gpu
19166 .stream()
19167 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
19168 self.gpu
19169 .stream()
19170 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
19171 self.gpu
19172 .stream()
19173 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
19174 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
19175 let f = self.func("fa_decode_vec_q_rows_v3_dc");
19176 let sh = (32 * head_dim * 2) as u32;
19177 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19178 f.set_attribute(
19179 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19180 sh as i32,
19181 )?;
19182 let cfg = LaunchConfig {
19183 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
19184 block_dim: (32, gqa, 1),
19185 shared_mem_bytes: sh,
19186 };
19187 let __s_b = self.gpu.stream();
19188 let mut b = __s_b.launch_builder(&f);
19189 b.arg(q)
19190 .arg(k)
19191 .arg(v)
19192 .arg(&mut *part_o)
19193 .arg(&mut *part_m)
19194 .arg(&mut *part_l)
19195 .arg(&hd)
19196 .arg(&nh)
19197 .arg(&nhkv)
19198 .arg(base_dev)
19199 .arg(&scale)
19200 .arg(&nspm)
19201 .arg(&spk)
19202 .arg(&ktb)
19203 .arg(&vtb);
19204 unsafe {
19205 b.launch(cfg)?;
19206 }
19207 let fc = self.func("fa_decode_combine_rows_dc");
19208 let cfg2 = LaunchConfig {
19209 grid_dim: (n_head as u32, t as u32, 1),
19210 block_dim: (head_dim as u32, 1, 1),
19211 shared_mem_bytes: 0,
19212 };
19213 let plus0 = 0i32;
19214 let __s_b2 = self.gpu.stream();
19215 let mut b2 = __s_b2.launch_builder(&fc);
19216 b2.arg(&*part_o)
19217 .arg(&*part_m)
19218 .arg(&*part_l)
19219 .arg(o)
19220 .arg(&hd)
19221 .arg(&nh)
19222 .arg(base_dev)
19223 .arg(&plus0)
19224 .arg(&nspm)
19225 .arg(&spk);
19226 unsafe {
19227 b2.launch(cfg2)?;
19228 }
19229 Ok(())
19230 }
19231
19232 pub fn fa_decode_dc(
19243 &self,
19244 q: &CudaSlice<f32>,
19245 k: &cudarc::driver::CudaView<u8>,
19246 v: &cudarc::driver::CudaView<u8>,
19247 o: &mut CudaSlice<f32>,
19248 head_dim: usize,
19249 n_head: usize,
19250 n_head_kv: usize,
19251 t_kv_dev: &CudaSlice<i32>,
19252 bucket_max: usize,
19253 scale: f32,
19254 k_tok_bytes: usize,
19255 v_tok_bytes: usize,
19256 g: bool,
19257 ) -> Result<(), Box<dyn std::error::Error>> {
19258 self.fa_decode_dc_q8(
19259 q,
19260 k,
19261 v,
19262 o,
19263 head_dim,
19264 n_head,
19265 n_head_kv,
19266 t_kv_dev,
19267 bucket_max,
19268 scale,
19269 k_tok_bytes,
19270 v_tok_bytes,
19271 g,
19272 None,
19273 )
19274 }
19275
19276 #[allow(clippy::too_many_arguments)]
19279 pub fn fa_decode_dc_q8(
19280 &self,
19281 q: &CudaSlice<f32>,
19282 k: &cudarc::driver::CudaView<u8>,
19283 v: &cudarc::driver::CudaView<u8>,
19284 o: &mut CudaSlice<f32>,
19285 head_dim: usize,
19286 n_head: usize,
19287 n_head_kv: usize,
19288 t_kv_dev: &CudaSlice<i32>,
19289 bucket_max: usize,
19290 scale: f32,
19291 k_tok_bytes: usize,
19292 v_tok_bytes: usize,
19293 g: bool,
19294 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
19295 ) -> Result<(), Box<dyn std::error::Error>> {
19296 let mut fa_vec =
19304 std::env::var("MEMRA_NO_FA_VEC").is_err() && bucket_max >= fa_vec_min_tkv();
19305 if g && head_dim == 256 && !fa_v4_at(bucket_max) {
19306 fa_vec = false;
19307 } let sp = fa_split_keys(bucket_max, n_head_kv);
19309 let n_splits = if fa_vec {
19310 ((bucket_max + sp - 1) / sp).max(1)
19311 } else {
19312 ((bucket_max + 255) / 256).max(1)
19313 };
19314 let o_len = n_head * n_splits * head_dim;
19315 let ml_len = n_head * n_splits;
19316 let mut part_guard = self.fa_part_pool.lock().unwrap();
19317 if part_guard
19318 .as_ref()
19319 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
19320 .unwrap_or(true)
19321 {
19322 let old = part_guard.take();
19333 let (co, cm) = old
19334 .as_ref()
19335 .map(|pp| (pp.0.len(), pp.1.len()))
19336 .unwrap_or((0, 0));
19337 if let Some(old) = old {
19338 self.fa_part_retired.lock().unwrap().push(old);
19339 }
19340 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
19341 eprintln!(
19342 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
19343 co, o_len, cm, ml_len
19344 );
19345 }
19346 *part_guard = Some((
19347 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
19348 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19349 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
19350 ));
19351 }
19352 let pg = part_guard.as_mut().unwrap();
19353 self.gpu
19354 .stream()
19355 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
19356 self.gpu
19357 .stream()
19358 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
19359 self.gpu
19360 .stream()
19361 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
19362 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
19363 let (hd, nh, nhkv, nsp) = (
19364 head_dim as i32,
19365 n_head as i32,
19366 n_head_kv as i32,
19367 n_splits as i32,
19368 );
19369 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
19370 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
19371 let deep = fa_vec
19374 && head_dim == 256
19375 && fa_v4_at(bucket_max)
19376 && !g
19377 && fa_deep_at(bucket_max)
19378 && !matches!(fa_v4_mode(), "noB3" | "stage");
19379 let (f, cfg) = if fa_vec
19380 && head_dim == 512
19381 && bucket_max >= {
19382 static FA512_MIN_DC: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
19383 *FA512_MIN_DC.get_or_init(|| {
19384 std::env::var("MEMRA_FA512_MIN")
19385 .ok()
19386 .and_then(|v| v.parse().ok())
19387 .unwrap_or(512)
19388 })
19389 } {
19390 let gqa = (n_head / n_head_kv).max(1) as u32;
19392 (
19393 self.fa_func("fa_decode_vec_q_dpl16_dc", head_dim),
19394 LaunchConfig {
19395 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19396 block_dim: (32, gqa, 1),
19397 shared_mem_bytes: 0,
19398 },
19399 )
19400 } else if fa_vec && head_dim == 512 {
19401 let q_view = q.as_view();
19404 let mut o_view = o.as_view_mut();
19405 return self.fa_decode_scalar_unified(
19406 &q_view,
19407 k,
19408 v,
19409 &mut o_view,
19410 head_dim,
19411 n_head,
19412 n_head_kv,
19413 0,
19414 Some(t_kv_dev),
19415 scale,
19416 n_splits,
19417 sp,
19418 k_tok_bytes,
19419 v_tok_bytes,
19420 g,
19421 &mut *part_o,
19422 &mut *part_m,
19423 &mut *part_l,
19424 q8_out,
19425 );
19426 } else if fa_vec && head_dim == 256 && fa_v4_at(bucket_max) {
19427 let gqa = (n_head / n_head_kv).max(1) as u32;
19430 let fv = if g {
19431 self.func_g("fa_decode_vec_q_v4_dc")
19432 } else if deep {
19433 self.func("fa_decode_vec_q_v4_deep_dc")
19434 } else {
19435 self.func("fa_decode_vec_q_v4_dc")
19436 };
19437 let shmem =
19438 (if deep { 12160 } else { 11520 } + 32 * head_dim * if g { 1 } else { 2 }) as u32;
19439 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19440 fv.set_attribute(
19441 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19442 shmem as i32,
19443 )?;
19444 (
19445 fv,
19446 LaunchConfig {
19447 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19448 block_dim: (32, gqa, 1),
19449 shared_mem_bytes: shmem,
19450 },
19451 )
19452 } else if fa_vec && fa_v3_active(head_dim) {
19453 let gqa = (n_head / n_head_kv).max(1) as u32;
19456 let fv = if g {
19457 self.func_g("fa_decode_vec_q_v3_dc")
19458 } else {
19459 self.func("fa_decode_vec_q_v3_dc")
19460 };
19461 let shmem = (32 * head_dim * 2) as u32; (
19463 fv,
19464 LaunchConfig {
19465 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19466 block_dim: (32, gqa, 1),
19467 shared_mem_bytes: shmem,
19468 },
19469 )
19470 } else if fa_vec && fa_v2_on() {
19471 let gqa = (n_head / n_head_kv).max(1) as u32;
19475 let fv = if g {
19476 self.func_g("fa_decode_vec_q_v2_dc")
19477 } else {
19478 self.func("fa_decode_vec_q_v2_dc")
19479 };
19480 let shmem = (2 * 32 * head_dim * 2) as u32; (
19482 fv,
19483 LaunchConfig {
19484 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19485 block_dim: (32, gqa, 1),
19486 shared_mem_bytes: shmem,
19487 },
19488 )
19489 } else if fa_vec {
19490 let gqa = (n_head / n_head_kv).max(1) as u32;
19491 let fv = if g {
19493 self.func_g("fa_decode_vec_q_dc")
19494 } else {
19495 self.func("fa_decode_vec_q_dc")
19496 };
19497 (
19498 fv,
19499 LaunchConfig {
19500 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
19501 block_dim: (32, gqa, 1),
19502 shared_mem_bytes: 0,
19503 },
19504 )
19505 } else {
19506 let q_view = q.as_view();
19507 let mut o_view = o.as_view_mut();
19508 return self.fa_decode_scalar_unified(
19509 &q_view,
19510 k,
19511 v,
19512 &mut o_view,
19513 head_dim,
19514 n_head,
19515 n_head_kv,
19516 0,
19517 Some(t_kv_dev),
19518 scale,
19519 n_splits,
19520 if fa_vec { sp } else { 256 },
19521 k_tok_bytes,
19522 v_tok_bytes,
19523 g,
19524 &mut *part_o,
19525 &mut *part_m,
19526 &mut *part_l,
19527 q8_out,
19528 );
19529 };
19530 let ski = sp as i32; let __s_b = self.gpu.stream();
19532 let mut b = __s_b.launch_builder(&f);
19533 b.arg(q)
19534 .arg(k)
19535 .arg(v)
19536 .arg(&mut *part_o)
19537 .arg(&mut *part_m)
19538 .arg(&mut *part_l)
19539 .arg(&hd)
19540 .arg(&nh)
19541 .arg(&nhkv)
19542 .arg(t_kv_dev)
19543 .arg(&scale)
19544 .arg(&nsp)
19545 .arg(&ski)
19546 .arg(&ktb)
19547 .arg(&vtb);
19548 unsafe {
19549 b.launch(cfg)?;
19550 }
19551 let cfg2 = LaunchConfig {
19552 grid_dim: (n_head as u32, 1, 1),
19553 block_dim: (head_dim as u32, 1, 1),
19554 shared_mem_bytes: 0,
19555 };
19556 if let Some((oq, od)) = q8_out {
19557 let fc = if g {
19558 self.func_g("fa_decode_combine_q8_1")
19559 } else {
19560 self.fa_func("fa_decode_combine_q8_1", head_dim)
19561 };
19562 let __s_b2 = self.gpu.stream();
19563 let mut b2 = __s_b2.launch_builder(&fc);
19564 b2.arg(&*part_o)
19565 .arg(&*part_m)
19566 .arg(&*part_l)
19567 .arg(oq)
19568 .arg(od)
19569 .arg(&hd)
19570 .arg(&nh)
19571 .arg(&nsp);
19572 unsafe {
19573 b2.launch(cfg2)?;
19574 }
19575 return Ok(());
19576 }
19577 let fc = if g {
19578 self.func_g("fa_decode_combine_f32")
19579 } else {
19580 self.fa_func("fa_decode_combine_f32", head_dim)
19581 };
19582 let __s_b2 = self.gpu.stream();
19583 let mut b2 = __s_b2.launch_builder(&fc);
19584 b2.arg(&*part_o)
19585 .arg(&*part_m)
19586 .arg(&*part_l)
19587 .arg(o)
19588 .arg(&hd)
19589 .arg(&nh)
19590 .arg(&nsp);
19591 unsafe {
19592 b2.launch(cfg2)?;
19593 }
19594 Ok(())
19595 }
19596
19597 pub fn fa_geom_eager(
19603 &self,
19604 t_kv: usize,
19605 head_dim: usize,
19606 n_head_kv: usize,
19607 g: bool,
19608 ) -> (bool, usize) {
19609 let fa_ok = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
19613 let vec512 = fa_ok && head_dim == 512 && t_kv >= fa512_min_tkv();
19619 let mut fa_vec = vec512 || (fa_ok && head_dim <= 256 && head_dim % 32 == 0);
19620 if g && head_dim == 256 && !fa_v4_at(t_kv) {
19626 fa_vec = false;
19627 }
19628 let sp = fa_split_keys(t_kv, n_head_kv);
19629 let n_splits = if fa_vec {
19630 ((t_kv + sp - 1) / sp).max(1)
19631 } else {
19632 ((t_kv + 255) / 256).max(1)
19633 };
19634 (fa_vec, n_splits)
19635 }
19636
19637 pub fn fa_bucket_key(
19643 &self,
19644 t_kv: usize,
19645 head_dim: usize,
19646 n_head_kv: usize,
19647 g: bool,
19648 ) -> (bool, usize) {
19649 self.fa_geom_eager(t_kv, head_dim, n_head_kv, g)
19650 }
19651
19652 pub fn capture_graph_retained<F>(
19664 &self,
19665 step: F,
19666 ) -> Result<
19667 (
19668 cudarc::driver::CudaGraph,
19669 Vec<Box<dyn std::any::Any + Send>>,
19670 ),
19671 Box<dyn std::error::Error>,
19672 >
19673 where
19674 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
19675 {
19676 use cudarc::driver::sys::CUgraphInstantiate_flags;
19677 self.capture_graph_retained_flags(
19678 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
19679 step,
19680 )
19681 }
19682
19683 pub fn capture_graph_retained_flags<F>(
19688 &self,
19689 flags: cudarc::driver::sys::CUgraphInstantiate_flags,
19690 mut step: F,
19691 ) -> Result<
19692 (
19693 cudarc::driver::CudaGraph,
19694 Vec<Box<dyn std::any::Any + Send>>,
19695 ),
19696 Box<dyn std::error::Error>,
19697 >
19698 where
19699 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
19700 {
19701 use cudarc::driver::sys::CUstreamCaptureMode;
19702 self.capture_keep.lock().unwrap().clear();
19710 let was_tracking = self.gpu.ctx.is_event_tracking();
19711 if was_tracking {
19712 unsafe {
19713 self.gpu.ctx.disable_event_tracking();
19714 }
19715 }
19716 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
19717 self.capture_keep_on
19718 .store(true, std::sync::atomic::Ordering::Relaxed);
19719 let w = (|| {
19720 step(self)?;
19721 step(self)
19722 })();
19723 self.capture_keep_on
19724 .store(false, std::sync::atomic::Ordering::Relaxed);
19725 w?;
19726 self.gpu.stream().synchronize()?;
19727 self.gpu
19728 .stream()
19729 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
19730 let r = step(self);
19731 let g = self.gpu.stream().end_capture(flags);
19732 r?;
19733 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
19734 graph.upload()?;
19735 Ok(graph)
19736 };
19737 let result = run();
19738 self.capture_keep_on
19739 .store(false, std::sync::atomic::Ordering::Relaxed);
19740 if was_tracking {
19741 unsafe {
19742 self.gpu.ctx.enable_event_tracking();
19743 }
19744 }
19745 let keeper = std::mem::take(&mut *self.capture_keep.lock().unwrap());
19746 Ok((result?, keeper))
19747 }
19748
19749 pub fn capture_graph<F>(
19750 &self,
19751 mut step: F,
19752 ) -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>>
19753 where
19754 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
19755 {
19756 use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
19757 let was_tracking = self.gpu.ctx.is_event_tracking();
19765 if was_tracking {
19766 unsafe {
19767 self.gpu.ctx.disable_event_tracking();
19768 }
19769 }
19770 let iflag = {
19777 static F: std::sync::OnceLock<CUgraphInstantiate_flags> = std::sync::OnceLock::new();
19778 *F.get_or_init(|| match std::env::var("MEMRA_GRAPH_IFLAG").as_deref() {
19779 Ok("upload") => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_UPLOAD,
19782 Ok("priority") => {
19783 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY
19784 }
19785 _ => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
19786 })
19787 };
19788 let ct = {
19795 static T: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19796 *T.get_or_init(|| std::env::var("MEMRA_GRAPH_CAPTIME").as_deref() == Ok("1"))
19797 };
19798 let warmups = {
19821 static W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
19822 *W.get_or_init(|| {
19823 std::env::var("MEMRA_GRAPH_WARMUPS")
19824 .ok()
19825 .and_then(|v| v.parse().ok())
19826 .filter(|n| *n >= 1)
19827 .unwrap_or(1)
19828 })
19829 };
19830 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
19831 let t_w = std::time::Instant::now();
19832 for _ in 0..warmups {
19834 step(self)?;
19835 }
19836 self.gpu.stream().synchronize()?;
19837 let ms_warm = t_w.elapsed().as_secs_f64() * 1e3;
19838 let t_c = std::time::Instant::now();
19840 self.gpu
19841 .stream()
19842 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
19843 let r = step(self);
19846 let ms_body = t_c.elapsed().as_secs_f64() * 1e3;
19847 let t_i = std::time::Instant::now();
19848 let g = self.gpu.stream().end_capture(iflag);
19849 let ms_inst = t_i.elapsed().as_secs_f64() * 1e3;
19850 r?;
19851 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
19852 let t_u = std::time::Instant::now();
19853 graph.upload()?;
19854 if ct {
19855 println!(
19856 "[graph-captime] warmup2x {ms_warm:.2} ms capture-body {ms_body:.2} ms \
19857 instantiate {ms_inst:.2} ms upload {:.2} ms",
19858 t_u.elapsed().as_secs_f64() * 1e3
19859 );
19860 }
19861 Ok(graph)
19862 };
19863 let result = run();
19864 if was_tracking {
19865 unsafe {
19866 self.gpu.ctx.enable_event_tracking();
19867 }
19868 }
19869 result
19870 }
19871
19872 pub fn gdn_scan_s128_view(
19874 &self,
19875 q: &CudaSlice<f32>,
19876 k: &CudaSlice<f32>,
19877 v: &CudaSlice<f32>,
19878 g: &CudaSlice<f32>,
19879 beta: &CudaSlice<f32>,
19880 state_in: &cudarc::driver::CudaView<f32>,
19881 state_out: &mut cudarc::driver::CudaViewMut<f32>,
19882 o: &mut CudaSlice<f32>,
19883 n_head: usize,
19884 t: usize,
19885 scale: f32,
19886 ) -> Result<(), Box<dyn std::error::Error>> {
19887 let f = self.func("gdn_scan_s128");
19888 const S_V: u32 = 128;
19889 const WARP: u32 = 32;
19890 const COLS: u32 = 4;
19891 let cfg = LaunchConfig {
19892 grid_dim: (n_head as u32, 1, S_V / COLS),
19893 block_dim: (WARP, COLS, 1),
19894 shared_mem_bytes: 0,
19895 };
19896 let (h, ti) = (n_head as i32, t as i32);
19897 let __s_b = self.gpu.stream();
19898 let mut b = __s_b.launch_builder(&f);
19899 b.arg(q)
19900 .arg(k)
19901 .arg(v)
19902 .arg(g)
19903 .arg(beta)
19904 .arg(state_in)
19905 .arg(state_out)
19906 .arg(o)
19907 .arg(&h)
19908 .arg(&ti)
19909 .arg(&scale);
19910 unsafe {
19911 b.launch(cfg)?;
19912 }
19913 Ok(())
19914 }
19915
19916 pub fn ssm_conv1d_view(
19918 &self,
19919 x: &cudarc::driver::CudaView<f32>,
19920 w: &CudaSlice<f32>,
19921 y: &mut CudaSlice<f32>,
19922 conv_dim: usize,
19923 t: usize,
19924 d_conv: usize,
19925 silu: bool,
19926 ) -> Result<(), Box<dyn std::error::Error>> {
19927 let f = self.func("ssm_conv1d_silu_f32");
19928 let cfg = LaunchConfig {
19930 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
19931 block_dim: (256, 1, 1),
19932 shared_mem_bytes: 0,
19933 };
19934 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
19935 let __s_b = self.gpu.stream();
19936 let mut b = __s_b.launch_builder(&f);
19937 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
19938 unsafe {
19939 b.launch(cfg)?;
19940 }
19941 Ok(())
19942 }
19943
19944 pub fn ssm_conv1d_tm(
19951 &self,
19952 qkv_tm: &CudaSlice<f32>,
19953 w: &CudaSlice<f32>,
19954 y: &mut CudaSlice<f32>,
19955 conv_dim: usize,
19956 t: usize,
19957 d_conv: usize,
19958 ) -> Result<(), Box<dyn std::error::Error>> {
19959 let f = self.func("ssm_conv1d_tm_f32");
19960 let cfg = LaunchConfig {
19961 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
19962 block_dim: (256, 1, 1),
19963 shared_mem_bytes: 0,
19964 };
19965 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
19966 let __s_b = self.gpu.stream();
19967 let mut b = __s_b.launch_builder(&f);
19968 b.arg(qkv_tm).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc);
19969 unsafe {
19970 b.launch(cfg)?;
19971 }
19972 Ok(())
19973 }
19974
19975 pub fn ssm_conv1d_tm_state(
19983 &self,
19984 qkv_tm: &CudaSlice<f32>,
19985 conv_state: &mut CudaSlice<f32>,
19986 w: &CudaSlice<f32>,
19987 y: &mut CudaSlice<f32>,
19988 conv_dim: usize,
19989 t: usize,
19990 d_conv: usize,
19991 ) -> Result<(), Box<dyn std::error::Error>> {
19992 self.ssm_conv1d_tm_state_pad(qkv_tm, conv_state, w, y, conv_dim, t, d_conv, None)
19993 }
19994
19995 #[allow(clippy::too_many_arguments)]
19998 pub fn ssm_conv1d_tm_state_pad(
19999 &self,
20000 qkv_tm: &CudaSlice<f32>,
20001 conv_state: &mut CudaSlice<f32>,
20002 w: &CudaSlice<f32>,
20003 y: &mut CudaSlice<f32>,
20004 conv_dim: usize,
20005 t: usize,
20006 d_conv: usize,
20007 pad_len: Option<&CudaSlice<i32>>,
20008 ) -> Result<(), Box<dyn std::error::Error>> {
20009 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
20010 let ring_old = if t < d_conv - 1 {
20014 Some(self.clone_dtod(conv_state)?)
20015 } else {
20016 None
20017 };
20018 {
20019 let f = self.func("ssm_conv1d_tm_state_f32");
20020 let cfg = LaunchConfig {
20021 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
20022 block_dim: (256, 1, 1),
20023 shared_mem_bytes: 0,
20024 };
20025 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20026 let __s_b = self.gpu.stream();
20027 let mut b = __s_b.launch_builder(&f);
20028 b.arg(qkv_tm)
20029 .arg(&*conv_state)
20030 .arg(w)
20031 .arg(y)
20032 .arg(&cd)
20033 .arg(&ti)
20034 .arg(&dc);
20035 unsafe {
20036 b.launch(cfg)?;
20037 }
20038 }
20039 match (ring_old, pad_len) {
20040 (None, Some(len_d)) => {
20041 let f = self.func("ssm_conv_ring_update_dev_f32");
20042 let n = conv_dim * (d_conv - 1);
20043 let cfg = LaunchConfig::for_num_elems(n as u32);
20044 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20045 let __s_b = self.gpu.stream();
20046 let mut b = __s_b.launch_builder(&f);
20047 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
20048 unsafe {
20049 b.launch(cfg)?;
20050 }
20051 }
20052 (None, None) => {
20053 let f = self.func("ssm_conv_ring_update_f32");
20054 let n = conv_dim * (d_conv - 1);
20055 let cfg = LaunchConfig::for_num_elems(n as u32);
20056 let (cd, ti, dc) = (conv_dim as i32, t 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(&cd).arg(&ti).arg(&dc);
20060 unsafe {
20061 b.launch(cfg)?;
20062 }
20063 }
20064 (Some(old), _) => {
20065 self.ssm_conv_ring_rebuild(qkv_tm, &old, conv_state, conv_dim, t, d_conv)?
20066 }
20067 }
20068 Ok(())
20069 }
20070
20071 pub fn ssm_conv1d_tm_state_pad_v(
20073 &self,
20074 qkv_tm: &cudarc::driver::CudaView<f32>,
20075 conv_state: &mut CudaSlice<f32>,
20076 w: &CudaSlice<f32>,
20077 y: &mut CudaSlice<f32>,
20078 conv_dim: usize,
20079 t: usize,
20080 d_conv: usize,
20081 pad_len: Option<&CudaSlice<i32>>,
20082 ) -> Result<(), Box<dyn std::error::Error>> {
20083 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
20084 let ring_old = if t < d_conv - 1 {
20088 Some(self.clone_dtod(conv_state)?)
20089 } else {
20090 None
20091 };
20092 {
20093 let f = self.func("ssm_conv1d_tm_state_f32");
20094 let cfg = LaunchConfig {
20095 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
20096 block_dim: (256, 1, 1),
20097 shared_mem_bytes: 0,
20098 };
20099 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20100 let __s_b = self.gpu.stream();
20101 let mut b = __s_b.launch_builder(&f);
20102 b.arg(qkv_tm)
20103 .arg(&*conv_state)
20104 .arg(w)
20105 .arg(y)
20106 .arg(&cd)
20107 .arg(&ti)
20108 .arg(&dc);
20109 unsafe {
20110 b.launch(cfg)?;
20111 }
20112 }
20113 match (ring_old, pad_len) {
20114 (None, Some(len_d)) => {
20115 let f = self.func("ssm_conv_ring_update_dev_f32");
20116 let n = conv_dim * (d_conv - 1);
20117 let cfg = LaunchConfig::for_num_elems(n as u32);
20118 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20119 let __s_b = self.gpu.stream();
20120 let mut b = __s_b.launch_builder(&f);
20121 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
20122 unsafe {
20123 b.launch(cfg)?;
20124 }
20125 }
20126 (None, None) => {
20127 let f = self.func("ssm_conv_ring_update_f32");
20128 let n = conv_dim * (d_conv - 1);
20129 let cfg = LaunchConfig::for_num_elems(n as u32);
20130 let (cd, ti, dc) = (conv_dim as i32, t 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(&cd).arg(&ti).arg(&dc);
20134 unsafe {
20135 b.launch(cfg)?;
20136 }
20137 }
20138 (Some(_), _) => unreachable!(
20139 "ssm_conv1d_tm_state_pad_v: T < d_conv-1 has no view path (PRIME_MIN_T gates it)"
20140 ),
20141 }
20142 Ok(())
20143 }
20144
20145 pub fn ssm_conv_ring_rebuild(
20150 &self,
20151 qkv_tm: &CudaSlice<f32>,
20152 ring_old: &CudaSlice<f32>,
20153 conv_state: &mut CudaSlice<f32>,
20154 conv_dim: usize,
20155 tc: usize,
20156 d_conv: usize,
20157 ) -> Result<(), Box<dyn std::error::Error>> {
20158 let f = self.func("ssm_conv_ring_rebuild_f32");
20159 let n = conv_dim * (d_conv - 1);
20160 let cfg = LaunchConfig::for_num_elems(n as u32);
20161 let (cd, ti, dc) = (conv_dim as i32, tc as i32, d_conv as i32);
20162 let __s_b = self.gpu.stream();
20163 let mut b = __s_b.launch_builder(&f);
20164 b.arg(qkv_tm)
20165 .arg(ring_old)
20166 .arg(conv_state)
20167 .arg(&cd)
20168 .arg(&ti)
20169 .arg(&dc);
20170 unsafe {
20171 b.launch(cfg)?;
20172 }
20173 Ok(())
20174 }
20175
20176 #[allow(clippy::too_many_arguments)]
20181 pub fn gdn_prep_decode(
20182 &self,
20183 conv_out: &CudaSlice<f32>,
20184 beta_raw: &CudaSlice<f32>,
20185 alpha: &CudaSlice<f32>,
20186 dt_bias: &CudaSlice<f32>,
20187 a: &CudaSlice<f32>,
20188 q_l2: &mut CudaSlice<f32>,
20189 k_l2: &mut CudaSlice<f32>,
20190 v_g: &mut CudaSlice<f32>,
20191 beta: &mut CudaSlice<f32>,
20192 g_log: &mut CudaSlice<f32>,
20193 d_state: usize,
20194 num_v: usize,
20195 num_k: usize,
20196 key_dim: usize,
20197 eps: f32,
20198 ) -> Result<(), Box<dyn std::error::Error>> {
20199 let f = self.func("gdn_prep_decode_f32");
20200 let cfg = LaunchConfig {
20201 grid_dim: (num_v as u32, 1, 1),
20202 block_dim: (32, 4, 1),
20203 shared_mem_bytes: 0,
20204 };
20205 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
20206 let __s_b = self.gpu.stream();
20207 let mut b = __s_b.launch_builder(&f);
20208 b.arg(conv_out)
20209 .arg(beta_raw)
20210 .arg(alpha)
20211 .arg(dt_bias)
20212 .arg(a)
20213 .arg(q_l2)
20214 .arg(k_l2)
20215 .arg(v_g)
20216 .arg(beta)
20217 .arg(g_log)
20218 .arg(&ds)
20219 .arg(&nv)
20220 .arg(&nk)
20221 .arg(&kd)
20222 .arg(&eps);
20223 unsafe {
20224 b.launch(cfg)?;
20225 }
20226 Ok(())
20227 }
20228
20229 #[allow(clippy::too_many_arguments)]
20233 pub fn ssm_conv1d_gdn(
20234 &self,
20235 qkv_tm: &CudaSlice<f32>,
20236 w: &CudaSlice<f32>,
20237 q_g: &mut CudaSlice<f32>,
20238 k_g: &mut CudaSlice<f32>,
20239 v_g: &mut CudaSlice<f32>,
20240 conv_dim: usize,
20241 t: usize,
20242 d_conv: usize,
20243 d_state: usize,
20244 num_v: usize,
20245 num_k: usize,
20246 key_dim: usize,
20247 ) -> Result<(), Box<dyn std::error::Error>> {
20248 let f = self.func("ssm_conv1d_gdn_f32");
20249 let cfg = LaunchConfig {
20250 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
20251 block_dim: (256, 1, 1),
20252 shared_mem_bytes: 0,
20253 };
20254 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20255 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
20256 let __s_b = self.gpu.stream();
20257 let mut b = __s_b.launch_builder(&f);
20258 b.arg(qkv_tm)
20259 .arg(w)
20260 .arg(q_g)
20261 .arg(k_g)
20262 .arg(v_g)
20263 .arg(&cd)
20264 .arg(&ti)
20265 .arg(&dc)
20266 .arg(&ds)
20267 .arg(&nv)
20268 .arg(&nk)
20269 .arg(&kd);
20270 unsafe {
20271 b.launch(cfg)?;
20272 }
20273 Ok(())
20274 }
20275
20276 pub fn ssm_conv1d(
20277 &self,
20278 x: &CudaSlice<f32>,
20279 w: &CudaSlice<f32>,
20280 y: &mut CudaSlice<f32>,
20281 conv_dim: usize,
20282 t: usize,
20283 d_conv: usize,
20284 silu: bool,
20285 ) -> Result<(), Box<dyn std::error::Error>> {
20286 let f = self.func("ssm_conv1d_silu_f32");
20287 let cfg = LaunchConfig {
20288 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
20289 block_dim: (256, 1, 1),
20290 shared_mem_bytes: 0,
20291 };
20292 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
20293 let __s_b = self.gpu.stream();
20294 let mut b = __s_b.launch_builder(&f);
20295 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
20296 unsafe {
20297 b.launch(cfg)?;
20298 }
20299 Ok(())
20300 }
20301
20302 pub fn gdn_scan_s128(
20305 &self,
20306 q: &CudaSlice<f32>,
20307 k: &CudaSlice<f32>,
20308 v: &CudaSlice<f32>,
20309 g: &CudaSlice<f32>,
20310 beta: &CudaSlice<f32>,
20311 state_in: &CudaSlice<f32>,
20312 state_out: &mut CudaSlice<f32>,
20313 o: &mut CudaSlice<f32>,
20314 n_head: usize,
20315 t: usize,
20316 scale: f32,
20317 ) -> Result<(), Box<dyn std::error::Error>> {
20318 let f = self.func("gdn_scan_s128");
20319 const S_V: u32 = 128;
20320 const WARP: u32 = 32;
20321 const COLS_PER_BLOCK: u32 = 4;
20322 let cfg = LaunchConfig {
20323 grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
20324 block_dim: (WARP, COLS_PER_BLOCK, 1),
20325 shared_mem_bytes: 0,
20326 };
20327 let (h, ti) = (n_head as i32, t as i32);
20328 let __s_b = self.gpu.stream();
20329 let mut b = __s_b.launch_builder(&f);
20330 b.arg(q)
20331 .arg(k)
20332 .arg(v)
20333 .arg(g)
20334 .arg(beta)
20335 .arg(state_in)
20336 .arg(state_out)
20337 .arg(o)
20338 .arg(&h)
20339 .arg(&ti)
20340 .arg(&scale);
20341 unsafe {
20342 b.launch(cfg)?;
20343 }
20344 Ok(())
20345 }
20346
20347 #[allow(clippy::too_many_arguments)]
20352 pub fn ssm_conv1d_fused_decode_b(
20353 &self,
20354 qkv_cols: &CudaSlice<f32>,
20355 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
20356 w: &CudaSlice<f32>,
20357 conv_outs: &mut CudaSlice<f32>,
20358 conv_dim: usize,
20359 d_conv: usize,
20360 b_n: usize,
20361 ) -> Result<(), Box<dyn std::error::Error>> {
20362 let f = self.func("ssm_conv1d_fused_decode_b_f32");
20363 let cfg = LaunchConfig {
20364 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
20365 block_dim: (256, 1, 1),
20366 shared_mem_bytes: 0,
20367 };
20368 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20369 let __s_b = self.gpu.stream();
20370 let mut b = __s_b.launch_builder(&f);
20371 b.arg(qkv_cols)
20372 .arg(conv_state_ptrs)
20373 .arg(w)
20374 .arg(conv_outs)
20375 .arg(&cd)
20376 .arg(&dc);
20377 unsafe {
20378 b.launch(cfg)?;
20379 }
20380 Ok(())
20381 }
20382
20383 #[allow(clippy::too_many_arguments)]
20384 pub fn gdn_prep_decode_b(
20385 &self,
20386 conv_outs: &CudaSlice<f32>,
20387 beta_raws: &CudaSlice<f32>,
20388 alphas: &CudaSlice<f32>,
20389 dt_bias: &CudaSlice<f32>,
20390 a: &CudaSlice<f32>,
20391 q_l2: &mut CudaSlice<f32>,
20392 k_l2: &mut CudaSlice<f32>,
20393 v_g: &mut CudaSlice<f32>,
20394 beta: &mut CudaSlice<f32>,
20395 g_log: &mut CudaSlice<f32>,
20396 d_state: usize,
20397 num_v: usize,
20398 num_k: usize,
20399 key_dim: usize,
20400 eps: f32,
20401 conv_dim: usize,
20402 b_n: usize,
20403 ) -> Result<(), Box<dyn std::error::Error>> {
20404 let f = self.func("gdn_prep_decode_b_f32");
20405 let cfg = LaunchConfig {
20406 grid_dim: (num_v as u32, 1, b_n as u32),
20407 block_dim: (32, 4, 1),
20408 shared_mem_bytes: 0,
20409 };
20410 let (ds, nv, nk, kd, cd) = (
20411 d_state as i32,
20412 num_v as i32,
20413 num_k as i32,
20414 key_dim as i32,
20415 conv_dim as i32,
20416 );
20417 let __s_b = self.gpu.stream();
20418 let mut b = __s_b.launch_builder(&f);
20419 b.arg(conv_outs)
20420 .arg(beta_raws)
20421 .arg(alphas)
20422 .arg(dt_bias)
20423 .arg(a)
20424 .arg(q_l2)
20425 .arg(k_l2)
20426 .arg(v_g)
20427 .arg(beta)
20428 .arg(g_log)
20429 .arg(&ds)
20430 .arg(&nv)
20431 .arg(&nk)
20432 .arg(&kd)
20433 .arg(&eps)
20434 .arg(&cd);
20435 unsafe {
20436 b.launch(cfg)?;
20437 }
20438 Ok(())
20439 }
20440
20441 #[allow(clippy::too_many_arguments)]
20442 pub fn gdn_scan_s128_batched(
20443 &self,
20444 q: &CudaSlice<f32>,
20445 k: &CudaSlice<f32>,
20446 v: &CudaSlice<f32>,
20447 g: &CudaSlice<f32>,
20448 beta: &CudaSlice<f32>,
20449 state_in_ptrs: &cudarc::driver::CudaView<u64>,
20450 state_out_ptrs: &cudarc::driver::CudaView<u64>,
20451 o: &mut CudaSlice<f32>,
20452 n_head: usize,
20453 b_n: usize,
20454 scale: f32,
20455 ) -> Result<(), Box<dyn std::error::Error>> {
20456 let f = self.func("gdn_scan_s128_b");
20457 const S_V: u32 = 128;
20458 const WARP: u32 = 32;
20459 const COLS_PER_BLOCK: u32 = 4;
20460 let cfg = LaunchConfig {
20461 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
20462 block_dim: (WARP, COLS_PER_BLOCK, 1),
20463 shared_mem_bytes: 0,
20464 };
20465 let h = n_head as i32;
20466 let __s_b = self.gpu.stream();
20467 let mut b = __s_b.launch_builder(&f);
20468 b.arg(q)
20469 .arg(k)
20470 .arg(v)
20471 .arg(g)
20472 .arg(beta)
20473 .arg(state_in_ptrs)
20474 .arg(state_out_ptrs)
20475 .arg(o)
20476 .arg(&h)
20477 .arg(&scale);
20478 unsafe {
20479 b.launch(cfg)?;
20480 }
20481 Ok(())
20482 }
20483
20484 #[allow(clippy::too_many_arguments)]
20490 pub fn ssm_conv1d_fused_decode_b_view(
20491 &self,
20492 qkv_cols: &cudarc::driver::CudaView<f32>,
20493 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
20494 w: &CudaSlice<f32>,
20495 conv_outs: &mut CudaSlice<f32>,
20496 conv_dim: usize,
20497 d_conv: usize,
20498 b_n: usize,
20499 ) -> Result<(), Box<dyn std::error::Error>> {
20500 let f = self.func("ssm_conv1d_fused_decode_b_f32");
20501 let cfg = LaunchConfig {
20502 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
20503 block_dim: (256, 1, 1),
20504 shared_mem_bytes: 0,
20505 };
20506 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20507 let __s_b = self.gpu.stream();
20508 let mut b = __s_b.launch_builder(&f);
20509 b.arg(qkv_cols)
20510 .arg(conv_state_ptrs)
20511 .arg(w)
20512 .arg(conv_outs)
20513 .arg(&cd)
20514 .arg(&dc);
20515 unsafe {
20516 b.launch(cfg)?;
20517 }
20518 Ok(())
20519 }
20520
20521 #[allow(clippy::too_many_arguments)]
20522 pub fn gdn_prep_decode_b_view(
20523 &self,
20524 conv_outs: &CudaSlice<f32>,
20525 beta_raws: &cudarc::driver::CudaView<f32>,
20526 alphas: &cudarc::driver::CudaView<f32>,
20527 dt_bias: &CudaSlice<f32>,
20528 a: &CudaSlice<f32>,
20529 q_l2: &mut CudaSlice<f32>,
20530 k_l2: &mut CudaSlice<f32>,
20531 v_g: &mut CudaSlice<f32>,
20532 beta: &mut CudaSlice<f32>,
20533 g_log: &mut CudaSlice<f32>,
20534 d_state: usize,
20535 num_v: usize,
20536 num_k: usize,
20537 key_dim: usize,
20538 eps: f32,
20539 conv_dim: usize,
20540 b_n: usize,
20541 ) -> Result<(), Box<dyn std::error::Error>> {
20542 let f = self.func("gdn_prep_decode_b_f32");
20543 let cfg = LaunchConfig {
20544 grid_dim: (num_v as u32, 1, b_n as u32),
20545 block_dim: (32, 4, 1),
20546 shared_mem_bytes: 0,
20547 };
20548 let (ds, nv, nk, kd, cd) = (
20549 d_state as i32,
20550 num_v as i32,
20551 num_k as i32,
20552 key_dim as i32,
20553 conv_dim as i32,
20554 );
20555 let __s_b = self.gpu.stream();
20556 let mut b = __s_b.launch_builder(&f);
20557 b.arg(conv_outs)
20558 .arg(beta_raws)
20559 .arg(alphas)
20560 .arg(dt_bias)
20561 .arg(a)
20562 .arg(q_l2)
20563 .arg(k_l2)
20564 .arg(v_g)
20565 .arg(beta)
20566 .arg(g_log)
20567 .arg(&ds)
20568 .arg(&nv)
20569 .arg(&nk)
20570 .arg(&kd)
20571 .arg(&eps)
20572 .arg(&cd);
20573 unsafe {
20574 b.launch(cfg)?;
20575 }
20576 Ok(())
20577 }
20578
20579 #[allow(clippy::too_many_arguments)]
20580 pub fn gdn_scan_s128_batched_view(
20581 &self,
20582 q: &CudaSlice<f32>,
20583 k: &CudaSlice<f32>,
20584 v: &CudaSlice<f32>,
20585 g: &CudaSlice<f32>,
20586 beta: &CudaSlice<f32>,
20587 state_in_ptrs: &cudarc::driver::CudaView<u64>,
20588 state_out_ptrs: &cudarc::driver::CudaView<u64>,
20589 o: &mut cudarc::driver::CudaViewMut<f32>,
20590 n_head: usize,
20591 b_n: usize,
20592 scale: f32,
20593 ) -> Result<(), Box<dyn std::error::Error>> {
20594 let f = self.func("gdn_scan_s128_b");
20595 const S_V: u32 = 128;
20596 const WARP: u32 = 32;
20597 const COLS_PER_BLOCK: u32 = 4;
20598 let cfg = LaunchConfig {
20599 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
20600 block_dim: (WARP, COLS_PER_BLOCK, 1),
20601 shared_mem_bytes: 0,
20602 };
20603 let h = n_head as i32;
20604 let __s_b = self.gpu.stream();
20605 let mut b = __s_b.launch_builder(&f);
20606 b.arg(q)
20607 .arg(k)
20608 .arg(v)
20609 .arg(g)
20610 .arg(beta)
20611 .arg(state_in_ptrs)
20612 .arg(state_out_ptrs)
20613 .arg(o)
20614 .arg(&h)
20615 .arg(&scale);
20616 unsafe {
20617 b.launch(cfg)?;
20618 }
20619 Ok(())
20620 }
20621
20622 pub fn gdn_chunked_enabled() -> bool {
20631 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
20632 *E.get_or_init(|| {
20633 std::env::var("MEMRA_GDN_CHUNKED")
20634 .map(|v| v != "0")
20635 .unwrap_or(true)
20636 })
20637 }
20638
20639 pub fn gdn_chunk_size() -> usize {
20644 static C: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
20645 *C.get_or_init(|| {
20646 let c: usize = std::env::var("MEMRA_GDN_CHUNK")
20647 .ok()
20648 .and_then(|v| v.parse().ok())
20649 .unwrap_or(32);
20650 c.clamp(32, 128) / 32 * 32
20651 })
20652 }
20653
20654 #[allow(clippy::too_many_arguments)]
20659 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
20662 #[allow(clippy::too_many_arguments)]
20663 pub fn gdn_chunk_k123(
20664 &self,
20665 q: &CudaSlice<f32>,
20666 k: &CudaSlice<f32>,
20667 v: &CudaSlice<f32>,
20668 g: &CudaSlice<f32>,
20669 beta: &CudaSlice<f32>,
20670 wb16: Option<&mut CudaSlice<u8>>,
20671 n_head: usize,
20672 t: usize,
20673 c: usize,
20674 hk: usize,
20675 k2w: Option<(&CudaSlice<u8>, &CudaSlice<u8>, &mut CudaSlice<u8>)>,
20676 ) -> Result<
20677 (
20678 CudaSlice<f32>,
20679 CudaSlice<f32>,
20680 CudaSlice<f32>,
20681 CudaSlice<f32>,
20682 ),
20683 Box<dyn std::error::Error>,
20684 > {
20685 const D: usize = 128;
20686 let h = n_head;
20687 let nc = (t + c - 1) / c;
20688 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
20689 let mut gcum = self.uninit(t * h)?;
20690 let mut a = self.uninit(nc * h * c * c)?;
20691 let mut p = self.uninit(nc * h * c * c)?;
20692 let mut u = self.uninit(nc * h * c * D)?;
20693 let mut w = self.uninit(nc * h * c * D)?;
20694 {
20695 let f = self.func("gdn_chunk_cumgate_f32");
20697 let cfg = LaunchConfig {
20698 grid_dim: (nc as u32, h as u32, 1),
20699 block_dim: (32, 1, 1),
20700 shared_mem_bytes: 0,
20701 };
20702 let __s_b = self.gpu.stream();
20703 let mut b = __s_b.launch_builder(&f);
20704 b.arg(g).arg(&mut gcum).arg(&hi).arg(&ti).arg(&ci);
20705 unsafe {
20706 b.launch(cfg)?;
20707 }
20708 }
20709 if let Some((qb, kb, pb)) = k2w {
20710 assert!(c == 32, "gdn_k2_wgmma is a C==32 tile");
20713 let f = self.func("gdn_k2_wgmma");
20714 let cfg = LaunchConfig {
20715 grid_dim: (nc as u32, h as u32, 1),
20716 block_dim: (128, 1, 1),
20717 shared_mem_bytes: 0,
20718 };
20719 let hki = hk as i32;
20720 let __s_b = self.gpu.stream();
20721 let mut b = __s_b.launch_builder(&f);
20722 b.arg(qb)
20723 .arg(kb)
20724 .arg(&gcum)
20725 .arg(beta)
20726 .arg(&mut a)
20727 .arg(&mut *pb)
20728 .arg(&hi)
20729 .arg(&ti)
20730 .arg(&ci)
20731 .arg(&hki);
20732 unsafe {
20733 b.launch(cfg)?;
20734 }
20735 } else if c <= 64 && !portable_mma_gated() {
20736 let f = self.func("gdn_chunk_attn_f32");
20738 let jt = ((c + 31) / 32) as u32;
20739 let cfg = LaunchConfig {
20740 grid_dim: (nc as u32, h as u32, jt),
20741 block_dim: (256, 1, 1),
20742 shared_mem_bytes: 0,
20743 };
20744 let hki = hk as i32;
20745 let __s_b = self.gpu.stream();
20746 let mut b = __s_b.launch_builder(&f);
20747 b.arg(q)
20748 .arg(k)
20749 .arg(&gcum)
20750 .arg(beta)
20751 .arg(&mut a)
20752 .arg(&mut p)
20753 .arg(&hi)
20754 .arg(&ti)
20755 .arg(&ci)
20756 .arg(&hki);
20757 unsafe {
20758 b.launch(cfg)?;
20759 }
20760 } else {
20761 assert!(
20763 hk == h,
20764 "generic K2 is broadcast-only (de-broadcast rides C==32)"
20765 );
20766 let f = self.func("gdn_chunk_attn_g_f32");
20767 let cfg = LaunchConfig {
20768 grid_dim: (nc as u32, h as u32, 1),
20769 block_dim: (32, 8, 1),
20770 shared_mem_bytes: 0,
20771 };
20772 let __s_b = self.gpu.stream();
20773 let mut b = __s_b.launch_builder(&f);
20774 b.arg(q)
20775 .arg(k)
20776 .arg(&gcum)
20777 .arg(beta)
20778 .arg(&mut a)
20779 .arg(&mut p)
20780 .arg(&hi)
20781 .arg(&ti)
20782 .arg(&ci);
20783 unsafe {
20784 b.launch(cfg)?;
20785 }
20786 }
20787 {
20788 let cfg = LaunchConfig {
20790 grid_dim: (nc as u32, h as u32, 1),
20791 block_dim: (256, 1, 1),
20792 shared_mem_bytes: 0,
20793 };
20794 match c {
20795 32 | 64 => {
20796 let f = self.func(if c == 32 {
20797 "gdn_chunk_solve32_f32"
20798 } else {
20799 "gdn_chunk_solve64_f32"
20800 });
20801 let wb: u64 = match wb16 {
20803 Some(d) => self.addr_u8(d),
20804 None => 0,
20805 };
20806 let hki = hk as i32;
20807 let __s_b = self.gpu.stream();
20808 let mut b = __s_b.launch_builder(&f);
20809 b.arg(v)
20810 .arg(k)
20811 .arg(&a)
20812 .arg(&gcum)
20813 .arg(&mut u)
20814 .arg(&mut w)
20815 .arg(&wb)
20816 .arg(&hi)
20817 .arg(&ti)
20818 .arg(&hki);
20819 unsafe {
20820 b.launch(cfg)?;
20821 }
20822 }
20823 _ => {
20824 assert!(hk == h, "generic K3 is broadcast-only");
20825 let f = self.func("gdn_chunk_solve_f32");
20826 let __s_b = self.gpu.stream();
20827 let mut b = __s_b.launch_builder(&f);
20828 b.arg(v)
20829 .arg(k)
20830 .arg(&a)
20831 .arg(&gcum)
20832 .arg(&mut u)
20833 .arg(&mut w)
20834 .arg(&hi)
20835 .arg(&ti)
20836 .arg(&ci);
20837 unsafe {
20838 b.launch(cfg)?;
20839 }
20840 }
20841 }
20842 }
20843 Ok((gcum, p, u, w))
20844 }
20845
20846 pub fn gdn_db_on() -> bool {
20850 std::env::var("MEMRA_GDN_DB").as_deref() != Ok("0")
20851 }
20852
20853 pub fn gdn_mma_enabled(&self, c: usize) -> bool {
20856 !portable_mma_gated()
20857 && c == 32
20858 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
20859 Ok("1") => true,
20860 Ok("0") => false,
20861 _ => cfg!(memra_hopper_mma),
20862 }
20863 }
20864
20865 pub fn gdn_wgmma_on(&self, c: usize) -> bool {
20868 self.gdn_mma_enabled(c)
20869 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
20870 Ok("0") => false,
20871 Ok("1") => true,
20872 _ => cfg!(memra_hopper_mma),
20873 }
20874 }
20875
20876 #[allow(clippy::too_many_arguments)]
20881 pub fn ssm_conv1d_gdn_state_pad(
20882 &self,
20883 qkv_tm: &cudarc::driver::CudaView<f32>,
20884 conv_state: &mut CudaSlice<f32>,
20885 w: &CudaSlice<f32>,
20886 q_g: &mut CudaSlice<f32>,
20887 k_g: &mut CudaSlice<f32>,
20888 v_g: &mut CudaSlice<f32>,
20889 conv_dim: usize,
20890 t: usize,
20891 d_conv: usize,
20892 d_state: usize,
20893 num_v: usize,
20894 num_k: usize,
20895 key_dim: usize,
20896 hk: usize,
20897 pad_len: Option<&CudaSlice<i32>>,
20898 ) -> Result<(), Box<dyn std::error::Error>> {
20899 assert!(
20900 t >= d_conv - 1,
20901 "fused state conv requires T >= pad (PRIME_MIN_T gates)"
20902 );
20903 {
20904 let f = self.func("ssm_conv1d_gdn_state_f32");
20905 let cfg = LaunchConfig {
20906 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
20907 block_dim: (256, 1, 1),
20908 shared_mem_bytes: 0,
20909 };
20910 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
20911 let (ds, nv, nk, kd, hki) = (
20912 d_state as i32,
20913 num_v as i32,
20914 num_k as i32,
20915 key_dim as i32,
20916 hk as i32,
20917 );
20918 let __s_b = self.gpu.stream();
20919 let mut b = __s_b.launch_builder(&f);
20920 b.arg(qkv_tm)
20921 .arg(&*conv_state)
20922 .arg(w)
20923 .arg(q_g)
20924 .arg(k_g)
20925 .arg(v_g)
20926 .arg(&cd)
20927 .arg(&ti)
20928 .arg(&dc)
20929 .arg(&ds)
20930 .arg(&nv)
20931 .arg(&nk)
20932 .arg(&kd)
20933 .arg(&hki);
20934 unsafe {
20935 b.launch(cfg)?;
20936 }
20937 }
20938 match pad_len {
20939 Some(len_d) => {
20940 let f = self.func("ssm_conv_ring_update_dev_f32");
20941 let n = conv_dim * (d_conv - 1);
20942 let cfg = LaunchConfig::for_num_elems(n as u32);
20943 let (cd, dc) = (conv_dim as i32, d_conv as i32);
20944 let __s_b = self.gpu.stream();
20945 let mut b = __s_b.launch_builder(&f);
20946 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
20947 unsafe {
20948 b.launch(cfg)?;
20949 }
20950 }
20951 None => {
20952 let f = self.func("ssm_conv_ring_update_f32");
20953 let n = conv_dim * (d_conv - 1);
20954 let cfg = LaunchConfig::for_num_elems(n as u32);
20955 let (cd, ti, dc) = (conv_dim as i32, t 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(&cd).arg(&ti).arg(&dc);
20959 unsafe {
20960 b.launch(cfg)?;
20961 }
20962 }
20963 }
20964 Ok(())
20965 }
20966
20967 pub fn gdn_chunk_alloc(
20971 &self,
20972 n_head: usize,
20973 t: usize,
20974 c: usize,
20975 hk: usize,
20976 ) -> Result<GdnChunkBufs, Box<dyn std::error::Error>> {
20977 const D: usize = 128;
20978 assert!(
20979 c == 32,
20980 "gdn_chunk_alloc: varlen chain is the C==32 mma pair"
20981 );
20982 let h = n_head;
20983 let nc = (t + c - 1) / c;
20984 Ok(GdnChunkBufs {
20985 gcum: self.uninit(t * h)?,
20986 a: self.uninit(nc * h * c * c)?,
20987 p: self.uninit(nc * h * c * c)?,
20988 u: self.uninit(nc * h * c * D)?,
20989 w: self.uninit(nc * h * c * D)?,
20990 kb16: self.alloc_u8_uninit(t * hk * D * 2)?,
20991 wb16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
20992 y16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
20993 ssnap16: self.alloc_u8_uninit(nc * h * D * D * 2)?,
20994 qb16: self.alloc_u8_uninit(t * hk * D * 2)?,
20995 pb16: self.alloc_u8_uninit(nc * h * c * c * 2)?,
20996 o: self.uninit(D * h * t)?,
20997 t,
20998 nc,
20999 })
21000 }
21001
21002 pub fn f32_to_bf16_v(
21004 &self,
21005 x: &cudarc::driver::CudaView<f32>,
21006 dst: &mut CudaSlice<u8>,
21007 n: usize,
21008 ) -> Result<(), Box<dyn std::error::Error>> {
21009 let f = self.func("f32_to_bf16_bulk");
21010 let ni = n as i64;
21011 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
21012 let __s_b = self.gpu.stream();
21013 let mut b = __s_b.launch_builder(&f);
21014 b.arg(x).arg(dst).arg(&ni);
21015 unsafe {
21016 b.launch(cfg)?;
21017 }
21018 Ok(())
21019 }
21020
21021 pub fn f32_to_bf16_into(
21023 &self,
21024 x: &CudaSlice<f32>,
21025 dst: &mut CudaSlice<u8>,
21026 n: usize,
21027 ) -> Result<(), Box<dyn std::error::Error>> {
21028 let f = self.func("f32_to_bf16_bulk");
21029 let ni = n as i64;
21030 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
21031 let __s_b = self.gpu.stream();
21032 let mut b = __s_b.launch_builder(&f);
21033 b.arg(x).arg(dst).arg(&ni);
21034 unsafe {
21035 b.launch(cfg)?;
21036 }
21037 Ok(())
21038 }
21039
21040 pub fn gdn_chunk_k123_vl8(
21043 &self,
21044 seqs: &[GdnSeqVl],
21045 n_head: usize,
21046 hk: usize,
21047 wq: Option<&GdnWVl8>,
21048 ) -> Result<(), Box<dyn std::error::Error>> {
21049 let b = seqs.len();
21050 assert!(b >= 1 && b <= 8, "gdn_chunk_k123_vl8: 1..=8 sequences");
21051 let mut packed = [GdnSeqVl::default(); 8];
21052 packed[..b].copy_from_slice(seqs);
21053 let v = GdnVl8(packed);
21054 let (hi, ci) = (n_head as i32, 32i32);
21055 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
21056 {
21057 let f = self.func("gdn_chunk_cumgate_vl");
21058 let cfg = LaunchConfig {
21059 grid_dim: (max_nc, n_head as u32, b as u32),
21060 block_dim: (32, 1, 1),
21061 shared_mem_bytes: 0,
21062 };
21063 let __s_lb = self.gpu.stream();
21064 let mut lb = __s_lb.launch_builder(&f);
21065 lb.arg(&v).arg(&hi).arg(&ci);
21066 unsafe {
21067 lb.launch(cfg)?;
21068 }
21069 }
21070 let hki = hk as i32;
21071 if let Some(w) = wq {
21072 let f = self.func("gdn_k2_wgmma_vl");
21074 let cfg = LaunchConfig {
21075 grid_dim: (max_nc, n_head as u32, b as u32),
21076 block_dim: (128, 1, 1),
21077 shared_mem_bytes: 0,
21078 };
21079 let __s_lb = self.gpu.stream();
21080 let mut lb = __s_lb.launch_builder(&f);
21081 lb.arg(&v).arg(w).arg(&hi).arg(&ci).arg(&hki);
21082 unsafe {
21083 lb.launch(cfg)?;
21084 }
21085 } else {
21086 let f = self.func("gdn_chunk_attn_vl");
21087 let cfg = LaunchConfig {
21088 grid_dim: (max_nc, n_head as u32, b as u32),
21089 block_dim: (256, 1, 1),
21090 shared_mem_bytes: 0,
21091 };
21092 let __s_lb = self.gpu.stream();
21093 let mut lb = __s_lb.launch_builder(&f);
21094 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
21095 unsafe {
21096 lb.launch(cfg)?;
21097 }
21098 }
21099 {
21100 let f = self.func("gdn_chunk_solve32_vl");
21101 let cfg = LaunchConfig {
21102 grid_dim: (max_nc, n_head as u32, b as u32),
21103 block_dim: (256, 1, 1),
21104 shared_mem_bytes: 0,
21105 };
21106 let __s_lb = self.gpu.stream();
21107 let mut lb = __s_lb.launch_builder(&f);
21108 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
21109 unsafe {
21110 lb.launch(cfg)?;
21111 }
21112 }
21113 Ok(())
21114 }
21115
21116 #[allow(clippy::too_many_arguments)]
21120 pub fn gdn_prep_vl8(
21121 &self,
21122 seqs: &[GdnPrepVl],
21123 conv_w: &CudaSlice<f32>,
21124 dt_bias: &CudaSlice<f32>,
21125 a: &CudaSlice<f32>,
21126 conv_dim: usize,
21127 d_conv: usize,
21128 d_state: usize,
21129 num_v: usize,
21130 num_k: usize,
21131 key_dim: usize,
21132 hk: usize,
21133 eps: f32,
21134 ) -> Result<(), Box<dyn std::error::Error>> {
21135 let b = seqs.len();
21136 assert!(b >= 1 && b <= 8);
21137 let mut packed = [GdnPrepVl::default(); 8];
21138 packed[..b].copy_from_slice(seqs);
21139 let v = GdnPrepVl8(packed);
21140 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
21141 let (cdi, dci) = (conv_dim as i32, d_conv as i32);
21142 let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
21143 assert!(
21144 conv_fuse || hk == num_v,
21145 "de-broadcast requires the fused conv"
21146 );
21147 if conv_fuse {
21148 let f = self.func("ssm_conv1d_gdn_state_vl");
21149 let cfg = LaunchConfig {
21150 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
21151 block_dim: (256, 1, 1),
21152 shared_mem_bytes: 0,
21153 };
21154 let (dsi, nvi, nki, kdi, hki) = (
21155 d_state as i32,
21156 num_v as i32,
21157 num_k as i32,
21158 key_dim as i32,
21159 hk as i32,
21160 );
21161 let __s_lb = self.gpu.stream();
21162 let mut lb = __s_lb.launch_builder(&f);
21163 lb.arg(&v)
21164 .arg(conv_w)
21165 .arg(&cdi)
21166 .arg(&dci)
21167 .arg(&dsi)
21168 .arg(&nvi)
21169 .arg(&nki)
21170 .arg(&kdi)
21171 .arg(&hki);
21172 unsafe {
21173 lb.launch(cfg)?;
21174 }
21175 } else {
21176 let f = self.func("ssm_conv1d_tm_state_vl");
21177 let cfg = LaunchConfig {
21178 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
21179 block_dim: (256, 1, 1),
21180 shared_mem_bytes: 0,
21181 };
21182 let __s_lb = self.gpu.stream();
21183 let mut lb = __s_lb.launch_builder(&f);
21184 lb.arg(&v).arg(conv_w).arg(&cdi).arg(&dci);
21185 unsafe {
21186 lb.launch(cfg)?;
21187 }
21188 }
21189 {
21190 let f = self.func("ssm_conv_ring_update_vl");
21191 let n = (conv_dim * (d_conv - 1)) as u32;
21192 let cfg = LaunchConfig {
21193 grid_dim: (n.div_ceil(256), 1, b as u32),
21194 block_dim: (256, 1, 1),
21195 shared_mem_bytes: 0,
21196 };
21197 let __s_lb = self.gpu.stream();
21198 let mut lb = __s_lb.launch_builder(&f);
21199 lb.arg(&v).arg(&cdi).arg(&dci);
21200 unsafe {
21201 lb.launch(cfg)?;
21202 }
21203 }
21204 if !conv_fuse {
21205 let f = self.func("qkv_to_gdn_repack_vl");
21206 let n = max_t * (num_v * d_state) as u32;
21207 let cfg = LaunchConfig {
21208 grid_dim: (n.div_ceil(256), 1, b as u32),
21209 block_dim: (256, 1, 1),
21210 shared_mem_bytes: 0,
21211 };
21212 let (dsi, nvi, nki, kdi) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
21213 let __s_lb = self.gpu.stream();
21214 let mut lb = __s_lb.launch_builder(&f);
21215 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&nki).arg(&kdi);
21216 unsafe {
21217 lb.launch(cfg)?;
21218 }
21219 }
21220 if Self::l2_v2_on(d_state) {
21221 let f = self.func("gdn_l2_v2_vl");
21222 let cfg = LaunchConfig {
21223 grid_dim: ((max_t * hk as u32).div_ceil(8), 2, b as u32),
21224 block_dim: (256, 1, 1),
21225 shared_mem_bytes: 0,
21226 };
21227 let (dsi, nvi) = (d_state as i32, hk as i32);
21228 let __s_lb = self.gpu.stream();
21229 let mut lb = __s_lb.launch_builder(&f);
21230 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
21231 unsafe {
21232 lb.launch(cfg)?;
21233 }
21234 } else {
21235 let f = self.func("gdn_l2_vl");
21236 let cfg = LaunchConfig {
21237 grid_dim: (max_t * hk as u32, 2, b as u32),
21238 block_dim: (256, 1, 1),
21239 shared_mem_bytes: 0,
21240 };
21241 let (dsi, nvi) = (d_state as i32, hk as i32);
21242 let __s_lb = self.gpu.stream();
21243 let mut lb = __s_lb.launch_builder(&f);
21244 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
21245 unsafe {
21246 lb.launch(cfg)?;
21247 }
21248 }
21249 {
21250 let f = self.func("gdn_gate_prep_vl");
21251 let n = max_t * num_v as u32;
21252 let cfg = LaunchConfig {
21253 grid_dim: (n.div_ceil(256), 1, b as u32),
21254 block_dim: (256, 1, 1),
21255 shared_mem_bytes: 0,
21256 };
21257 let nvi = num_v as i32;
21258 let __s_lb = self.gpu.stream();
21259 let mut lb = __s_lb.launch_builder(&f);
21260 lb.arg(&v).arg(dt_bias).arg(a).arg(&nvi);
21261 unsafe {
21262 lb.launch(cfg)?;
21263 }
21264 }
21265 Ok(())
21266 }
21267
21268 pub fn gdn_mirror_vl8(
21270 &self,
21271 seqs: &[GdnSeqVl],
21272 n_head: usize,
21273 which: i32,
21274 hk: usize,
21275 ) -> Result<(), Box<dyn std::error::Error>> {
21276 let b = seqs.len();
21277 assert!(b >= 1 && b <= 8);
21278 let mut packed = [GdnSeqVl::default(); 8];
21279 packed[..b].copy_from_slice(seqs);
21280 let v = GdnVl8(packed);
21281 let ept = (if which == 0 { hk } else { n_head } * 128) as i32;
21282 let max_n = seqs
21283 .iter()
21284 .map(|s| {
21285 if which == 0 {
21286 s.t as i64 * ept as i64
21287 } else {
21288 s.nc as i64 * ept as i64 * 32
21289 }
21290 })
21291 .max()
21292 .unwrap();
21293 let f = self.func("gdn_mirror_vl");
21294 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
21295 let cfg = LaunchConfig {
21296 grid_dim: (blocks, 1, b as u32),
21297 block_dim: (256, 1, 1),
21298 shared_mem_bytes: 0,
21299 };
21300 let __s_lb = self.gpu.stream();
21301 let mut lb = __s_lb.launch_builder(&f);
21302 lb.arg(&v).arg(&ept).arg(&which);
21303 unsafe {
21304 lb.launch(cfg)?;
21305 }
21306 Ok(())
21307 }
21308
21309 pub fn gdn_tail_vl8(
21311 &self,
21312 seqs: &[GdnPrepVl],
21313 norm_w: &CudaSlice<f32>,
21314 d_state: usize,
21315 num_v: usize,
21316 eps: f32,
21317 ) -> Result<(), Box<dyn std::error::Error>> {
21318 let b = seqs.len();
21319 assert!(b >= 1 && b <= 8);
21320 let mut packed = [GdnPrepVl::default(); 8];
21321 packed[..b].copy_from_slice(seqs);
21322 let v = GdnPrepVl8(packed);
21323 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
21324 let f = self.func("gated_rmsnorm_f16out_vl");
21325 let cfg = LaunchConfig {
21327 grid_dim: (max_t * num_v as u32, 1, b as u32),
21328 block_dim: (128, 1, 1),
21329 shared_mem_bytes: 0,
21330 };
21331 let (dsi, nvi) = (d_state as i32, num_v as i32);
21332 let __s_lb = self.gpu.stream();
21333 let mut lb = __s_lb.launch_builder(&f);
21334 lb.arg(&v).arg(norm_w).arg(&dsi).arg(&nvi).arg(&eps);
21335 unsafe {
21336 lb.launch(cfg)?;
21337 }
21338 Ok(())
21339 }
21340
21341 pub fn addr_f32(&self, x: &CudaSlice<f32>) -> u64 {
21344 use cudarc::driver::DevicePtr;
21345 let s = self.gpu.stream();
21346 let (p, _g) = x.device_ptr(&s);
21347 p as u64
21348 }
21349 pub fn addr_f32_mut(&self, x: &mut CudaSlice<f32>) -> u64 {
21350 use cudarc::driver::DevicePtrMut;
21351 let s = self.gpu.stream();
21352 let (p, _g) = x.device_ptr_mut(&s);
21353 p as u64
21354 }
21355 pub fn addr_f32v(&self, x: &cudarc::driver::CudaView<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_u8(&self, x: &CudaSlice<u8>) -> u64 {
21362 use cudarc::driver::DevicePtr;
21363 let s = self.gpu.stream();
21364 let (p, _g) = x.device_ptr(&s);
21365 p as u64
21366 }
21367
21368 pub fn gdn_chunk_vl8(
21372 &self,
21373 seqs: &[GdnSeqVl],
21374 n_head: usize,
21375 scale: f32,
21376 hk: usize,
21377 wq: Option<&GdnWVl8>,
21378 ) -> Result<(), Box<dyn std::error::Error>> {
21379 const NSPLIT: u32 = 4;
21380 let b = seqs.len();
21381 assert!(b >= 1 && b <= 8, "gdn_chunk_vl8: 1..=8 sequences");
21382 let mut packed = [GdnSeqVl::default(); 8];
21383 packed[..b].copy_from_slice(seqs);
21384 let v = GdnVl8(packed);
21385 let (hi, ci) = (n_head as i32, 32i32);
21386 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
21387 let hki = hk as i32;
21388 if let Some(w) = wq {
21389 let f = self.func("gdn_k45_wgmma_vl");
21391 let cfg = LaunchConfig {
21392 grid_dim: (n_head as u32, NSPLIT, b as u32),
21393 block_dim: (256, 1, 1),
21394 shared_mem_bytes: 0,
21395 };
21396 let __s_lb = self.gpu.stream();
21397 let mut lb = __s_lb.launch_builder(&f);
21398 lb.arg(&v).arg(w).arg(&scale).arg(&hi).arg(&ci).arg(&hki);
21399 unsafe {
21400 lb.launch(cfg)?;
21401 }
21402 let _ = max_nc;
21403 return Ok(());
21404 }
21405 {
21406 let f = self.func("gdn_chunk_state_mma_vl");
21407 let cfg = LaunchConfig {
21408 grid_dim: (n_head as u32, NSPLIT, b as u32),
21409 block_dim: (256, 1, 1),
21410 shared_mem_bytes: 0,
21411 };
21412 let __s_lb = self.gpu.stream();
21413 let mut lb = __s_lb.launch_builder(&f);
21414 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
21415 unsafe {
21416 lb.launch(cfg)?;
21417 }
21418 }
21419 {
21420 let f = self.func("gdn_chunk_output_mma_vl");
21421 let cfg = LaunchConfig {
21422 grid_dim: (max_nc, n_head as u32, b as u32),
21423 block_dim: (256, 1, 1),
21424 shared_mem_bytes: 0,
21425 };
21426 let __s_lb = self.gpu.stream();
21427 let mut lb = __s_lb.launch_builder(&f);
21428 lb.arg(&v).arg(&hi).arg(&ci).arg(&scale).arg(&hki);
21429 unsafe {
21430 lb.launch(cfg)?;
21431 }
21432 }
21433 Ok(())
21434 }
21435 pub fn gdn_scan_chunked(
21436 &self,
21437 q: &CudaSlice<f32>,
21438 k: &CudaSlice<f32>,
21439 v: &CudaSlice<f32>,
21440 g: &CudaSlice<f32>,
21441 beta: &CudaSlice<f32>,
21442 kb16_pre: Option<&CudaSlice<u8>>,
21443 qb16_pre: Option<&CudaSlice<u8>>,
21444 state_in: &CudaSlice<f32>,
21445 state_out: &mut CudaSlice<f32>,
21446 o: &mut CudaSlice<f32>,
21447 n_head: usize,
21448 t: usize,
21449 scale: f32,
21450 c: usize,
21451 hk: usize,
21452 ) -> Result<(), Box<dyn std::error::Error>> {
21453 const D: usize = 128;
21454 const NSPLIT: u32 = 4;
21455 assert!(c >= 1 && c <= 128, "gdn_scan_chunked: C must be in 1..=128");
21456 let h = n_head;
21457 let nc = (t + c - 1) / c;
21458 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
21459 let gdn_mma_pre = !portable_mma_gated()
21463 && c == 32
21464 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
21465 Ok("1") => true,
21466 Ok("0") => false,
21467 _ => cfg!(memra_hopper_mma),
21468 };
21469 let mut wb16_pre: Option<CudaSlice<u8>> = if gdn_mma_pre {
21470 Some(self.alloc_u8_uninit(nc * h * c * D * 2)?)
21471 } else {
21472 None
21473 };
21474 let gdn_wgmma_pre = gdn_mma_pre
21478 && match std::env::var("MEMRA_GDN_WGMMA").as_deref() {
21479 Ok("0") => false,
21480 Ok("1") => true,
21481 _ => cfg!(memra_hopper_mma),
21482 };
21483 let nk = t * hk * D;
21484 let mut kb16_local: Option<CudaSlice<u8>> = None;
21485 if gdn_mma_pre && kb16_pre.is_none() {
21486 let mut kb = self.alloc_u8_uninit(nk * 2)?;
21487 let f = self.func("f32_to_bf16_bulk");
21488 let n2 = nk as i64;
21489 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
21490 let __s_b = self.gpu.stream();
21491 let mut b = __s_b.launch_builder(&f);
21492 b.arg(k).arg(&mut kb).arg(&n2);
21493 unsafe {
21494 b.launch(cfg2)?;
21495 }
21496 kb16_local = Some(kb);
21497 }
21498 let kb16_ref0: Option<&CudaSlice<u8>> = kb16_local.as_ref().or(kb16_pre);
21499 if let Some(kb) = kb16_pre {
21500 assert!(kb.len() >= nk * 2, "kb16_pre too small");
21501 }
21502 let mut qb16: Option<CudaSlice<u8>> = None;
21503 let mut pb16: Option<CudaSlice<u8>> = None;
21504 if gdn_wgmma_pre {
21505 if qb16_pre.is_none() {
21508 let mut qb = self.alloc_u8_uninit(nk * 2)?;
21509 let f = self.func("f32_to_bf16_bulk");
21510 let n2 = nk as i64;
21511 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
21512 let __s_b = self.gpu.stream();
21513 let mut b = __s_b.launch_builder(&f);
21514 b.arg(q).arg(&mut qb).arg(&n2);
21515 unsafe {
21516 b.launch(cfg2)?;
21517 }
21518 qb16 = Some(qb);
21519 } else if let Some(qb) = qb16_pre {
21520 assert!(qb.len() >= nk * 2, "qb16_pre too small");
21521 }
21522 pb16 = Some(self.alloc_u8_uninit(nc * h * c * c * 2)?);
21523 }
21524 let qb16_ref0: Option<&CudaSlice<u8>> = qb16.as_ref().or(qb16_pre);
21525 let k2w = if gdn_wgmma_pre {
21526 Some((
21527 *qb16_ref0.as_ref().unwrap(),
21528 *kb16_ref0.as_ref().unwrap(),
21529 pb16.as_mut().unwrap(),
21530 ))
21531 } else {
21532 None
21533 };
21534 let (gcum, p, u, w) =
21535 self.gdn_chunk_k123(q, k, v, g, beta, wb16_pre.as_mut(), n_head, t, c, hk, k2w)?;
21536 let _ = &w;
21537 let mut y = self.uninit(nc * h * c * D)?;
21538 let mut ssnap = self.uninit(nc * h * D * D)?; let gdn_mma = !portable_mma_gated()
21550 && c == 32
21551 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
21552 Ok("1") => true,
21553 Ok("0") => false,
21554 _ => cfg!(memra_hopper_mma),
21555 };
21556 if gdn_mma {
21557 let wb16 = wb16_pre
21558 .take()
21559 .expect("mma path pre-allocates wb16 (K3 store fold)");
21560 let kb16_ref: &CudaSlice<u8> = kb16_ref0.expect("mma path pre-builds kb16 above K123");
21561 if gdn_wgmma_pre {
21573 let qb16 = qb16_ref0.unwrap();
21575 let pb16 = pb16.as_ref().unwrap();
21576 {
21577 let f = self.func("gdn_k45_wgmma");
21578 let cfg = LaunchConfig {
21579 grid_dim: (h as u32, 4, 1),
21580 block_dim: (256, 1, 1),
21581 shared_mem_bytes: 0,
21582 };
21583 let hki = hk as i32;
21584 let __s_b = self.gpu.stream();
21585 let mut b = __s_b.launch_builder(&f);
21586 b.arg(kb16_ref)
21587 .arg(&gcum)
21588 .arg(beta)
21589 .arg(&u)
21590 .arg(&wb16)
21591 .arg(qb16)
21592 .arg(pb16)
21593 .arg(o)
21594 .arg(&scale)
21595 .arg(state_in)
21596 .arg(&mut *state_out)
21597 .arg(&hi)
21598 .arg(&ti)
21599 .arg(&ci)
21600 .arg(&hki);
21601 unsafe {
21602 b.launch(cfg)?;
21603 }
21604 }
21605 return Ok(());
21606 }
21607 let mut y16 = self.alloc_u8_uninit(nc * h * c * D * 2)?;
21611 let mut ssnap16 = self.alloc_u8_uninit(nc * h * D * D * 2)?;
21612 {
21613 let f = self.func("gdn_chunk_state_mma");
21614 let cfg = LaunchConfig {
21615 grid_dim: (h as u32, NSPLIT, 1),
21616 block_dim: (256, 1, 1),
21617 shared_mem_bytes: 0,
21618 };
21619 let hki = hk as i32;
21620 let __s_b = self.gpu.stream();
21621 let mut b = __s_b.launch_builder(&f);
21622 b.arg(kb16_ref)
21623 .arg(&gcum)
21624 .arg(beta)
21625 .arg(&u)
21626 .arg(&wb16)
21627 .arg(&mut y16)
21628 .arg(&mut ssnap16)
21629 .arg(state_in)
21630 .arg(&mut *state_out)
21631 .arg(&hi)
21632 .arg(&ti)
21633 .arg(&ci)
21634 .arg(&hki);
21635 unsafe {
21636 b.launch(cfg)?;
21637 }
21638 }
21639 {
21640 let f = self.func("gdn_chunk_output_mma");
21642 let jt = ((c + 31) / 32) as u32;
21643 let cfg = LaunchConfig {
21644 grid_dim: (nc as u32, h as u32, jt),
21645 block_dim: (256, 1, 1),
21646 shared_mem_bytes: 0,
21647 };
21648 let hki = hk as i32;
21649 let __s_b = self.gpu.stream();
21650 let mut b = __s_b.launch_builder(&f);
21651 b.arg(q)
21652 .arg(&gcum)
21653 .arg(&p)
21654 .arg(&y16)
21655 .arg(&ssnap16)
21656 .arg(o)
21657 .arg(&hi)
21658 .arg(&ti)
21659 .arg(&ci)
21660 .arg(&scale)
21661 .arg(&hki);
21662 unsafe {
21663 b.launch(cfg)?;
21664 }
21665 }
21666 return Ok(());
21667 }
21668 {
21669 let f = self.func("gdn_chunk_state_f32");
21671 let cfg = LaunchConfig {
21672 grid_dim: (h as u32, NSPLIT, 1),
21673 block_dim: (256, 1, 1),
21674 shared_mem_bytes: 0,
21675 };
21676 let __s_b = self.gpu.stream();
21677 let mut b = __s_b.launch_builder(&f);
21678 b.arg(k)
21679 .arg(&gcum)
21680 .arg(beta)
21681 .arg(&u)
21682 .arg(&w)
21683 .arg(&mut y)
21684 .arg(&mut ssnap)
21685 .arg(state_in)
21686 .arg(&mut *state_out)
21687 .arg(&hi)
21688 .arg(&ti)
21689 .arg(&ci);
21690 unsafe {
21691 b.launch(cfg)?;
21692 }
21693 }
21694 {
21695 let f = self.func("gdn_chunk_output_f32");
21697 let jt = ((c + 31) / 32) as u32;
21698 let cfg = LaunchConfig {
21699 grid_dim: (nc as u32, h as u32, jt),
21700 block_dim: (256, 1, 1),
21701 shared_mem_bytes: 0,
21702 };
21703 let __s_b = self.gpu.stream();
21704 let mut b = __s_b.launch_builder(&f);
21705 b.arg(q)
21706 .arg(&gcum)
21707 .arg(&p)
21708 .arg(&y)
21709 .arg(&ssnap)
21710 .arg(o)
21711 .arg(&hi)
21712 .arg(&ti)
21713 .arg(&ci)
21714 .arg(&scale);
21715 unsafe {
21716 b.launch(cfg)?;
21717 }
21718 }
21719 Ok(())
21720 }
21721
21722 #[allow(clippy::too_many_arguments)]
21731 #[allow(clippy::too_many_arguments)]
21732 pub fn gdn_scan_prefill(
21733 &self,
21734 q: &CudaSlice<f32>,
21735 k: &CudaSlice<f32>,
21736 v: &CudaSlice<f32>,
21737 g: &CudaSlice<f32>,
21738 beta: &CudaSlice<f32>,
21739 kb16_pre: Option<&CudaSlice<u8>>,
21740 qb16_pre: Option<&CudaSlice<u8>>,
21741 state_in: &CudaSlice<f32>,
21742 state_out: &mut CudaSlice<f32>,
21743 o: &mut CudaSlice<f32>,
21744 n_head: usize,
21745 t: usize,
21746 scale: f32,
21747 hk: usize,
21748 ) -> Result<(), Box<dyn std::error::Error>> {
21749 if std::env::var("MEMRA_GDN_DIFF").is_ok() && t >= 16 {
21750 assert!(hk == n_head, "GDN_DIFF oracle is broadcast-only");
21751 return self.gdn_scan_diff(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale);
21752 }
21753 if Self::gdn_chunked_enabled() && t >= 16 {
21754 self.gdn_scan_chunked(
21755 q,
21756 k,
21757 v,
21758 g,
21759 beta,
21760 kb16_pre,
21761 qb16_pre,
21762 state_in,
21763 state_out,
21764 o,
21765 n_head,
21766 t,
21767 scale,
21768 Self::gdn_chunk_size(),
21769 hk,
21770 )
21771 } else {
21772 assert!(
21773 hk == n_head,
21774 "s128 scan is broadcast-only (prep guarantees by predicate)"
21775 );
21776 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)
21777 }
21778 }
21779
21780 #[allow(clippy::too_many_arguments)]
21782 fn gdn_scan_diff(
21783 &self,
21784 q: &CudaSlice<f32>,
21785 k: &CudaSlice<f32>,
21786 v: &CudaSlice<f32>,
21787 g: &CudaSlice<f32>,
21788 beta: &CudaSlice<f32>,
21789 state_in: &CudaSlice<f32>,
21790 state_out: &mut CudaSlice<f32>,
21791 o: &mut CudaSlice<f32>,
21792 n_head: usize,
21793 t: usize,
21794 scale: f32,
21795 ) -> Result<(), Box<dyn std::error::Error>> {
21796 static CALL: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
21797 let call = CALL.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
21798 let mut o_c = self.uninit(o.len())?;
21799 let mut st_c = self.uninit(state_out.len())?;
21800 self.gdn_scan_chunked(
21801 q,
21802 k,
21803 v,
21804 g,
21805 beta,
21806 None,
21807 None,
21808 state_in,
21809 &mut st_c,
21810 &mut o_c,
21811 n_head,
21812 t,
21813 scale,
21814 Self::gdn_chunk_size(),
21815 n_head,
21816 )?;
21817 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)?;
21818 let (oh_s, oh_c) = (self.dtoh(o)?, self.dtoh(&o_c)?);
21819 let (sh_s, sh_c) = (self.dtoh(state_out)?, self.dtoh(&st_c)?);
21820 let stats = |a: &[f32], b: &[f32]| -> (f32, f32, f64) {
21821 let mut max_abs = 0f32;
21822 let mut max_rel = 0f32;
21823 let mut sum_rel = 0f64;
21824 for (x, y) in a.iter().zip(b) {
21825 let ad = (x - y).abs();
21826 let rel = ad / x.abs().max(y.abs()).max(1e-3);
21827 if ad > max_abs {
21828 max_abs = ad;
21829 }
21830 if rel > max_rel {
21831 max_rel = rel;
21832 }
21833 sum_rel += rel as f64;
21834 }
21835 (max_abs, max_rel, sum_rel / a.len() as f64)
21836 };
21837 let (o_ma, o_mr, o_mean) = stats(&oh_s, &oh_c);
21838 let (s_ma, s_mr, s_mean) = stats(&sh_s, &sh_c);
21839 println!(
21840 "[gdn-diff call {call:3} T={t} C={}] out: max_abs={o_ma:.3e} max_rel={o_mr:.3e} mean_rel={o_mean:.3e} | \
21841 state: max_abs={s_ma:.3e} max_rel={s_mr:.3e} mean_rel={s_mean:.3e}",
21842 Self::gdn_chunk_size()
21843 );
21844 Ok(())
21845 }
21846
21847 pub fn gdn_glog(
21849 &self,
21850 alpha: &CudaSlice<f32>,
21851 dt_bias: &CudaSlice<f32>,
21852 a: &CudaSlice<f32>,
21853 g_log: &mut CudaSlice<f32>,
21854 n_head: usize,
21855 t: usize,
21856 ) -> Result<(), Box<dyn std::error::Error>> {
21857 let f = self.func("gdn_glog_f32");
21858 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
21859 let (h, ti) = (n_head as i32, t as i32);
21860 let __s_b = self.gpu.stream();
21861 let mut b = __s_b.launch_builder(&f);
21862 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
21863 unsafe {
21864 b.launch(cfg)?;
21865 }
21866 Ok(())
21867 }
21868
21869 pub fn sigmoid_v(
21872 &self,
21873 x: &cudarc::driver::CudaView<f32>,
21874 y: &mut CudaSlice<f32>,
21875 n: usize,
21876 ) -> Result<(), Box<dyn std::error::Error>> {
21877 let f = self.func("sigmoid_f32");
21878 let cfg = LaunchConfig::for_num_elems(n as u32);
21879 let ni = n as i32;
21880 let __s_b = self.gpu.stream();
21881 let mut b = __s_b.launch_builder(&f);
21882 b.arg(x).arg(y).arg(&ni);
21883 unsafe {
21884 b.launch(cfg)?;
21885 }
21886 Ok(())
21887 }
21888
21889 pub fn gdn_glog_v(
21890 &self,
21891 alpha: &cudarc::driver::CudaView<f32>,
21892 dt_bias: &CudaSlice<f32>,
21893 a: &CudaSlice<f32>,
21894 g_log: &mut CudaSlice<f32>,
21895 n_head: usize,
21896 t: usize,
21897 ) -> Result<(), Box<dyn std::error::Error>> {
21898 let f = self.func("gdn_glog_f32");
21899 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
21900 let (h, ti) = (n_head as i32, t as i32);
21901 let __s_b = self.gpu.stream();
21902 let mut b = __s_b.launch_builder(&f);
21903 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
21904 unsafe {
21905 b.launch(cfg)?;
21906 }
21907 Ok(())
21908 }
21909
21910 pub fn sigmoid(
21911 &self,
21912 x: &CudaSlice<f32>,
21913 y: &mut CudaSlice<f32>,
21914 n: usize,
21915 ) -> Result<(), Box<dyn std::error::Error>> {
21916 let f = self.func("sigmoid_f32");
21917 let cfg = LaunchConfig::for_num_elems(n as u32);
21918 let ni = n as i32;
21919 let __s_b = self.gpu.stream();
21920 let mut b = __s_b.launch_builder(&f);
21921 b.arg(x).arg(y).arg(&ni);
21922 unsafe {
21923 b.launch(cfg)?;
21924 }
21925 Ok(())
21926 }
21927
21928 pub fn sig_mul_f16out(
21931 &self,
21932 a: &CudaSlice<f32>,
21933 g: &CudaSlice<f32>,
21934 dst: &mut CudaSlice<f32>,
21935 dst16: &mut CudaSlice<u8>,
21936 n: usize,
21937 ) -> Result<(), Box<dyn std::error::Error>> {
21938 let f = self.func("sig_mul_f16out_f32");
21939 let cfg = LaunchConfig::for_num_elems(n as u32);
21940 let ni = n as i32;
21941 let __s_b = self.gpu.stream();
21942 let mut b = __s_b.launch_builder(&f);
21943 b.arg(a).arg(g).arg(dst).arg(dst16).arg(&ni);
21944 unsafe {
21945 b.launch(cfg)?;
21946 }
21947 Ok(())
21948 }
21949
21950 #[allow(clippy::too_many_arguments)]
21959 pub fn attn_head_gate(
21960 &self,
21961 a: &CudaSlice<f32>,
21962 g: &CudaSlice<f32>,
21963 dst: &mut CudaSlice<f32>,
21964 dst16: Option<&mut CudaSlice<u8>>,
21965 head_dim: usize,
21966 n_head: usize,
21967 t: usize,
21968 ) -> Result<(), Box<dyn std::error::Error>> {
21969 let f = self.func("attn_head_gate_f32");
21970 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
21971 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
21972 let d16: u64 = match dst16 {
21974 Some(d) => self.addr_u8(d),
21975 None => 0,
21976 };
21977 let __s_b = self.gpu.stream();
21978 let mut b = __s_b.launch_builder(&f);
21979 b.arg(a)
21980 .arg(g)
21981 .arg(dst)
21982 .arg(&d16)
21983 .arg(&hd)
21984 .arg(&nh)
21985 .arg(&ti);
21986 unsafe {
21987 b.launch(cfg)?;
21988 }
21989 Ok(())
21990 }
21991
21992 #[allow(clippy::too_many_arguments)]
22001 pub fn swiglu_clamped_mul_scaled(
22002 &self,
22003 gate: &CudaSlice<f32>,
22004 up: &CudaSlice<f32>,
22005 gs: f32,
22006 us: f32,
22007 limit: f32,
22008 dst: &mut CudaSlice<f32>,
22009 n: usize,
22010 ) -> Result<(), Box<dyn std::error::Error>> {
22011 debug_assert!(
22012 limit > 1e-6,
22013 "swiglu_clamped needs a live limit; use silu_mul_scaled"
22014 );
22015 let f = self.func("swiglu_clamped_mul_scaled_f32");
22016 let cfg = LaunchConfig::for_num_elems(n as u32);
22017 let ni = n as i32;
22018 let __s_b = self.gpu.stream();
22019 let mut b = __s_b.launch_builder(&f);
22020 b.arg(gate)
22021 .arg(up)
22022 .arg(&gs)
22023 .arg(&us)
22024 .arg(&limit)
22025 .arg(dst)
22026 .arg(&ni);
22027 unsafe {
22028 b.launch(cfg)?;
22029 }
22030 Ok(())
22031 }
22032
22033 pub fn gated_rmsnorm(
22035 &self,
22036 o: &CudaSlice<f32>,
22037 w: &CudaSlice<f32>,
22038 z: &CudaSlice<f32>,
22039 dst: &mut CudaSlice<f32>,
22040 ncols: usize,
22041 nrows: usize,
22042 eps: f32,
22043 ) -> Result<(), Box<dyn std::error::Error>> {
22044 let f = self.func("gated_rmsnorm_f32");
22045 let cfg = LaunchConfig {
22046 grid_dim: (nrows as u32, 1, 1),
22047 block_dim: (128, 1, 1),
22048 shared_mem_bytes: 0,
22049 };
22050 let (nc, e) = (ncols as i32, eps);
22051 let __s_b = self.gpu.stream();
22052 let mut b = __s_b.launch_builder(&f);
22053 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
22054 unsafe {
22055 b.launch(cfg)?;
22056 }
22057 Ok(())
22058 }
22059
22060 pub fn gated_rmsnorm_f16out(
22063 &self,
22064 o: &CudaSlice<f32>,
22065 w: &CudaSlice<f32>,
22066 z: &CudaSlice<f32>,
22067 dst: &mut CudaSlice<f32>,
22068 dst16: &mut CudaSlice<u8>,
22069 ncols: usize,
22070 nrows: usize,
22071 eps: f32,
22072 ) -> Result<(), Box<dyn std::error::Error>> {
22073 let f = self.func("gated_rmsnorm_f16out_f32");
22074 let cfg = LaunchConfig {
22076 grid_dim: (nrows as u32, 1, 1),
22077 block_dim: (128, 1, 1),
22078 shared_mem_bytes: 0,
22079 };
22080 let (nc, e) = (ncols as i32, eps);
22081 let __s_b = self.gpu.stream();
22082 let mut b = __s_b.launch_builder(&f);
22083 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
22084 unsafe {
22085 b.launch(cfg)?;
22086 }
22087 Ok(())
22088 }
22089
22090 #[allow(clippy::too_many_arguments)]
22094 pub fn add_rms_norm_zq8(
22095 &self,
22096 a: &CudaSlice<f32>,
22097 b_in: &CudaSlice<f32>,
22098 w: &CudaSlice<f32>,
22099 res: &mut CudaSlice<f32>,
22100 z: &mut CudaSlice<f32>,
22101 ncols: usize,
22102 nrows: usize,
22103 eps: f32,
22104 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
22105 assert!(ncols % 32 == 0);
22106 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
22107 let mut d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
22108 let f = self.func("add_rms_norm_zq8");
22109 let cfg = LaunchConfig {
22110 grid_dim: (nrows as u32, 1, 1),
22111 block_dim: (1024, 1, 1),
22112 shared_mem_bytes: 0,
22113 };
22114 let (nc, ep) = (ncols as i32, eps);
22115 let __s_b = self.gpu.stream();
22116 let mut b = __s_b.launch_builder(&f);
22117 b.arg(a)
22118 .arg(b_in)
22119 .arg(w)
22120 .arg(res)
22121 .arg(z)
22122 .arg(&mut q)
22123 .arg(&mut d)
22124 .arg(&nc)
22125 .arg(&ep);
22126 unsafe {
22127 b.launch(cfg)?;
22128 }
22129 Ok((q, d))
22130 }
22131
22132 pub fn gated_rmsnorm_zv(
22137 &self,
22138 o: &CudaSlice<f32>,
22139 w: &CudaSlice<f32>,
22140 z: &cudarc::driver::CudaView<f32>,
22141 dst: &mut CudaSlice<f32>,
22142 ncols: usize,
22143 nrows: usize,
22144 eps: f32,
22145 ) -> Result<(), Box<dyn std::error::Error>> {
22146 let f = self.func("gated_rmsnorm_f32");
22147 let cfg = LaunchConfig {
22148 grid_dim: (nrows as u32, 1, 1),
22149 block_dim: (128, 1, 1),
22150 shared_mem_bytes: 0,
22151 };
22152 let (nc, e) = (ncols as i32, eps);
22153 let __s_b = self.gpu.stream();
22154 let mut b = __s_b.launch_builder(&f);
22155 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
22156 unsafe {
22157 b.launch(cfg)?;
22158 }
22159 Ok(())
22160 }
22161
22162 pub fn gated_rmsnorm_f16out_zv(
22163 &self,
22164 o: &CudaSlice<f32>,
22165 w: &CudaSlice<f32>,
22166 z: &cudarc::driver::CudaView<f32>,
22167 dst: &mut CudaSlice<f32>,
22168 dst16: &mut CudaSlice<u8>,
22169 ncols: usize,
22170 nrows: usize,
22171 eps: f32,
22172 ) -> Result<(), Box<dyn std::error::Error>> {
22173 let f = self.func("gated_rmsnorm_f16out_f32");
22174 let cfg = LaunchConfig {
22176 grid_dim: (nrows as u32, 1, 1),
22177 block_dim: (128, 1, 1),
22178 shared_mem_bytes: 0,
22179 };
22180 let (nc, e) = (ncols as i32, eps);
22181 let __s_b = self.gpu.stream();
22182 let mut b = __s_b.launch_builder(&f);
22183 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
22184 unsafe {
22185 b.launch(cfg)?;
22186 }
22187 Ok(())
22188 }
22189
22190 pub fn gated_rmsnorm_q8_1(
22191 &self,
22192 o: &CudaSlice<f32>,
22193 w: &CudaSlice<f32>,
22194 z: &CudaSlice<f32>,
22195 ncols: usize,
22196 nrows: usize,
22197 eps: f32,
22198 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
22199 assert!(ncols % 32 == 0);
22200 let f = self.func("gated_rmsnorm_q8_1");
22201 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
22202 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
22203 let cfg = LaunchConfig {
22204 grid_dim: (nrows as u32, 1, 1),
22205 block_dim: (128, 1, 1),
22206 shared_mem_bytes: 0,
22207 };
22208 let (nc, ep) = (ncols as i32, eps);
22209 let __s_b = self.gpu.stream();
22210 let mut b = __s_b.launch_builder(&f);
22211 b.arg(o)
22212 .arg(w)
22213 .arg(z)
22214 .arg(&mut out_q)
22215 .arg(&mut out_d)
22216 .arg(&nc)
22217 .arg(&ep);
22218 unsafe {
22219 b.launch(cfg)?;
22220 }
22221 Ok((out_q, out_d))
22222 }
22223
22224 pub fn transpose(
22226 &self,
22227 inp: &CudaSlice<f32>,
22228 rows: usize,
22229 cols: usize,
22230 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22231 let f = self.func("transpose_f32");
22232 let mut out = self.zeros(rows * cols)?;
22233 let cfg = LaunchConfig::for_num_elems((rows * cols) as u32);
22234 let (r, c) = (rows as i32, cols as i32);
22235 let __s_b = self.gpu.stream();
22236 let mut b = __s_b.launch_builder(&f);
22237 b.arg(inp).arg(&mut out).arg(&r).arg(&c);
22238 unsafe {
22239 b.launch(cfg)?;
22240 }
22241 Ok(out)
22242 }
22243
22244 pub fn repeat_heads(
22246 &self,
22247 inp: &CudaSlice<f32>,
22248 out: &mut CudaSlice<f32>,
22249 head_dim: usize,
22250 n_in: usize,
22251 n_out: usize,
22252 t: usize,
22253 ) -> Result<(), Box<dyn std::error::Error>> {
22254 let f = self.func("repeat_heads_f32");
22255 let cfg = LaunchConfig::for_num_elems((head_dim * n_out * t) as u32);
22256 let (hd, ni, no, ti) = (head_dim as i32, n_in as i32, n_out as i32, t as i32);
22257 let __s_b = self.gpu.stream();
22258 let mut b = __s_b.launch_builder(&f);
22259 b.arg(inp).arg(out).arg(&hd).arg(&ni).arg(&no).arg(&ti);
22260 unsafe {
22261 b.launch(cfg)?;
22262 }
22263 Ok(())
22264 }
22265
22266 pub fn q_gate_split(
22273 &self,
22274 qf: &CudaSlice<f32>,
22275 q_out: &mut CudaSlice<f32>,
22276 gate_out: &mut CudaSlice<f32>,
22277 head_dim: usize,
22278 n_head: usize,
22279 t: usize,
22280 ) -> Result<(), Box<dyn std::error::Error>> {
22281 memra_gguf::config::check_fused_q_gate_extent(qf.len(), head_dim, n_head, t)?;
22282 let out_need = head_dim * n_head * t;
22283 if q_out.len() < out_need || gate_out.len() < out_need {
22284 return Err(format!(
22285 "q_gate_split destinations too small: need {out_need} each, have q={} gate={}",
22286 q_out.len(),
22287 gate_out.len()
22288 )
22289 .into());
22290 }
22291 let f = self.func("q_gate_split_f32");
22292 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
22293 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
22294 let __s_b = self.gpu.stream();
22295 let mut b = __s_b.launch_builder(&f);
22296 b.arg(qf)
22297 .arg(q_out)
22298 .arg(gate_out)
22299 .arg(&hd)
22300 .arg(&nh)
22301 .arg(&ti);
22302 unsafe {
22303 b.launch(cfg)?;
22304 }
22305 Ok(())
22306 }
22307
22308 pub fn qkv_to_gdn_repack(
22312 &self,
22313 conv_out: &CudaSlice<f32>,
22314 q_g: &mut CudaSlice<f32>,
22315 k_g: &mut CudaSlice<f32>,
22316 v_g: &mut CudaSlice<f32>,
22317 d_state: usize,
22318 num_v: usize,
22319 num_k: usize,
22320 key_dim: usize,
22321 t: usize,
22322 ) -> Result<(), Box<dyn std::error::Error>> {
22323 let f = self.func("qkv_to_gdn_repack_f32");
22324 let cfg = LaunchConfig::for_num_elems((d_state * num_v * t) as u32);
22325 let (ds, nv, nk, kd, ti) = (
22326 d_state as i32,
22327 num_v as i32,
22328 num_k as i32,
22329 key_dim as i32,
22330 t as i32,
22331 );
22332 let __s_b = self.gpu.stream();
22333 let mut b = __s_b.launch_builder(&f);
22334 b.arg(conv_out)
22335 .arg(q_g)
22336 .arg(k_g)
22337 .arg(v_g)
22338 .arg(&ds)
22339 .arg(&nv)
22340 .arg(&nk)
22341 .arg(&kd)
22342 .arg(&ti);
22343 unsafe {
22344 b.launch(cfg)?;
22345 }
22346 Ok(())
22347 }
22348
22349 pub fn conv_left_pad(
22352 &self,
22353 src: &CudaSlice<f32>,
22354 dst: &mut CudaSlice<f32>,
22355 conv_dim: usize,
22356 t: usize,
22357 pad: usize,
22358 ) -> Result<(), Box<dyn std::error::Error>> {
22359 let f = self.func("conv_left_pad_f32");
22360 let cfg = LaunchConfig::for_num_elems((conv_dim * t) as u32);
22361 let (cd, ti, p) = (conv_dim as i32, t as i32, pad as i32);
22362 let __s_b = self.gpu.stream();
22363 let mut b = __s_b.launch_builder(&f);
22364 b.arg(src).arg(dst).arg(&cd).arg(&ti).arg(&p);
22365 unsafe {
22366 b.launch(cfg)?;
22367 }
22368 Ok(())
22369 }
22370
22371 pub fn conv_assemble_and_roll(
22375 &self,
22376 qkv_col: &CudaSlice<f32>,
22377 conv_state: &mut CudaSlice<f32>,
22378 conv_in: &mut CudaSlice<f32>,
22379 conv_dim: usize,
22380 pad: usize,
22381 ) -> Result<(), Box<dyn std::error::Error>> {
22382 let f = self.func("conv_assemble_and_roll_f32");
22383 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
22384 let (cd, p) = (conv_dim as i32, pad as i32);
22385 let __s_b = self.gpu.stream();
22386 let mut b = __s_b.launch_builder(&f);
22387 b.arg(qkv_col).arg(conv_state).arg(conv_in).arg(&cd).arg(&p);
22388 unsafe {
22389 b.launch(cfg)?;
22390 }
22391 Ok(())
22392 }
22393
22394 pub fn ssm_conv1d_fused_decode(
22400 &self,
22401 qkv_col: &CudaSlice<f32>,
22402 conv_state: &mut CudaSlice<f32>,
22403 w: &CudaSlice<f32>,
22404 conv_out: &mut CudaSlice<f32>,
22405 conv_dim: usize,
22406 d_conv: usize,
22407 ) -> Result<(), Box<dyn std::error::Error>> {
22408 let f = self.func("ssm_conv1d_fused_decode_f32");
22409 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
22410 let (cd, dc) = (conv_dim as i32, d_conv as i32);
22411 let __s_b = self.gpu.stream();
22412 let mut b = __s_b.launch_builder(&f);
22413 b.arg(qkv_col)
22414 .arg(conv_state)
22415 .arg(w)
22416 .arg(conv_out)
22417 .arg(&cd)
22418 .arg(&dc);
22419 unsafe {
22420 b.launch(cfg)?;
22421 }
22422 Ok(())
22423 }
22424
22425 pub fn slice_range(
22428 &self,
22429 src: &CudaSlice<f32>,
22430 start: usize,
22431 len: usize,
22432 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22433 let host = self.gpu.stream().clone_dtoh(src)?;
22434 self.gpu.stream().synchronize()?;
22435 Ok(self.htod(&host[start..start + len])?)
22436 }
22437}
22438
22439#[cfg(test)]
22440mod target_dispatch_tests {
22441 use super::legacy_quant_gemm_allowed;
22442
22443 #[test]
22444 fn legacy_quant_gemm_arch_policy_honors_the_escape_hatch() {
22445 assert!(legacy_quant_gemm_allowed(false, false, false));
22447 assert!(!legacy_quant_gemm_allowed(false, false, true));
22448 assert!(!legacy_quant_gemm_allowed(true, false, false));
22450 assert!(!legacy_quant_gemm_allowed(true, false, true));
22451 assert!(legacy_quant_gemm_allowed(true, true, false));
22453 assert!(!legacy_quant_gemm_allowed(true, true, true));
22454 }
22455
22456 #[cfg(all(memra_portable_cuda, not(memra_hopper_mma)))]
22457 #[test]
22458 fn portable_build_disables_legacy_quant_gemm_without_an_env_override() {
22459 assert!(!legacy_quant_gemm_allowed(
22460 cfg!(memra_portable_cuda),
22461 cfg!(memra_hopper_mma),
22462 false
22463 ));
22464 }
22465
22466 #[cfg(memra_hopper_mma)]
22467 #[test]
22468 fn hopper_mma_build_re_admits_legacy_quant_gemm() {
22469 assert!(legacy_quant_gemm_allowed(
22470 cfg!(memra_portable_cuda),
22471 cfg!(memra_hopper_mma),
22472 false
22473 ));
22474 assert!(super::portable_mma_gated() == false);
22475 }
22476}
22477
22478impl memra_kv::KvDev for Engine {
22481 fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22482 Engine::zeros(self, n)
22483 }
22484 fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22485 Engine::uninit(self, n)
22486 }
22487 fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
22488 Engine::alloc_u8(self, n)
22489 }
22490 fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
22491 Engine::htod_i32(self, v)
22492 }
22493 fn clone_dtod(
22494 &self,
22495 src: &CudaSlice<f32>,
22496 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
22497 Engine::clone_dtod(self, src)
22498 }
22499 fn copy_into(
22500 &self,
22501 dst: &mut CudaSlice<f32>,
22502 off: usize,
22503 src: &CudaSlice<f32>,
22504 len: usize,
22505 ) -> Result<(), Box<dyn std::error::Error>> {
22506 Engine::copy_into(self, dst, off, src, len)
22507 }
22508 fn set_i32_one(
22509 &self,
22510 d: &mut CudaSlice<i32>,
22511 v: i32,
22512 ) -> Result<(), Box<dyn std::error::Error>> {
22513 Engine::set_i32_one(self, d, v)
22514 }
22515}
22516
22517#[cfg(test)]
22518mod fused_gate_bounds_tests {
22519 use super::*;
22520
22521 #[test]
22534 #[ignore = "requires a CUDA GPU"]
22535 fn q_gate_split_refuses_a_separate_gate_wq_instead_of_reading_past_it() {
22536 let e = Engine::new(0).unwrap();
22537 let (head_dim, n_head, t) = (8usize, 4usize, 2usize);
22538 let fused = 2 * head_dim * n_head * t;
22539 let out_n = head_dim * n_head * t;
22540
22541 let narrow = e.htod(&vec![1.0f32; out_n]).unwrap();
22543 let mut q = e.uninit(out_n).unwrap();
22544 let mut gate = e.uninit(out_n).unwrap();
22545 let err = e
22546 .q_gate_split(&narrow, &mut q, &mut gate, head_dim, n_head, t)
22547 .expect_err("half-width wq must be refused, not read past")
22548 .to_string();
22549 assert!(err.contains("NO fused gate"), "{err}");
22550 assert!(err.contains(&format!("{fused}")), "{err}");
22551
22552 let host: Vec<f32> = (0..fused).map(|i| i as f32).collect();
22555 let wide = e.htod(&host).unwrap();
22556 e.q_gate_split(&wide, &mut q, &mut gate, head_dim, n_head, t)
22557 .expect("full-width wq splits");
22558 let (qh, gh) = (e.dtoh(&q).unwrap(), e.dtoh(&gate).unwrap());
22559 for tok in 0..t {
22560 for hh in 0..n_head {
22561 for d in 0..head_dim {
22562 let base = tok * (n_head * 2 * head_dim) + hh * (2 * head_dim);
22563 let idx = tok * (n_head * head_dim) + hh * head_dim + d;
22564 assert_eq!(qh[idx], host[base + d], "q t{tok} h{hh} d{d}");
22565 assert_eq!(gh[idx], host[base + head_dim + d], "gate t{tok} h{hh} d{d}");
22566 }
22567 }
22568 }
22569
22570 let mut small = e.uninit(out_n - 1).unwrap();
22572 assert!(
22573 e.q_gate_split(&wide, &mut small, &mut gate, head_dim, n_head, t)
22574 .is_err()
22575 );
22576 }
22577}
22578
22579#[cfg(test)]
22583mod fused_rope_width_tests {
22584 use super::Engine;
22585
22586 #[test]
22589 fn full_width_is_accepted() {
22590 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope", 256, 256).is_ok());
22591 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_cat", 512, 512).is_ok());
22592 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_append", 128, 128).is_ok());
22593 }
22594
22595 #[test]
22608 fn gemma4_official_artifact_widths_pass() {
22609 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope", 512, 512).is_ok());
22610 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_append_dc", 256, 256).is_ok());
22611 }
22612
22613 #[test]
22616 fn partial_rotary_is_refused_with_the_geometry_named() {
22617 let err = Engine::full_width_rope_only("rms_norm_qkv_rope", 64, 256)
22619 .expect_err("partial rotary must refuse");
22620 let msg = err.to_string();
22621 assert!(msg.contains("PARTIAL ROTARY REFUSED"), "{msg}");
22622 assert!(msg.contains("n_rot 64"), "{msg}");
22623 assert!(msg.contains("head_dim 256"), "{msg}");
22624 assert!(
22625 msg.contains("64..256"),
22626 "names the band it would corrupt: {msg}"
22627 );
22628 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_append_dc", 64, 128).is_err());
22630 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope", 256, 128).is_err());
22632 }
22633}